@kolisachint/hoocode-agent 0.5.12 → 0.5.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/dist/core/extensions/loader.d.ts +18 -6
  3. package/dist/core/extensions/loader.d.ts.map +1 -1
  4. package/dist/core/extensions/loader.js +35 -19
  5. package/dist/core/extensions/loader.js.map +1 -1
  6. package/dist/core/extensions/plugins/install.d.ts +51 -10
  7. package/dist/core/extensions/plugins/install.d.ts.map +1 -1
  8. package/dist/core/extensions/plugins/install.js +178 -31
  9. package/dist/core/extensions/plugins/install.js.map +1 -1
  10. package/dist/core/extensions/plugins/listing.d.ts +8 -2
  11. package/dist/core/extensions/plugins/listing.d.ts.map +1 -1
  12. package/dist/core/extensions/plugins/listing.js +29 -5
  13. package/dist/core/extensions/plugins/listing.js.map +1 -1
  14. package/dist/core/extensions/plugins/locations.d.ts +62 -12
  15. package/dist/core/extensions/plugins/locations.d.ts.map +1 -1
  16. package/dist/core/extensions/plugins/locations.js +74 -18
  17. package/dist/core/extensions/plugins/locations.js.map +1 -1
  18. package/dist/core/extensions/plugins/marketplace.d.ts +29 -3
  19. package/dist/core/extensions/plugins/marketplace.d.ts.map +1 -1
  20. package/dist/core/extensions/plugins/marketplace.js +40 -7
  21. package/dist/core/extensions/plugins/marketplace.js.map +1 -1
  22. package/dist/core/extensions/plugins/trust.d.ts +56 -0
  23. package/dist/core/extensions/plugins/trust.d.ts.map +1 -0
  24. package/dist/core/extensions/plugins/trust.js +88 -0
  25. package/dist/core/extensions/plugins/trust.js.map +1 -0
  26. package/dist/core/settings-defaults.d.ts +1 -0
  27. package/dist/core/settings-defaults.d.ts.map +1 -1
  28. package/dist/core/settings-defaults.js +1 -0
  29. package/dist/core/settings-defaults.js.map +1 -1
  30. package/dist/core/settings-manager.d.ts +7 -0
  31. package/dist/core/settings-manager.d.ts.map +1 -1
  32. package/dist/core/settings-manager.js +14 -0
  33. package/dist/core/settings-manager.js.map +1 -1
  34. package/dist/core/settings-types.d.ts +1 -0
  35. package/dist/core/settings-types.d.ts.map +1 -1
  36. package/dist/core/settings-types.js.map +1 -1
  37. package/dist/core/tools/plugins.d.ts.map +1 -1
  38. package/dist/core/tools/plugins.js +18 -3
  39. package/dist/core/tools/plugins.js.map +1 -1
  40. package/dist/extensions/core/marketplace.d.ts +7 -3
  41. package/dist/extensions/core/marketplace.d.ts.map +1 -1
  42. package/dist/extensions/core/marketplace.js +103 -11
  43. package/dist/extensions/core/marketplace.js.map +1 -1
  44. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  45. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  46. package/dist/modes/interactive/components/settings-selector.js +15 -2
  47. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  48. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  49. package/dist/modes/interactive/interactive-mode.js +6 -0
  50. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  51. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  52. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  53. package/examples/extensions/sandbox/package.json +1 -1
  54. package/examples/extensions/with-deps/package.json +1 -1
  55. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"marketplace.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/marketplace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D;;;;;;;;GAQG;AACH,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAO9D,uFAAuF;AACvF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,EAAE,CASxE;AAED,8DAA8D;AAC9D,UAAU,0BAA0B;IACnC,MAAM,EAAE,KAAK,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,4EAA4E;AAC5E,UAAU,gCAAgC;IACzC,MAAM,EAAE,YAAY,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,uDAAuD;AACvD,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,0BAA0B,GAAG,gCAAgC,CAAC;AAE7G,UAAU,sBAAsB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,MAAM,EAAE,uBAAuB,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACxC;;;;;;OAMG;IACH,eAAe,EAAE,mBAAmB,EAAE,CAAC;IACvC,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,sBAAsB,EAAE,CAAC;CAClC;AAiED,6CAA6C;AAC7C,MAAM,MAAM,oBAAoB,GAC7B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAUjC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAwD7E;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,GAAG,oBAAoB,CAYlH;AAMD,MAAM,WAAW,iBAAiB;IACjC,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,GAAG,EAAE,MAAM,CAAC;CACZ;AAMD,8DAA8D;AAC9D,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAG3E;AAED,mDAAmD;AACnD,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAG3F","sourcesContent":["/**\n * Plugin marketplaces — install plugins from a curated index hosted in a git repo\n * (or a local directory).\n *\n * A marketplace is a directory containing an index manifest in one of three formats:\n * - Native: `.agents-plugin/marketplace.json` (preferred; the `.agents` surface)\n * - Claude: `.claude-plugin/marketplace.json`\n * - Copilot: `.github/marketplace.json` (Copilot-style git index)\n *\n * All three use the same shape: `{ name?, owner?, plugins: [{ name, source, description? }] }`.\n * A plugin `source` is either a path relative to the marketplace root (a plugin\n * directory inside the repo), a git URL, or an `npm:<spec>` reference.\n *\n * When more than one is present the native `.agents-plugin` format wins, then\n * Claude, then Copilot (no merge) — matching the \"`.agents/` first\" policy used\n * across hoocode's resource surfaces.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { PLUGIN_FORMATS } from \"./formats/index.js\";\nimport { normalizePlatformToken } from \"./formats/platform-targets.js\";\nimport type { MarketplacePlatform } from \"./formats/types.js\";\n\n/**\n * Canonical platform token used by the optional `supportPlatform` field. The\n * `.github/marketplace.json` (Copilot-style) format is surfaced as `\"github\"` —\n * friendlier than the internal `format: \"copilot\"` and matching where the file\n * lives. Authored aliases (`copilot`, `gh`) normalize to `github`.\n *\n * Re-exported from the format registry so the platform vocabulary has a single\n * source of truth.\n */\nexport type { MarketplacePlatform } from \"./formats/types.js\";\n\n/** Map an internal marketplace `format` to its public platform token. */\nfunction formatToPlatform(format: NormalizedMarketplace[\"format\"]): MarketplacePlatform {\n\treturn format === \"copilot\" ? \"github\" : format;\n}\n\n/** Normalize an authored `supportPlatform` (string | string[]) to canonical tokens. */\nexport function normalizePlatforms(value: unknown): MarketplacePlatform[] {\n\tconst raw = Array.isArray(value) ? value : value == null ? [] : [value];\n\tconst out: MarketplacePlatform[] = [];\n\tfor (const v of raw) {\n\t\tif (typeof v !== \"string\") continue;\n\t\tconst canonical = normalizePlatformToken(v);\n\t\tif (canonical && !out.includes(canonical)) out.push(canonical);\n\t}\n\treturn out;\n}\n\n/** Structured source pointing at an entire git repository. */\ninterface MarketplacePluginSourceUrl {\n\tsource: \"url\";\n\turl: string;\n\tref?: string;\n\tsha?: string;\n}\n\n/** Structured source pointing at a subdirectory within a git repository. */\ninterface MarketplacePluginSourceGitSubdir {\n\tsource: \"git-subdir\";\n\turl: string;\n\t/** Subdirectory path inside the repository. */\n\tpath: string;\n\tref?: string;\n\tsha?: string;\n}\n\n/** Source value authored in a marketplace manifest. */\nexport type MarketplacePluginSource = string | MarketplacePluginSourceUrl | MarketplacePluginSourceGitSubdir;\n\ninterface MarketplacePluginEntry {\n\tname: string;\n\t/** Relative path, git URL, `npm:<spec>`, or structured source object. */\n\tsource: MarketplacePluginSource;\n\tdescription?: string;\n\t/**\n\t * Optional platform(s) this entry targets. Only set when authored on the\n\t * entry; absent means \"no per-entry restriction\" (the marketplace's\n\t * platforms apply). Purely informational today — nothing filters on it.\n\t */\n\tsupportPlatform?: MarketplacePlatform[];\n}\n\nexport interface NormalizedMarketplace {\n\tname: string;\n\t/** Precedence winner when several index files conflict (agents > claude > copilot). */\n\tformat: \"agents\" | \"claude\" | \"copilot\";\n\t/**\n\t * Every platform this marketplace directory offers, so a conflict between\n\t * multiple index files (e.g. both `.github/` and `.claude-plugin/`) is\n\t * recorded rather than hidden. Always includes the resolved `format`'s\n\t * platform; also folds in any authored top-level `supportPlatform`. Never\n\t * empty.\n\t */\n\tsupportPlatform: MarketplacePlatform[];\n\t/** Absolute path to the marketplace directory. */\n\troot: string;\n\tmanifestPath: string;\n\tplugins: MarketplacePluginEntry[];\n}\n\ninterface RawMarketplace {\n\tname?: string;\n\t/** Both Claude Code and Copilot CLI define `owner` as `{ name, email? }`; a bare string is tolerated. */\n\towner?: string | { name?: string; email?: string };\n\t/** Shared optional metadata: `pluginRoot` is a base dir prepended to relative plugin source paths. */\n\tmetadata?: { description?: string; version?: string; pluginRoot?: string };\n\t/** Optional authored platform hint (string | string[]); folded into supportPlatform. */\n\tsupportPlatform?: unknown;\n\tplugins?: Array<{ name?: string; source?: unknown; description?: string; supportPlatform?: unknown }>;\n}\n\n/** True when a string source is a path (not npm:/git/URL) that `pluginRoot` should prefix. */\nfunction isRelativePathSource(source: string): boolean {\n\tconst s = source.trim();\n\treturn (\n\t\t!s.startsWith(\"npm:\") &&\n\t\t!/^https?:\\/\\//.test(s) &&\n\t\t!s.startsWith(\"git@\") &&\n\t\t!s.endsWith(\".git\") &&\n\t\t!path.isAbsolute(s)\n\t);\n}\n\n/**\n * Apply `metadata.pluginRoot` (Claude Code and Copilot CLI share the field):\n * a base directory prepended to relative plugin source paths, so\n * `\"source\": \"formatter\"` with `\"pluginRoot\": \"./plugins\"` resolves like\n * `\"./plugins/formatter\"`.\n */\nfunction applyPluginRoot(source: MarketplacePluginSource, pluginRoot: string | undefined): MarketplacePluginSource {\n\tif (!pluginRoot || typeof source !== \"string\" || !isRelativePathSource(source)) return source;\n\treturn `${pluginRoot.replace(/\\/+$/, \"\")}/${source.trim().replace(/^\\.\\//, \"\")}`;\n}\n\n/** Validate and normalize an authored source value. Returns null for unsupported/invalid shapes. */\nfunction normalizeSource(source: unknown): MarketplacePluginSource | null {\n\tif (typeof source === \"string\") return source;\n\tif (!source || typeof source !== \"object\") return null;\n\tconst obj = source as Record<string, unknown>;\n\tconst src = obj.source;\n\tconst url = obj.url;\n\tconst pin = {\n\t\t...(typeof obj.ref === \"string\" ? { ref: obj.ref } : {}),\n\t\t...(typeof obj.sha === \"string\" ? { sha: obj.sha } : {}),\n\t};\n\tif (src === \"url\" && typeof url === \"string\") {\n\t\treturn { source: \"url\", url, ...pin };\n\t}\n\tif (src === \"git-subdir\" && typeof url === \"string\" && typeof obj.path === \"string\") {\n\t\treturn { source: \"git-subdir\", url, path: obj.path, ...pin };\n\t}\n\t// Copilot marketplace shorthand: { source: \"github\", repo: \"owner/name\", path? }\n\t// (used by github/copilot-plugins). Normalize to the equivalent git source.\n\tif (src === \"github\" && typeof obj.repo === \"string\" && /^[\\w.-]+\\/[\\w.-]+$/.test(obj.repo)) {\n\t\tconst repoUrl = `https://github.com/${obj.repo}.git`;\n\t\tif (typeof obj.path === \"string\" && obj.path.length > 0) {\n\t\t\treturn { source: \"git-subdir\", url: repoUrl, path: obj.path, ...pin };\n\t\t}\n\t\treturn { source: \"url\", url: repoUrl, ...pin };\n\t}\n\treturn null;\n}\n\n/** A resolved, installable plugin source. */\nexport type ResolvedPluginSource =\n\t| { kind: \"local\"; path: string }\n\t| { kind: \"git\"; url: string; ref?: string; sha?: string }\n\t| { kind: \"git-subdir\"; url: string; path: string; ref?: string; sha?: string }\n\t| { kind: \"npm\"; spec: string };\n\nfunction readJson<T>(file: string): T | null {\n\ttry {\n\t\treturn JSON.parse(fs.readFileSync(file, \"utf8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Parse a marketplace directory. Native `.agents-plugin` wins, then Claude, then\n * Copilot when more than one file is present. Returns null if no manifest exists\n * or it is invalid.\n */\nexport function parseMarketplaceDir(dir: string): NormalizedMarketplace | null {\n\t// Record every index format present so a conflict (more than one file) is\n\t// visible via supportPlatform, not silently dropped. PLUGIN_FORMATS is\n\t// precedence-ordered (native > Claude > Copilot), so `present` inherits it.\n\tconst present: Array<{ format: NormalizedMarketplace[\"format\"]; path: string }> = [];\n\tfor (const fmt of PLUGIN_FORMATS) {\n\t\tfor (const candidate of fmt.marketplaceFiles) {\n\t\t\tconst p = path.join(dir, candidate);\n\t\t\tif (fs.existsSync(p)) {\n\t\t\t\tpresent.push({ format: fmt.id, path: p });\n\t\t\t\tbreak; // first candidate wins within a format\n\t\t\t}\n\t\t}\n\t}\n\tif (present.length === 0) return null;\n\n\t// Precedence winner (present is built in agents > claude > copilot order).\n\tconst { format, path: manifestPath } = present[0];\n\n\tconst raw = readJson<RawMarketplace>(manifestPath);\n\tif (!raw) return null;\n\n\tconst pluginRoot =\n\t\ttypeof raw.metadata?.pluginRoot === \"string\" && raw.metadata.pluginRoot.trim()\n\t\t\t? raw.metadata.pluginRoot.trim()\n\t\t\t: undefined;\n\n\tconst plugins: MarketplacePluginEntry[] = [];\n\tfor (const entry of raw.plugins ?? []) {\n\t\tif (!entry?.name || !entry.source) continue;\n\t\tconst normalized = normalizeSource(entry.source);\n\t\tif (!normalized) continue;\n\t\tconst entryPlatforms = normalizePlatforms(entry.supportPlatform);\n\t\tplugins.push({\n\t\t\tname: entry.name,\n\t\t\tsource: applyPluginRoot(normalized, pluginRoot),\n\t\t\tdescription: entry.description,\n\t\t\t...(entryPlatforms.length > 0 ? { supportPlatform: entryPlatforms } : {}),\n\t\t});\n\t}\n\n\t// supportPlatform = platforms of all present index files, plus any authored\n\t// top-level hint, deduped. Always non-empty (at least the resolved format).\n\tconst supportPlatform: MarketplacePlatform[] = [];\n\tfor (const p of [...present.map((e) => formatToPlatform(e.format)), ...normalizePlatforms(raw.supportPlatform)]) {\n\t\tif (!supportPlatform.includes(p)) supportPlatform.push(p);\n\t}\n\n\treturn {\n\t\tname: (raw.name ?? path.basename(dir)).trim() || path.basename(dir),\n\t\tformat,\n\t\tsupportPlatform,\n\t\troot: dir,\n\t\tmanifestPath,\n\t\tplugins,\n\t};\n}\n\n/** Classify and resolve a plugin `source` against its marketplace root. */\nexport function resolvePluginSource(source: MarketplacePluginSource, marketplaceRoot: string): ResolvedPluginSource {\n\tif (typeof source === \"string\") {\n\t\tconst s = source.trim();\n\t\tif (s.startsWith(\"npm:\")) return { kind: \"npm\", spec: s.slice(4) };\n\t\tif (/^https?:\\/\\//.test(s) || s.startsWith(\"git@\") || s.endsWith(\".git\")) return { kind: \"git\", url: s };\n\t\t// Otherwise a path relative to (or absolute within) the marketplace repo.\n\t\treturn { kind: \"local\", path: path.isAbsolute(s) ? s : path.resolve(marketplaceRoot, s) };\n\t}\n\tif (source.source === \"url\") {\n\t\treturn { kind: \"git\", url: source.url, ref: source.ref, sha: source.sha };\n\t}\n\treturn { kind: \"git-subdir\", url: source.url, path: source.path, ref: source.ref, sha: source.sha };\n}\n\n// ============================================================================\n// Marketplace registry (persisted list of added marketplaces)\n// ============================================================================\n\nexport interface MarketplaceRecord {\n\t/** Original location the user added (git URL or local path). */\n\tlocation: string;\n\t/** Local directory to read the manifest from (clone dir for git, else location). */\n\tdir: string;\n}\n\ninterface MarketplaceStoreFile {\n\tmarketplaces?: MarketplaceRecord[];\n}\n\n/** Read the persisted marketplace list from `<storePath>`. */\nexport function readMarketplaceStore(storePath: string): MarketplaceRecord[] {\n\tconst parsed = readJson<MarketplaceStoreFile>(storePath);\n\treturn Array.isArray(parsed?.marketplaces) ? parsed.marketplaces : [];\n}\n\n/** Write the marketplace list to `<storePath>`. */\nexport function writeMarketplaceStore(storePath: string, records: MarketplaceRecord[]): void {\n\tfs.mkdirSync(path.dirname(storePath), { recursive: true });\n\tfs.writeFileSync(storePath, `${JSON.stringify({ marketplaces: records }, null, 2)}\\n`, \"utf8\");\n}\n"]}
1
+ {"version":3,"file":"marketplace.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/marketplace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plugin marketplaces — install plugins from a curated index hosted in a git repo\n * (or a local directory).\n *\n * A marketplace is a directory containing an index manifest in one of three formats:\n * - Native: `.agents-plugin/marketplace.json` (preferred; the `.agents` surface)\n * - Claude: `.claude-plugin/marketplace.json`\n * - Copilot: `.github/marketplace.json` (Copilot-style git index)\n *\n * All three use the same shape: `{ name?, owner?, plugins: [{ name, source, description? }] }`.\n * A plugin `source` is either a path relative to the marketplace root (a plugin\n * directory inside the repo), a git URL, or an `npm:<spec>` reference.\n *\n * When more than one is present the native `.agents-plugin` format wins, then\n * Claude, then Copilot (no merge) — matching the \"`.agents/` first\" policy used\n * across hoocode's resource surfaces.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { PLUGIN_FORMATS } from \"./formats/index.js\";\nimport { normalizePlatformToken } from \"./formats/platform-targets.js\";\nimport type { MarketplacePlatform } from \"./formats/types.js\";\n\n/**\n * Canonical platform token used by the optional `supportPlatform` field. The\n * `.github/marketplace.json` (Copilot-style) format is surfaced as `\"github\"` —\n * friendlier than the internal `format: \"copilot\"` and matching where the file\n * lives. Authored aliases (`copilot`, `gh`) normalize to `github`.\n *\n * Re-exported from the format registry so the platform vocabulary has a single\n * source of truth.\n */\nexport type { MarketplacePlatform } from \"./formats/types.js\";\n\n/** Map an internal marketplace `format` to its public platform token. */\nfunction formatToPlatform(format: NormalizedMarketplace[\"format\"]): MarketplacePlatform {\n\treturn format === \"copilot\" ? \"github\" : format;\n}\n\n/** Normalize an authored `supportPlatform` (string | string[]) to canonical tokens. */\nexport function normalizePlatforms(value: unknown): MarketplacePlatform[] {\n\tconst raw = Array.isArray(value) ? value : value == null ? [] : [value];\n\tconst out: MarketplacePlatform[] = [];\n\tfor (const v of raw) {\n\t\tif (typeof v !== \"string\") continue;\n\t\tconst canonical = normalizePlatformToken(v);\n\t\tif (canonical && !out.includes(canonical)) out.push(canonical);\n\t}\n\treturn out;\n}\n\n/** Structured source pointing at an entire git repository. */\ninterface MarketplacePluginSourceUrl {\n\tsource: \"url\";\n\turl: string;\n\tref?: string;\n\tsha?: string;\n}\n\n/** Structured source pointing at a subdirectory within a git repository. */\ninterface MarketplacePluginSourceGitSubdir {\n\tsource: \"git-subdir\";\n\turl: string;\n\t/** Subdirectory path inside the repository. */\n\tpath: string;\n\tref?: string;\n\tsha?: string;\n}\n\n/** Structured source pointing at an npm package. */\ninterface MarketplacePluginSourceNpm {\n\tsource: \"npm\";\n\tpackage: string;\n\tversion?: string;\n\tregistry?: string;\n}\n\n/** Structured source pointing at a zip archive served over HTTPS. */\ninterface MarketplacePluginSourceArchive {\n\tsource: \"archive\";\n\turl: string;\n\tsha256?: string;\n}\n\n/**\n * Source value authored in a marketplace manifest.\n *\n * `npm` and `archive` are in the union despite not being installable here. They\n * are documented vendor source types, so an entry using one is a *valid* catalog\n * entry hoocode cannot fetch — which is a different thing from a malformed entry,\n * and has to read differently. Dropping them at parse time made the plugin vanish\n * from SearchPlugins entirely, so the failure mode was \"that plugin does not\n * exist\" rather than \"hoocode cannot install that kind yet\".\n */\nexport type MarketplacePluginSource =\n\t| string\n\t| MarketplacePluginSourceUrl\n\t| MarketplacePluginSourceGitSubdir\n\t| MarketplacePluginSourceNpm\n\t| MarketplacePluginSourceArchive;\n\ninterface MarketplacePluginEntry {\n\tname: string;\n\t/** Relative path, git URL, `npm:<spec>`, or structured source object. */\n\tsource: MarketplacePluginSource;\n\tdescription?: string;\n\t/**\n\t * Optional platform(s) this entry targets. Only set when authored on the\n\t * entry; absent means \"no per-entry restriction\" (the marketplace's\n\t * platforms apply). Purely informational today — nothing filters on it.\n\t */\n\tsupportPlatform?: MarketplacePlatform[];\n}\n\nexport interface NormalizedMarketplace {\n\tname: string;\n\t/** Precedence winner when several index files conflict (agents > claude > copilot). */\n\tformat: \"agents\" | \"claude\" | \"copilot\";\n\t/**\n\t * Every platform this marketplace directory offers, so a conflict between\n\t * multiple index files (e.g. both `.github/` and `.claude-plugin/`) is\n\t * recorded rather than hidden. Always includes the resolved `format`'s\n\t * platform; also folds in any authored top-level `supportPlatform`. Never\n\t * empty.\n\t */\n\tsupportPlatform: MarketplacePlatform[];\n\t/** Absolute path to the marketplace directory. */\n\troot: string;\n\tmanifestPath: string;\n\tplugins: MarketplacePluginEntry[];\n}\n\ninterface RawMarketplace {\n\tname?: string;\n\t/** Both Claude Code and Copilot CLI define `owner` as `{ name, email? }`; a bare string is tolerated. */\n\towner?: string | { name?: string; email?: string };\n\t/** Shared optional metadata: `pluginRoot` is a base dir prepended to relative plugin source paths. */\n\tmetadata?: { description?: string; version?: string; pluginRoot?: string };\n\t/** Optional authored platform hint (string | string[]); folded into supportPlatform. */\n\tsupportPlatform?: unknown;\n\tplugins?: Array<{ name?: string; source?: unknown; description?: string; supportPlatform?: unknown }>;\n}\n\n/** True when a string source is a path (not npm:/git/URL) that `pluginRoot` should prefix. */\nfunction isRelativePathSource(source: string): boolean {\n\tconst s = source.trim();\n\treturn (\n\t\t!s.startsWith(\"npm:\") &&\n\t\t!/^https?:\\/\\//.test(s) &&\n\t\t!s.startsWith(\"git@\") &&\n\t\t!s.endsWith(\".git\") &&\n\t\t!path.isAbsolute(s)\n\t);\n}\n\n/**\n * Apply `metadata.pluginRoot` (Claude Code and Copilot CLI share the field):\n * a base directory prepended to relative plugin source paths, so\n * `\"source\": \"formatter\"` with `\"pluginRoot\": \"./plugins\"` resolves like\n * `\"./plugins/formatter\"`.\n */\nfunction applyPluginRoot(source: MarketplacePluginSource, pluginRoot: string | undefined): MarketplacePluginSource {\n\tif (!pluginRoot || typeof source !== \"string\" || !isRelativePathSource(source)) return source;\n\treturn `${pluginRoot.replace(/\\/+$/, \"\")}/${source.trim().replace(/^\\.\\//, \"\")}`;\n}\n\n/** Validate and normalize an authored source value. Returns null for unsupported/invalid shapes. */\nfunction normalizeSource(source: unknown): MarketplacePluginSource | null {\n\tif (typeof source === \"string\") return source;\n\tif (!source || typeof source !== \"object\") return null;\n\tconst obj = source as Record<string, unknown>;\n\tconst src = obj.source;\n\tconst url = obj.url;\n\t// `commit` is not in the documented schema but appears in the wild alongside\n\t// `sha` (anthropics/claude-plugins-official ships two such entries). It names\n\t// the same thing, so it stands in as the pin when `sha` is absent rather than\n\t// silently leaving the entry unpinned.\n\tconst shaValue = typeof obj.sha === \"string\" ? obj.sha : typeof obj.commit === \"string\" ? obj.commit : undefined;\n\tconst pin = {\n\t\t...(typeof obj.ref === \"string\" ? { ref: obj.ref } : {}),\n\t\t...(shaValue ? { sha: shaValue } : {}),\n\t};\n\tconst subdir = typeof obj.path === \"string\" && obj.path.length > 0 ? obj.path : undefined;\n\tif (src === \"url\" && typeof url === \"string\") {\n\t\t// A `url` entry carrying `path` means the plugin is a subdirectory of that\n\t\t// repo, exactly like `git-subdir`. Dropping the `path` used to install the\n\t\t// whole repository instead — six entries in the official Claude marketplace\n\t\t// resolve that way, and each produced a plugin directory with none of the\n\t\t// plugin in it.\n\t\tif (subdir) return { source: \"git-subdir\", url, path: subdir, ...pin };\n\t\treturn { source: \"url\", url, ...pin };\n\t}\n\tif (src === \"git-subdir\" && typeof url === \"string\" && subdir) {\n\t\treturn { source: \"git-subdir\", url, path: subdir, ...pin };\n\t}\n\t// Copilot marketplace shorthand: { source: \"github\", repo: \"owner/name\", path? }\n\t// (used by github/copilot-plugins and github/awesome-copilot). Normalize to the\n\t// equivalent git source.\n\tif (src === \"github\" && typeof obj.repo === \"string\" && /^[\\w.-]+\\/[\\w.-]+$/.test(obj.repo)) {\n\t\tconst repoUrl = `https://github.com/${obj.repo}.git`;\n\t\tif (subdir) return { source: \"git-subdir\", url: repoUrl, path: subdir, ...pin };\n\t\treturn { source: \"url\", url: repoUrl, ...pin };\n\t}\n\t// Documented types hoocode cannot fetch. Kept so the entry stays visible and\n\t// install can explain itself; see the MarketplacePluginSource docstring.\n\tif (src === \"npm\" && typeof obj.package === \"string\") {\n\t\treturn {\n\t\t\tsource: \"npm\",\n\t\t\tpackage: obj.package,\n\t\t\t...(typeof obj.version === \"string\" ? { version: obj.version } : {}),\n\t\t\t...(typeof obj.registry === \"string\" ? { registry: obj.registry } : {}),\n\t\t};\n\t}\n\tif (src === \"archive\" && typeof url === \"string\") {\n\t\treturn { source: \"archive\", url, ...(typeof obj.sha256 === \"string\" ? { sha256: obj.sha256 } : {}) };\n\t}\n\t// Anything else is malformed rather than merely unsupported, and stays dropped.\n\treturn null;\n}\n\n/** A resolved plugin source. The last two are recognized but not fetchable here. */\nexport type ResolvedPluginSource =\n\t| { kind: \"local\"; path: string }\n\t| { kind: \"git\"; url: string; ref?: string; sha?: string }\n\t| { kind: \"git-subdir\"; url: string; path: string; ref?: string; sha?: string }\n\t| { kind: \"npm\"; spec: string }\n\t| { kind: \"archive\"; url: string; sha256?: string };\n\nfunction readJson<T>(file: string): T | null {\n\ttry {\n\t\treturn JSON.parse(fs.readFileSync(file, \"utf8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Parse a marketplace directory. Native `.agents-plugin` wins, then Claude, then\n * Copilot when more than one file is present. Returns null if no manifest exists\n * or it is invalid.\n */\nexport function parseMarketplaceDir(dir: string): NormalizedMarketplace | null {\n\t// Record every index format present so a conflict (more than one file) is\n\t// visible via supportPlatform, not silently dropped. PLUGIN_FORMATS is\n\t// precedence-ordered (native > Claude > Copilot), so `present` inherits it.\n\tconst present: Array<{ format: NormalizedMarketplace[\"format\"]; path: string }> = [];\n\tfor (const fmt of PLUGIN_FORMATS) {\n\t\tfor (const candidate of fmt.marketplaceFiles) {\n\t\t\tconst p = path.join(dir, candidate);\n\t\t\tif (fs.existsSync(p)) {\n\t\t\t\tpresent.push({ format: fmt.id, path: p });\n\t\t\t\tbreak; // first candidate wins within a format\n\t\t\t}\n\t\t}\n\t}\n\tif (present.length === 0) return null;\n\n\t// Precedence winner (present is built in agents > claude > copilot order).\n\tconst { format, path: manifestPath } = present[0];\n\n\tconst raw = readJson<RawMarketplace>(manifestPath);\n\tif (!raw) return null;\n\n\tconst pluginRoot =\n\t\ttypeof raw.metadata?.pluginRoot === \"string\" && raw.metadata.pluginRoot.trim()\n\t\t\t? raw.metadata.pluginRoot.trim()\n\t\t\t: undefined;\n\n\tconst plugins: MarketplacePluginEntry[] = [];\n\tfor (const entry of raw.plugins ?? []) {\n\t\tif (!entry?.name || !entry.source) continue;\n\t\tconst normalized = normalizeSource(entry.source);\n\t\tif (!normalized) continue;\n\t\tconst entryPlatforms = normalizePlatforms(entry.supportPlatform);\n\t\tplugins.push({\n\t\t\tname: entry.name,\n\t\t\tsource: applyPluginRoot(normalized, pluginRoot),\n\t\t\tdescription: entry.description,\n\t\t\t...(entryPlatforms.length > 0 ? { supportPlatform: entryPlatforms } : {}),\n\t\t});\n\t}\n\n\t// supportPlatform = platforms of all present index files, plus any authored\n\t// top-level hint, deduped. Always non-empty (at least the resolved format).\n\tconst supportPlatform: MarketplacePlatform[] = [];\n\tfor (const p of [...present.map((e) => formatToPlatform(e.format)), ...normalizePlatforms(raw.supportPlatform)]) {\n\t\tif (!supportPlatform.includes(p)) supportPlatform.push(p);\n\t}\n\n\treturn {\n\t\tname: (raw.name ?? path.basename(dir)).trim() || path.basename(dir),\n\t\tformat,\n\t\tsupportPlatform,\n\t\troot: dir,\n\t\tmanifestPath,\n\t\tplugins,\n\t};\n}\n\n/** Classify and resolve a plugin `source` against its marketplace root. */\nexport function resolvePluginSource(source: MarketplacePluginSource, marketplaceRoot: string): ResolvedPluginSource {\n\tif (typeof source === \"string\") {\n\t\tconst s = source.trim();\n\t\tif (s.startsWith(\"npm:\")) return { kind: \"npm\", spec: s.slice(4) };\n\t\tif (/^https?:\\/\\//.test(s) || s.startsWith(\"git@\") || s.endsWith(\".git\")) return { kind: \"git\", url: s };\n\t\t// Otherwise a path relative to (or absolute within) the marketplace repo.\n\t\treturn { kind: \"local\", path: path.isAbsolute(s) ? s : path.resolve(marketplaceRoot, s) };\n\t}\n\tif (source.source === \"url\") {\n\t\treturn { kind: \"git\", url: source.url, ref: source.ref, sha: source.sha };\n\t}\n\tif (source.source === \"npm\") {\n\t\treturn { kind: \"npm\", spec: source.version ? `${source.package}@${source.version}` : source.package };\n\t}\n\tif (source.source === \"archive\") {\n\t\treturn { kind: \"archive\", url: source.url, sha256: source.sha256 };\n\t}\n\treturn { kind: \"git-subdir\", url: source.url, path: source.path, ref: source.ref, sha: source.sha };\n}\n\n// ============================================================================\n// Marketplace registry (persisted list of added marketplaces)\n// ============================================================================\n\nexport interface MarketplaceRecord {\n\t/** Original location the user added (git URL or local path). */\n\tlocation: string;\n\t/** Local directory to read the manifest from (clone dir for git, else location). */\n\tdir: string;\n}\n\ninterface MarketplaceStoreFile {\n\tmarketplaces?: MarketplaceRecord[];\n}\n\n/** Read the persisted marketplace list from `<storePath>`. */\nexport function readMarketplaceStore(storePath: string): MarketplaceRecord[] {\n\tconst parsed = readJson<MarketplaceStoreFile>(storePath);\n\treturn Array.isArray(parsed?.marketplaces) ? parsed.marketplaces : [];\n}\n\n/** Write the marketplace list to `<storePath>`. */\nexport function writeMarketplaceStore(storePath: string, records: MarketplaceRecord[]): void {\n\tfs.mkdirSync(path.dirname(storePath), { recursive: true });\n\tfs.writeFileSync(storePath, `${JSON.stringify({ marketplaces: records }, null, 2)}\\n`, \"utf8\");\n}\n"]}
@@ -65,25 +65,52 @@ function normalizeSource(source) {
65
65
  const obj = source;
66
66
  const src = obj.source;
67
67
  const url = obj.url;
68
+ // `commit` is not in the documented schema but appears in the wild alongside
69
+ // `sha` (anthropics/claude-plugins-official ships two such entries). It names
70
+ // the same thing, so it stands in as the pin when `sha` is absent rather than
71
+ // silently leaving the entry unpinned.
72
+ const shaValue = typeof obj.sha === "string" ? obj.sha : typeof obj.commit === "string" ? obj.commit : undefined;
68
73
  const pin = {
69
74
  ...(typeof obj.ref === "string" ? { ref: obj.ref } : {}),
70
- ...(typeof obj.sha === "string" ? { sha: obj.sha } : {}),
75
+ ...(shaValue ? { sha: shaValue } : {}),
71
76
  };
77
+ const subdir = typeof obj.path === "string" && obj.path.length > 0 ? obj.path : undefined;
72
78
  if (src === "url" && typeof url === "string") {
79
+ // A `url` entry carrying `path` means the plugin is a subdirectory of that
80
+ // repo, exactly like `git-subdir`. Dropping the `path` used to install the
81
+ // whole repository instead — six entries in the official Claude marketplace
82
+ // resolve that way, and each produced a plugin directory with none of the
83
+ // plugin in it.
84
+ if (subdir)
85
+ return { source: "git-subdir", url, path: subdir, ...pin };
73
86
  return { source: "url", url, ...pin };
74
87
  }
75
- if (src === "git-subdir" && typeof url === "string" && typeof obj.path === "string") {
76
- return { source: "git-subdir", url, path: obj.path, ...pin };
88
+ if (src === "git-subdir" && typeof url === "string" && subdir) {
89
+ return { source: "git-subdir", url, path: subdir, ...pin };
77
90
  }
78
91
  // Copilot marketplace shorthand: { source: "github", repo: "owner/name", path? }
79
- // (used by github/copilot-plugins). Normalize to the equivalent git source.
92
+ // (used by github/copilot-plugins and github/awesome-copilot). Normalize to the
93
+ // equivalent git source.
80
94
  if (src === "github" && typeof obj.repo === "string" && /^[\w.-]+\/[\w.-]+$/.test(obj.repo)) {
81
95
  const repoUrl = `https://github.com/${obj.repo}.git`;
82
- if (typeof obj.path === "string" && obj.path.length > 0) {
83
- return { source: "git-subdir", url: repoUrl, path: obj.path, ...pin };
84
- }
96
+ if (subdir)
97
+ return { source: "git-subdir", url: repoUrl, path: subdir, ...pin };
85
98
  return { source: "url", url: repoUrl, ...pin };
86
99
  }
100
+ // Documented types hoocode cannot fetch. Kept so the entry stays visible and
101
+ // install can explain itself; see the MarketplacePluginSource docstring.
102
+ if (src === "npm" && typeof obj.package === "string") {
103
+ return {
104
+ source: "npm",
105
+ package: obj.package,
106
+ ...(typeof obj.version === "string" ? { version: obj.version } : {}),
107
+ ...(typeof obj.registry === "string" ? { registry: obj.registry } : {}),
108
+ };
109
+ }
110
+ if (src === "archive" && typeof url === "string") {
111
+ return { source: "archive", url, ...(typeof obj.sha256 === "string" ? { sha256: obj.sha256 } : {}) };
112
+ }
113
+ // Anything else is malformed rather than merely unsupported, and stays dropped.
87
114
  return null;
88
115
  }
89
116
  function readJson(file) {
@@ -168,6 +195,12 @@ export function resolvePluginSource(source, marketplaceRoot) {
168
195
  if (source.source === "url") {
169
196
  return { kind: "git", url: source.url, ref: source.ref, sha: source.sha };
170
197
  }
198
+ if (source.source === "npm") {
199
+ return { kind: "npm", spec: source.version ? `${source.package}@${source.version}` : source.package };
200
+ }
201
+ if (source.source === "archive") {
202
+ return { kind: "archive", url: source.url, sha256: source.sha256 };
203
+ }
171
204
  return { kind: "git-subdir", url: source.url, path: source.path, ref: source.ref, sha: source.sha };
172
205
  }
173
206
  /** Read the persisted marketplace list from `<storePath>`. */
@@ -1 +1 @@
1
- {"version":3,"file":"marketplace.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/marketplace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plugin marketplaces — install plugins from a curated index hosted in a git repo\n * (or a local directory).\n *\n * A marketplace is a directory containing an index manifest in one of three formats:\n * - Native: `.agents-plugin/marketplace.json` (preferred; the `.agents` surface)\n * - Claude: `.claude-plugin/marketplace.json`\n * - Copilot: `.github/marketplace.json` (Copilot-style git index)\n *\n * All three use the same shape: `{ name?, owner?, plugins: [{ name, source, description? }] }`.\n * A plugin `source` is either a path relative to the marketplace root (a plugin\n * directory inside the repo), a git URL, or an `npm:<spec>` reference.\n *\n * When more than one is present the native `.agents-plugin` format wins, then\n * Claude, then Copilot (no merge) — matching the \"`.agents/` first\" policy used\n * across hoocode's resource surfaces.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { PLUGIN_FORMATS } from \"./formats/index.js\";\nimport { normalizePlatformToken } from \"./formats/platform-targets.js\";\nimport type { MarketplacePlatform } from \"./formats/types.js\";\n\n/**\n * Canonical platform token used by the optional `supportPlatform` field. The\n * `.github/marketplace.json` (Copilot-style) format is surfaced as `\"github\"` —\n * friendlier than the internal `format: \"copilot\"` and matching where the file\n * lives. Authored aliases (`copilot`, `gh`) normalize to `github`.\n *\n * Re-exported from the format registry so the platform vocabulary has a single\n * source of truth.\n */\nexport type { MarketplacePlatform } from \"./formats/types.js\";\n\n/** Map an internal marketplace `format` to its public platform token. */\nfunction formatToPlatform(format: NormalizedMarketplace[\"format\"]): MarketplacePlatform {\n\treturn format === \"copilot\" ? \"github\" : format;\n}\n\n/** Normalize an authored `supportPlatform` (string | string[]) to canonical tokens. */\nexport function normalizePlatforms(value: unknown): MarketplacePlatform[] {\n\tconst raw = Array.isArray(value) ? value : value == null ? [] : [value];\n\tconst out: MarketplacePlatform[] = [];\n\tfor (const v of raw) {\n\t\tif (typeof v !== \"string\") continue;\n\t\tconst canonical = normalizePlatformToken(v);\n\t\tif (canonical && !out.includes(canonical)) out.push(canonical);\n\t}\n\treturn out;\n}\n\n/** Structured source pointing at an entire git repository. */\ninterface MarketplacePluginSourceUrl {\n\tsource: \"url\";\n\turl: string;\n\tref?: string;\n\tsha?: string;\n}\n\n/** Structured source pointing at a subdirectory within a git repository. */\ninterface MarketplacePluginSourceGitSubdir {\n\tsource: \"git-subdir\";\n\turl: string;\n\t/** Subdirectory path inside the repository. */\n\tpath: string;\n\tref?: string;\n\tsha?: string;\n}\n\n/** Source value authored in a marketplace manifest. */\nexport type MarketplacePluginSource = string | MarketplacePluginSourceUrl | MarketplacePluginSourceGitSubdir;\n\ninterface MarketplacePluginEntry {\n\tname: string;\n\t/** Relative path, git URL, `npm:<spec>`, or structured source object. */\n\tsource: MarketplacePluginSource;\n\tdescription?: string;\n\t/**\n\t * Optional platform(s) this entry targets. Only set when authored on the\n\t * entry; absent means \"no per-entry restriction\" (the marketplace's\n\t * platforms apply). Purely informational today — nothing filters on it.\n\t */\n\tsupportPlatform?: MarketplacePlatform[];\n}\n\nexport interface NormalizedMarketplace {\n\tname: string;\n\t/** Precedence winner when several index files conflict (agents > claude > copilot). */\n\tformat: \"agents\" | \"claude\" | \"copilot\";\n\t/**\n\t * Every platform this marketplace directory offers, so a conflict between\n\t * multiple index files (e.g. both `.github/` and `.claude-plugin/`) is\n\t * recorded rather than hidden. Always includes the resolved `format`'s\n\t * platform; also folds in any authored top-level `supportPlatform`. Never\n\t * empty.\n\t */\n\tsupportPlatform: MarketplacePlatform[];\n\t/** Absolute path to the marketplace directory. */\n\troot: string;\n\tmanifestPath: string;\n\tplugins: MarketplacePluginEntry[];\n}\n\ninterface RawMarketplace {\n\tname?: string;\n\t/** Both Claude Code and Copilot CLI define `owner` as `{ name, email? }`; a bare string is tolerated. */\n\towner?: string | { name?: string; email?: string };\n\t/** Shared optional metadata: `pluginRoot` is a base dir prepended to relative plugin source paths. */\n\tmetadata?: { description?: string; version?: string; pluginRoot?: string };\n\t/** Optional authored platform hint (string | string[]); folded into supportPlatform. */\n\tsupportPlatform?: unknown;\n\tplugins?: Array<{ name?: string; source?: unknown; description?: string; supportPlatform?: unknown }>;\n}\n\n/** True when a string source is a path (not npm:/git/URL) that `pluginRoot` should prefix. */\nfunction isRelativePathSource(source: string): boolean {\n\tconst s = source.trim();\n\treturn (\n\t\t!s.startsWith(\"npm:\") &&\n\t\t!/^https?:\\/\\//.test(s) &&\n\t\t!s.startsWith(\"git@\") &&\n\t\t!s.endsWith(\".git\") &&\n\t\t!path.isAbsolute(s)\n\t);\n}\n\n/**\n * Apply `metadata.pluginRoot` (Claude Code and Copilot CLI share the field):\n * a base directory prepended to relative plugin source paths, so\n * `\"source\": \"formatter\"` with `\"pluginRoot\": \"./plugins\"` resolves like\n * `\"./plugins/formatter\"`.\n */\nfunction applyPluginRoot(source: MarketplacePluginSource, pluginRoot: string | undefined): MarketplacePluginSource {\n\tif (!pluginRoot || typeof source !== \"string\" || !isRelativePathSource(source)) return source;\n\treturn `${pluginRoot.replace(/\\/+$/, \"\")}/${source.trim().replace(/^\\.\\//, \"\")}`;\n}\n\n/** Validate and normalize an authored source value. Returns null for unsupported/invalid shapes. */\nfunction normalizeSource(source: unknown): MarketplacePluginSource | null {\n\tif (typeof source === \"string\") return source;\n\tif (!source || typeof source !== \"object\") return null;\n\tconst obj = source as Record<string, unknown>;\n\tconst src = obj.source;\n\tconst url = obj.url;\n\tconst pin = {\n\t\t...(typeof obj.ref === \"string\" ? { ref: obj.ref } : {}),\n\t\t...(typeof obj.sha === \"string\" ? { sha: obj.sha } : {}),\n\t};\n\tif (src === \"url\" && typeof url === \"string\") {\n\t\treturn { source: \"url\", url, ...pin };\n\t}\n\tif (src === \"git-subdir\" && typeof url === \"string\" && typeof obj.path === \"string\") {\n\t\treturn { source: \"git-subdir\", url, path: obj.path, ...pin };\n\t}\n\t// Copilot marketplace shorthand: { source: \"github\", repo: \"owner/name\", path? }\n\t// (used by github/copilot-plugins). Normalize to the equivalent git source.\n\tif (src === \"github\" && typeof obj.repo === \"string\" && /^[\\w.-]+\\/[\\w.-]+$/.test(obj.repo)) {\n\t\tconst repoUrl = `https://github.com/${obj.repo}.git`;\n\t\tif (typeof obj.path === \"string\" && obj.path.length > 0) {\n\t\t\treturn { source: \"git-subdir\", url: repoUrl, path: obj.path, ...pin };\n\t\t}\n\t\treturn { source: \"url\", url: repoUrl, ...pin };\n\t}\n\treturn null;\n}\n\n/** A resolved, installable plugin source. */\nexport type ResolvedPluginSource =\n\t| { kind: \"local\"; path: string }\n\t| { kind: \"git\"; url: string; ref?: string; sha?: string }\n\t| { kind: \"git-subdir\"; url: string; path: string; ref?: string; sha?: string }\n\t| { kind: \"npm\"; spec: string };\n\nfunction readJson<T>(file: string): T | null {\n\ttry {\n\t\treturn JSON.parse(fs.readFileSync(file, \"utf8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Parse a marketplace directory. Native `.agents-plugin` wins, then Claude, then\n * Copilot when more than one file is present. Returns null if no manifest exists\n * or it is invalid.\n */\nexport function parseMarketplaceDir(dir: string): NormalizedMarketplace | null {\n\t// Record every index format present so a conflict (more than one file) is\n\t// visible via supportPlatform, not silently dropped. PLUGIN_FORMATS is\n\t// precedence-ordered (native > Claude > Copilot), so `present` inherits it.\n\tconst present: Array<{ format: NormalizedMarketplace[\"format\"]; path: string }> = [];\n\tfor (const fmt of PLUGIN_FORMATS) {\n\t\tfor (const candidate of fmt.marketplaceFiles) {\n\t\t\tconst p = path.join(dir, candidate);\n\t\t\tif (fs.existsSync(p)) {\n\t\t\t\tpresent.push({ format: fmt.id, path: p });\n\t\t\t\tbreak; // first candidate wins within a format\n\t\t\t}\n\t\t}\n\t}\n\tif (present.length === 0) return null;\n\n\t// Precedence winner (present is built in agents > claude > copilot order).\n\tconst { format, path: manifestPath } = present[0];\n\n\tconst raw = readJson<RawMarketplace>(manifestPath);\n\tif (!raw) return null;\n\n\tconst pluginRoot =\n\t\ttypeof raw.metadata?.pluginRoot === \"string\" && raw.metadata.pluginRoot.trim()\n\t\t\t? raw.metadata.pluginRoot.trim()\n\t\t\t: undefined;\n\n\tconst plugins: MarketplacePluginEntry[] = [];\n\tfor (const entry of raw.plugins ?? []) {\n\t\tif (!entry?.name || !entry.source) continue;\n\t\tconst normalized = normalizeSource(entry.source);\n\t\tif (!normalized) continue;\n\t\tconst entryPlatforms = normalizePlatforms(entry.supportPlatform);\n\t\tplugins.push({\n\t\t\tname: entry.name,\n\t\t\tsource: applyPluginRoot(normalized, pluginRoot),\n\t\t\tdescription: entry.description,\n\t\t\t...(entryPlatforms.length > 0 ? { supportPlatform: entryPlatforms } : {}),\n\t\t});\n\t}\n\n\t// supportPlatform = platforms of all present index files, plus any authored\n\t// top-level hint, deduped. Always non-empty (at least the resolved format).\n\tconst supportPlatform: MarketplacePlatform[] = [];\n\tfor (const p of [...present.map((e) => formatToPlatform(e.format)), ...normalizePlatforms(raw.supportPlatform)]) {\n\t\tif (!supportPlatform.includes(p)) supportPlatform.push(p);\n\t}\n\n\treturn {\n\t\tname: (raw.name ?? path.basename(dir)).trim() || path.basename(dir),\n\t\tformat,\n\t\tsupportPlatform,\n\t\troot: dir,\n\t\tmanifestPath,\n\t\tplugins,\n\t};\n}\n\n/** Classify and resolve a plugin `source` against its marketplace root. */\nexport function resolvePluginSource(source: MarketplacePluginSource, marketplaceRoot: string): ResolvedPluginSource {\n\tif (typeof source === \"string\") {\n\t\tconst s = source.trim();\n\t\tif (s.startsWith(\"npm:\")) return { kind: \"npm\", spec: s.slice(4) };\n\t\tif (/^https?:\\/\\//.test(s) || s.startsWith(\"git@\") || s.endsWith(\".git\")) return { kind: \"git\", url: s };\n\t\t// Otherwise a path relative to (or absolute within) the marketplace repo.\n\t\treturn { kind: \"local\", path: path.isAbsolute(s) ? s : path.resolve(marketplaceRoot, s) };\n\t}\n\tif (source.source === \"url\") {\n\t\treturn { kind: \"git\", url: source.url, ref: source.ref, sha: source.sha };\n\t}\n\treturn { kind: \"git-subdir\", url: source.url, path: source.path, ref: source.ref, sha: source.sha };\n}\n\n// ============================================================================\n// Marketplace registry (persisted list of added marketplaces)\n// ============================================================================\n\nexport interface MarketplaceRecord {\n\t/** Original location the user added (git URL or local path). */\n\tlocation: string;\n\t/** Local directory to read the manifest from (clone dir for git, else location). */\n\tdir: string;\n}\n\ninterface MarketplaceStoreFile {\n\tmarketplaces?: MarketplaceRecord[];\n}\n\n/** Read the persisted marketplace list from `<storePath>`. */\nexport function readMarketplaceStore(storePath: string): MarketplaceRecord[] {\n\tconst parsed = readJson<MarketplaceStoreFile>(storePath);\n\treturn Array.isArray(parsed?.marketplaces) ? parsed.marketplaces : [];\n}\n\n/** Write the marketplace list to `<storePath>`. */\nexport function writeMarketplaceStore(storePath: string, records: MarketplaceRecord[]): void {\n\tfs.mkdirSync(path.dirname(storePath), { recursive: true });\n\tfs.writeFileSync(storePath, `${JSON.stringify({ marketplaces: records }, null, 2)}\\n`, \"utf8\");\n}\n"]}
1
+ {"version":3,"file":"marketplace.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/marketplace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plugin marketplaces — install plugins from a curated index hosted in a git repo\n * (or a local directory).\n *\n * A marketplace is a directory containing an index manifest in one of three formats:\n * - Native: `.agents-plugin/marketplace.json` (preferred; the `.agents` surface)\n * - Claude: `.claude-plugin/marketplace.json`\n * - Copilot: `.github/marketplace.json` (Copilot-style git index)\n *\n * All three use the same shape: `{ name?, owner?, plugins: [{ name, source, description? }] }`.\n * A plugin `source` is either a path relative to the marketplace root (a plugin\n * directory inside the repo), a git URL, or an `npm:<spec>` reference.\n *\n * When more than one is present the native `.agents-plugin` format wins, then\n * Claude, then Copilot (no merge) — matching the \"`.agents/` first\" policy used\n * across hoocode's resource surfaces.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { PLUGIN_FORMATS } from \"./formats/index.js\";\nimport { normalizePlatformToken } from \"./formats/platform-targets.js\";\nimport type { MarketplacePlatform } from \"./formats/types.js\";\n\n/**\n * Canonical platform token used by the optional `supportPlatform` field. The\n * `.github/marketplace.json` (Copilot-style) format is surfaced as `\"github\"` —\n * friendlier than the internal `format: \"copilot\"` and matching where the file\n * lives. Authored aliases (`copilot`, `gh`) normalize to `github`.\n *\n * Re-exported from the format registry so the platform vocabulary has a single\n * source of truth.\n */\nexport type { MarketplacePlatform } from \"./formats/types.js\";\n\n/** Map an internal marketplace `format` to its public platform token. */\nfunction formatToPlatform(format: NormalizedMarketplace[\"format\"]): MarketplacePlatform {\n\treturn format === \"copilot\" ? \"github\" : format;\n}\n\n/** Normalize an authored `supportPlatform` (string | string[]) to canonical tokens. */\nexport function normalizePlatforms(value: unknown): MarketplacePlatform[] {\n\tconst raw = Array.isArray(value) ? value : value == null ? [] : [value];\n\tconst out: MarketplacePlatform[] = [];\n\tfor (const v of raw) {\n\t\tif (typeof v !== \"string\") continue;\n\t\tconst canonical = normalizePlatformToken(v);\n\t\tif (canonical && !out.includes(canonical)) out.push(canonical);\n\t}\n\treturn out;\n}\n\n/** Structured source pointing at an entire git repository. */\ninterface MarketplacePluginSourceUrl {\n\tsource: \"url\";\n\turl: string;\n\tref?: string;\n\tsha?: string;\n}\n\n/** Structured source pointing at a subdirectory within a git repository. */\ninterface MarketplacePluginSourceGitSubdir {\n\tsource: \"git-subdir\";\n\turl: string;\n\t/** Subdirectory path inside the repository. */\n\tpath: string;\n\tref?: string;\n\tsha?: string;\n}\n\n/** Structured source pointing at an npm package. */\ninterface MarketplacePluginSourceNpm {\n\tsource: \"npm\";\n\tpackage: string;\n\tversion?: string;\n\tregistry?: string;\n}\n\n/** Structured source pointing at a zip archive served over HTTPS. */\ninterface MarketplacePluginSourceArchive {\n\tsource: \"archive\";\n\turl: string;\n\tsha256?: string;\n}\n\n/**\n * Source value authored in a marketplace manifest.\n *\n * `npm` and `archive` are in the union despite not being installable here. They\n * are documented vendor source types, so an entry using one is a *valid* catalog\n * entry hoocode cannot fetch — which is a different thing from a malformed entry,\n * and has to read differently. Dropping them at parse time made the plugin vanish\n * from SearchPlugins entirely, so the failure mode was \"that plugin does not\n * exist\" rather than \"hoocode cannot install that kind yet\".\n */\nexport type MarketplacePluginSource =\n\t| string\n\t| MarketplacePluginSourceUrl\n\t| MarketplacePluginSourceGitSubdir\n\t| MarketplacePluginSourceNpm\n\t| MarketplacePluginSourceArchive;\n\ninterface MarketplacePluginEntry {\n\tname: string;\n\t/** Relative path, git URL, `npm:<spec>`, or structured source object. */\n\tsource: MarketplacePluginSource;\n\tdescription?: string;\n\t/**\n\t * Optional platform(s) this entry targets. Only set when authored on the\n\t * entry; absent means \"no per-entry restriction\" (the marketplace's\n\t * platforms apply). Purely informational today — nothing filters on it.\n\t */\n\tsupportPlatform?: MarketplacePlatform[];\n}\n\nexport interface NormalizedMarketplace {\n\tname: string;\n\t/** Precedence winner when several index files conflict (agents > claude > copilot). */\n\tformat: \"agents\" | \"claude\" | \"copilot\";\n\t/**\n\t * Every platform this marketplace directory offers, so a conflict between\n\t * multiple index files (e.g. both `.github/` and `.claude-plugin/`) is\n\t * recorded rather than hidden. Always includes the resolved `format`'s\n\t * platform; also folds in any authored top-level `supportPlatform`. Never\n\t * empty.\n\t */\n\tsupportPlatform: MarketplacePlatform[];\n\t/** Absolute path to the marketplace directory. */\n\troot: string;\n\tmanifestPath: string;\n\tplugins: MarketplacePluginEntry[];\n}\n\ninterface RawMarketplace {\n\tname?: string;\n\t/** Both Claude Code and Copilot CLI define `owner` as `{ name, email? }`; a bare string is tolerated. */\n\towner?: string | { name?: string; email?: string };\n\t/** Shared optional metadata: `pluginRoot` is a base dir prepended to relative plugin source paths. */\n\tmetadata?: { description?: string; version?: string; pluginRoot?: string };\n\t/** Optional authored platform hint (string | string[]); folded into supportPlatform. */\n\tsupportPlatform?: unknown;\n\tplugins?: Array<{ name?: string; source?: unknown; description?: string; supportPlatform?: unknown }>;\n}\n\n/** True when a string source is a path (not npm:/git/URL) that `pluginRoot` should prefix. */\nfunction isRelativePathSource(source: string): boolean {\n\tconst s = source.trim();\n\treturn (\n\t\t!s.startsWith(\"npm:\") &&\n\t\t!/^https?:\\/\\//.test(s) &&\n\t\t!s.startsWith(\"git@\") &&\n\t\t!s.endsWith(\".git\") &&\n\t\t!path.isAbsolute(s)\n\t);\n}\n\n/**\n * Apply `metadata.pluginRoot` (Claude Code and Copilot CLI share the field):\n * a base directory prepended to relative plugin source paths, so\n * `\"source\": \"formatter\"` with `\"pluginRoot\": \"./plugins\"` resolves like\n * `\"./plugins/formatter\"`.\n */\nfunction applyPluginRoot(source: MarketplacePluginSource, pluginRoot: string | undefined): MarketplacePluginSource {\n\tif (!pluginRoot || typeof source !== \"string\" || !isRelativePathSource(source)) return source;\n\treturn `${pluginRoot.replace(/\\/+$/, \"\")}/${source.trim().replace(/^\\.\\//, \"\")}`;\n}\n\n/** Validate and normalize an authored source value. Returns null for unsupported/invalid shapes. */\nfunction normalizeSource(source: unknown): MarketplacePluginSource | null {\n\tif (typeof source === \"string\") return source;\n\tif (!source || typeof source !== \"object\") return null;\n\tconst obj = source as Record<string, unknown>;\n\tconst src = obj.source;\n\tconst url = obj.url;\n\t// `commit` is not in the documented schema but appears in the wild alongside\n\t// `sha` (anthropics/claude-plugins-official ships two such entries). It names\n\t// the same thing, so it stands in as the pin when `sha` is absent rather than\n\t// silently leaving the entry unpinned.\n\tconst shaValue = typeof obj.sha === \"string\" ? obj.sha : typeof obj.commit === \"string\" ? obj.commit : undefined;\n\tconst pin = {\n\t\t...(typeof obj.ref === \"string\" ? { ref: obj.ref } : {}),\n\t\t...(shaValue ? { sha: shaValue } : {}),\n\t};\n\tconst subdir = typeof obj.path === \"string\" && obj.path.length > 0 ? obj.path : undefined;\n\tif (src === \"url\" && typeof url === \"string\") {\n\t\t// A `url` entry carrying `path` means the plugin is a subdirectory of that\n\t\t// repo, exactly like `git-subdir`. Dropping the `path` used to install the\n\t\t// whole repository instead — six entries in the official Claude marketplace\n\t\t// resolve that way, and each produced a plugin directory with none of the\n\t\t// plugin in it.\n\t\tif (subdir) return { source: \"git-subdir\", url, path: subdir, ...pin };\n\t\treturn { source: \"url\", url, ...pin };\n\t}\n\tif (src === \"git-subdir\" && typeof url === \"string\" && subdir) {\n\t\treturn { source: \"git-subdir\", url, path: subdir, ...pin };\n\t}\n\t// Copilot marketplace shorthand: { source: \"github\", repo: \"owner/name\", path? }\n\t// (used by github/copilot-plugins and github/awesome-copilot). Normalize to the\n\t// equivalent git source.\n\tif (src === \"github\" && typeof obj.repo === \"string\" && /^[\\w.-]+\\/[\\w.-]+$/.test(obj.repo)) {\n\t\tconst repoUrl = `https://github.com/${obj.repo}.git`;\n\t\tif (subdir) return { source: \"git-subdir\", url: repoUrl, path: subdir, ...pin };\n\t\treturn { source: \"url\", url: repoUrl, ...pin };\n\t}\n\t// Documented types hoocode cannot fetch. Kept so the entry stays visible and\n\t// install can explain itself; see the MarketplacePluginSource docstring.\n\tif (src === \"npm\" && typeof obj.package === \"string\") {\n\t\treturn {\n\t\t\tsource: \"npm\",\n\t\t\tpackage: obj.package,\n\t\t\t...(typeof obj.version === \"string\" ? { version: obj.version } : {}),\n\t\t\t...(typeof obj.registry === \"string\" ? { registry: obj.registry } : {}),\n\t\t};\n\t}\n\tif (src === \"archive\" && typeof url === \"string\") {\n\t\treturn { source: \"archive\", url, ...(typeof obj.sha256 === \"string\" ? { sha256: obj.sha256 } : {}) };\n\t}\n\t// Anything else is malformed rather than merely unsupported, and stays dropped.\n\treturn null;\n}\n\n/** A resolved plugin source. The last two are recognized but not fetchable here. */\nexport type ResolvedPluginSource =\n\t| { kind: \"local\"; path: string }\n\t| { kind: \"git\"; url: string; ref?: string; sha?: string }\n\t| { kind: \"git-subdir\"; url: string; path: string; ref?: string; sha?: string }\n\t| { kind: \"npm\"; spec: string }\n\t| { kind: \"archive\"; url: string; sha256?: string };\n\nfunction readJson<T>(file: string): T | null {\n\ttry {\n\t\treturn JSON.parse(fs.readFileSync(file, \"utf8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Parse a marketplace directory. Native `.agents-plugin` wins, then Claude, then\n * Copilot when more than one file is present. Returns null if no manifest exists\n * or it is invalid.\n */\nexport function parseMarketplaceDir(dir: string): NormalizedMarketplace | null {\n\t// Record every index format present so a conflict (more than one file) is\n\t// visible via supportPlatform, not silently dropped. PLUGIN_FORMATS is\n\t// precedence-ordered (native > Claude > Copilot), so `present` inherits it.\n\tconst present: Array<{ format: NormalizedMarketplace[\"format\"]; path: string }> = [];\n\tfor (const fmt of PLUGIN_FORMATS) {\n\t\tfor (const candidate of fmt.marketplaceFiles) {\n\t\t\tconst p = path.join(dir, candidate);\n\t\t\tif (fs.existsSync(p)) {\n\t\t\t\tpresent.push({ format: fmt.id, path: p });\n\t\t\t\tbreak; // first candidate wins within a format\n\t\t\t}\n\t\t}\n\t}\n\tif (present.length === 0) return null;\n\n\t// Precedence winner (present is built in agents > claude > copilot order).\n\tconst { format, path: manifestPath } = present[0];\n\n\tconst raw = readJson<RawMarketplace>(manifestPath);\n\tif (!raw) return null;\n\n\tconst pluginRoot =\n\t\ttypeof raw.metadata?.pluginRoot === \"string\" && raw.metadata.pluginRoot.trim()\n\t\t\t? raw.metadata.pluginRoot.trim()\n\t\t\t: undefined;\n\n\tconst plugins: MarketplacePluginEntry[] = [];\n\tfor (const entry of raw.plugins ?? []) {\n\t\tif (!entry?.name || !entry.source) continue;\n\t\tconst normalized = normalizeSource(entry.source);\n\t\tif (!normalized) continue;\n\t\tconst entryPlatforms = normalizePlatforms(entry.supportPlatform);\n\t\tplugins.push({\n\t\t\tname: entry.name,\n\t\t\tsource: applyPluginRoot(normalized, pluginRoot),\n\t\t\tdescription: entry.description,\n\t\t\t...(entryPlatforms.length > 0 ? { supportPlatform: entryPlatforms } : {}),\n\t\t});\n\t}\n\n\t// supportPlatform = platforms of all present index files, plus any authored\n\t// top-level hint, deduped. Always non-empty (at least the resolved format).\n\tconst supportPlatform: MarketplacePlatform[] = [];\n\tfor (const p of [...present.map((e) => formatToPlatform(e.format)), ...normalizePlatforms(raw.supportPlatform)]) {\n\t\tif (!supportPlatform.includes(p)) supportPlatform.push(p);\n\t}\n\n\treturn {\n\t\tname: (raw.name ?? path.basename(dir)).trim() || path.basename(dir),\n\t\tformat,\n\t\tsupportPlatform,\n\t\troot: dir,\n\t\tmanifestPath,\n\t\tplugins,\n\t};\n}\n\n/** Classify and resolve a plugin `source` against its marketplace root. */\nexport function resolvePluginSource(source: MarketplacePluginSource, marketplaceRoot: string): ResolvedPluginSource {\n\tif (typeof source === \"string\") {\n\t\tconst s = source.trim();\n\t\tif (s.startsWith(\"npm:\")) return { kind: \"npm\", spec: s.slice(4) };\n\t\tif (/^https?:\\/\\//.test(s) || s.startsWith(\"git@\") || s.endsWith(\".git\")) return { kind: \"git\", url: s };\n\t\t// Otherwise a path relative to (or absolute within) the marketplace repo.\n\t\treturn { kind: \"local\", path: path.isAbsolute(s) ? s : path.resolve(marketplaceRoot, s) };\n\t}\n\tif (source.source === \"url\") {\n\t\treturn { kind: \"git\", url: source.url, ref: source.ref, sha: source.sha };\n\t}\n\tif (source.source === \"npm\") {\n\t\treturn { kind: \"npm\", spec: source.version ? `${source.package}@${source.version}` : source.package };\n\t}\n\tif (source.source === \"archive\") {\n\t\treturn { kind: \"archive\", url: source.url, sha256: source.sha256 };\n\t}\n\treturn { kind: \"git-subdir\", url: source.url, path: source.path, ref: source.ref, sha: source.sha };\n}\n\n// ============================================================================\n// Marketplace registry (persisted list of added marketplaces)\n// ============================================================================\n\nexport interface MarketplaceRecord {\n\t/** Original location the user added (git URL or local path). */\n\tlocation: string;\n\t/** Local directory to read the manifest from (clone dir for git, else location). */\n\tdir: string;\n}\n\ninterface MarketplaceStoreFile {\n\tmarketplaces?: MarketplaceRecord[];\n}\n\n/** Read the persisted marketplace list from `<storePath>`. */\nexport function readMarketplaceStore(storePath: string): MarketplaceRecord[] {\n\tconst parsed = readJson<MarketplaceStoreFile>(storePath);\n\treturn Array.isArray(parsed?.marketplaces) ? parsed.marketplaces : [];\n}\n\n/** Write the marketplace list to `<storePath>`. */\nexport function writeMarketplaceStore(storePath: string, records: MarketplaceRecord[]): void {\n\tfs.mkdirSync(path.dirname(storePath), { recursive: true });\n\tfs.writeFileSync(storePath, `${JSON.stringify({ marketplaces: records }, null, 2)}\\n`, \"utf8\");\n}\n"]}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Workspace trust — the per-machine record of which working directories the user
3
+ * has agreed to run repository-supplied plugin code from.
4
+ *
5
+ * The problem it solves (docs/plugin-system-architecture.md §5.9): a plugin
6
+ * committed to a repository is code that runs for whoever clones it next. Its
7
+ * skills and commands are text the model reads, which is no worse than reading
8
+ * the repository itself, but its **hooks and MCP servers are processes** that
9
+ * start on session load. Nothing about a plugin's location can distinguish "I
10
+ * installed this here" from "this arrived in the clone", because everything in
11
+ * the repository travels with it — including any marker a plugin might carry to
12
+ * claim otherwise.
13
+ *
14
+ * So the record lives **outside the repository**, in the agent dir, keyed by
15
+ * absolute path. That is the whole design: trust cannot be forged by repository
16
+ * content, because repository content cannot write here. It is the same shape
17
+ * Claude Code's workspace trust dialog and VS Code's trusted folders use, and it
18
+ * carries the same known consequence — once a directory is trusted, code pulled
19
+ * into it later is trusted too. Trust is a statement about a *place you work*,
20
+ * not about a specific commit.
21
+ *
22
+ * Granting is a human act (`/plugin trust`, or an explicit `/plugin install
23
+ * --scope project`, where the person is demonstrably operating in the directory
24
+ * on purpose). The autonomous install path never grants it: a model deciding
25
+ * that a workspace should execute repository code is exactly the decision this
26
+ * record exists to keep with a person.
27
+ */
28
+ /** One trusted working directory. */
29
+ export interface TrustedWorkspace {
30
+ /** Absolute, resolved path. */
31
+ path: string;
32
+ /** When trust was granted, ISO-8601. Informational — nothing expires today. */
33
+ at: string;
34
+ }
35
+ /**
36
+ * `~/.agents/trusted-workspaces.json`.
37
+ *
38
+ * Beside the marketplace registry rather than inside the repo, for the reason in
39
+ * the module docstring: a file the repository can write is not a trust record.
40
+ */
41
+ export declare function trustStorePath(agentDir?: string): string;
42
+ /** Every trusted workspace, newest grant first. */
43
+ export declare function listTrustedWorkspaces(agentDir?: string): TrustedWorkspace[];
44
+ /**
45
+ * Whether `cwd` is trusted.
46
+ *
47
+ * Exact-path only, deliberately: trusting `~/src` must not silently trust every
48
+ * repository ever cloned beneath it, which is what a prefix match would do the
49
+ * first time someone trusts a directory one level too high.
50
+ */
51
+ export declare function isWorkspaceTrusted(cwd: string, agentDir?: string): boolean;
52
+ /** Grant trust to `cwd`. Idempotent — re-granting refreshes the timestamp. */
53
+ export declare function trustWorkspace(cwd: string, agentDir?: string): TrustedWorkspace;
54
+ /** Revoke trust for `cwd`. Returns false when it was not trusted to begin with. */
55
+ export declare function untrustWorkspace(cwd: string, agentDir?: string): boolean;
56
+ //# sourceMappingURL=trust.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/trust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAMH,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAChC,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,iFAA+E;IAC/E,EAAE,EAAE,MAAM,CAAC;CACX;AAMD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAEvE;AAmBD,mDAAmD;AACnD,wBAAgB,qBAAqB,CAAC,QAAQ,GAAE,MAAsB,GAAG,gBAAgB,EAAE,CAE1F;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,OAAO,CAGzF;AAED,gFAA8E;AAC9E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,gBAAgB,CAK9F;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,OAAO,CAOvF","sourcesContent":["/**\n * Workspace trust — the per-machine record of which working directories the user\n * has agreed to run repository-supplied plugin code from.\n *\n * The problem it solves (docs/plugin-system-architecture.md §5.9): a plugin\n * committed to a repository is code that runs for whoever clones it next. Its\n * skills and commands are text the model reads, which is no worse than reading\n * the repository itself, but its **hooks and MCP servers are processes** that\n * start on session load. Nothing about a plugin's location can distinguish \"I\n * installed this here\" from \"this arrived in the clone\", because everything in\n * the repository travels with it — including any marker a plugin might carry to\n * claim otherwise.\n *\n * So the record lives **outside the repository**, in the agent dir, keyed by\n * absolute path. That is the whole design: trust cannot be forged by repository\n * content, because repository content cannot write here. It is the same shape\n * Claude Code's workspace trust dialog and VS Code's trusted folders use, and it\n * carries the same known consequence — once a directory is trusted, code pulled\n * into it later is trusted too. Trust is a statement about a *place you work*,\n * not about a specific commit.\n *\n * Granting is a human act (`/plugin trust`, or an explicit `/plugin install\n * --scope project`, where the person is demonstrably operating in the directory\n * on purpose). The autonomous install path never grants it: a model deciding\n * that a workspace should execute repository code is exactly the decision this\n * record exists to keep with a person.\n */\n\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport { getAgentDir } from \"../../../config.js\";\n\n/** One trusted working directory. */\nexport interface TrustedWorkspace {\n\t/** Absolute, resolved path. */\n\tpath: string;\n\t/** When trust was granted, ISO-8601. Informational — nothing expires today. */\n\tat: string;\n}\n\ninterface TrustStoreFile {\n\tworkspaces?: TrustedWorkspace[];\n}\n\n/**\n * `~/.agents/trusted-workspaces.json`.\n *\n * Beside the marketplace registry rather than inside the repo, for the reason in\n * the module docstring: a file the repository can write is not a trust record.\n */\nexport function trustStorePath(agentDir: string = getAgentDir()): string {\n\treturn path.join(path.dirname(agentDir), \".agents\", \"trusted-workspaces.json\");\n}\n\nfunction readStore(agentDir: string): TrustedWorkspace[] {\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(trustStorePath(agentDir), \"utf8\")) as TrustStoreFile;\n\t\treturn Array.isArray(parsed?.workspaces)\n\t\t\t? parsed.workspaces.filter((w): w is TrustedWorkspace => typeof w?.path === \"string\")\n\t\t\t: [];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction writeStore(agentDir: string, workspaces: TrustedWorkspace[]): void {\n\tconst file = trustStorePath(agentDir);\n\tmkdirSync(path.dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify({ workspaces }, null, 2)}\\n`, \"utf8\");\n}\n\n/** Every trusted workspace, newest grant first. */\nexport function listTrustedWorkspaces(agentDir: string = getAgentDir()): TrustedWorkspace[] {\n\treturn [...readStore(agentDir)].sort((a, b) => (a.at < b.at ? 1 : -1));\n}\n\n/**\n * Whether `cwd` is trusted.\n *\n * Exact-path only, deliberately: trusting `~/src` must not silently trust every\n * repository ever cloned beneath it, which is what a prefix match would do the\n * first time someone trusts a directory one level too high.\n */\nexport function isWorkspaceTrusted(cwd: string, agentDir: string = getAgentDir()): boolean {\n\tconst target = path.resolve(cwd);\n\treturn readStore(agentDir).some((w) => path.resolve(w.path) === target);\n}\n\n/** Grant trust to `cwd`. Idempotent — re-granting refreshes the timestamp. */\nexport function trustWorkspace(cwd: string, agentDir: string = getAgentDir()): TrustedWorkspace {\n\tconst target = path.resolve(cwd);\n\tconst record: TrustedWorkspace = { path: target, at: new Date().toISOString() };\n\twriteStore(agentDir, [...readStore(agentDir).filter((w) => path.resolve(w.path) !== target), record]);\n\treturn record;\n}\n\n/** Revoke trust for `cwd`. Returns false when it was not trusted to begin with. */\nexport function untrustWorkspace(cwd: string, agentDir: string = getAgentDir()): boolean {\n\tconst target = path.resolve(cwd);\n\tconst before = readStore(agentDir);\n\tconst after = before.filter((w) => path.resolve(w.path) !== target);\n\tif (after.length === before.length) return false;\n\twriteStore(agentDir, after);\n\treturn true;\n}\n"]}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Workspace trust — the per-machine record of which working directories the user
3
+ * has agreed to run repository-supplied plugin code from.
4
+ *
5
+ * The problem it solves (docs/plugin-system-architecture.md §5.9): a plugin
6
+ * committed to a repository is code that runs for whoever clones it next. Its
7
+ * skills and commands are text the model reads, which is no worse than reading
8
+ * the repository itself, but its **hooks and MCP servers are processes** that
9
+ * start on session load. Nothing about a plugin's location can distinguish "I
10
+ * installed this here" from "this arrived in the clone", because everything in
11
+ * the repository travels with it — including any marker a plugin might carry to
12
+ * claim otherwise.
13
+ *
14
+ * So the record lives **outside the repository**, in the agent dir, keyed by
15
+ * absolute path. That is the whole design: trust cannot be forged by repository
16
+ * content, because repository content cannot write here. It is the same shape
17
+ * Claude Code's workspace trust dialog and VS Code's trusted folders use, and it
18
+ * carries the same known consequence — once a directory is trusted, code pulled
19
+ * into it later is trusted too. Trust is a statement about a *place you work*,
20
+ * not about a specific commit.
21
+ *
22
+ * Granting is a human act (`/plugin trust`, or an explicit `/plugin install
23
+ * --scope project`, where the person is demonstrably operating in the directory
24
+ * on purpose). The autonomous install path never grants it: a model deciding
25
+ * that a workspace should execute repository code is exactly the decision this
26
+ * record exists to keep with a person.
27
+ */
28
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
29
+ import * as path from "node:path";
30
+ import { getAgentDir } from "../../../config.js";
31
+ /**
32
+ * `~/.agents/trusted-workspaces.json`.
33
+ *
34
+ * Beside the marketplace registry rather than inside the repo, for the reason in
35
+ * the module docstring: a file the repository can write is not a trust record.
36
+ */
37
+ export function trustStorePath(agentDir = getAgentDir()) {
38
+ return path.join(path.dirname(agentDir), ".agents", "trusted-workspaces.json");
39
+ }
40
+ function readStore(agentDir) {
41
+ try {
42
+ const parsed = JSON.parse(readFileSync(trustStorePath(agentDir), "utf8"));
43
+ return Array.isArray(parsed?.workspaces)
44
+ ? parsed.workspaces.filter((w) => typeof w?.path === "string")
45
+ : [];
46
+ }
47
+ catch {
48
+ return [];
49
+ }
50
+ }
51
+ function writeStore(agentDir, workspaces) {
52
+ const file = trustStorePath(agentDir);
53
+ mkdirSync(path.dirname(file), { recursive: true });
54
+ writeFileSync(file, `${JSON.stringify({ workspaces }, null, 2)}\n`, "utf8");
55
+ }
56
+ /** Every trusted workspace, newest grant first. */
57
+ export function listTrustedWorkspaces(agentDir = getAgentDir()) {
58
+ return [...readStore(agentDir)].sort((a, b) => (a.at < b.at ? 1 : -1));
59
+ }
60
+ /**
61
+ * Whether `cwd` is trusted.
62
+ *
63
+ * Exact-path only, deliberately: trusting `~/src` must not silently trust every
64
+ * repository ever cloned beneath it, which is what a prefix match would do the
65
+ * first time someone trusts a directory one level too high.
66
+ */
67
+ export function isWorkspaceTrusted(cwd, agentDir = getAgentDir()) {
68
+ const target = path.resolve(cwd);
69
+ return readStore(agentDir).some((w) => path.resolve(w.path) === target);
70
+ }
71
+ /** Grant trust to `cwd`. Idempotent — re-granting refreshes the timestamp. */
72
+ export function trustWorkspace(cwd, agentDir = getAgentDir()) {
73
+ const target = path.resolve(cwd);
74
+ const record = { path: target, at: new Date().toISOString() };
75
+ writeStore(agentDir, [...readStore(agentDir).filter((w) => path.resolve(w.path) !== target), record]);
76
+ return record;
77
+ }
78
+ /** Revoke trust for `cwd`. Returns false when it was not trusted to begin with. */
79
+ export function untrustWorkspace(cwd, agentDir = getAgentDir()) {
80
+ const target = path.resolve(cwd);
81
+ const before = readStore(agentDir);
82
+ const after = before.filter((w) => path.resolve(w.path) !== target);
83
+ if (after.length === before.length)
84
+ return false;
85
+ writeStore(agentDir, after);
86
+ return true;
87
+ }
88
+ //# sourceMappingURL=trust.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/trust.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Workspace trust — the per-machine record of which working directories the user\n * has agreed to run repository-supplied plugin code from.\n *\n * The problem it solves (docs/plugin-system-architecture.md §5.9): a plugin\n * committed to a repository is code that runs for whoever clones it next. Its\n * skills and commands are text the model reads, which is no worse than reading\n * the repository itself, but its **hooks and MCP servers are processes** that\n * start on session load. Nothing about a plugin's location can distinguish \"I\n * installed this here\" from \"this arrived in the clone\", because everything in\n * the repository travels with it — including any marker a plugin might carry to\n * claim otherwise.\n *\n * So the record lives **outside the repository**, in the agent dir, keyed by\n * absolute path. That is the whole design: trust cannot be forged by repository\n * content, because repository content cannot write here. It is the same shape\n * Claude Code's workspace trust dialog and VS Code's trusted folders use, and it\n * carries the same known consequence — once a directory is trusted, code pulled\n * into it later is trusted too. Trust is a statement about a *place you work*,\n * not about a specific commit.\n *\n * Granting is a human act (`/plugin trust`, or an explicit `/plugin install\n * --scope project`, where the person is demonstrably operating in the directory\n * on purpose). The autonomous install path never grants it: a model deciding\n * that a workspace should execute repository code is exactly the decision this\n * record exists to keep with a person.\n */\n\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport { getAgentDir } from \"../../../config.js\";\n\n/** One trusted working directory. */\nexport interface TrustedWorkspace {\n\t/** Absolute, resolved path. */\n\tpath: string;\n\t/** When trust was granted, ISO-8601. Informational — nothing expires today. */\n\tat: string;\n}\n\ninterface TrustStoreFile {\n\tworkspaces?: TrustedWorkspace[];\n}\n\n/**\n * `~/.agents/trusted-workspaces.json`.\n *\n * Beside the marketplace registry rather than inside the repo, for the reason in\n * the module docstring: a file the repository can write is not a trust record.\n */\nexport function trustStorePath(agentDir: string = getAgentDir()): string {\n\treturn path.join(path.dirname(agentDir), \".agents\", \"trusted-workspaces.json\");\n}\n\nfunction readStore(agentDir: string): TrustedWorkspace[] {\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(trustStorePath(agentDir), \"utf8\")) as TrustStoreFile;\n\t\treturn Array.isArray(parsed?.workspaces)\n\t\t\t? parsed.workspaces.filter((w): w is TrustedWorkspace => typeof w?.path === \"string\")\n\t\t\t: [];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction writeStore(agentDir: string, workspaces: TrustedWorkspace[]): void {\n\tconst file = trustStorePath(agentDir);\n\tmkdirSync(path.dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify({ workspaces }, null, 2)}\\n`, \"utf8\");\n}\n\n/** Every trusted workspace, newest grant first. */\nexport function listTrustedWorkspaces(agentDir: string = getAgentDir()): TrustedWorkspace[] {\n\treturn [...readStore(agentDir)].sort((a, b) => (a.at < b.at ? 1 : -1));\n}\n\n/**\n * Whether `cwd` is trusted.\n *\n * Exact-path only, deliberately: trusting `~/src` must not silently trust every\n * repository ever cloned beneath it, which is what a prefix match would do the\n * first time someone trusts a directory one level too high.\n */\nexport function isWorkspaceTrusted(cwd: string, agentDir: string = getAgentDir()): boolean {\n\tconst target = path.resolve(cwd);\n\treturn readStore(agentDir).some((w) => path.resolve(w.path) === target);\n}\n\n/** Grant trust to `cwd`. Idempotent — re-granting refreshes the timestamp. */\nexport function trustWorkspace(cwd: string, agentDir: string = getAgentDir()): TrustedWorkspace {\n\tconst target = path.resolve(cwd);\n\tconst record: TrustedWorkspace = { path: target, at: new Date().toISOString() };\n\twriteStore(agentDir, [...readStore(agentDir).filter((w) => path.resolve(w.path) !== target), record]);\n\treturn record;\n}\n\n/** Revoke trust for `cwd`. Returns false when it was not trusted to begin with. */\nexport function untrustWorkspace(cwd: string, agentDir: string = getAgentDir()): boolean {\n\tconst target = path.resolve(cwd);\n\tconst before = readStore(agentDir);\n\tconst after = before.filter((w) => path.resolve(w.path) !== target);\n\tif (after.length === before.length) return false;\n\twriteStore(agentDir, after);\n\treturn true;\n}\n"]}
@@ -46,6 +46,7 @@ export declare const DEFAULT_SETTINGS: {
46
46
  nestedSubagentConcurrency: number;
47
47
  enableTodoWrite: true;
48
48
  enablePluginTools: false;
49
+ pluginInstallScope: "user";
49
50
  deferMcpSchemas: true;
50
51
  enableWebTools: false;
51
52
  enableEmbsearchTools: true;
@@ -1 +1 @@
1
- {"version":3,"file":"settings-defaults.d.ts","sourceRoot":"","sources":["../../src/core/settings-defaults.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiFT,CAAC","sourcesContent":["import type { Settings } from \"./settings-manager.js\";\n\nexport const DEFAULT_SETTINGS = {\n\ttransport: \"auto\",\n\tsteeringMode: \"one-at-a-time\",\n\tfollowUpMode: \"one-at-a-time\",\n\tcompaction: {\n\t\tenabled: true,\n\t\treserveTokens: 16384,\n\t\tkeepRecentTokens: 20000,\n\t\tmaxContextRatio: 0.75,\n\t},\n\ttoolOutput: {\n\t\tmaxBytes: 32 * 1024,\n\t\tmaxLines: 800,\n\t},\n\tcontextGc: {\n\t\tenabled: true,\n\t},\n\tvoice: {\n\t\tsilenceMs: 800,\n\t},\n\twebtools: {\n\t\ttimeoutSecs: 15,\n\t},\n\tdisabledTools: [],\n\ttoolOutputDisplay: \"standard\",\n\tbranchSummary: {\n\t\treserveTokens: 16384,\n\t\tskipPrompt: false,\n\t},\n\tretry: {\n\t\tenabled: true,\n\t\tmaxRetries: 3,\n\t\tbaseDelayMs: 2000,\n\t\tprovider: {\n\t\t\tmaxRetryDelayMs: 60000,\n\t\t},\n\t},\n\thideThinkingBlock: false,\n\tquietStartup: false,\n\tcollapseChangelog: false,\n\tenableInstallTelemetry: true,\n\tenableSkillCommands: true,\n\tenableSubagent: true,\n\twarmSubagents: false,\n\tmaxSubagentDepth: 2,\n\tnestedSubagentConcurrency: 2,\n\tenableTodoWrite: true,\n\tenablePluginTools: false,\n\tdeferMcpSchemas: true,\n\tenableWebTools: false,\n\tenableEmbsearchTools: true,\n\tembsearchThresholdBytes: 0,\n\tlight: false,\n\tterminal: {\n\t\tshowImages: true,\n\t\timageWidthCells: 60,\n\t\tclearOnShrink: false,\n\t\tshowTerminalProgress: false,\n\t\tchimeOnTurnComplete: false,\n\t},\n\timages: {\n\t\tautoResize: true,\n\t\tblockImages: false,\n\t},\n\tdoubleEscapeAction: \"tree\",\n\ttreeFilterMode: \"default\",\n\teditorBorder: \"box\",\n\teditorPaddingX: 1,\n\tautocompleteMaxVisible: 5,\n\tmarkdown: {\n\t\tcodeBlockIndent: \" \",\n\t},\n\twarnings: {\n\t\tanthropicExtraUsage: true,\n\t},\n\tpackages: [],\n\textensions: [],\n\tskills: [],\n\tprompts: [],\n\tslashCommands: [],\n\tthemes: [],\n} satisfies Settings;\n"]}
1
+ {"version":3,"file":"settings-defaults.d.ts","sourceRoot":"","sources":["../../src/core/settings-defaults.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkFT,CAAC","sourcesContent":["import type { Settings } from \"./settings-manager.js\";\n\nexport const DEFAULT_SETTINGS = {\n\ttransport: \"auto\",\n\tsteeringMode: \"one-at-a-time\",\n\tfollowUpMode: \"one-at-a-time\",\n\tcompaction: {\n\t\tenabled: true,\n\t\treserveTokens: 16384,\n\t\tkeepRecentTokens: 20000,\n\t\tmaxContextRatio: 0.75,\n\t},\n\ttoolOutput: {\n\t\tmaxBytes: 32 * 1024,\n\t\tmaxLines: 800,\n\t},\n\tcontextGc: {\n\t\tenabled: true,\n\t},\n\tvoice: {\n\t\tsilenceMs: 800,\n\t},\n\twebtools: {\n\t\ttimeoutSecs: 15,\n\t},\n\tdisabledTools: [],\n\ttoolOutputDisplay: \"standard\",\n\tbranchSummary: {\n\t\treserveTokens: 16384,\n\t\tskipPrompt: false,\n\t},\n\tretry: {\n\t\tenabled: true,\n\t\tmaxRetries: 3,\n\t\tbaseDelayMs: 2000,\n\t\tprovider: {\n\t\t\tmaxRetryDelayMs: 60000,\n\t\t},\n\t},\n\thideThinkingBlock: false,\n\tquietStartup: false,\n\tcollapseChangelog: false,\n\tenableInstallTelemetry: true,\n\tenableSkillCommands: true,\n\tenableSubagent: true,\n\twarmSubagents: false,\n\tmaxSubagentDepth: 2,\n\tnestedSubagentConcurrency: 2,\n\tenableTodoWrite: true,\n\tenablePluginTools: false,\n\tpluginInstallScope: \"user\",\n\tdeferMcpSchemas: true,\n\tenableWebTools: false,\n\tenableEmbsearchTools: true,\n\tembsearchThresholdBytes: 0,\n\tlight: false,\n\tterminal: {\n\t\tshowImages: true,\n\t\timageWidthCells: 60,\n\t\tclearOnShrink: false,\n\t\tshowTerminalProgress: false,\n\t\tchimeOnTurnComplete: false,\n\t},\n\timages: {\n\t\tautoResize: true,\n\t\tblockImages: false,\n\t},\n\tdoubleEscapeAction: \"tree\",\n\ttreeFilterMode: \"default\",\n\teditorBorder: \"box\",\n\teditorPaddingX: 1,\n\tautocompleteMaxVisible: 5,\n\tmarkdown: {\n\t\tcodeBlockIndent: \" \",\n\t},\n\twarnings: {\n\t\tanthropicExtraUsage: true,\n\t},\n\tpackages: [],\n\textensions: [],\n\tskills: [],\n\tprompts: [],\n\tslashCommands: [],\n\tthemes: [],\n} satisfies Settings;\n"]}
@@ -46,6 +46,7 @@ export const DEFAULT_SETTINGS = {
46
46
  nestedSubagentConcurrency: 2,
47
47
  enableTodoWrite: true,
48
48
  enablePluginTools: false,
49
+ pluginInstallScope: "user",
49
50
  deferMcpSchemas: true,
50
51
  enableWebTools: false,
51
52
  enableEmbsearchTools: true,
@@ -1 +1 @@
1
- {"version":3,"file":"settings-defaults.js","sourceRoot":"","sources":["../../src/core/settings-defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC/B,SAAS,EAAE,MAAM;IACjB,YAAY,EAAE,eAAe;IAC7B,YAAY,EAAE,eAAe;IAC7B,UAAU,EAAE;QACX,OAAO,EAAE,IAAI;QACb,aAAa,EAAE,KAAK;QACpB,gBAAgB,EAAE,KAAK;QACvB,eAAe,EAAE,IAAI;KACrB;IACD,UAAU,EAAE;QACX,QAAQ,EAAE,EAAE,GAAG,IAAI;QACnB,QAAQ,EAAE,GAAG;KACb;IACD,SAAS,EAAE;QACV,OAAO,EAAE,IAAI;KACb;IACD,KAAK,EAAE;QACN,SAAS,EAAE,GAAG;KACd;IACD,QAAQ,EAAE;QACT,WAAW,EAAE,EAAE;KACf;IACD,aAAa,EAAE,EAAE;IACjB,iBAAiB,EAAE,UAAU;IAC7B,aAAa,EAAE;QACd,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,KAAK;KACjB;IACD,KAAK,EAAE;QACN,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,CAAC;QACb,WAAW,EAAE,IAAI;QACjB,QAAQ,EAAE;YACT,eAAe,EAAE,KAAK;SACtB;KACD;IACD,iBAAiB,EAAE,KAAK;IACxB,YAAY,EAAE,KAAK;IACnB,iBAAiB,EAAE,KAAK;IACxB,sBAAsB,EAAE,IAAI;IAC5B,mBAAmB,EAAE,IAAI;IACzB,cAAc,EAAE,IAAI;IACpB,aAAa,EAAE,KAAK;IACpB,gBAAgB,EAAE,CAAC;IACnB,yBAAyB,EAAE,CAAC;IAC5B,eAAe,EAAE,IAAI;IACrB,iBAAiB,EAAE,KAAK;IACxB,eAAe,EAAE,IAAI;IACrB,cAAc,EAAE,KAAK;IACrB,oBAAoB,EAAE,IAAI;IAC1B,uBAAuB,EAAE,CAAC;IAC1B,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE;QACT,UAAU,EAAE,IAAI;QAChB,eAAe,EAAE,EAAE;QACnB,aAAa,EAAE,KAAK;QACpB,oBAAoB,EAAE,KAAK;QAC3B,mBAAmB,EAAE,KAAK;KAC1B;IACD,MAAM,EAAE;QACP,UAAU,EAAE,IAAI;QAChB,WAAW,EAAE,KAAK;KAClB;IACD,kBAAkB,EAAE,MAAM;IAC1B,cAAc,EAAE,SAAS;IACzB,YAAY,EAAE,KAAK;IACnB,cAAc,EAAE,CAAC;IACjB,sBAAsB,EAAE,CAAC;IACzB,QAAQ,EAAE;QACT,eAAe,EAAE,IAAI;KACrB;IACD,QAAQ,EAAE;QACT,mBAAmB,EAAE,IAAI;KACzB;IACD,QAAQ,EAAE,EAAE;IACZ,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,EAAE;IACV,OAAO,EAAE,EAAE;IACX,aAAa,EAAE,EAAE;IACjB,MAAM,EAAE,EAAE;CACS,CAAC","sourcesContent":["import type { Settings } from \"./settings-manager.js\";\n\nexport const DEFAULT_SETTINGS = {\n\ttransport: \"auto\",\n\tsteeringMode: \"one-at-a-time\",\n\tfollowUpMode: \"one-at-a-time\",\n\tcompaction: {\n\t\tenabled: true,\n\t\treserveTokens: 16384,\n\t\tkeepRecentTokens: 20000,\n\t\tmaxContextRatio: 0.75,\n\t},\n\ttoolOutput: {\n\t\tmaxBytes: 32 * 1024,\n\t\tmaxLines: 800,\n\t},\n\tcontextGc: {\n\t\tenabled: true,\n\t},\n\tvoice: {\n\t\tsilenceMs: 800,\n\t},\n\twebtools: {\n\t\ttimeoutSecs: 15,\n\t},\n\tdisabledTools: [],\n\ttoolOutputDisplay: \"standard\",\n\tbranchSummary: {\n\t\treserveTokens: 16384,\n\t\tskipPrompt: false,\n\t},\n\tretry: {\n\t\tenabled: true,\n\t\tmaxRetries: 3,\n\t\tbaseDelayMs: 2000,\n\t\tprovider: {\n\t\t\tmaxRetryDelayMs: 60000,\n\t\t},\n\t},\n\thideThinkingBlock: false,\n\tquietStartup: false,\n\tcollapseChangelog: false,\n\tenableInstallTelemetry: true,\n\tenableSkillCommands: true,\n\tenableSubagent: true,\n\twarmSubagents: false,\n\tmaxSubagentDepth: 2,\n\tnestedSubagentConcurrency: 2,\n\tenableTodoWrite: true,\n\tenablePluginTools: false,\n\tdeferMcpSchemas: true,\n\tenableWebTools: false,\n\tenableEmbsearchTools: true,\n\tembsearchThresholdBytes: 0,\n\tlight: false,\n\tterminal: {\n\t\tshowImages: true,\n\t\timageWidthCells: 60,\n\t\tclearOnShrink: false,\n\t\tshowTerminalProgress: false,\n\t\tchimeOnTurnComplete: false,\n\t},\n\timages: {\n\t\tautoResize: true,\n\t\tblockImages: false,\n\t},\n\tdoubleEscapeAction: \"tree\",\n\ttreeFilterMode: \"default\",\n\teditorBorder: \"box\",\n\teditorPaddingX: 1,\n\tautocompleteMaxVisible: 5,\n\tmarkdown: {\n\t\tcodeBlockIndent: \" \",\n\t},\n\twarnings: {\n\t\tanthropicExtraUsage: true,\n\t},\n\tpackages: [],\n\textensions: [],\n\tskills: [],\n\tprompts: [],\n\tslashCommands: [],\n\tthemes: [],\n} satisfies Settings;\n"]}
1
+ {"version":3,"file":"settings-defaults.js","sourceRoot":"","sources":["../../src/core/settings-defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC/B,SAAS,EAAE,MAAM;IACjB,YAAY,EAAE,eAAe;IAC7B,YAAY,EAAE,eAAe;IAC7B,UAAU,EAAE;QACX,OAAO,EAAE,IAAI;QACb,aAAa,EAAE,KAAK;QACpB,gBAAgB,EAAE,KAAK;QACvB,eAAe,EAAE,IAAI;KACrB;IACD,UAAU,EAAE;QACX,QAAQ,EAAE,EAAE,GAAG,IAAI;QACnB,QAAQ,EAAE,GAAG;KACb;IACD,SAAS,EAAE;QACV,OAAO,EAAE,IAAI;KACb;IACD,KAAK,EAAE;QACN,SAAS,EAAE,GAAG;KACd;IACD,QAAQ,EAAE;QACT,WAAW,EAAE,EAAE;KACf;IACD,aAAa,EAAE,EAAE;IACjB,iBAAiB,EAAE,UAAU;IAC7B,aAAa,EAAE;QACd,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,KAAK;KACjB;IACD,KAAK,EAAE;QACN,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,CAAC;QACb,WAAW,EAAE,IAAI;QACjB,QAAQ,EAAE;YACT,eAAe,EAAE,KAAK;SACtB;KACD;IACD,iBAAiB,EAAE,KAAK;IACxB,YAAY,EAAE,KAAK;IACnB,iBAAiB,EAAE,KAAK;IACxB,sBAAsB,EAAE,IAAI;IAC5B,mBAAmB,EAAE,IAAI;IACzB,cAAc,EAAE,IAAI;IACpB,aAAa,EAAE,KAAK;IACpB,gBAAgB,EAAE,CAAC;IACnB,yBAAyB,EAAE,CAAC;IAC5B,eAAe,EAAE,IAAI;IACrB,iBAAiB,EAAE,KAAK;IACxB,kBAAkB,EAAE,MAAM;IAC1B,eAAe,EAAE,IAAI;IACrB,cAAc,EAAE,KAAK;IACrB,oBAAoB,EAAE,IAAI;IAC1B,uBAAuB,EAAE,CAAC;IAC1B,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE;QACT,UAAU,EAAE,IAAI;QAChB,eAAe,EAAE,EAAE;QACnB,aAAa,EAAE,KAAK;QACpB,oBAAoB,EAAE,KAAK;QAC3B,mBAAmB,EAAE,KAAK;KAC1B;IACD,MAAM,EAAE;QACP,UAAU,EAAE,IAAI;QAChB,WAAW,EAAE,KAAK;KAClB;IACD,kBAAkB,EAAE,MAAM;IAC1B,cAAc,EAAE,SAAS;IACzB,YAAY,EAAE,KAAK;IACnB,cAAc,EAAE,CAAC;IACjB,sBAAsB,EAAE,CAAC;IACzB,QAAQ,EAAE;QACT,eAAe,EAAE,IAAI;KACrB;IACD,QAAQ,EAAE;QACT,mBAAmB,EAAE,IAAI;KACzB;IACD,QAAQ,EAAE,EAAE;IACZ,UAAU,EAAE,EAAE;IACd,MAAM,EAAE,EAAE;IACV,OAAO,EAAE,EAAE;IACX,aAAa,EAAE,EAAE;IACjB,MAAM,EAAE,EAAE;CACS,CAAC","sourcesContent":["import type { Settings } from \"./settings-manager.js\";\n\nexport const DEFAULT_SETTINGS = {\n\ttransport: \"auto\",\n\tsteeringMode: \"one-at-a-time\",\n\tfollowUpMode: \"one-at-a-time\",\n\tcompaction: {\n\t\tenabled: true,\n\t\treserveTokens: 16384,\n\t\tkeepRecentTokens: 20000,\n\t\tmaxContextRatio: 0.75,\n\t},\n\ttoolOutput: {\n\t\tmaxBytes: 32 * 1024,\n\t\tmaxLines: 800,\n\t},\n\tcontextGc: {\n\t\tenabled: true,\n\t},\n\tvoice: {\n\t\tsilenceMs: 800,\n\t},\n\twebtools: {\n\t\ttimeoutSecs: 15,\n\t},\n\tdisabledTools: [],\n\ttoolOutputDisplay: \"standard\",\n\tbranchSummary: {\n\t\treserveTokens: 16384,\n\t\tskipPrompt: false,\n\t},\n\tretry: {\n\t\tenabled: true,\n\t\tmaxRetries: 3,\n\t\tbaseDelayMs: 2000,\n\t\tprovider: {\n\t\t\tmaxRetryDelayMs: 60000,\n\t\t},\n\t},\n\thideThinkingBlock: false,\n\tquietStartup: false,\n\tcollapseChangelog: false,\n\tenableInstallTelemetry: true,\n\tenableSkillCommands: true,\n\tenableSubagent: true,\n\twarmSubagents: false,\n\tmaxSubagentDepth: 2,\n\tnestedSubagentConcurrency: 2,\n\tenableTodoWrite: true,\n\tenablePluginTools: false,\n\tpluginInstallScope: \"user\",\n\tdeferMcpSchemas: true,\n\tenableWebTools: false,\n\tenableEmbsearchTools: true,\n\tembsearchThresholdBytes: 0,\n\tlight: false,\n\tterminal: {\n\t\tshowImages: true,\n\t\timageWidthCells: 60,\n\t\tclearOnShrink: false,\n\t\tshowTerminalProgress: false,\n\t\tchimeOnTurnComplete: false,\n\t},\n\timages: {\n\t\tautoResize: true,\n\t\tblockImages: false,\n\t},\n\tdoubleEscapeAction: \"tree\",\n\ttreeFilterMode: \"default\",\n\teditorBorder: \"box\",\n\teditorPaddingX: 1,\n\tautocompleteMaxVisible: 5,\n\tmarkdown: {\n\t\tcodeBlockIndent: \" \",\n\t},\n\twarnings: {\n\t\tanthropicExtraUsage: true,\n\t},\n\tpackages: [],\n\textensions: [],\n\tskills: [],\n\tprompts: [],\n\tslashCommands: [],\n\tthemes: [],\n} satisfies Settings;\n"]}
@@ -161,6 +161,13 @@ export declare class SettingsManager {
161
161
  getPlatform(): string[] | undefined;
162
162
  getEnablePluginTools(): boolean;
163
163
  setEnablePluginTools(enabled: boolean): void;
164
+ /**
165
+ * Where an autonomous InstallPlugin puts a plugin. `/plugin install` asks the
166
+ * human instead of reading this — the setting exists precisely for the path
167
+ * where there is nobody to ask.
168
+ */
169
+ getPluginInstallScope(): "user" | "project";
170
+ setPluginInstallScope(scope: "user" | "project"): void;
164
171
  getDeferMcpSchemas(): boolean;
165
172
  setDeferMcpSchemas(enabled: boolean): void;
166
173
  getEnableWebTools(): boolean;