Summary
discoverManifests() in commands-extensions.ts skips symlinked directories when scanning ~/.gsd/agent/extensions/, causing extensions installed as directory symlinks to be invisible to /gsd extensions list, enable, and info.
Root cause
// commands-extensions.ts ~line 110
for (const entry of readdirSync(extDir, { withFileTypes: true })) {
if (!entry.isDirectory()) continue; // symlinks return false here
Dirent.isDirectory() returns false for symbolic links even when they point to directories. The extension loader in loader.ts correctly handles this case with entry.isDirectory() || entry.isSymbolicLink(), but discoverManifests() does not.
Impact
- Extension installed as
ln -s /path/to/ext ~/.gsd/agent/extensions/my-ext → not listed, cannot be enabled/disabled/inspected
- The extension does load (loader handles symlinks), but all management commands fail with "Extension not found"
- Common development workflow: symlinking a local extension directory for live reload
Fix
for (const entry of readdirSync(extDir, { withFileTypes: true })) {
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
const m = readManifest(join(extDir, entry.name));
Same pattern already used in discoverExtensionsInDir in loader.ts.
Workaround
Use a real directory with symlinked contents instead of a directory symlink:
mkdir -p ~/.gsd/agent/extensions/my-ext
ln -s /path/to/my-ext/extension-manifest.json ~/.gsd/agent/extensions/my-ext/extension-manifest.json
ln -s /path/to/my-ext/package.json ~/.gsd/agent/extensions/my-ext/package.json
ln -s /path/to/my-ext/src ~/.gsd/agent/extensions/my-ext/src
Summary
discoverManifests()incommands-extensions.tsskips symlinked directories when scanning~/.gsd/agent/extensions/, causing extensions installed as directory symlinks to be invisible to/gsd extensions list,enable, andinfo.Root cause
Dirent.isDirectory()returnsfalsefor symbolic links even when they point to directories. The extension loader inloader.tscorrectly handles this case withentry.isDirectory() || entry.isSymbolicLink(), butdiscoverManifests()does not.Impact
ln -s /path/to/ext ~/.gsd/agent/extensions/my-ext→ not listed, cannot be enabled/disabled/inspectedFix
Same pattern already used in
discoverExtensionsInDirinloader.ts.Workaround
Use a real directory with symlinked contents instead of a directory symlink: