{"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"]}