Skip to content

feat: extension symlink discovery follows directory symlinks but manifest scanner does not #20

@igouss

Description

@igouss

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions