@kolisachint/hoocode-agent 0.5.12 → 0.5.14
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.
- package/CHANGELOG.md +73 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +16 -0
- package/dist/config.js.map +1 -1
- package/dist/core/context-files.d.ts +16 -3
- package/dist/core/context-files.d.ts.map +1 -1
- package/dist/core/context-files.js +77 -6
- package/dist/core/context-files.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +18 -6
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +35 -19
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/plugins/install.d.ts +51 -10
- package/dist/core/extensions/plugins/install.d.ts.map +1 -1
- package/dist/core/extensions/plugins/install.js +178 -31
- package/dist/core/extensions/plugins/install.js.map +1 -1
- package/dist/core/extensions/plugins/listing.d.ts +8 -2
- package/dist/core/extensions/plugins/listing.d.ts.map +1 -1
- package/dist/core/extensions/plugins/listing.js +29 -5
- package/dist/core/extensions/plugins/listing.js.map +1 -1
- package/dist/core/extensions/plugins/locations.d.ts +62 -12
- package/dist/core/extensions/plugins/locations.d.ts.map +1 -1
- package/dist/core/extensions/plugins/locations.js +74 -18
- package/dist/core/extensions/plugins/locations.js.map +1 -1
- package/dist/core/extensions/plugins/marketplace.d.ts +29 -3
- package/dist/core/extensions/plugins/marketplace.d.ts.map +1 -1
- package/dist/core/extensions/plugins/marketplace.js +40 -7
- package/dist/core/extensions/plugins/marketplace.js.map +1 -1
- package/dist/core/extensions/plugins/trust.d.ts +56 -0
- package/dist/core/extensions/plugins/trust.d.ts.map +1 -0
- package/dist/core/extensions/plugins/trust.js +88 -0
- package/dist/core/extensions/plugins/trust.js.map +1 -0
- package/dist/core/learn/digest.d.ts +17 -0
- package/dist/core/learn/digest.d.ts.map +1 -0
- package/dist/core/learn/digest.js +131 -0
- package/dist/core/learn/digest.js.map +1 -0
- package/dist/core/learn/extract.d.ts +178 -0
- package/dist/core/learn/extract.d.ts.map +1 -0
- package/dist/core/learn/extract.js +705 -0
- package/dist/core/learn/extract.js.map +1 -0
- package/dist/core/learn/normalize.d.ts +65 -0
- package/dist/core/learn/normalize.d.ts.map +1 -0
- package/dist/core/learn/normalize.js +245 -0
- package/dist/core/learn/normalize.js.map +1 -0
- package/dist/core/learn/state.d.ts +115 -0
- package/dist/core/learn/state.d.ts.map +1 -0
- package/dist/core/learn/state.js +151 -0
- package/dist/core/learn/state.js.map +1 -0
- package/dist/core/settings-defaults.d.ts +6 -0
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +6 -0
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-manager.d.ts +21 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +31 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +6 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/tools/plugins.d.ts.map +1 -1
- package/dist/core/tools/plugins.js +18 -3
- package/dist/core/tools/plugins.js.map +1 -1
- package/dist/extensions/core/hoo-core.d.ts.map +1 -1
- package/dist/extensions/core/hoo-core.js +2 -0
- package/dist/extensions/core/hoo-core.js.map +1 -1
- package/dist/extensions/core/learn.d.ts +22 -0
- package/dist/extensions/core/learn.d.ts.map +1 -0
- package/dist/extensions/core/learn.js +167 -0
- package/dist/extensions/core/learn.js.map +1 -0
- package/dist/extensions/core/marketplace.d.ts +7 -3
- package/dist/extensions/core/marketplace.d.ts.map +1 -1
- package/dist/extensions/core/marketplace.js +103 -11
- package/dist/extensions/core/marketplace.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +15 -2
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +6 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/resource-display.d.ts.map +1 -1
- package/dist/modes/interactive/resource-display.js +6 -1
- package/dist/modes/interactive/resource-display.js.map +1 -1
- package/docs/settings.md +31 -0
- package/docs/usage.md +64 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/install.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plugin install/uninstall/discovery — the shared engine behind both the\n * `/plugin` slash command (human-driven) and the model-facing lifecycle tools\n * (`SearchPlugins`, `InstallPlugin`, ...). Keeping it in one place means the two\n * surfaces can never drift on where plugins live or how sources resolve.\n *\n * Trust model (see docs/plugin-system-spec.md): *adding* a marketplace is the\n * human trust boundary; *installing* from an already-trusted marketplace is the\n * model's discretion (package-manager model), and must stay transparent and\n * reversible. This module performs the mechanical install/remove; the gating\n * (announce, injection carve-out) lives with the callers.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { getAgentDir } from \"../../../config.js\";\nimport { defaultPluginDirs } from \"../loader.js\";\nimport { hasAnyManifest } from \"./formats/index.js\";\nimport type { MarketplacePlatform } from \"./formats/types.js\";\nimport { discoverPlugins } from \"./index.js\";\nimport {\n\tcandidatePluginDirs,\n\tconsumptionPluginsDir,\n\tmarketplaceCacheDir,\n\tmarketplaceCacheMetaPath,\n\tmarketplaceCacheRoot,\n\tmarketplaceStorePath,\n\tsanitizeForDir,\n} from \"./locations.js\";\nimport type { NormalizedPlugin } from \"./manifest.js\";\nimport { parsePluginDir } from \"./manifest.js\";\nimport {\n\ttype MarketplacePluginSource,\n\ttype MarketplaceRecord,\n\tparseMarketplaceDir,\n\treadMarketplaceStore,\n\tresolvePluginSource,\n} from \"./marketplace.js\";\n\n/**\n * Marketplace registries older versions wrote into the working tree, newest\n * convention first. Still read so a user who added a marketplace before the\n * registry moved to the agent dir does not silently lose it; nothing writes here.\n */\nfunction legacyStorePaths(cwd: string): string[] {\n\treturn [path.join(cwd, \".agents\", \"marketplaces.json\"), path.join(cwd, \".hoocode\", \"marketplaces.json\")];\n}\n\n/** Absolute path to the curated default marketplace bundled with hoocode. */\nfunction defaultMarketplaceDir(): string {\n\treturn fileURLToPath(new URL(\"./default-marketplace/\", import.meta.url));\n}\n\n/** The default marketplace record — always present so source-level trust is meaningful out of the box. */\nexport function defaultMarketplaceRecord(): MarketplaceRecord {\n\treturn { location: \"hoocode-default\", dir: defaultMarketplaceDir() };\n}\n\n/**\n * Curated well-known marketplaces, trusted out of the box. Shipping an entry\n * here is a maintainer-level trust decision (the human half of the \"adding a\n * marketplace is the human trust boundary\" rule) — keep this list short and\n * high-trust. Indices are cloned lazily into the marketplace cache on first\n * search and refreshed on a TTL thereafter. Refreshing the *index* only changes\n * what is discoverable; installed plugins are still never auto-updated, and\n * plugin sources inside the official index are sha-pinned.\n */\nexport const WELL_KNOWN_MARKETPLACES: ReadonlyArray<{ name: string; url: string }> = [\n\t{ name: \"claude-plugins-official\", url: \"https://github.com/anthropics/claude-plugins-official\" },\n\t// The Copilot plugin directory ships both a `.github/plugin/marketplace.json`\n\t// and a `.claude-plugin/marketplace.json`, so it parses with\n\t// supportPlatform [\"claude\", \"github\"] — the default `github` source.\n\t{ name: \"copilot-plugins\", url: \"https://github.com/github/copilot-plugins\" },\n];\n\n/**\n * How long a cached marketplace index is considered fresh.\n *\n * Refreshing an *index* is safe to automate: it changes what is discoverable,\n * not what is installed. The rule that an installed plugin is never auto-updated\n * from a remote is untouched — that is the supply-chain boundary, and this is a\n * catalog listing.\n */\nconst MARKETPLACE_TTL_MS = 24 * 60 * 60 * 1000;\n\nfunction marketplaceTtlMs(): number {\n\tconst raw = Number(process.env.HOOCODE_MARKETPLACE_TTL_MS);\n\treturn Number.isFinite(raw) && raw >= 0 ? raw : MARKETPLACE_TTL_MS;\n}\n\ntype CacheMeta = Record<string, string>;\n\nfunction readCacheMeta(agentDir: string): CacheMeta {\n\tconst parsed = readJsonFile<CacheMeta>(marketplaceCacheMetaPath(agentDir));\n\treturn parsed && typeof parsed === \"object\" ? parsed : {};\n}\n\nfunction recordFetch(agentDir: string, url: string): void {\n\tconst meta = readCacheMeta(agentDir);\n\tmeta[url] = new Date().toISOString();\n\tconst file = marketplaceCacheMetaPath(agentDir);\n\tmkdirSync(path.dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify(meta, null, 2)}\\n`, \"utf8\");\n}\n\nfunction isStale(agentDir: string, url: string): boolean {\n\tconst ttl = marketplaceTtlMs();\n\tif (ttl === 0) return true;\n\tconst at = readCacheMeta(agentDir)[url];\n\tif (!at) return true;\n\tconst age = Date.now() - Date.parse(at);\n\treturn !Number.isFinite(age) || age > ttl;\n}\n\nfunction readJsonFile<T>(file: string): T | null {\n\ttry {\n\t\treturn JSON.parse(readFileSync(file, \"utf8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Bring an existing clone up to date, falling back to a fresh clone when the pull\n * cannot fast-forward (a force-pushed remote, or a corrupt cache).\n *\n * The re-clone goes to a temp directory and is swapped in only on success. An\n * earlier version deleted the cache first, which meant a refresh attempted while\n * offline destroyed a perfectly good index — the opposite of the \"offline is\n * non-fatal\" property the caller depends on.\n */\nasync function refreshClone(url: string, dir: string): Promise<{ code: number; stdout: string; stderr: string }> {\n\tconst pulled = await execGit([\"-C\", dir, \"pull\", \"--ff-only\"]);\n\tif (pulled.code === 0) return pulled;\n\n\tconst staging = mkdtempSync(path.join(os.tmpdir(), \"hoo-market-refresh-\"));\n\tconst target = path.join(staging, \"clone\");\n\tconst cloned = await cloneGitRepo(url, target);\n\tif (cloned.code !== 0) {\n\t\trmSync(staging, { recursive: true, force: true });\n\t\treturn cloned;\n\t}\n\trmSync(dir, { recursive: true, force: true });\n\tmkdirSync(path.dirname(dir), { recursive: true });\n\ttry {\n\t\trenameSync(target, dir);\n\t} catch {\n\t\tcpSync(target, dir, { recursive: true });\n\t}\n\trmSync(staging, { recursive: true, force: true });\n\treturn cloned;\n}\n\nexport interface EnsureMarketplacesOptions {\n\t/** Refresh regardless of the TTL. */\n\tforce?: boolean;\n}\n\n/**\n * Make sure every well-known marketplace index is cached and reasonably fresh.\n *\n * Clones what is missing and pulls what has gone stale. Without the TTL the\n * indices were fetched exactly once and never again, so a plugin added upstream\n * stayed permanently invisible — the inherit half of the system quietly stopped\n * inheriting. Offline is non-fatal: each failure is returned and search degrades\n * to whatever is already on disk.\n */\nexport async function ensureWellKnownMarketplaces(\n\tagentDir: string = getAgentDir(),\n\toptions: EnsureMarketplacesOptions = {},\n): Promise<string[]> {\n\tconst errors: string[] = [];\n\tfor (const wk of WELL_KNOWN_MARKETPLACES) {\n\t\tconst dir = marketplaceCacheDir(wk.url, agentDir);\n\t\tconst present = existsSync(dir);\n\t\tif (present && !options.force && !isStale(agentDir, wk.url)) continue;\n\n\t\tmkdirSync(path.dirname(dir), { recursive: true });\n\t\tconst res = present ? await refreshClone(wk.url, dir) : await cloneGitRepo(wk.url, dir);\n\t\tif (res.code !== 0) {\n\t\t\t// A failed *refresh* keeps the stale copy: an out-of-date index is far\n\t\t\t// more useful than none, and the network may simply be down.\n\t\t\tif (!present) rmSync(dir, { recursive: true, force: true });\n\t\t\terrors.push(`${wk.name}: ${(res.stderr || res.stdout).trim()}`);\n\t\t\tcontinue;\n\t\t}\n\t\trecordFetch(agentDir, wk.url);\n\t}\n\treturn errors;\n}\n\n/**\n * Refresh every cached index — well-known and user-added — ignoring the TTL.\n * The explicit half of the freshness story, for when a user knows the upstream\n * changed and does not want to wait out the interval.\n */\nexport async function refreshMarketplaces(\n\tcwd: string,\n\tagentDir: string = getAgentDir(),\n): Promise<{ refreshed: string[]; errors: string[] }> {\n\tconst errors = await ensureWellKnownMarketplaces(agentDir, { force: true });\n\tconst refreshed = WELL_KNOWN_MARKETPLACES.filter((wk) => existsSync(marketplaceCacheDir(wk.url, agentDir))).map(\n\t\t(wk) => wk.name,\n\t);\n\n\t// User-added marketplaces are cached the same way when they came from git; a\n\t// local-path marketplace is read in place and has nothing to refresh.\n\tfor (const record of readMarketplaceRecords(cwd, agentDir)) {\n\t\tconst isCachedClone = record.dir.startsWith(marketplaceCacheRoot(agentDir));\n\t\tif (!isCachedClone || WELL_KNOWN_MARKETPLACES.some((wk) => wk.url === record.location)) continue;\n\t\tconst res = await refreshClone(record.location, record.dir);\n\t\tif (res.code !== 0) errors.push(`${record.location}: ${(res.stderr || res.stdout).trim()}`);\n\t\telse {\n\t\t\trecordFetch(agentDir, record.location);\n\t\t\trefreshed.push(record.location);\n\t\t}\n\t}\n\treturn { refreshed, errors };\n}\n\n/**\n * All marketplace records in effect: the bundled default first (curated,\n * trusted), then user-added ones from `.agents/` (falling back to legacy\n * `.hoocode/`). Deduplicated by directory.\n */\nexport function readMarketplaceRecords(cwd: string, agentDir: string = getAgentDir()): MarketplaceRecord[] {\n\tconst records: MarketplaceRecord[] = [];\n\tconst def = defaultMarketplaceRecord();\n\tif (existsSync(def.dir)) records.push(def);\n\n\t// Well-known marketplaces participate once their index is cached locally\n\t// (see ensureWellKnownMarketplaces; SearchPlugins fetches lazily).\n\tfor (const wk of WELL_KNOWN_MARKETPLACES) {\n\t\tconst dir = marketplaceCacheDir(wk.url, agentDir);\n\t\tif (existsSync(dir) && !records.some((x) => x.dir === dir)) {\n\t\t\trecords.push({ location: wk.url, dir });\n\t\t}\n\t}\n\n\t// The registry lives in the agent dir. Project-local ones are a migration\n\t// read-path: merged in, never written back, so an older setup keeps working.\n\tconst user = [\n\t\t...readMarketplaceStore(marketplaceStorePath(agentDir)),\n\t\t...legacyStorePaths(cwd).flatMap((p) => readMarketplaceStore(p)),\n\t];\n\tfor (const r of user) {\n\t\tif (!records.some((x) => x.dir === r.dir)) records.push(r);\n\t}\n\treturn records;\n}\n\n/** A plugin offered by some registered marketplace (not necessarily installed). */\nexport interface AvailablePlugin {\n\tname: string;\n\tdescription?: string;\n\t/** Relative path, git URL, `npm:<spec>`, or structured source object. */\n\tsource: MarketplacePluginSource;\n\t/** Resolved source kind, for display and gating. */\n\tsourceKind: \"local\" | \"git\" | \"git-subdir\" | \"npm\";\n\tmarketplaceName: string;\n\tmarketplaceRoot: string;\n\t/** Platforms this entry targets (per-entry hint, else the marketplace's). */\n\tsupportPlatform: MarketplacePlatform[];\n}\n\n/** Every plugin offered across all registered marketplaces (first marketplace wins on name clash). */\nexport function listAvailablePlugins(cwd: string, agentDir: string = getAgentDir()): AvailablePlugin[] {\n\tconst out: AvailablePlugin[] = [];\n\tconst seen = new Set<string>();\n\tfor (const rec of readMarketplaceRecords(cwd, agentDir)) {\n\t\tconst market = parseMarketplaceDir(rec.dir);\n\t\tif (!market) continue;\n\t\tfor (const entry of market.plugins) {\n\t\t\tif (seen.has(entry.name)) continue;\n\t\t\tseen.add(entry.name);\n\t\t\tout.push({\n\t\t\t\tname: entry.name,\n\t\t\t\tdescription: entry.description,\n\t\t\t\tsource: entry.source,\n\t\t\t\tsourceKind: resolvePluginSource(entry.source, market.root).kind,\n\t\t\t\tmarketplaceName: market.name,\n\t\t\t\tmarketplaceRoot: market.root,\n\t\t\t\tsupportPlatform: entry.supportPlatform ?? market.supportPlatform,\n\t\t\t});\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Find a single available plugin by exact name. */\nexport function findAvailablePlugin(\n\tcwd: string,\n\tname: string,\n\tagentDir: string = getAgentDir(),\n): AvailablePlugin | undefined {\n\treturn listAvailablePlugins(cwd, agentDir).find((p) => p.name === name);\n}\n\n/** All currently installed plugins (project + global plugin dirs). */\nexport function listInstalledPlugins(cwd: string, agentDir: string = getAgentDir()): NormalizedPlugin[] {\n\treturn discoverPlugins(defaultPluginDirs(cwd, agentDir));\n}\n\n/** Whether a plugin with `name` (by id) is already installed. */\nexport function isPluginInstalled(cwd: string, name: string, agentDir: string = getAgentDir()): boolean {\n\treturn listInstalledPlugins(cwd, agentDir).some((p) => p.id === name);\n}\n\nfunction execGit(args: string[]): Promise<{ code: number; stdout: string; stderr: string }> {\n\treturn new Promise((resolve) => {\n\t\tconst child = spawn(\"git\", args);\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tchild.stdout?.on(\"data\", (d) => {\n\t\t\tstdout += d.toString();\n\t\t});\n\t\tchild.stderr?.on(\"data\", (d) => {\n\t\t\tstderr += d.toString();\n\t\t});\n\t\tchild.on(\"error\", (e) => resolve({ code: 1, stdout, stderr: stderr || String(e) }));\n\t\tchild.on(\"close\", (code) => resolve({ code: code ?? 1, stdout, stderr }));\n\t});\n}\n\n/**\n * Clone a git repository into `dest`.\n * If `ref` is provided, does a shallow clone of that branch/tag.\n * If only `sha` is provided, does a full clone and checks out the commit.\n * Returns the clone result; on failure `dest` may be partially created.\n */\nasync function cloneGitRepo(\n\turl: string,\n\tdest: string,\n\tref?: string,\n\tsha?: string,\n): Promise<{ code: number; stdout: string; stderr: string }> {\n\tif (ref) {\n\t\treturn execGit([\"clone\", \"--branch\", ref, \"--depth\", \"1\", \"--\", url, dest]);\n\t}\n\tif (sha) {\n\t\tconst cloneRes = await execGit([\"clone\", \"--\", url, dest]);\n\t\tif (cloneRes.code !== 0) return cloneRes;\n\t\treturn execGit([\"-C\", dest, \"checkout\", \"--quiet\", sha]);\n\t}\n\treturn execGit([\"clone\", \"--depth\", \"1\", \"--\", url, dest]);\n}\n\n/**\n * Resolve a git-subdir source: clone the repo, checkout the requested ref/sha,\n * and copy the subdirectory to `dest`. Cleans up the temporary clone on failure.\n */\nasync function installGitSubdir(\n\turl: string,\n\tsubdir: string,\n\tdest: string,\n\tref?: string,\n\tsha?: string,\n): Promise<{ ok: true } | { ok: false; message: string }> {\n\tconst tmpDir = mkdtempSync(path.join(os.tmpdir(), \"hoo-plugin-clone-\"));\n\ttry {\n\t\tconst cloneRes = await cloneGitRepo(url, tmpDir, ref, sha);\n\t\tif (cloneRes.code !== 0) {\n\t\t\treturn { ok: false, message: `git clone failed: ${cloneRes.stderr || cloneRes.stdout}`.trim() };\n\t\t}\n\t\tconst src = path.resolve(tmpDir, subdir);\n\t\tif (!existsSync(src)) {\n\t\t\treturn { ok: false, message: `Plugin subdirectory not found in cloned repo: ${subdir}` };\n\t\t}\n\t\tcpSync(src, dest, { recursive: true });\n\t\treturn { ok: true };\n\t} finally {\n\t\trmSync(tmpDir, { recursive: true, force: true });\n\t}\n}\n\nexport interface InstallOutcome {\n\tinstalled: boolean;\n\t/** Install destination directory (when installed). */\n\tdest?: string;\n\t/** Platforms the installed plugin supports (read back from disk). */\n\tsupportPlatform?: MarketplacePlatform[];\n\t/** Human-readable summary suitable for a tool result or notification. */\n\tmessage: string;\n}\n\n/**\n * Install an available plugin by name into the **consumption home** — the global\n * `~/.agents/plugins/`, not the working tree. A plugin is portable and reusable\n * across projects, so installing into the repo would both dirty `git status` and\n * hide the capability from every other checkout.\n *\n * Copies local sources; clones git sources. Transparent + reversible by\n * construction — the plugin lands in a named directory and\n * {@link uninstallPlugin} removes it. Callers activate the result (live\n * activation via AgentSession.activatePlugin, or a reload).\n */\nexport async function installAvailablePlugin(\n\tcwd: string,\n\tname: string,\n\tagentDir: string = getAgentDir(),\n): Promise<InstallOutcome> {\n\tconst found = findAvailablePlugin(cwd, name, agentDir);\n\tif (!found) return { installed: false, message: `Plugin \"${name}\" not found in any registered marketplace.` };\n\n\tconst resolved = resolvePluginSource(found.source, found.marketplaceRoot);\n\tconst home = consumptionPluginsDir(agentDir);\n\tconst dest = path.join(home, sanitizeForDir(name));\n\trmSync(dest, { recursive: true, force: true });\n\tmkdirSync(home, { recursive: true });\n\n\tif (resolved.kind === \"local\") {\n\t\tif (!existsSync(resolved.path)) {\n\t\t\treturn { installed: false, message: `Plugin source path not found: ${resolved.path}` };\n\t\t}\n\t\tcpSync(resolved.path, dest, { recursive: true });\n\t} else if (resolved.kind === \"git\") {\n\t\tconst res = await cloneGitRepo(resolved.url, dest, resolved.ref, resolved.sha);\n\t\tif (res.code !== 0) {\n\t\t\trmSync(dest, { recursive: true, force: true });\n\t\t\treturn { installed: false, message: `git clone failed: ${res.stderr || res.stdout}`.trim() };\n\t\t}\n\t} else if (resolved.kind === \"git-subdir\") {\n\t\tconst res = await installGitSubdir(resolved.url, resolved.path, dest, resolved.ref, resolved.sha);\n\t\tif (!res.ok) {\n\t\t\trmSync(dest, { recursive: true, force: true });\n\t\t\treturn { installed: false, message: res.message };\n\t\t}\n\t} else {\n\t\treturn { installed: false, message: `npm plugin sources are not supported yet (${resolved.spec}).` };\n\t}\n\n\tlet parsed = parsePluginDir(dest);\n\tif (!hasAnyManifest(dest)) {\n\t\t// Manifest-less plugin dir (some marketplaces index bare capability trees,\n\t\t// e.g. a plugin that is just a `skills/` directory). Such a directory now\n\t\t// *parses* on its own — the vendors make the manifest optional — but the\n\t\t// entry's name and description live only in the marketplace index, and the\n\t\t// derived id would be the sanitized directory name. Synthesize a native\n\t\t// manifest so both survive the install.\n\t\tconst manifestDir = path.join(dest, \".agents-plugin\");\n\t\tmkdirSync(manifestDir, { recursive: true });\n\t\twriteFileSync(\n\t\t\tpath.join(manifestDir, \"plugin.json\"),\n\t\t\t`${JSON.stringify(\n\t\t\t\t{ name: found.name, ...(found.description ? { description: found.description } : {}) },\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t)}\\n`,\n\t\t\t\"utf8\",\n\t\t);\n\t\tparsed = parsePluginDir(dest);\n\t}\n\tif (!parsed) {\n\t\trmSync(dest, { recursive: true, force: true });\n\t\treturn { installed: false, message: `Installed source for \"${name}\" has no recognizable plugin manifest.` };\n\t}\n\treturn {\n\t\tinstalled: true,\n\t\tdest,\n\t\tsupportPlatform: parsed.supportPlatform,\n\t\tmessage:\n\t\t\t`Installed \"${name}\" from marketplace \"${found.marketplaceName}\" ` +\n\t\t\t`(${parsed.supportPlatform.join(\", \")}) to ${dest}. ` +\n\t\t\t`Remove it with UninstallPlugin.`,\n\t};\n}\n\nexport interface UninstallOutcome {\n\tremoved: boolean;\n\tmessage: string;\n}\n\n/**\n * Remove an installed or authored plugin from every location it could occupy:\n * the consumption home, both production homes, and the legacy project-local\n * directories that older versions wrote into.\n */\nexport function uninstallPlugin(cwd: string, name: string, agentDir: string = getAgentDir()): UninstallOutcome {\n\tconst candidates = [\n\t\t...candidatePluginDirs(cwd, name, agentDir),\n\t\tpath.join(cwd, \".hoocode\", \"plugins\", sanitizeForDir(name)),\n\t];\n\tconst present = candidates.filter((p) => existsSync(p));\n\tif (present.length === 0) return { removed: false, message: `Plugin \"${name}\" is not installed.` };\n\tfor (const p of present) rmSync(p, { recursive: true, force: true });\n\treturn { removed: true, message: `Removed \"${name}\".` };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/install.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plugin install/uninstall/discovery — the shared engine behind both the\n * `/plugin` slash command (human-driven) and the model-facing lifecycle tools\n * (`SearchPlugins`, `InstallPlugin`, ...). Keeping it in one place means the two\n * surfaces can never drift on where plugins live or how sources resolve.\n *\n * Trust model (see docs/plugin-system-spec.md): *adding* a marketplace is the\n * human trust boundary; *installing* from an already-trusted marketplace is the\n * model's discretion (package-manager model), and must stay transparent and\n * reversible. This module performs the mechanical install/remove; the gating\n * (announce, injection carve-out) lives with the callers.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { getAgentDir } from \"../../../config.js\";\nimport { defaultPluginDirs } from \"../loader.js\";\nimport { hasAnyManifest } from \"./formats/index.js\";\nimport type { MarketplacePlatform } from \"./formats/types.js\";\nimport { discoverPlugins } from \"./index.js\";\nimport {\n\tcandidatePluginDirs,\n\tinstallHomeForScope,\n\tmarketplaceCacheDir,\n\tmarketplaceCacheMetaPath,\n\tmarketplaceCacheRoot,\n\tmarketplaceStorePath,\n\ttype PluginInstallScope,\n\tpluginHomeRoots,\n\tsanitizeForDir,\n} from \"./locations.js\";\nimport type { NormalizedPlugin } from \"./manifest.js\";\nimport { parsePluginDir } from \"./manifest.js\";\nimport {\n\ttype MarketplacePluginSource,\n\ttype MarketplaceRecord,\n\tparseMarketplaceDir,\n\treadMarketplaceStore,\n\tresolvePluginSource,\n} from \"./marketplace.js\";\n\n/**\n * Marketplace registries older versions wrote into the working tree, newest\n * convention first. Still read so a user who added a marketplace before the\n * registry moved to the agent dir does not silently lose it; nothing writes here.\n */\nfunction legacyStorePaths(cwd: string): string[] {\n\treturn [path.join(cwd, \".agents\", \"marketplaces.json\"), path.join(cwd, \".hoocode\", \"marketplaces.json\")];\n}\n\n/** Absolute path to the curated default marketplace bundled with hoocode. */\nfunction defaultMarketplaceDir(): string {\n\treturn fileURLToPath(new URL(\"./default-marketplace/\", import.meta.url));\n}\n\n/** The default marketplace record — always present so source-level trust is meaningful out of the box. */\nexport function defaultMarketplaceRecord(): MarketplaceRecord {\n\treturn { location: \"hoocode-default\", dir: defaultMarketplaceDir() };\n}\n\n/**\n * Curated well-known marketplaces, trusted out of the box. Shipping an entry\n * here is a maintainer-level trust decision (the human half of the \"adding a\n * marketplace is the human trust boundary\" rule) — keep this list short and\n * high-trust. Indices are cloned lazily into the marketplace cache on first\n * search and refreshed on a TTL thereafter. Refreshing the *index* only changes\n * what is discoverable; installed plugins are still never auto-updated, and\n * plugin sources inside the official index are sha-pinned.\n */\nexport const WELL_KNOWN_MARKETPLACES: ReadonlyArray<{ name: string; url: string }> = [\n\t{ name: \"claude-plugins-official\", url: \"https://github.com/anthropics/claude-plugins-official\" },\n\t// The Copilot plugin directory ships both a `.github/plugin/marketplace.json`\n\t// and a `.claude-plugin/marketplace.json`, so it parses with\n\t// supportPlatform [\"claude\", \"github\"] — the default `github` source.\n\t{ name: \"copilot-plugins\", url: \"https://github.com/github/copilot-plugins\" },\n\t// GitHub's community index, `.github/plugin/marketplace.json`. Same owner as\n\t// copilot-plugins, which is what makes it a source-trust decision of the same\n\t// kind. Note that most of its in-repo entries carry their content in Copilot\n\t// UI `extensions/`, a surface hoocode does not model — those install and load\n\t// nothing, and say so.\n\t{ name: \"awesome-copilot\", url: \"https://github.com/github/awesome-copilot\" },\n];\n\n/**\n * How long a cached marketplace index is considered fresh.\n *\n * Refreshing an *index* is safe to automate: it changes what is discoverable,\n * not what is installed. The rule that an installed plugin is never auto-updated\n * from a remote is untouched — that is the supply-chain boundary, and this is a\n * catalog listing.\n */\nconst MARKETPLACE_TTL_MS = 24 * 60 * 60 * 1000;\n\nfunction marketplaceTtlMs(): number {\n\tconst raw = Number(process.env.HOOCODE_MARKETPLACE_TTL_MS);\n\treturn Number.isFinite(raw) && raw >= 0 ? raw : MARKETPLACE_TTL_MS;\n}\n\ntype CacheMeta = Record<string, string>;\n\nfunction readCacheMeta(agentDir: string): CacheMeta {\n\tconst parsed = readJsonFile<CacheMeta>(marketplaceCacheMetaPath(agentDir));\n\treturn parsed && typeof parsed === \"object\" ? parsed : {};\n}\n\nfunction recordFetch(agentDir: string, url: string): void {\n\tconst meta = readCacheMeta(agentDir);\n\tmeta[url] = new Date().toISOString();\n\tconst file = marketplaceCacheMetaPath(agentDir);\n\tmkdirSync(path.dirname(file), { recursive: true });\n\twriteFileSync(file, `${JSON.stringify(meta, null, 2)}\\n`, \"utf8\");\n}\n\nfunction isStale(agentDir: string, url: string): boolean {\n\tconst ttl = marketplaceTtlMs();\n\tif (ttl === 0) return true;\n\tconst at = readCacheMeta(agentDir)[url];\n\tif (!at) return true;\n\tconst age = Date.now() - Date.parse(at);\n\treturn !Number.isFinite(age) || age > ttl;\n}\n\nfunction readJsonFile<T>(file: string): T | null {\n\ttry {\n\t\treturn JSON.parse(readFileSync(file, \"utf8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Bring an existing clone up to date, falling back to a fresh clone when the pull\n * cannot fast-forward (a force-pushed remote, or a corrupt cache).\n *\n * The re-clone goes to a temp directory and is swapped in only on success. An\n * earlier version deleted the cache first, which meant a refresh attempted while\n * offline destroyed a perfectly good index — the opposite of the \"offline is\n * non-fatal\" property the caller depends on.\n */\nasync function refreshClone(url: string, dir: string): Promise<{ code: number; stdout: string; stderr: string }> {\n\tconst pulled = await execGit([\"-C\", dir, \"pull\", \"--ff-only\"]);\n\tif (pulled.code === 0) return pulled;\n\n\tconst staging = mkdtempSync(path.join(os.tmpdir(), \"hoo-market-refresh-\"));\n\tconst target = path.join(staging, \"clone\");\n\tconst cloned = await cloneGitRepo(url, target);\n\tif (cloned.code !== 0) {\n\t\trmSync(staging, { recursive: true, force: true });\n\t\treturn cloned;\n\t}\n\trmSync(dir, { recursive: true, force: true });\n\tmkdirSync(path.dirname(dir), { recursive: true });\n\ttry {\n\t\trenameSync(target, dir);\n\t} catch {\n\t\tcpSync(target, dir, { recursive: true });\n\t}\n\trmSync(staging, { recursive: true, force: true });\n\treturn cloned;\n}\n\nexport interface EnsureMarketplacesOptions {\n\t/** Refresh regardless of the TTL. */\n\tforce?: boolean;\n}\n\n/**\n * Make sure every well-known marketplace index is cached and reasonably fresh.\n *\n * Clones what is missing and pulls what has gone stale. Without the TTL the\n * indices were fetched exactly once and never again, so a plugin added upstream\n * stayed permanently invisible — the inherit half of the system quietly stopped\n * inheriting. Offline is non-fatal: each failure is returned and search degrades\n * to whatever is already on disk.\n */\nexport async function ensureWellKnownMarketplaces(\n\tagentDir: string = getAgentDir(),\n\toptions: EnsureMarketplacesOptions = {},\n): Promise<string[]> {\n\tconst errors: string[] = [];\n\tfor (const wk of WELL_KNOWN_MARKETPLACES) {\n\t\tconst dir = marketplaceCacheDir(wk.url, agentDir);\n\t\tconst present = existsSync(dir);\n\t\tif (present && !options.force && !isStale(agentDir, wk.url)) continue;\n\n\t\tmkdirSync(path.dirname(dir), { recursive: true });\n\t\tconst res = present ? await refreshClone(wk.url, dir) : await cloneGitRepo(wk.url, dir);\n\t\tif (res.code !== 0) {\n\t\t\t// A failed *refresh* keeps the stale copy: an out-of-date index is far\n\t\t\t// more useful than none, and the network may simply be down.\n\t\t\tif (!present) rmSync(dir, { recursive: true, force: true });\n\t\t\terrors.push(`${wk.name}: ${(res.stderr || res.stdout).trim()}`);\n\t\t\tcontinue;\n\t\t}\n\t\trecordFetch(agentDir, wk.url);\n\t}\n\treturn errors;\n}\n\n/**\n * Refresh every cached index — well-known and user-added — ignoring the TTL.\n * The explicit half of the freshness story, for when a user knows the upstream\n * changed and does not want to wait out the interval.\n */\nexport async function refreshMarketplaces(\n\tcwd: string,\n\tagentDir: string = getAgentDir(),\n): Promise<{ refreshed: string[]; errors: string[] }> {\n\tconst errors = await ensureWellKnownMarketplaces(agentDir, { force: true });\n\tconst refreshed = WELL_KNOWN_MARKETPLACES.filter((wk) => existsSync(marketplaceCacheDir(wk.url, agentDir))).map(\n\t\t(wk) => wk.name,\n\t);\n\n\t// User-added marketplaces are cached the same way when they came from git; a\n\t// local-path marketplace is read in place and has nothing to refresh.\n\tfor (const record of readMarketplaceRecords(cwd, agentDir)) {\n\t\tconst isCachedClone = record.dir.startsWith(marketplaceCacheRoot(agentDir));\n\t\tif (!isCachedClone || WELL_KNOWN_MARKETPLACES.some((wk) => wk.url === record.location)) continue;\n\t\tconst res = await refreshClone(record.location, record.dir);\n\t\tif (res.code !== 0) errors.push(`${record.location}: ${(res.stderr || res.stdout).trim()}`);\n\t\telse {\n\t\t\trecordFetch(agentDir, record.location);\n\t\t\trefreshed.push(record.location);\n\t\t}\n\t}\n\treturn { refreshed, errors };\n}\n\n/**\n * All marketplace records in effect: the bundled default first (curated,\n * trusted), then user-added ones from `.agents/` (falling back to legacy\n * `.hoocode/`). Deduplicated by directory.\n */\nexport function readMarketplaceRecords(cwd: string, agentDir: string = getAgentDir()): MarketplaceRecord[] {\n\tconst records: MarketplaceRecord[] = [];\n\tconst def = defaultMarketplaceRecord();\n\tif (existsSync(def.dir)) records.push(def);\n\n\t// Well-known marketplaces participate once their index is cached locally\n\t// (see ensureWellKnownMarketplaces; SearchPlugins fetches lazily).\n\tfor (const wk of WELL_KNOWN_MARKETPLACES) {\n\t\tconst dir = marketplaceCacheDir(wk.url, agentDir);\n\t\tif (existsSync(dir) && !records.some((x) => x.dir === dir)) {\n\t\t\trecords.push({ location: wk.url, dir });\n\t\t}\n\t}\n\n\t// The registry lives in the agent dir. Project-local ones are a migration\n\t// read-path: merged in, never written back, so an older setup keeps working.\n\tconst user = [\n\t\t...readMarketplaceStore(marketplaceStorePath(agentDir)),\n\t\t...legacyStorePaths(cwd).flatMap((p) => readMarketplaceStore(p)),\n\t];\n\tfor (const r of user) {\n\t\tif (!records.some((x) => x.dir === r.dir)) records.push(r);\n\t}\n\treturn records;\n}\n\n/** A plugin offered by some registered marketplace (not necessarily installed). */\nexport interface AvailablePlugin {\n\tname: string;\n\tdescription?: string;\n\t/** Relative path, git URL, `npm:<spec>`, or structured source object. */\n\tsource: MarketplacePluginSource;\n\t/** Resolved source kind, for display and gating. `npm`/`archive` are listed but not installable. */\n\tsourceKind: \"local\" | \"git\" | \"git-subdir\" | \"npm\" | \"archive\";\n\tmarketplaceName: string;\n\tmarketplaceRoot: string;\n\t/** Platforms this entry targets (per-entry hint, else the marketplace's). */\n\tsupportPlatform: MarketplacePlatform[];\n}\n\n/** Every plugin offered across all registered marketplaces (first marketplace wins on name clash). */\nexport function listAvailablePlugins(cwd: string, agentDir: string = getAgentDir()): AvailablePlugin[] {\n\tconst out: AvailablePlugin[] = [];\n\tconst seen = new Set<string>();\n\tfor (const rec of readMarketplaceRecords(cwd, agentDir)) {\n\t\tconst market = parseMarketplaceDir(rec.dir);\n\t\tif (!market) continue;\n\t\tfor (const entry of market.plugins) {\n\t\t\tif (seen.has(entry.name)) continue;\n\t\t\tseen.add(entry.name);\n\t\t\tout.push({\n\t\t\t\tname: entry.name,\n\t\t\t\tdescription: entry.description,\n\t\t\t\tsource: entry.source,\n\t\t\t\tsourceKind: resolvePluginSource(entry.source, market.root).kind,\n\t\t\t\tmarketplaceName: market.name,\n\t\t\t\tmarketplaceRoot: market.root,\n\t\t\t\tsupportPlatform: entry.supportPlatform ?? market.supportPlatform,\n\t\t\t});\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Find a single available plugin by exact name. */\nexport function findAvailablePlugin(\n\tcwd: string,\n\tname: string,\n\tagentDir: string = getAgentDir(),\n): AvailablePlugin | undefined {\n\treturn listAvailablePlugins(cwd, agentDir).find((p) => p.name === name);\n}\n\n/** All currently installed plugins (project + global plugin dirs). */\nexport function listInstalledPlugins(cwd: string, agentDir: string = getAgentDir()): NormalizedPlugin[] {\n\treturn discoverPlugins(defaultPluginDirs(cwd, agentDir));\n}\n\n/**\n * Whether `name` is already installed, by either name it can go by.\n *\n * A marketplace entry name and the plugin's own manifest `name` are allowed to\n * differ — both vendors say so outright — and installs land in a directory named\n * for the *entry*, while discovery reports the *manifest* id. Matching ids alone\n * meant a plugin installed as `42crunch-api-security-testing` (manifest id\n * `api-security-testing`) never counted as installed, so every InstallPlugin\n * call for the catalog name re-cloned it.\n */\nexport function isPluginInstalled(cwd: string, name: string, agentDir: string = getAgentDir()): boolean {\n\tif (listInstalledPlugins(cwd, agentDir).some((p) => p.id === name)) return true;\n\treturn candidatePluginDirs(cwd, name, agentDir).some((p) => existsSync(p));\n}\n\nfunction execGit(args: string[]): Promise<{ code: number; stdout: string; stderr: string }> {\n\treturn new Promise((resolve) => {\n\t\tconst child = spawn(\"git\", args);\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tchild.stdout?.on(\"data\", (d) => {\n\t\t\tstdout += d.toString();\n\t\t});\n\t\tchild.stderr?.on(\"data\", (d) => {\n\t\t\tstderr += d.toString();\n\t\t});\n\t\tchild.on(\"error\", (e) => resolve({ code: 1, stdout, stderr: stderr || String(e) }));\n\t\tchild.on(\"close\", (code) => resolve({ code: code ?? 1, stdout, stderr }));\n\t});\n}\n\n/**\n * Materialize exactly `sha` at `dest`, without cloning the repository's history.\n *\n * `git fetch --depth 1 <sha>` is what makes honoring a pin affordable: a pinned\n * commit is usually not the tip of any branch, so the alternative is a full\n * clone — 55s on a large plugin repo against ~1s here, on sources where two\n * thirds of the official Claude marketplace is pinned. Servers that refuse a\n * by-sha fetch (AWS CodeCommit and other non-GitHub hosts) fall back to a clone\n * deep enough to reach the commit, then check it out.\n */\nasync function fetchGitCommit(\n\turl: string,\n\tdest: string,\n\tsha: string,\n\tref?: string,\n): Promise<{ code: number; stdout: string; stderr: string }> {\n\tmkdirSync(dest, { recursive: true });\n\tconst init = await execGit([\"init\", \"--quiet\", dest]);\n\tif (init.code === 0) {\n\t\tconst remote = await execGit([\"-C\", dest, \"remote\", \"add\", \"origin\", url]);\n\t\tif (remote.code === 0) {\n\t\t\tconst fetched = await execGit([\"-C\", dest, \"fetch\", \"--quiet\", \"--depth\", \"1\", \"origin\", sha]);\n\t\t\tif (fetched.code === 0) {\n\t\t\t\tconst checkout = await execGit([\"-C\", dest, \"checkout\", \"--quiet\", \"FETCH_HEAD\"]);\n\t\t\t\tif (checkout.code === 0) return checkout;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Fallback: a full clone (shallow cannot reach an arbitrary commit), then the\n\t// pin. `ref` narrows it when the entry named one.\n\trmSync(dest, { recursive: true, force: true });\n\tconst cloned = ref\n\t\t? await execGit([\"clone\", \"--branch\", ref, \"--\", url, dest])\n\t\t: await execGit([\"clone\", \"--\", url, dest]);\n\tif (cloned.code !== 0) return cloned;\n\treturn execGit([\"-C\", dest, \"checkout\", \"--quiet\", sha]);\n}\n\n/**\n * Clone a git repository into `dest` at the requested pin.\n *\n * **`sha` outranks `ref`.** Both vendors define it that way (\"when both `ref`\n * and `sha` are set, the `sha` is the effective pin\"), and a marketplace that\n * ships both is stating the exact commit it vouches for — a tag can be moved or\n * re-pointed after the catalog pinned it, so preferring `ref` installed a\n * different commit than the index promised. It is not hypothetical: the official\n * catalog's `42crunch-api-security-testing` pins `30287f5` while its `v1.5.5`\n * tag now resolves to `faf5305`.\n *\n * Returns the git result; on failure `dest` may be partially created.\n */\nasync function cloneGitRepo(\n\turl: string,\n\tdest: string,\n\tref?: string,\n\tsha?: string,\n): Promise<{ code: number; stdout: string; stderr: string }> {\n\tif (sha) return fetchGitCommit(url, dest, sha, ref);\n\tif (ref) return execGit([\"clone\", \"--branch\", ref, \"--depth\", \"1\", \"--\", url, dest]);\n\treturn execGit([\"clone\", \"--depth\", \"1\", \"--\", url, dest]);\n}\n\n/**\n * Resolve a git-subdir source: clone the repo, checkout the requested ref/sha,\n * and copy the subdirectory to `dest`. Cleans up the temporary clone on failure.\n */\nasync function installGitSubdir(\n\turl: string,\n\tsubdir: string,\n\tdest: string,\n\tref?: string,\n\tsha?: string,\n): Promise<{ ok: true } | { ok: false; message: string }> {\n\tconst tmpDir = mkdtempSync(path.join(os.tmpdir(), \"hoo-plugin-clone-\"));\n\ttry {\n\t\tconst cloneRes = await cloneGitRepo(url, tmpDir, ref, sha);\n\t\tif (cloneRes.code !== 0) {\n\t\t\treturn { ok: false, message: `git clone failed: ${cloneRes.stderr || cloneRes.stdout}`.trim() };\n\t\t}\n\t\tconst src = path.resolve(tmpDir, subdir);\n\t\tif (!existsSync(src)) {\n\t\t\treturn { ok: false, message: `Plugin subdirectory not found in cloned repo: ${subdir}` };\n\t\t}\n\t\tcpSync(src, dest, { recursive: true });\n\t\treturn { ok: true };\n\t} finally {\n\t\trmSync(tmpDir, { recursive: true, force: true });\n\t}\n}\n\nexport interface InstallOptions {\n\t/**\n\t * Where the plugin lands. Defaults to `user`. The human path (`/plugin\n\t * install`) asks; the autonomous path passes the `pluginInstallScope` setting.\n\t */\n\tscope?: PluginInstallScope;\n}\n\nexport interface InstallOutcome {\n\tinstalled: boolean;\n\t/** Install destination directory (when installed). */\n\tdest?: string;\n\t/** The scope the plugin was installed at. */\n\tscope?: PluginInstallScope;\n\t/**\n\t * The plugin's own manifest id, when it differs from the marketplace entry\n\t * name it was installed by. This is the id `ListPlugins` reports, so the\n\t * caller can name both rather than leaving the model to reconcile them.\n\t */\n\tid?: string;\n\t/** Platforms the installed plugin supports (read back from disk). */\n\tsupportPlatform?: MarketplacePlatform[];\n\t/** Human-readable summary suitable for a tool result or notification. */\n\tmessage: string;\n}\n\n/** Capability surfaces hoocode can actually load out of an installed plugin. */\nfunction loadableCapabilities(plugin: NormalizedPlugin): string[] {\n\treturn [\n\t\tplugin.skillsDir && \"skills\",\n\t\tplugin.commandsDir && \"commands\",\n\t\tplugin.agentsDir && \"subagents\",\n\t\tplugin.themesDir && \"themes\",\n\t\tplugin.hooks && \"hooks\",\n\t\tplugin.mcpServers && Object.keys(plugin.mcpServers).length > 0 && \"MCP servers\",\n\t\tplugin.providers?.length && \"providers\",\n\t].filter((c): c is string => typeof c === \"string\");\n}\n\n/**\n * Install an available plugin by name into the consumption home for\n * `options.scope` — `~/.agents/plugins/` for `user` (the default), or\n * `<cwd>/.agents/plugins/` for `project`.\n *\n * `user` is the default for a reason worth keeping in view: a plugin is portable\n * and reusable across projects, so installing into the repo hides the capability\n * from every other checkout and puts content into `git status` that has nothing\n * to do with the change being made. `project` inverts both of those on purpose —\n * it is how a team pins a plugin to a repository — so it is chosen, never\n * defaulted into.\n *\n * Copies local sources; clones git sources. Transparent + reversible by\n * construction — the plugin lands in a named directory and\n * {@link uninstallPlugin} removes it from either scope. Callers activate the\n * result (live activation via AgentSession.activatePlugin, or a reload).\n */\nexport async function installAvailablePlugin(\n\tcwd: string,\n\tname: string,\n\tagentDir: string = getAgentDir(),\n\toptions: InstallOptions = {},\n): Promise<InstallOutcome> {\n\tconst found = findAvailablePlugin(cwd, name, agentDir);\n\tif (!found) return { installed: false, message: `Plugin \"${name}\" not found in any registered marketplace.` };\n\n\tconst scope = options.scope ?? \"user\";\n\tconst resolved = resolvePluginSource(found.source, found.marketplaceRoot);\n\tconst home = installHomeForScope(scope, cwd, agentDir);\n\tconst dest = path.join(home, sanitizeForDir(name));\n\trmSync(dest, { recursive: true, force: true });\n\tmkdirSync(home, { recursive: true });\n\n\tif (resolved.kind === \"local\") {\n\t\tif (!existsSync(resolved.path)) {\n\t\t\treturn { installed: false, message: `Plugin source path not found: ${resolved.path}` };\n\t\t}\n\t\tcpSync(resolved.path, dest, { recursive: true });\n\t} else if (resolved.kind === \"git\") {\n\t\tconst res = await cloneGitRepo(resolved.url, dest, resolved.ref, resolved.sha);\n\t\tif (res.code !== 0) {\n\t\t\trmSync(dest, { recursive: true, force: true });\n\t\t\treturn { installed: false, message: `git clone failed: ${res.stderr || res.stdout}`.trim() };\n\t\t}\n\t} else if (resolved.kind === \"git-subdir\") {\n\t\tconst res = await installGitSubdir(resolved.url, resolved.path, dest, resolved.ref, resolved.sha);\n\t\tif (!res.ok) {\n\t\t\trmSync(dest, { recursive: true, force: true });\n\t\t\treturn { installed: false, message: res.message };\n\t\t}\n\t} else if (resolved.kind === \"npm\") {\n\t\treturn {\n\t\t\tinstalled: false,\n\t\t\tmessage:\n\t\t\t\t`\"${name}\" is published as an npm package (${resolved.spec}), a source type hoocode cannot install yet. ` +\n\t\t\t\t\"The entry is listed so you know it exists; install it through the vendor CLI, or ask the marketplace \" +\n\t\t\t\t\"for a git source.\",\n\t\t};\n\t} else {\n\t\treturn {\n\t\t\tinstalled: false,\n\t\t\tmessage:\n\t\t\t\t`\"${name}\" is published as a zip archive (${resolved.url}), a source type hoocode cannot install yet. ` +\n\t\t\t\t\"The entry is listed so you know it exists; install it through the vendor CLI, or ask the marketplace \" +\n\t\t\t\t\"for a git source.\",\n\t\t};\n\t}\n\n\t// Drop the clone's `.git`. Nothing reads it — an installed plugin is never\n\t// updated from its remote, by design (that is the supply-chain boundary) — and\n\t// keeping it actively breaks project scope: a repository nested inside the\n\t// user's repository is an embedded repo, so `git add` writes a gitlink instead\n\t// of the plugin's files and the install cannot actually be committed. Which is\n\t// the one thing project scope is for. Cheaper installs at user scope too.\n\trmSync(path.join(dest, \".git\"), { recursive: true, force: true });\n\n\tlet parsed = parsePluginDir(dest);\n\tif (!hasAnyManifest(dest)) {\n\t\t// Manifest-less plugin dir (some marketplaces index bare capability trees,\n\t\t// e.g. a plugin that is just a `skills/` directory). Such a directory now\n\t\t// *parses* on its own — the vendors make the manifest optional — but the\n\t\t// entry's name and description live only in the marketplace index, and the\n\t\t// derived id would be the sanitized directory name. Synthesize a native\n\t\t// manifest so both survive the install.\n\t\tconst manifestDir = path.join(dest, \".agents-plugin\");\n\t\tmkdirSync(manifestDir, { recursive: true });\n\t\twriteFileSync(\n\t\t\tpath.join(manifestDir, \"plugin.json\"),\n\t\t\t`${JSON.stringify(\n\t\t\t\t{ name: found.name, ...(found.description ? { description: found.description } : {}) },\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t)}\\n`,\n\t\t\t\"utf8\",\n\t\t);\n\t\tparsed = parsePluginDir(dest);\n\t}\n\tif (!parsed) {\n\t\trmSync(dest, { recursive: true, force: true });\n\t\treturn { installed: false, message: `Installed source for \"${name}\" has no recognizable plugin manifest.` };\n\t}\n\tconst capabilities = loadableCapabilities(parsed);\n\tlet message =\n\t\t`Installed \"${name}\" from marketplace \"${found.marketplaceName}\" ` +\n\t\t`(${parsed.supportPlatform.join(\", \")}) to ${dest} [${scope} scope]. ` +\n\t\t`Remove it with UninstallPlugin.`;\n\tif (scope === \"project\") {\n\t\t// Project scope puts the plugin in the working tree, which is the point —\n\t\t// but it is also a `git status` entry and, once committed, code that runs\n\t\t// for whoever clones next. Executable capabilities are the half that\n\t\t// matters: hoocode has no workspace-trust gate for `<cwd>/.agents/plugins`\n\t\t// (see docs/plugin-system-architecture.md §5.9), so a committed hook or MCP\n\t\t// server loads for collaborators without a prompt.\n\t\tconst executables = [parsed.hooks && \"hooks\", parsed.mcpServers && \"MCP servers\"].filter(Boolean);\n\t\tmessage += ` It is now part of the working tree — commit it to share it with collaborators.`;\n\t\tif (executables.length > 0) {\n\t\t\tmessage +=\n\t\t\t\t` It carries ${executables.join(\" and \")}, which run for anyone who clones the repository;` +\n\t\t\t\t` keep it out of version control if that is not what you want.`;\n\t\t}\n\t}\n\tif (parsed.id !== name) {\n\t\t// The entry name and the manifest name are allowed to differ, and the\n\t\t// difference is otherwise invisible until ListPlugins reports an id the\n\t\t// caller never asked for.\n\t\tmessage += ` Listed by ListPlugins as \"${parsed.id}\"; either name uninstalls it.`;\n\t}\n\tif (capabilities.length === 0) {\n\t\t// An install that adds nothing should not read as an install that worked.\n\t\t// Common in github/awesome-copilot, where most entries carry their content\n\t\t// in Copilot UI `extensions/` that hoocode has no analog for.\n\t\tconst surfaces = parsed.unsupportedSurfaces?.length\n\t\t\t? ` Unsupported surfaces present: ${parsed.unsupportedSurfaces.join(\", \")}.`\n\t\t\t: \"\";\n\t\tmessage +=\n\t\t\t\" Note: it contributes no capabilities hoocode can load\" +\n\t\t\t` (no skills, commands, subagents, hooks or MCP servers).${surfaces}`;\n\t}\n\treturn {\n\t\tinstalled: true,\n\t\tdest,\n\t\tscope,\n\t\t...(parsed.id !== name ? { id: parsed.id } : {}),\n\t\tsupportPlatform: parsed.supportPlatform,\n\t\tmessage,\n\t};\n}\n\nexport interface UninstallOutcome {\n\tremoved: boolean;\n\tmessage: string;\n}\n\n/** True when `target` is `root` or sits inside it. */\nfunction isUnderDir(target: string, root: string): boolean {\n\tconst normalized = path.resolve(root);\n\tif (path.resolve(target) === normalized) return true;\n\treturn path.resolve(target).startsWith(normalized.endsWith(path.sep) ? normalized : `${normalized}${path.sep}`);\n}\n\n/**\n * Remove an installed or authored plugin from every location it could occupy:\n * the consumption home, both production homes, and the legacy project-local\n * directories that older versions wrote into.\n *\n * `name` is matched two ways, because a plugin has two names: the marketplace\n * entry name it was installed under (which is what the directory is called) and\n * the manifest id `ListPlugins` reports. Resolving only the first left a plugin\n * whose names differ impossible to remove through the tool surface — the id the\n * model was shown was the one uninstall did not accept.\n *\n * Removal by id stays inside {@link pluginHomeRoots}, so a plugin discovered in\n * `<cwd>/.claude/skills` — repository content the team committed, which hoocode\n * did not install — is never deleted out of the working tree.\n */\nexport function uninstallPlugin(cwd: string, name: string, agentDir: string = getAgentDir()): UninstallOutcome {\n\tconst homes = pluginHomeRoots(cwd, agentDir);\n\tconst byManifestId = listInstalledPlugins(cwd, agentDir)\n\t\t.filter((p) => p.id === name)\n\t\t.map((p) => p.root)\n\t\t.filter((root) => homes.some((home) => isUnderDir(root, home)));\n\tconst candidates = [...new Set([...candidatePluginDirs(cwd, name, agentDir), ...byManifestId])];\n\tconst present = candidates.filter((p) => existsSync(p));\n\tif (present.length === 0) return { removed: false, message: `Plugin \"${name}\" is not installed.` };\n\tfor (const p of present) rmSync(p, { recursive: true, force: true });\n\treturn { removed: true, message: `Removed \"${name}\".` };\n}\n"]}
|
|
@@ -29,6 +29,12 @@ export declare function pluginCapabilities(plugin: NormalizedPlugin): string[];
|
|
|
29
29
|
export declare function availablePluginGroups(plugins: readonly AvailablePlugin[], options?: {
|
|
30
30
|
installed?: ReadonlySet<string>;
|
|
31
31
|
}): ListGroup[];
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
export interface InstalledRowOptions {
|
|
33
|
+
/** Working directory, used to place each plugin in a scope. */
|
|
34
|
+
cwd?: string;
|
|
35
|
+
/** Whether `cwd` is a trusted workspace; decides if executables actually load. */
|
|
36
|
+
trusted?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** Installed plugins as rows: id@version, platforms, format, scope, capabilities. */
|
|
39
|
+
export declare function installedPluginRows(plugins: readonly NormalizedPlugin[], options?: InstalledRowOptions): ListRow[];
|
|
34
40
|
//# sourceMappingURL=listing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listing.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/listing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"listing.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/listing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,GAAG,MAAM,CAO5E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,EAAE,CAUrE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAO,GAC/C,SAAS,EAAE,CAoBb;AAED,MAAM,WAAW,mBAAmB;IACnC,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,SAAS,gBAAgB,EAAE,EACpC,OAAO,GAAE,mBAAwB,GAC/B,OAAO,EAAE,CAuBX","sourcesContent":["/**\n * Row shapes for the plugin listings, shared by every surface that prints one.\n *\n * `/plugin list`, `SearchPlugins` and `ListPlugins` describe the same objects,\n * and each used to build its own `name [a, b] — description` string, so the same\n * plugin printed three ways and the model's view never quite matched the user's.\n * They all go through here now; only the styling and the width differ.\n */\n\nimport type { ListGroup, ListRow } from \"../../format-list.js\";\nimport type { AvailablePlugin } from \"./install.js\";\nimport { pluginScopeOf } from \"./locations.js\";\nimport type { NormalizedPlugin } from \"./manifest.js\";\n\n/** Human-readable source for an available plugin (a URL, a path, an npm spec). */\nexport function formatPluginSource(source: AvailablePlugin[\"source\"]): string {\n\tif (typeof source === \"string\") return source;\n\tif (source.source === \"npm\") return source.package;\n\t// `archive` carries a url like `url` does; without this it printed\n\t// `https://…/p.zip/undefined` through the git-subdir branch below.\n\tif (source.source === \"url\" || source.source === \"archive\") return source.url;\n\treturn `${source.url}/${source.path}`;\n}\n\n/**\n * Capability classes a plugin actually ships.\n *\n * Covers every capability `NormalizedPlugin` can carry — themes and providers\n * included, which the old `ListPlugins` inline list omitted, so a plugin that\n * shipped only a theme or only a provider reported no capabilities at all.\n */\nexport function pluginCapabilities(plugin: NormalizedPlugin): string[] {\n\treturn [\n\t\tplugin.skillsDir && \"skills\",\n\t\tplugin.commandsDir && \"commands\",\n\t\tplugin.agentsDir && \"agents\",\n\t\tplugin.themesDir && \"themes\",\n\t\tplugin.hooks && \"hooks\",\n\t\tplugin.mcpServers && \"mcp\",\n\t\tplugin.providers?.length && \"providers\",\n\t].filter((value): value is string => typeof value === \"string\");\n}\n\n/**\n * Available plugins, grouped by the marketplace offering them.\n *\n * Grouping is what makes provenance legible: a flat list cannot answer \"where\n * would this come from if I installed it\", which is the one question that\n * matters at a trust boundary. Marketplace order is preserved from the caller.\n */\nexport function availablePluginGroups(\n\tplugins: readonly AvailablePlugin[],\n\toptions: { installed?: ReadonlySet<string> } = {},\n): ListGroup[] {\n\tconst groups = new Map<string, ListRow[]>();\n\tfor (const plugin of plugins) {\n\t\tconst rows = groups.get(plugin.marketplaceName) ?? [];\n\t\t// npm and archive entries are catalogued but not fetchable here, and saying\n\t\t// so in the row is cheaper than an InstallPlugin round trip that only ends\n\t\t// in the same message.\n\t\tconst installable = plugin.sourceKind !== \"npm\" && plugin.sourceKind !== \"archive\";\n\t\trows.push({\n\t\t\tname: plugin.name,\n\t\t\tmarker: options.installed?.has(plugin.name) ? \"✓\" : undefined,\n\t\t\tfacts: [\n\t\t\t\tplugin.supportPlatform.join(\"/\"),\n\t\t\t\tinstallable ? plugin.sourceKind : `${plugin.sourceKind}, not installable`,\n\t\t\t],\n\t\t\tdetail: plugin.description ?? formatPluginSource(plugin.source),\n\t\t});\n\t\tgroups.set(plugin.marketplaceName, rows);\n\t}\n\treturn Array.from(groups, ([title, rows]) => ({ title, rows }));\n}\n\nexport interface InstalledRowOptions {\n\t/** Working directory, used to place each plugin in a scope. */\n\tcwd?: string;\n\t/** Whether `cwd` is a trusted workspace; decides if executables actually load. */\n\ttrusted?: boolean;\n}\n\n/** Installed plugins as rows: id@version, platforms, format, scope, capabilities. */\nexport function installedPluginRows(\n\tplugins: readonly NormalizedPlugin[],\n\toptions: InstalledRowOptions = {},\n): ListRow[] {\n\treturn plugins.map((plugin) => {\n\t\tconst capabilities = pluginCapabilities(plugin);\n\t\t// Scope answers \"why is this loaded, and who else gets it\" — the plugin's\n\t\t// own path always knew, and nothing surfaced it.\n\t\tconst scope = options.cwd ? pluginScopeOf(plugin.root, options.cwd) : undefined;\n\t\t// A working-tree plugin's executables load only in a trusted workspace, so\n\t\t// listing \"hooks\" unqualified would overstate what is actually running.\n\t\tconst withheld = scope && scope !== \"user\" && !options.trusted && (plugin.hooks || plugin.mcpServers);\n\t\treturn {\n\t\t\tname: `${plugin.id}${plugin.version ? `@${plugin.version}` : \"\"}`,\n\t\t\t// The manifest format is parenthesised because it is usually the same\n\t\t\t// word as the platform (\"claude/github · claude\" reads as a stutter).\n\t\t\tfacts: [\n\t\t\t\tplugin.supportPlatform.join(\"/\"),\n\t\t\t\t`(${plugin.format})`,\n\t\t\t\t...(scope ? [scope] : []),\n\t\t\t\tcapabilities.join(\", \") || \"no capabilities\",\n\t\t\t\t...(withheld ? [\"hooks/mcp withheld, workspace not trusted\"] : []),\n\t\t\t],\n\t\t\tdetail: plugin.description,\n\t\t};\n\t});\n}\n"]}
|
|
@@ -6,11 +6,16 @@
|
|
|
6
6
|
* plugin printed three ways and the model's view never quite matched the user's.
|
|
7
7
|
* They all go through here now; only the styling and the width differ.
|
|
8
8
|
*/
|
|
9
|
+
import { pluginScopeOf } from "./locations.js";
|
|
9
10
|
/** Human-readable source for an available plugin (a URL, a path, an npm spec). */
|
|
10
11
|
export function formatPluginSource(source) {
|
|
11
12
|
if (typeof source === "string")
|
|
12
13
|
return source;
|
|
13
|
-
if (source.source === "
|
|
14
|
+
if (source.source === "npm")
|
|
15
|
+
return source.package;
|
|
16
|
+
// `archive` carries a url like `url` does; without this it printed
|
|
17
|
+
// `https://…/p.zip/undefined` through the git-subdir branch below.
|
|
18
|
+
if (source.source === "url" || source.source === "archive")
|
|
14
19
|
return source.url;
|
|
15
20
|
return `${source.url}/${source.path}`;
|
|
16
21
|
}
|
|
@@ -43,25 +48,44 @@ export function availablePluginGroups(plugins, options = {}) {
|
|
|
43
48
|
const groups = new Map();
|
|
44
49
|
for (const plugin of plugins) {
|
|
45
50
|
const rows = groups.get(plugin.marketplaceName) ?? [];
|
|
51
|
+
// npm and archive entries are catalogued but not fetchable here, and saying
|
|
52
|
+
// so in the row is cheaper than an InstallPlugin round trip that only ends
|
|
53
|
+
// in the same message.
|
|
54
|
+
const installable = plugin.sourceKind !== "npm" && plugin.sourceKind !== "archive";
|
|
46
55
|
rows.push({
|
|
47
56
|
name: plugin.name,
|
|
48
57
|
marker: options.installed?.has(plugin.name) ? "✓" : undefined,
|
|
49
|
-
facts: [
|
|
58
|
+
facts: [
|
|
59
|
+
plugin.supportPlatform.join("/"),
|
|
60
|
+
installable ? plugin.sourceKind : `${plugin.sourceKind}, not installable`,
|
|
61
|
+
],
|
|
50
62
|
detail: plugin.description ?? formatPluginSource(plugin.source),
|
|
51
63
|
});
|
|
52
64
|
groups.set(plugin.marketplaceName, rows);
|
|
53
65
|
}
|
|
54
66
|
return Array.from(groups, ([title, rows]) => ({ title, rows }));
|
|
55
67
|
}
|
|
56
|
-
/** Installed plugins as rows: id@version, platforms, format, capabilities. */
|
|
57
|
-
export function installedPluginRows(plugins) {
|
|
68
|
+
/** Installed plugins as rows: id@version, platforms, format, scope, capabilities. */
|
|
69
|
+
export function installedPluginRows(plugins, options = {}) {
|
|
58
70
|
return plugins.map((plugin) => {
|
|
59
71
|
const capabilities = pluginCapabilities(plugin);
|
|
72
|
+
// Scope answers "why is this loaded, and who else gets it" — the plugin's
|
|
73
|
+
// own path always knew, and nothing surfaced it.
|
|
74
|
+
const scope = options.cwd ? pluginScopeOf(plugin.root, options.cwd) : undefined;
|
|
75
|
+
// A working-tree plugin's executables load only in a trusted workspace, so
|
|
76
|
+
// listing "hooks" unqualified would overstate what is actually running.
|
|
77
|
+
const withheld = scope && scope !== "user" && !options.trusted && (plugin.hooks || plugin.mcpServers);
|
|
60
78
|
return {
|
|
61
79
|
name: `${plugin.id}${plugin.version ? `@${plugin.version}` : ""}`,
|
|
62
80
|
// The manifest format is parenthesised because it is usually the same
|
|
63
81
|
// word as the platform ("claude/github · claude" reads as a stutter).
|
|
64
|
-
facts: [
|
|
82
|
+
facts: [
|
|
83
|
+
plugin.supportPlatform.join("/"),
|
|
84
|
+
`(${plugin.format})`,
|
|
85
|
+
...(scope ? [scope] : []),
|
|
86
|
+
capabilities.join(", ") || "no capabilities",
|
|
87
|
+
...(withheld ? ["hooks/mcp withheld, workspace not trusted"] : []),
|
|
88
|
+
],
|
|
65
89
|
detail: plugin.description,
|
|
66
90
|
};
|
|
67
91
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listing.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/listing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"listing.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/listing.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Row shapes for the plugin listings, shared by every surface that prints one.\n *\n * `/plugin list`, `SearchPlugins` and `ListPlugins` describe the same objects,\n * and each used to build its own `name [a, b] — description` string, so the same\n * plugin printed three ways and the model's view never quite matched the user's.\n * They all go through here now; only the styling and the width differ.\n */\n\nimport type { ListGroup, ListRow } from \"../../format-list.js\";\nimport type { AvailablePlugin } from \"./install.js\";\nimport { pluginScopeOf } from \"./locations.js\";\nimport type { NormalizedPlugin } from \"./manifest.js\";\n\n/** Human-readable source for an available plugin (a URL, a path, an npm spec). */\nexport function formatPluginSource(source: AvailablePlugin[\"source\"]): string {\n\tif (typeof source === \"string\") return source;\n\tif (source.source === \"npm\") return source.package;\n\t// `archive` carries a url like `url` does; without this it printed\n\t// `https://…/p.zip/undefined` through the git-subdir branch below.\n\tif (source.source === \"url\" || source.source === \"archive\") return source.url;\n\treturn `${source.url}/${source.path}`;\n}\n\n/**\n * Capability classes a plugin actually ships.\n *\n * Covers every capability `NormalizedPlugin` can carry — themes and providers\n * included, which the old `ListPlugins` inline list omitted, so a plugin that\n * shipped only a theme or only a provider reported no capabilities at all.\n */\nexport function pluginCapabilities(plugin: NormalizedPlugin): string[] {\n\treturn [\n\t\tplugin.skillsDir && \"skills\",\n\t\tplugin.commandsDir && \"commands\",\n\t\tplugin.agentsDir && \"agents\",\n\t\tplugin.themesDir && \"themes\",\n\t\tplugin.hooks && \"hooks\",\n\t\tplugin.mcpServers && \"mcp\",\n\t\tplugin.providers?.length && \"providers\",\n\t].filter((value): value is string => typeof value === \"string\");\n}\n\n/**\n * Available plugins, grouped by the marketplace offering them.\n *\n * Grouping is what makes provenance legible: a flat list cannot answer \"where\n * would this come from if I installed it\", which is the one question that\n * matters at a trust boundary. Marketplace order is preserved from the caller.\n */\nexport function availablePluginGroups(\n\tplugins: readonly AvailablePlugin[],\n\toptions: { installed?: ReadonlySet<string> } = {},\n): ListGroup[] {\n\tconst groups = new Map<string, ListRow[]>();\n\tfor (const plugin of plugins) {\n\t\tconst rows = groups.get(plugin.marketplaceName) ?? [];\n\t\t// npm and archive entries are catalogued but not fetchable here, and saying\n\t\t// so in the row is cheaper than an InstallPlugin round trip that only ends\n\t\t// in the same message.\n\t\tconst installable = plugin.sourceKind !== \"npm\" && plugin.sourceKind !== \"archive\";\n\t\trows.push({\n\t\t\tname: plugin.name,\n\t\t\tmarker: options.installed?.has(plugin.name) ? \"✓\" : undefined,\n\t\t\tfacts: [\n\t\t\t\tplugin.supportPlatform.join(\"/\"),\n\t\t\t\tinstallable ? plugin.sourceKind : `${plugin.sourceKind}, not installable`,\n\t\t\t],\n\t\t\tdetail: plugin.description ?? formatPluginSource(plugin.source),\n\t\t});\n\t\tgroups.set(plugin.marketplaceName, rows);\n\t}\n\treturn Array.from(groups, ([title, rows]) => ({ title, rows }));\n}\n\nexport interface InstalledRowOptions {\n\t/** Working directory, used to place each plugin in a scope. */\n\tcwd?: string;\n\t/** Whether `cwd` is a trusted workspace; decides if executables actually load. */\n\ttrusted?: boolean;\n}\n\n/** Installed plugins as rows: id@version, platforms, format, scope, capabilities. */\nexport function installedPluginRows(\n\tplugins: readonly NormalizedPlugin[],\n\toptions: InstalledRowOptions = {},\n): ListRow[] {\n\treturn plugins.map((plugin) => {\n\t\tconst capabilities = pluginCapabilities(plugin);\n\t\t// Scope answers \"why is this loaded, and who else gets it\" — the plugin's\n\t\t// own path always knew, and nothing surfaced it.\n\t\tconst scope = options.cwd ? pluginScopeOf(plugin.root, options.cwd) : undefined;\n\t\t// A working-tree plugin's executables load only in a trusted workspace, so\n\t\t// listing \"hooks\" unqualified would overstate what is actually running.\n\t\tconst withheld = scope && scope !== \"user\" && !options.trusted && (plugin.hooks || plugin.mcpServers);\n\t\treturn {\n\t\t\tname: `${plugin.id}${plugin.version ? `@${plugin.version}` : \"\"}`,\n\t\t\t// The manifest format is parenthesised because it is usually the same\n\t\t\t// word as the platform (\"claude/github · claude\" reads as a stutter).\n\t\t\tfacts: [\n\t\t\t\tplugin.supportPlatform.join(\"/\"),\n\t\t\t\t`(${plugin.format})`,\n\t\t\t\t...(scope ? [scope] : []),\n\t\t\t\tcapabilities.join(\", \") || \"no capabilities\",\n\t\t\t\t...(withheld ? [\"hooks/mcp withheld, workspace not trusted\"] : []),\n\t\t\t],\n\t\t\tdetail: plugin.description,\n\t\t};\n\t});\n}\n"]}
|
|
@@ -8,17 +8,26 @@
|
|
|
8
8
|
* run, and is deleted on any failure. Nothing here is ever
|
|
9
9
|
* loaded; it is promoted into a production home or thrown
|
|
10
10
|
* away. {@link makeDraftDir}
|
|
11
|
-
* Consumption home Persistent
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* Consumption home Persistent. Where marketplace installs land, in whatever
|
|
12
|
+
* format the marketplace served. hoocode installing for
|
|
13
|
+
* itself, at the scope the caller chose
|
|
14
|
+
* ({@link installHomeForScope}).
|
|
14
15
|
* Production home Persistent, global, per platform. Where a plugin hoocode
|
|
15
16
|
* *authored* lives. {@link productionPluginDir}
|
|
16
17
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* Draft and production are user-scoped without exception. Consumption is the one
|
|
19
|
+
* that takes a scope, and `user` stays the default: a plugin is portable,
|
|
20
|
+
* versioned and reusable across projects, so the working tree is the wrong place
|
|
21
|
+
* for it *by default* — the capability would be invisible in every other repo,
|
|
22
|
+
* and an autonomous install would dirty `git status` with content unrelated to
|
|
23
|
+
* the task. `project` exists for the other case, where a team wants the plugin
|
|
24
|
+
* pinned in the repo and shared with collaborators, and is a deliberate choice
|
|
25
|
+
* someone makes per install or per setting rather than a default anything falls
|
|
26
|
+
* into.
|
|
27
|
+
*
|
|
28
|
+
* Authoring stays user-scoped regardless (architecture doc §5.5): a project
|
|
29
|
+
* destination for an *authored* plugin has no coherent production home, whereas
|
|
30
|
+
* an installed plugin has an obvious one.
|
|
22
31
|
*
|
|
23
32
|
* The two production homes are asymmetric because the vendors are. Claude Code
|
|
24
33
|
* discovers `~/.claude/skills/<id>/` in place, with no install step, so an
|
|
@@ -32,10 +41,40 @@
|
|
|
32
41
|
import type { PluginPlatform } from "./formats/platform-targets.js";
|
|
33
42
|
/** Filesystem-safe directory name derived from a plugin name (matches the `/plugin` command). */
|
|
34
43
|
export declare function sanitizeForDir(s: string): string;
|
|
35
|
-
/** `~/.agents/plugins/` — where marketplace installs land. Format-agnostic. */
|
|
44
|
+
/** `~/.agents/plugins/` — where user-scoped marketplace installs land. Format-agnostic. */
|
|
36
45
|
export declare function consumptionPluginsDir(agentDir?: string): string;
|
|
37
|
-
/**
|
|
38
|
-
|
|
46
|
+
/**
|
|
47
|
+
* `<cwd>/.agents/plugins/` — where project-scoped installs land.
|
|
48
|
+
*
|
|
49
|
+
* Also the home older versions installed *everything* into, back when scope was
|
|
50
|
+
* not a choice (see docs/plugin-system-architecture.md §5.4). It stayed on the
|
|
51
|
+
* discovery path the whole time, which is why project scope needs no loader
|
|
52
|
+
* change: `defaultPluginDirs` already reads it, ahead of the user home, so a
|
|
53
|
+
* project-scoped plugin shadows a user-scoped one of the same id.
|
|
54
|
+
*/
|
|
55
|
+
export declare function projectPluginsDir(cwd: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* Where a marketplace install goes.
|
|
58
|
+
*
|
|
59
|
+
* hoocode has no `enabledPlugins`-style registry — a plugin on the discovery
|
|
60
|
+
* path is enabled — so scope is a *destination*, not a flag written elsewhere:
|
|
61
|
+
*
|
|
62
|
+
* user `~/.agents/plugins/` portable across every checkout, invisible to collaborators
|
|
63
|
+
* project `<cwd>/.agents/plugins/` committed with the repo, shared, shadows the user copy
|
|
64
|
+
*/
|
|
65
|
+
export type PluginInstallScope = "user" | "project";
|
|
66
|
+
/** Resolve the install home for `scope`. */
|
|
67
|
+
export declare function installHomeForScope(scope: PluginInstallScope, cwd: string, agentDir?: string): string;
|
|
68
|
+
/**
|
|
69
|
+
* Which scope a plugin already on disk belongs to, derived from where it lives.
|
|
70
|
+
*
|
|
71
|
+
* `repo` is the third answer the install scopes do not cover: a plugin under
|
|
72
|
+
* `<cwd>/.claude/skills` arrived with the repository rather than through an
|
|
73
|
+
* install, so it is project-*located* but nobody here chose it. Keeping it
|
|
74
|
+
* distinct from `project` is what lets `ListPlugins` say which plugins are
|
|
75
|
+
* running because someone installed them and which because they were cloned.
|
|
76
|
+
*/
|
|
77
|
+
export declare function pluginScopeOf(pluginRoot: string, cwd: string): PluginInstallScope | "repo";
|
|
39
78
|
/**
|
|
40
79
|
* Root of a platform's production home — the parent that {@link productionPluginDir}
|
|
41
80
|
* places plugin directories under.
|
|
@@ -57,10 +96,21 @@ export declare function marketplaceCacheDir(url: string, agentDir?: string): str
|
|
|
57
96
|
* we last talk to the remote" is the only question the TTL is asking.
|
|
58
97
|
*/
|
|
59
98
|
export declare function marketplaceCacheMetaPath(agentDir?: string): string;
|
|
99
|
+
/**
|
|
100
|
+
* The parent directories hoocode owns and may therefore remove a plugin from:
|
|
101
|
+
* the two production homes, both consumption homes (user and project), and the
|
|
102
|
+
* `.hoocode/plugins` home older versions installed into.
|
|
103
|
+
*
|
|
104
|
+
* Deliberately excludes `<cwd>/.claude/skills` and `<cwd>/.agents/skills`. Those
|
|
105
|
+
* are discovered (loader.ts `defaultPluginDirs`) but are repository content a
|
|
106
|
+
* team committed, so uninstall must not delete out of them — hoocode installed
|
|
107
|
+
* nothing there.
|
|
108
|
+
*/
|
|
109
|
+
export declare function pluginHomeRoots(cwd: string, agentDir?: string): string[];
|
|
60
110
|
/**
|
|
61
111
|
* Every directory a plugin with `id` could occupy, in the order a lookup should
|
|
62
112
|
* try them: the platform production homes, then the consumption home, then the
|
|
63
|
-
* legacy project
|
|
113
|
+
* legacy project homes. Used to find an existing plugin without knowing which
|
|
64
114
|
* role wrote it.
|
|
65
115
|
*/
|
|
66
116
|
export declare function candidatePluginDirs(cwd: string, id: string, agentDir?: string): string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locations.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/locations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAEpE,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAWD,iFAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAE9E;AAED,mGAAmG;AACnG,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAOjG;AAED,qDAAqD;AACrD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAElH;AAED,wEAAsE;AACtE,wBAAgB,oBAAoB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAE7E;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAE7E;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAEzF;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAEjF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,EAAE,CAQvG;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAElF;AAED,gDAAgD;AAChD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAIxF;AAED,kGAAkG;AAClG,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEjD","sourcesContent":["/**\n * Where plugins live on disk.\n *\n * Three distinct roles, deliberately named apart — an earlier design called all\n * three \"staging\", which is how the wrong directory ends up in code:\n *\n * Draft dir Ephemeral. Holds an authored plugin while the eval gates\n * run, and is deleted on any failure. Nothing here is ever\n * loaded; it is promoted into a production home or thrown\n * away. {@link makeDraftDir}\n * Consumption home Persistent, global. Where marketplace installs land, in\n * whatever format the marketplace served. hoocode\n * installing for itself. {@link consumptionPluginsDir}\n * Production home Persistent, global, per platform. Where a plugin hoocode\n * *authored* lives. {@link productionPluginDir}\n *\n * All three are user-scoped. A plugin is portable, versioned and reusable across\n * projects, so writing one into the working tree means the capability is\n * invisible in every other repo and the agent dirties `git status` with content\n * unrelated to the task. The project-local home is still *read* so plugins\n * installed by older versions keep working ({@link legacyProjectPluginsDir}).\n *\n * The two production homes are asymmetric because the vendors are. Claude Code\n * discovers `~/.claude/skills/<id>/` in place, with no install step, so an\n * authored plugin is live there on its next session. Copilot CLI has no\n * equivalent — `copilot plugin install` copies into a cache it owns — so a\n * github artifact lives in a hoocode-owned home and reaches the ecosystem\n * through the publish lane instead.\n *\n * See docs/plugin-system-architecture.md §5.3 and §8.3.\n */\n\nimport { mkdirSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport * as path from \"node:path\";\nimport { getAgentDir } from \"../../../config.js\";\nimport type { PluginPlatform } from \"./formats/platform-targets.js\";\n\n/** Filesystem-safe directory name derived from a plugin name (matches the `/plugin` command). */\nexport function sanitizeForDir(s: string): string {\n\treturn s.replace(/[^a-zA-Z0-9._-]+/g, \"_\").slice(0, 80);\n}\n\n/**\n * Home root the global surfaces hang off. Derived from the agent dir rather than\n * `homedir()` so `HOOCODE_AGENT_DIR` relocates every plugin location together —\n * which is what makes these paths testable.\n */\nfunction homeRoot(agentDir: string): string {\n\treturn path.dirname(agentDir);\n}\n\n/** `~/.agents/plugins/` — where marketplace installs land. Format-agnostic. */\nexport function consumptionPluginsDir(agentDir: string = getAgentDir()): string {\n\treturn path.join(homeRoot(agentDir), \".agents\", \"plugins\");\n}\n\n/** The project-local home older versions installed into. Read-only now; still uninstalled from. */\nexport function legacyProjectPluginsDir(cwd: string): string {\n\treturn path.join(cwd, \".agents\", \"plugins\");\n}\n\n/**\n * Root of a platform's production home — the parent that {@link productionPluginDir}\n * places plugin directories under.\n */\nexport function productionRoot(platform: PluginPlatform, agentDir: string = getAgentDir()): string {\n\t// Claude: the documented skills-directory drop-in. A folder here carrying a\n\t// `.claude-plugin/plugin.json` loads as `<id>@skills-dir` with no install step.\n\tif (platform === \"claude\") return path.join(homeRoot(agentDir), \".claude\", \"skills\");\n\t// GitHub: no vendor drop-in exists, so this is hoocode's own home for a\n\t// github-targeted artifact. PackagePlugin works in it and publish reads from it.\n\treturn path.join(homeRoot(agentDir), \".agents\", \"publish\", \"github\");\n}\n\n/** Where an authored plugin for `platform` lives. */\nexport function productionPluginDir(platform: PluginPlatform, id: string, agentDir: string = getAgentDir()): string {\n\treturn path.join(productionRoot(platform, agentDir), sanitizeForDir(id));\n}\n\n/** `~/.agents/marketplaces.json` — the added-marketplace registry. */\nexport function marketplaceStorePath(agentDir: string = getAgentDir()): string {\n\treturn path.join(homeRoot(agentDir), \".agents\", \"marketplaces.json\");\n}\n\n/** Root of the local marketplace clone cache. A cache is never repo content. */\nexport function marketplaceCacheRoot(agentDir: string = getAgentDir()): string {\n\treturn path.join(homeRoot(agentDir), \".agents\", \"marketplace-cache\");\n}\n\n/** Local cache directory for a marketplace fetched from `url`. */\nexport function marketplaceCacheDir(url: string, agentDir: string = getAgentDir()): string {\n\treturn path.join(marketplaceCacheRoot(agentDir), sanitizeForDir(url));\n}\n\n/**\n * Records when each cached marketplace index was last fetched.\n *\n * Kept beside the caches rather than inferred from directory mtimes: a clone's\n * mtime moves for reasons that have nothing to do with freshness, and \"when did\n * we last talk to the remote\" is the only question the TTL is asking.\n */\nexport function marketplaceCacheMetaPath(agentDir: string = getAgentDir()): string {\n\treturn path.join(marketplaceCacheRoot(agentDir), \".fetched.json\");\n}\n\n/**\n * Every directory a plugin with `id` could occupy, in the order a lookup should\n * try them: the platform production homes, then the consumption home, then the\n * legacy project home. Used to find an existing plugin without knowing which\n * role wrote it.\n */\nexport function candidatePluginDirs(cwd: string, id: string, agentDir: string = getAgentDir()): string[] {\n\tconst slug = sanitizeForDir(id);\n\treturn [\n\t\tproductionPluginDir(\"claude\", id, agentDir),\n\t\tproductionPluginDir(\"github\", id, agentDir),\n\t\tpath.join(consumptionPluginsDir(agentDir), slug),\n\t\tpath.join(legacyProjectPluginsDir(cwd), slug),\n\t];\n}\n\n/**\n * Persistent, writable directory unique to an installed plugin — the target of\n * `${CLAUDE_PLUGIN_DATA}` / `${COPILOT_PLUGIN_DATA}`, which both vendors document\n * as the place for plugin runtime state.\n *\n * Deliberately outside every plugin home. A plugin's own directory is replaced\n * wholesale on promote and deleted on uninstall, and the vendors are explicit\n * that this must not live \"inside the installed-plugins cache directory\" — state\n * that vanishes on reinstall is not state.\n */\nexport function pluginDataDir(id: string, agentDir: string = getAgentDir()): string {\n\treturn path.join(homeRoot(agentDir), \".agents\", \"plugin-data\", sanitizeForDir(id));\n}\n\n/** {@link pluginDataDir}, created if absent. */\nexport function ensurePluginDataDir(id: string, agentDir: string = getAgentDir()): string {\n\tconst dir = pluginDataDir(id, agentDir);\n\tmkdirSync(dir, { recursive: true });\n\treturn dir;\n}\n\n/** Create an ephemeral draft directory. The caller must promote or {@link discardDraftDir} it. */\nexport function makeDraftDir(): string {\n\treturn mkdtempSync(path.join(tmpdir(), \"hoo-plugin-draft-\"));\n}\n\n/** Delete a draft directory. Safe to call twice. */\nexport function discardDraftDir(dir: string): void {\n\trmSync(dir, { recursive: true, force: true });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"locations.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/locations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAEpE,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAWD,6FAA2F;AAC3F,wBAAgB,qBAAqB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAE9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,SAAS,CAAC;AAEpD,4CAA4C;AAC5C,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAEpH;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,kBAAkB,GAAG,MAAM,CAS1F;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAOjG;AAED,qDAAqD;AACrD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAElH;AAED,wEAAsE;AACtE,wBAAgB,oBAAoB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAE7E;AAED,gFAAgF;AAChF,wBAAgB,oBAAoB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAE7E;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAEzF;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAEjF;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,EAAE,CAQvF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,EAAE,CAGvG;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAElF;AAED,gDAAgD;AAChD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAsB,GAAG,MAAM,CAIxF;AAED,kGAAkG;AAClG,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEjD","sourcesContent":["/**\n * Where plugins live on disk.\n *\n * Three distinct roles, deliberately named apart — an earlier design called all\n * three \"staging\", which is how the wrong directory ends up in code:\n *\n * Draft dir Ephemeral. Holds an authored plugin while the eval gates\n * run, and is deleted on any failure. Nothing here is ever\n * loaded; it is promoted into a production home or thrown\n * away. {@link makeDraftDir}\n * Consumption home Persistent. Where marketplace installs land, in whatever\n * format the marketplace served. hoocode installing for\n * itself, at the scope the caller chose\n * ({@link installHomeForScope}).\n * Production home Persistent, global, per platform. Where a plugin hoocode\n * *authored* lives. {@link productionPluginDir}\n *\n * Draft and production are user-scoped without exception. Consumption is the one\n * that takes a scope, and `user` stays the default: a plugin is portable,\n * versioned and reusable across projects, so the working tree is the wrong place\n * for it *by default* — the capability would be invisible in every other repo,\n * and an autonomous install would dirty `git status` with content unrelated to\n * the task. `project` exists for the other case, where a team wants the plugin\n * pinned in the repo and shared with collaborators, and is a deliberate choice\n * someone makes per install or per setting rather than a default anything falls\n * into.\n *\n * Authoring stays user-scoped regardless (architecture doc §5.5): a project\n * destination for an *authored* plugin has no coherent production home, whereas\n * an installed plugin has an obvious one.\n *\n * The two production homes are asymmetric because the vendors are. Claude Code\n * discovers `~/.claude/skills/<id>/` in place, with no install step, so an\n * authored plugin is live there on its next session. Copilot CLI has no\n * equivalent — `copilot plugin install` copies into a cache it owns — so a\n * github artifact lives in a hoocode-owned home and reaches the ecosystem\n * through the publish lane instead.\n *\n * See docs/plugin-system-architecture.md §5.3 and §8.3.\n */\n\nimport { mkdirSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport * as path from \"node:path\";\nimport { getAgentDir } from \"../../../config.js\";\nimport type { PluginPlatform } from \"./formats/platform-targets.js\";\n\n/** Filesystem-safe directory name derived from a plugin name (matches the `/plugin` command). */\nexport function sanitizeForDir(s: string): string {\n\treturn s.replace(/[^a-zA-Z0-9._-]+/g, \"_\").slice(0, 80);\n}\n\n/**\n * Home root the global surfaces hang off. Derived from the agent dir rather than\n * `homedir()` so `HOOCODE_AGENT_DIR` relocates every plugin location together —\n * which is what makes these paths testable.\n */\nfunction homeRoot(agentDir: string): string {\n\treturn path.dirname(agentDir);\n}\n\n/** `~/.agents/plugins/` — where user-scoped marketplace installs land. Format-agnostic. */\nexport function consumptionPluginsDir(agentDir: string = getAgentDir()): string {\n\treturn path.join(homeRoot(agentDir), \".agents\", \"plugins\");\n}\n\n/**\n * `<cwd>/.agents/plugins/` — where project-scoped installs land.\n *\n * Also the home older versions installed *everything* into, back when scope was\n * not a choice (see docs/plugin-system-architecture.md §5.4). It stayed on the\n * discovery path the whole time, which is why project scope needs no loader\n * change: `defaultPluginDirs` already reads it, ahead of the user home, so a\n * project-scoped plugin shadows a user-scoped one of the same id.\n */\nexport function projectPluginsDir(cwd: string): string {\n\treturn path.join(cwd, \".agents\", \"plugins\");\n}\n\n/**\n * Where a marketplace install goes.\n *\n * hoocode has no `enabledPlugins`-style registry — a plugin on the discovery\n * path is enabled — so scope is a *destination*, not a flag written elsewhere:\n *\n * user `~/.agents/plugins/` portable across every checkout, invisible to collaborators\n * project `<cwd>/.agents/plugins/` committed with the repo, shared, shadows the user copy\n */\nexport type PluginInstallScope = \"user\" | \"project\";\n\n/** Resolve the install home for `scope`. */\nexport function installHomeForScope(scope: PluginInstallScope, cwd: string, agentDir: string = getAgentDir()): string {\n\treturn scope === \"project\" ? projectPluginsDir(cwd) : consumptionPluginsDir(agentDir);\n}\n\n/**\n * Which scope a plugin already on disk belongs to, derived from where it lives.\n *\n * `repo` is the third answer the install scopes do not cover: a plugin under\n * `<cwd>/.claude/skills` arrived with the repository rather than through an\n * install, so it is project-*located* but nobody here chose it. Keeping it\n * distinct from `project` is what lets `ListPlugins` say which plugins are\n * running because someone installed them and which because they were cloned.\n */\nexport function pluginScopeOf(pluginRoot: string, cwd: string): PluginInstallScope | \"repo\" {\n\tconst under = (root: string): boolean => {\n\t\tconst normalized = path.resolve(root);\n\t\tconst target = path.resolve(pluginRoot);\n\t\treturn target === normalized || target.startsWith(`${normalized}${path.sep}`);\n\t};\n\tif (under(projectPluginsDir(cwd)) || under(path.join(cwd, \".hoocode\", \"plugins\"))) return \"project\";\n\tif (under(path.join(cwd, \".claude\", \"skills\")) || under(path.join(cwd, \".agents\", \"skills\"))) return \"repo\";\n\treturn \"user\";\n}\n\n/**\n * Root of a platform's production home — the parent that {@link productionPluginDir}\n * places plugin directories under.\n */\nexport function productionRoot(platform: PluginPlatform, agentDir: string = getAgentDir()): string {\n\t// Claude: the documented skills-directory drop-in. A folder here carrying a\n\t// `.claude-plugin/plugin.json` loads as `<id>@skills-dir` with no install step.\n\tif (platform === \"claude\") return path.join(homeRoot(agentDir), \".claude\", \"skills\");\n\t// GitHub: no vendor drop-in exists, so this is hoocode's own home for a\n\t// github-targeted artifact. PackagePlugin works in it and publish reads from it.\n\treturn path.join(homeRoot(agentDir), \".agents\", \"publish\", \"github\");\n}\n\n/** Where an authored plugin for `platform` lives. */\nexport function productionPluginDir(platform: PluginPlatform, id: string, agentDir: string = getAgentDir()): string {\n\treturn path.join(productionRoot(platform, agentDir), sanitizeForDir(id));\n}\n\n/** `~/.agents/marketplaces.json` — the added-marketplace registry. */\nexport function marketplaceStorePath(agentDir: string = getAgentDir()): string {\n\treturn path.join(homeRoot(agentDir), \".agents\", \"marketplaces.json\");\n}\n\n/** Root of the local marketplace clone cache. A cache is never repo content. */\nexport function marketplaceCacheRoot(agentDir: string = getAgentDir()): string {\n\treturn path.join(homeRoot(agentDir), \".agents\", \"marketplace-cache\");\n}\n\n/** Local cache directory for a marketplace fetched from `url`. */\nexport function marketplaceCacheDir(url: string, agentDir: string = getAgentDir()): string {\n\treturn path.join(marketplaceCacheRoot(agentDir), sanitizeForDir(url));\n}\n\n/**\n * Records when each cached marketplace index was last fetched.\n *\n * Kept beside the caches rather than inferred from directory mtimes: a clone's\n * mtime moves for reasons that have nothing to do with freshness, and \"when did\n * we last talk to the remote\" is the only question the TTL is asking.\n */\nexport function marketplaceCacheMetaPath(agentDir: string = getAgentDir()): string {\n\treturn path.join(marketplaceCacheRoot(agentDir), \".fetched.json\");\n}\n\n/**\n * The parent directories hoocode owns and may therefore remove a plugin from:\n * the two production homes, both consumption homes (user and project), and the\n * `.hoocode/plugins` home older versions installed into.\n *\n * Deliberately excludes `<cwd>/.claude/skills` and `<cwd>/.agents/skills`. Those\n * are discovered (loader.ts `defaultPluginDirs`) but are repository content a\n * team committed, so uninstall must not delete out of them — hoocode installed\n * nothing there.\n */\nexport function pluginHomeRoots(cwd: string, agentDir: string = getAgentDir()): string[] {\n\treturn [\n\t\tproductionRoot(\"claude\", agentDir),\n\t\tproductionRoot(\"github\", agentDir),\n\t\tconsumptionPluginsDir(agentDir),\n\t\tprojectPluginsDir(cwd),\n\t\tpath.join(cwd, \".hoocode\", \"plugins\"),\n\t];\n}\n\n/**\n * Every directory a plugin with `id` could occupy, in the order a lookup should\n * try them: the platform production homes, then the consumption home, then the\n * legacy project homes. Used to find an existing plugin without knowing which\n * role wrote it.\n */\nexport function candidatePluginDirs(cwd: string, id: string, agentDir: string = getAgentDir()): string[] {\n\tconst slug = sanitizeForDir(id);\n\treturn pluginHomeRoots(cwd, agentDir).map((root) => path.join(root, slug));\n}\n\n/**\n * Persistent, writable directory unique to an installed plugin — the target of\n * `${CLAUDE_PLUGIN_DATA}` / `${COPILOT_PLUGIN_DATA}`, which both vendors document\n * as the place for plugin runtime state.\n *\n * Deliberately outside every plugin home. A plugin's own directory is replaced\n * wholesale on promote and deleted on uninstall, and the vendors are explicit\n * that this must not live \"inside the installed-plugins cache directory\" — state\n * that vanishes on reinstall is not state.\n */\nexport function pluginDataDir(id: string, agentDir: string = getAgentDir()): string {\n\treturn path.join(homeRoot(agentDir), \".agents\", \"plugin-data\", sanitizeForDir(id));\n}\n\n/** {@link pluginDataDir}, created if absent. */\nexport function ensurePluginDataDir(id: string, agentDir: string = getAgentDir()): string {\n\tconst dir = pluginDataDir(id, agentDir);\n\tmkdirSync(dir, { recursive: true });\n\treturn dir;\n}\n\n/** Create an ephemeral draft directory. The caller must promote or {@link discardDraftDir} it. */\nexport function makeDraftDir(): string {\n\treturn mkdtempSync(path.join(tmpdir(), \"hoo-plugin-draft-\"));\n}\n\n/** Delete a draft directory. Safe to call twice. */\nexport function discardDraftDir(dir: string): void {\n\trmSync(dir, { recursive: true, force: true });\n}\n"]}
|