agents-inc 0.164.0 → 0.165.0
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 +48 -0
- package/README.md +8 -9
- package/dist/{chunk-B6FSLXYN.js → chunk-42TNV2TK.js} +3 -3
- package/dist/chunk-4AAOOAIN.js +49 -0
- package/dist/chunk-4AAOOAIN.js.map +1 -0
- package/dist/{chunk-NQXO4LR3.js → chunk-4C7GL4F7.js} +16 -11
- package/dist/chunk-4C7GL4F7.js.map +1 -0
- package/dist/{chunk-Q4IRRVXK.js → chunk-57KU6SPH.js} +3 -3
- package/dist/{chunk-S63LXWMC.js → chunk-5M3PQNXI.js} +2 -2
- package/dist/{chunk-3WVH2CTK.js → chunk-5P26XBZO.js} +15 -8
- package/dist/chunk-5P26XBZO.js.map +1 -0
- package/dist/{chunk-G2JJ5BO5.js → chunk-6AIZPSEI.js} +337 -140
- package/dist/chunk-6AIZPSEI.js.map +1 -0
- package/dist/{chunk-NKGYKOB7.js → chunk-6CY6PW5X.js} +7 -8
- package/dist/chunk-6CY6PW5X.js.map +1 -0
- package/dist/{chunk-XHPWTL4I.js → chunk-6SW6SB7H.js} +3 -3
- package/dist/chunk-AAWZED5M.js +980 -0
- package/dist/chunk-AAWZED5M.js.map +1 -0
- package/dist/{chunk-UITEKY2R.js → chunk-AQXKAPCD.js} +30 -4
- package/dist/chunk-AQXKAPCD.js.map +1 -0
- package/dist/{chunk-X4SSJZLY.js → chunk-B5UUHBAM.js} +12 -261
- package/dist/chunk-B5UUHBAM.js.map +1 -0
- package/dist/{chunk-ARYNOWKF.js → chunk-BGU25XAY.js} +8 -8
- package/dist/{chunk-QGUP3QJF.js → chunk-CBMAO6OP.js} +22 -18
- package/dist/{chunk-QGUP3QJF.js.map → chunk-CBMAO6OP.js.map} +1 -1
- package/dist/{chunk-YRS3QZP2.js → chunk-CEBDGZID.js} +3 -3
- package/dist/{chunk-ABAZMMG4.js → chunk-CK5RM3DJ.js} +2 -2
- package/dist/{chunk-PHCPUQVR.js → chunk-CTUJO23Z.js} +2 -2
- package/dist/{chunk-QZX7SYKU.js → chunk-E6CHHXGY.js} +2 -2
- package/dist/{chunk-ZVXGW2IC.js → chunk-F2ZWFWTP.js} +4 -6
- package/dist/chunk-F2ZWFWTP.js.map +1 -0
- package/dist/{chunk-AVK2HEKB.js → chunk-GKPW33FP.js} +2 -6
- package/dist/{chunk-AVK2HEKB.js.map → chunk-GKPW33FP.js.map} +1 -1
- package/dist/{chunk-ECURHW3T.js → chunk-HR4YDSJK.js} +13 -12
- package/dist/chunk-HR4YDSJK.js.map +1 -0
- package/dist/{chunk-XPDVAJDH.js → chunk-IM45LLQX.js} +3599 -1558
- package/dist/chunk-IM45LLQX.js.map +1 -0
- package/dist/chunk-IVNKU4DM.js +39 -0
- package/dist/chunk-IVNKU4DM.js.map +1 -0
- package/dist/{chunk-XVAQG2CV.js → chunk-JOEA52NR.js} +10 -8
- package/dist/chunk-JOEA52NR.js.map +1 -0
- package/dist/{chunk-RGYDFBCT.js → chunk-JV4TKXEN.js} +5 -5
- package/dist/{chunk-4VL3CEOS.js → chunk-LXXGZAD3.js} +3 -3
- package/dist/chunk-MBUGQI2M.js +73 -0
- package/dist/chunk-MBUGQI2M.js.map +1 -0
- package/dist/{chunk-URXIQE7I.js → chunk-MWDY76MT.js} +2 -2
- package/dist/{chunk-V5GLL2JB.js → chunk-P3KJCYSR.js} +7 -7
- package/dist/{chunk-6MV4JOCL.js → chunk-PSX7CT6V.js} +2 -2
- package/dist/{chunk-B3Y3QMWI.js → chunk-TRTVOVHR.js} +4 -4
- package/dist/{chunk-AOTCIUGX.js → chunk-TV5Y53R2.js} +2 -2
- package/dist/{chunk-K5ITEA3I.js → chunk-UBSSIIWD.js} +34 -17
- package/dist/chunk-UBSSIIWD.js.map +1 -0
- package/dist/chunk-UCORXS4D.js +250 -0
- package/dist/chunk-UCORXS4D.js.map +1 -0
- package/dist/chunk-UR3RRTPO.js +159 -0
- package/dist/chunk-UR3RRTPO.js.map +1 -0
- package/dist/chunk-V5CWACR5.js +375 -0
- package/dist/chunk-V5CWACR5.js.map +1 -0
- package/dist/{chunk-LT5GXEB2.js → chunk-VPJUCVU7.js} +8 -7
- package/dist/chunk-VPJUCVU7.js.map +1 -0
- package/dist/chunk-VRXX4D6K.js +31 -0
- package/dist/chunk-VRXX4D6K.js.map +1 -0
- package/dist/{chunk-2CQNPLVF.js → chunk-WIFIAQZU.js} +63 -42
- package/dist/chunk-WIFIAQZU.js.map +1 -0
- package/dist/chunk-WSGX4IWC.js +356 -0
- package/dist/chunk-WSGX4IWC.js.map +1 -0
- package/dist/{chunk-X37UYIE3.js → chunk-WTANOSXK.js} +436 -739
- package/dist/chunk-WTANOSXK.js.map +1 -0
- package/dist/{chunk-XATLUKXK.js → chunk-X2KUI7V3.js} +6 -6
- package/dist/{chunk-NTF4UXGK.js → chunk-XATIKM3P.js} +73 -30
- package/dist/chunk-XATIKM3P.js.map +1 -0
- package/dist/{chunk-J27W6TXS.js → chunk-XQHZPINL.js} +8 -4
- package/dist/chunk-XQHZPINL.js.map +1 -0
- package/dist/{chunk-DSL66I4N.js → chunk-Y5SNGMB7.js} +6 -6
- package/dist/{chunk-7REKOTKG.js → chunk-YNOOZFIB.js} +3 -3
- package/dist/commands/build/marketplace.js +17 -17
- package/dist/commands/build/marketplace.js.map +1 -1
- package/dist/commands/build/plugins.js +11 -9
- package/dist/commands/build/plugins.js.map +1 -1
- package/dist/commands/compile.js +106 -27
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/doctor.js +427 -192
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/edit.js +504 -314
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/eject.js +41 -23
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/init.js +44 -40
- package/dist/commands/list.js +44 -10
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/new/marketplace.js +13 -10
- package/dist/commands/new/marketplace.js.map +1 -1
- package/dist/commands/search.js +12 -10
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/share.js +103 -15
- package/dist/commands/share.js.map +1 -1
- package/dist/commands/uninstall.js +264 -106
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +50 -20
- package/dist/commands/update.js.map +1 -1
- package/dist/components/common/prompt-confirm.js +1 -1
- package/dist/components/common/removal-plan-confirm.js +2 -2
- package/dist/components/common/select-list.js +3 -3
- package/dist/components/common/spinner.js +6 -3
- package/dist/components/wizard/category-grid.js +5 -5
- package/dist/components/wizard/checkbox-grid.js +4 -4
- package/dist/components/wizard/domain-selection.js +9 -9
- package/dist/components/wizard/run-wizard-session.js +29 -29
- package/dist/components/wizard/scroll-affordance.js +2 -2
- package/dist/components/wizard/skill-agent-summary.js +5 -5
- package/dist/components/wizard/source-grid.js +6 -6
- package/dist/components/wizard/stack-selection.js +8 -8
- package/dist/components/wizard/step-agents.js +8 -8
- package/dist/components/wizard/step-build.js +8 -8
- package/dist/components/wizard/step-confirm.js +9 -9
- package/dist/components/wizard/step-sources.js +9 -9
- package/dist/components/wizard/step-stack.js +9 -9
- package/dist/components/wizard/summary-panel.js +8 -8
- package/dist/components/wizard/toast.js +2 -2
- package/dist/components/wizard/wizard-layout.js +12 -12
- package/dist/components/wizard/wizard-tabs.js +2 -2
- package/dist/components/wizard/wizard.js +27 -27
- package/dist/hooks/init.js +59 -43
- package/dist/hooks/init.js.map +1 -1
- package/dist/load-agent-defs-JHLE6MAE.js +14 -0
- package/dist/recompile-project-agents-ZPP5NOLZ.js +18 -0
- package/dist/{source-loader-GRBVNRAW.js → source-loader-RWZAYJ7E.js} +6 -4
- package/dist/src/agents/_templates/agent-body.liquid +55 -0
- package/dist/src/agents/_templates/agent.liquid +1 -55
- package/dist/src/agents/meta/agent-summoner/output.md +1 -1
- package/dist/src/agents/meta/agent-summoner/playbook.md +15 -7
- package/dist/stores/wizard-store.js +4 -4
- package/package.json +10 -9
- package/src/agents/_templates/agent-body.liquid +55 -0
- package/src/agents/_templates/agent.liquid +1 -55
- package/src/agents/meta/agent-summoner/output.md +1 -1
- package/src/agents/meta/agent-summoner/playbook.md +15 -7
- package/src/schemas/project-config.schema.json +1 -1
- package/src/schemas/project-source-config.schema.json +1 -1
- package/dist/chunk-2CQNPLVF.js.map +0 -1
- package/dist/chunk-3WVH2CTK.js.map +0 -1
- package/dist/chunk-7ANJQLPJ.js +0 -35
- package/dist/chunk-7ANJQLPJ.js.map +0 -1
- package/dist/chunk-ECURHW3T.js.map +0 -1
- package/dist/chunk-FLW25ORT.js +0 -130
- package/dist/chunk-FLW25ORT.js.map +0 -1
- package/dist/chunk-G2JJ5BO5.js.map +0 -1
- package/dist/chunk-J27W6TXS.js.map +0 -1
- package/dist/chunk-JCPANDNR.js +0 -35
- package/dist/chunk-JCPANDNR.js.map +0 -1
- package/dist/chunk-JIERYE6B.js +0 -480
- package/dist/chunk-JIERYE6B.js.map +0 -1
- package/dist/chunk-JU4ZGJ7A.js +0 -668
- package/dist/chunk-JU4ZGJ7A.js.map +0 -1
- package/dist/chunk-K5ITEA3I.js.map +0 -1
- package/dist/chunk-LT5GXEB2.js.map +0 -1
- package/dist/chunk-NKGYKOB7.js.map +0 -1
- package/dist/chunk-NQXO4LR3.js.map +0 -1
- package/dist/chunk-NTF4UXGK.js.map +0 -1
- package/dist/chunk-UITEKY2R.js.map +0 -1
- package/dist/chunk-X37UYIE3.js.map +0 -1
- package/dist/chunk-X4SSJZLY.js.map +0 -1
- package/dist/chunk-XPDVAJDH.js.map +0 -1
- package/dist/chunk-XVAQG2CV.js.map +0 -1
- package/dist/chunk-ZBPEFQ4G.js +0 -12
- package/dist/chunk-ZBPEFQ4G.js.map +0 -1
- package/dist/chunk-ZVXGW2IC.js.map +0 -1
- package/dist/load-agent-defs-7IM3GBEQ.js +0 -13
- package/dist/recompile-project-agents-VR4NLGOU.js +0 -17
- /package/dist/{chunk-B6FSLXYN.js.map → chunk-42TNV2TK.js.map} +0 -0
- /package/dist/{chunk-Q4IRRVXK.js.map → chunk-57KU6SPH.js.map} +0 -0
- /package/dist/{chunk-S63LXWMC.js.map → chunk-5M3PQNXI.js.map} +0 -0
- /package/dist/{chunk-XHPWTL4I.js.map → chunk-6SW6SB7H.js.map} +0 -0
- /package/dist/{chunk-ARYNOWKF.js.map → chunk-BGU25XAY.js.map} +0 -0
- /package/dist/{chunk-YRS3QZP2.js.map → chunk-CEBDGZID.js.map} +0 -0
- /package/dist/{chunk-ABAZMMG4.js.map → chunk-CK5RM3DJ.js.map} +0 -0
- /package/dist/{chunk-PHCPUQVR.js.map → chunk-CTUJO23Z.js.map} +0 -0
- /package/dist/{chunk-QZX7SYKU.js.map → chunk-E6CHHXGY.js.map} +0 -0
- /package/dist/{chunk-RGYDFBCT.js.map → chunk-JV4TKXEN.js.map} +0 -0
- /package/dist/{chunk-4VL3CEOS.js.map → chunk-LXXGZAD3.js.map} +0 -0
- /package/dist/{chunk-URXIQE7I.js.map → chunk-MWDY76MT.js.map} +0 -0
- /package/dist/{chunk-V5GLL2JB.js.map → chunk-P3KJCYSR.js.map} +0 -0
- /package/dist/{chunk-6MV4JOCL.js.map → chunk-PSX7CT6V.js.map} +0 -0
- /package/dist/{chunk-B3Y3QMWI.js.map → chunk-TRTVOVHR.js.map} +0 -0
- /package/dist/{chunk-AOTCIUGX.js.map → chunk-TV5Y53R2.js.map} +0 -0
- /package/dist/{chunk-XATLUKXK.js.map → chunk-X2KUI7V3.js.map} +0 -0
- /package/dist/{chunk-DSL66I4N.js.map → chunk-Y5SNGMB7.js.map} +0 -0
- /package/dist/{chunk-7REKOTKG.js.map → chunk-YNOOZFIB.js.map} +0 -0
- /package/dist/{load-agent-defs-7IM3GBEQ.js.map → load-agent-defs-JHLE6MAE.js.map} +0 -0
- /package/dist/{recompile-project-agents-VR4NLGOU.js.map → recompile-project-agents-ZPP5NOLZ.js.map} +0 -0
- /package/dist/{source-loader-GRBVNRAW.js.map → source-loader-RWZAYJ7E.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/lib/agents/list-compiled-agents.ts","../src/cli/lib/agents/agent-recompiler.ts","../src/cli/lib/agents/write-compiled-agents.ts","../src/cli/lib/compiler.ts","../src/cli/lib/agents/agent-provenance.ts"],"sourcesContent":["import path from \"path\";\nimport { partition } from \"remeda\";\nimport { glob, readFileOptional, remove } from \"../../utils/fs\";\nimport { isAgentName } from \"../../utils/type-guards\";\nimport { type AgentCodec, agentCodec } from \"../installation/install-layout\";\nimport type { AgentName } from \"../../types\";\n\n/**\n * Claude's codec, the default every reader below falls back to. A caller holding an installation\n * of another host passes that host's — `agentCodec(providerInUse(dir))` — because a Codex role is a\n * `.toml` whose marker sits inside `developer_instructions`, and a reader that only knew `*.md`\n * found no compiled agent on Codex at all: `uninstall` left every role behind while saying it had\n * removed them (CLI-896, found on a real Codex run 2026-09-26).\n */\nconst CLAUDE_AGENTS = agentCodec(\"claude\");\n\n/** Compiled-agent basenames in a directory, split by whether this CLI compiled them. */\nexport type AgentProvenanceSplit = {\n /** Files carrying the provenance marker — provably written by this CLI. */\n marked: string[];\n /** Everything else in the directory, the user's own agents among them. */\n unmarked: string[];\n};\n\n/** Lists compiled-agent markdown filenames (e.g. \"web-developer.md\") in a directory. */\nexport async function listAgentMdFiles(agentsDir: string): Promise<string[]> {\n return listAgentFilesOf(agentsDir, CLAUDE_AGENTS);\n}\n\n/** Lists the compiled-agent files `codec`'s host keeps in a directory (`*.md`, `*.toml`). */\nexport async function listAgentFilesOf(agentsDir: string, codec: AgentCodec): Promise<string[]> {\n return glob(codec.listGlob, agentsDir);\n}\n\n/**\n * Splits the agent files in a directory by the provenance marker each compiled agent carries.\n *\n * This is the only thing that identifies this CLI's own output once the configuration naming\n * it is gone, so the split errs one way by construction: a file that cannot be read yields no\n * marker and lands in `unmarked`, because \"cannot prove it is ours\" and \"is not ours\" call for\n * the same answer — leave it alone.\n */\nexport async function splitAgentsByProvenance(\n agentsDir: string,\n codec: AgentCodec = CLAUDE_AGENTS,\n): Promise<AgentProvenanceSplit> {\n const files = await listAgentFilesOf(agentsDir, codec);\n const classified = await Promise.all(\n files.map((file) => readAgentProvenance(agentsDir, file, codec)),\n );\n const [marked, unmarked] = partition(classified, (agent) => agent.compiledHere);\n\n return { marked: marked.map(agentName), unmarked: unmarked.map(agentName) };\n}\n\ntype ClassifiedAgent = { name: string; compiledHere: boolean };\n\nasync function readAgentProvenance(\n agentsDir: string,\n file: string,\n codec: AgentCodec,\n): Promise<ClassifiedAgent> {\n const content = await readFileOptional(path.join(agentsDir, file));\n return { name: path.basename(file, codec.extension), compiledHere: codec.hasMarker(content) };\n}\n\nfunction agentName(agent: ClassifiedAgent): string {\n return agent.name;\n}\n\n/** Lists compiled-agent names (filenames without the `.md` extension) in a directory. */\nexport async function listCompiledAgentNames(agentsDir: string): Promise<AgentName[]> {\n const files = await listAgentMdFiles(agentsDir);\n // Boundary cast: compiled .md filenames are agent names by convention; custom\n // agents from marketplace stacks may not be members of the AgentName union.\n return files.map((f) => path.basename(f, \".md\") as AgentName);\n}\n\n/**\n * Removes stale CLI-compiled agent files from `agentsDir`: deletes any agent file (`codec`'s\n * extension) whose basename is a built-in CLI agent name (`isAgentName`) that is NOT in\n * `keep`. The built-in-name check protects hand-authored agents (whose basename\n * is not a built-in name) — it is never the removal criterion. Only call this\n * when `keep` is the COMPLETE set of agents that should live in `agentsDir`.\n */\nexport async function pruneStaleCompiledAgents(\n agentsDir: string,\n keep: ReadonlySet<AgentName>,\n codec: AgentCodec = CLAUDE_AGENTS,\n): Promise<void> {\n // A non-built-in basename is a hand-authored agent — always preserved.\n const isStaleCompiledAgent = (file: string): boolean => {\n const name = path.basename(file, codec.extension);\n return isAgentName(name) && !keep.has(name);\n };\n\n const files = await listAgentFilesOf(agentsDir, codec);\n await Promise.all(\n files.filter(isStaleCompiledAgent).map((file) => remove(path.join(agentsDir, file))),\n );\n}\n","import { partition } from \"remeda\";\nimport { writeCompiledAgentsByScope, type AgentWriteOutcome } from \"./write-compiled-agents\";\nimport { listCompiledAgentNames } from \"./list-compiled-agents\";\nimport path from \"path\";\n\nimport { getErrorMessage } from \"../../utils/errors\";\nimport type {\n AgentDefinition,\n AgentName,\n CompileAgentConfig,\n CompileConfig,\n ProjectConfig,\n SkillDefinitionMap,\n SkillId,\n SkillScope,\n} from \"../../types\";\n\nimport { buildCompileAgents } from \"../installation/local-installer\";\nimport { ensureDir } from \"../../utils/fs\";\nimport { verbose } from \"../../utils/logger\";\nimport { typedEntries, typedFromEntries, typedKeys } from \"../../utils/typed-object\";\nimport { createLiquidEngine } from \"../compiler\";\nimport { loadProjectConfig, effectivelyExcludedSkillIds } from \"../configuration\";\nimport { loadAllAgents, loadProjectAgents } from \"../loading\";\nimport { getPluginAgentsDir } from \"../plugins\";\nimport { discoverAllPluginSkills } from \"../plugins/plugin-discovery\";\nimport { resolveAgents } from \"../resolver\";\n\nexport type RecompileAgentsOptions = {\n pluginDir: string;\n sourcePath: string;\n agents?: AgentName[];\n skills?: SkillDefinitionMap;\n projectDir?: string;\n outputDir?: string;\n /** When provided, routes agents by scope: global agents to ~/.claude/agents/, project agents to outputDir */\n agentScopeMap?: Map<AgentName, SkillScope>;\n};\n\nexport type RecompileAgentsResult = {\n compiled: AgentName[];\n /**\n * The subset of `compiled` whose file this pass actually wrote. `compiled` is\n * every agent that came through the pass with a correct file at the end of it;\n * `rewritten` is the ones whose content moved, so `compiled minus rewritten` is\n * the count a summary reports as unchanged.\n */\n rewritten: AgentName[];\n failed: AgentName[];\n warnings: string[];\n};\n\nasync function getExistingAgentNames(pluginDir: string): Promise<AgentName[]> {\n return listCompiledAgentNames(getPluginAgentsDir(pluginDir));\n}\n\ntype ResolveAgentNamesParams = {\n specifiedAgents?: AgentName[];\n projectConfig: ProjectConfig | null;\n allAgents: Partial<Record<AgentName, AgentDefinition>>;\n outputDir?: string;\n pluginDir: string;\n};\n\nasync function resolveAgentNames(params: ResolveAgentNamesParams): Promise<AgentName[]> {\n const { specifiedAgents, projectConfig, allAgents, outputDir, pluginDir } = params;\n\n if (specifiedAgents) {\n return specifiedAgents;\n }\n\n // A present config is authoritative over its agent roster — even when empty.\n // `agents: []` means \"no agents\", so it must NOT fall through to the\n // all-agents branch below; that branch exists only for the config-LESS case.\n if (projectConfig) {\n const agentNames = projectConfig.agents.map((a) => a.name);\n verbose(`Using agents from config: ${agentNames.join(\", \")}`);\n return agentNames;\n }\n\n if (outputDir) {\n const names = typedKeys<AgentName>(allAgents);\n verbose(`Using all available agents from source: ${names.join(\", \")}`);\n return names;\n }\n\n return getExistingAgentNames(pluginDir);\n}\n\n/** Splits write outcomes into the recompile result; logs each success at verbose level. */\nfunction buildRecompileResult(\n outcomes: AgentWriteOutcome[],\n priorWarnings: string[],\n): RecompileAgentsResult {\n const [succeeded, failedOutcomes] = partition(\n outcomes,\n (outcome): outcome is Extract<AgentWriteOutcome, { ok: true }> => outcome.ok,\n );\n succeeded.forEach((outcome) =>\n verbose(\n ` ${outcome.rewritten ? \"Rewrote\" : \"Unchanged\"}: ${outcome.name} (${outcome.scope} -> ${outcome.targetDir})`,\n ),\n );\n return {\n compiled: succeeded.map((outcome) => outcome.name),\n rewritten: succeeded.filter((outcome) => outcome.rewritten).map((outcome) => outcome.name),\n failed: failedOutcomes.map((outcome) => outcome.name),\n warnings: [\n ...priorWarnings,\n ...failedOutcomes.map(\n (outcome) => `Failed to compile ${outcome.name}: ${getErrorMessage(outcome.error)}`,\n ),\n ],\n };\n}\n\nexport function filterExcludedEntries(config: ProjectConfig): ProjectConfig {\n const excludedIds = effectivelyExcludedSkillIds(config.skills);\n const activeSkills = config.skills.filter((s) => !s.excluded);\n const activeAgents = config.agents.filter((a) => !a.excluded);\n\n // A config with no stack keeps none — the key stays absent rather than becoming an\n // explicit `undefined`.\n const filteredStack =\n config.stack === undefined\n ? {}\n : { stack: withoutExcludedAssignments(config.stack, excludedIds) };\n\n return { ...config, skills: activeSkills, agents: activeAgents, ...filteredStack };\n}\n\n/**\n * The stack with every excluded skill's assignment taken out. Each agent and category keeps its\n * key, even where that leaves a category with no assignments.\n */\nexport function withoutExcludedAssignments(\n stack: NonNullable<ProjectConfig[\"stack\"]>,\n excludedIds: ReadonlySet<SkillId>,\n): NonNullable<ProjectConfig[\"stack\"]> {\n return Object.fromEntries(\n typedEntries(stack).map(([agentName, agentStack]) => [\n agentName,\n Object.fromEntries(\n typedEntries(agentStack).map(([category, assignments]) => [\n category,\n assignments.filter((assignment) => !excludedIds.has(assignment.id)),\n ]),\n ),\n ]),\n );\n}\n\nexport async function recompileAgents(\n options: RecompileAgentsOptions,\n): Promise<RecompileAgentsResult> {\n const { pluginDir, sourcePath, skills: providedSkills, projectDir, outputDir } = options;\n\n const configDir = projectDir ?? pluginDir;\n const loadedConfig = await loadProjectConfig(configDir);\n const projectConfig = loadedConfig?.config ?? null;\n\n // Filter excluded entries once at the entry point — callees receive clean data\n const filteredConfig = projectConfig ? filterExcludedEntries(projectConfig) : null;\n\n const builtinAgents = await loadAllAgents(sourcePath);\n const projectAgents = projectDir ? await loadProjectAgents(projectDir) : {};\n\n // Priority: project agents > built-in agents\n const allAgents: Partial<Record<AgentName, AgentDefinition>> = {\n ...builtinAgents,\n ...projectAgents,\n };\n\n const agentNames = await resolveAgentNames({\n ...(options.agents !== undefined && { specifiedAgents: options.agents }),\n projectConfig: filteredConfig,\n allAgents,\n ...(outputDir !== undefined && { outputDir }),\n pluginDir,\n });\n\n // An empty pass has nothing to say: every caller reports one off `compiled`, and a warning here\n // only ever surfaced merged into the OTHER scope's failure report, beside agents that compiled.\n if (agentNames.length === 0) {\n return { compiled: [], rewritten: [], failed: [], warnings: [] };\n }\n\n verbose(`Recompiling ${agentNames.length} agents in ${outputDir ?? pluginDir}`);\n\n // When skills are not provided, discover from all plugin directories.\n const pluginSkills: SkillDefinitionMap =\n providedSkills ?? (await discoverAllPluginSkills(projectDir ?? pluginDir));\n\n const { configAgents, missingWarnings } = compileEntriesFor(\n agentNames,\n filteredConfig,\n allAgents,\n );\n\n const compileConfig: CompileConfig = {\n name: filteredConfig?.name || path.basename(pluginDir),\n description: filteredConfig?.description || \"Recompiled plugin\",\n agents: configAgents,\n };\n\n const engine = await createLiquidEngine(projectDir);\n const resolvedAgents = await resolveAgents(allAgents, pluginSkills, compileConfig, sourcePath);\n\n const agentsDir = outputDir ?? getPluginAgentsDir(pluginDir);\n await ensureDir(agentsDir);\n\n const outcomes = await writeCompiledAgentsByScope({\n resolvedAgents,\n sourcePath,\n engine,\n projectAgentsDir: agentsDir,\n // The same root this pass loaded its config from, so the provider that decides the FORMAT\n // and the provider that decided the configuration are read off one installation.\n projectDir: configDir,\n ...(options.agentScopeMap !== undefined && { agentScopeMap: options.agentScopeMap }),\n });\n return buildRecompileResult(outcomes, missingWarnings);\n}\n\n/**\n * The compile entries for exactly the agents this pass compiles, and a warning for each named\n * agent that neither the config nor the source defines.\n *\n * `buildCompileAgents` returns entries for ALL agents in the config, but when scopeFilter is\n * active, we only want the agents matching that scope. Without this restriction, a project pass\n * would compile global agents without their stack (since the project config omits global agent\n * stack entries) and overwrite correctly compiled global agent files.\n */\nfunction compileEntriesFor(\n agentNames: AgentName[],\n config: ProjectConfig | null,\n allAgents: Partial<Record<AgentName, AgentDefinition>>,\n): { configAgents: Partial<Record<AgentName, CompileAgentConfig>>; missingWarnings: string[] } {\n const allConfigAgents = config ? buildCompileAgents(config, allAgents) : {};\n\n const [knownAgents, missingAgents] = partition(agentNames, (name) =>\n Boolean(allConfigAgents[name] || allAgents[name]),\n );\n\n return {\n configAgents: typedFromEntries<AgentName, CompileAgentConfig>(\n knownAgents.map((name) => [name, allConfigAgents[name] ?? {}]),\n ),\n missingWarnings: missingAgents.map((name) => `Agent \"${name}\" not found in source definitions`),\n };\n}\n","import os from \"os\";\nimport path from \"path\";\nimport type { Liquid } from \"liquidjs\";\nimport { compileAgentForHost } from \"../compiler.js\";\nimport { resolveInstallPaths } from \"../installation/install-base-dir.js\";\nimport { agentCodec, hostCompilesAgent, providerInUse } from \"../installation/install-layout.js\";\nimport { writeFile, fileExists, readFile } from \"../../utils/fs.js\";\nimport { typedEntries } from \"../../utils/typed-object.js\";\nimport type { Provider } from \"../../consts.js\";\nimport type { AgentConfig, AgentName, SkillScope } from \"../../types/index.js\";\n\nexport type AgentWriteOutcome =\n | {\n name: AgentName;\n ok: true;\n scope: SkillScope;\n targetDir: string;\n /**\n * Whether this pass actually wrote the file. False means the compiled output\n * matched what was already on disk, so nothing was written — which is what\n * lets a recompile summary tell a real one from a no-op instead of counting\n * the roster it walked.\n */\n rewritten: boolean;\n }\n | { name: AgentName; ok: false; error: unknown };\n\n/**\n * Whether the target already holds exactly this content, making a write a no-op.\n *\n * Skipping that write is what gives \"unchanged\" a meaning a caller can check: an\n * agent reported unchanged keeps its mtime, and an mtime is the only trace a\n * rewrite-with-identical-bytes leaves anywhere.\n */\nasync function holdsExactly(filePath: string, content: string): Promise<boolean> {\n return (await fileExists(filePath)) && (await readFile(filePath)) === content;\n}\n\n/**\n * Where an agent goes when `agentScopeMap` has nothing to say about it — either because the caller\n * passed no map at all or because this agent is not in the one it passed (a hand-authored agent\n * under `.claude/agents/` has no config row).\n *\n * This is a ROUTING answer, not a selection default: `\"project\"` here means \"the directory the\n * caller named in `projectAgentsDir`\", which for an unrouted write is the only defensible target.\n * It is deliberately NOT `DEFAULT_SELECTION_OPTIONS.scope` from `@workspace/matrix` — that constant\n * says what an untouched *pick* installs as, and adopting it here would relocate the agents of every\n * caller that never asked for global routing into the user's `~/.claude/agents`.\n */\nconst UNROUTED_AGENT_SCOPE: SkillScope = \"project\";\n\n/**\n * Compiles each resolved agent and writes it to its scope's agents directory:\n * global agents to `~/.claude/agents/`, project agents to `projectAgentsDir`.\n * Per-agent failures are collected as outcomes — callers own the policy\n * (recompile reports and continues; install hard-errors).\n *\n * Neither directory is created up front. `writeFile` makes a target's parent on\n * the way past, so a directory appears exactly when an agent routes into it —\n * which is what keeps a wholly project-scoped pass from leaving an empty\n * `~/.claude/agents/` behind in a home that has no global install.\n */\nexport async function writeCompiledAgentsByScope(params: {\n resolvedAgents: Partial<Record<AgentName, AgentConfig>>;\n sourcePath: string;\n engine: Liquid;\n projectAgentsDir: string;\n /** The root whose folder says which host the PROJECT scope's sub-agents are written for. */\n projectDir: string;\n agentScopeMap?: Map<AgentName, SkillScope>;\n}): Promise<AgentWriteOutcome[]> {\n const hosts: Record<SkillScope, AgentHost> = {\n // Each scope reads its OWN root, the way `resolveInstallPaths` reads one beside this: a\n // project on Codex under a global on Claude is an arrangement this product allows, and one\n // provider answered for both scopes is how a pass writes the wrong format into one of them.\n global: {\n provider: providerInUse(os.homedir()),\n agentsDir: resolveInstallPaths(os.homedir(), \"global\").agentsDir,\n },\n project: { provider: providerInUse(params.projectDir), agentsDir: params.projectAgentsDir },\n };\n\n const outcomes: AgentWriteOutcome[] = [];\n for (const [name, agent] of typedEntries<AgentName, AgentConfig>(params.resolvedAgents)) {\n const scope = params.agentScopeMap?.get(name) ?? UNROUTED_AGENT_SCOPE;\n const host = hosts[scope];\n\n // Before the render, and it produces NO outcome: a sub-agent this host does not carry was\n // not compiled and did not fail, so counting it either way would make a deliberate omission\n // read as a result. The one line the user is owed is the command's, once per run.\n if (!hostCompilesAgent(host.provider, name)) continue;\n\n outcomes.push(await writeOneAgent({ ...params, name, agent, scope, host }));\n }\n return outcomes;\n}\n\n/**\n * One sub-agent rendered for its host and written where that host reads it.\n *\n * The failure is RETURNED rather than thrown, because the policy belongs to the caller: recompile\n * reports and continues, install hard-errors. A pass that stopped at the first bad sub-agent would\n * make that decision for both of them.\n */\nasync function writeOneAgent(params: {\n name: AgentName;\n agent: AgentConfig;\n scope: SkillScope;\n host: AgentHost;\n sourcePath: string;\n engine: Liquid;\n}): Promise<AgentWriteOutcome> {\n const { name, scope, host } = params;\n\n try {\n const output = await compileAgentForHost(\n host.provider,\n name,\n params.agent,\n params.sourcePath,\n params.engine,\n );\n const targetPath = path.join(host.agentsDir, fileNameFor(name, host.provider));\n const rewritten = !(await holdsExactly(targetPath, output));\n if (rewritten) await writeFile(targetPath, output);\n\n return { name, ok: true, scope, targetDir: host.agentsDir, rewritten };\n } catch (error) {\n return { name, ok: false, error };\n }\n}\n\n/** Where one scope's compiled sub-agents go, and in whose format. */\ntype AgentHost = { provider: Provider; agentsDir: string };\n\n/**\n * The file one compiled sub-agent is written to, extension included.\n *\n * The extension comes from {@link agentCodec} rather than from a literal here, because the defect\n * this whole funnel exists against is `web-developer.md` written into `<project>/.codex/agents/`\n * by a pass that then reports success: Codex ignores such a file, nothing recognises it as ours\n * again, and the run exits 0.\n */\nfunction fileNameFor(name: AgentName, provider: Provider): string {\n return `${name}${agentCodec(provider).extension}`;\n}\n","import { Liquid } from \"liquidjs\";\nimport path from \"path\";\nimport {\n buildAgentTemplateContext,\n pluginRefFor,\n renderAgent,\n renderAgentBody,\n renderAgentRoleToml,\n type AgentFiles,\n} from \"@workspace/compile/agent-source\";\nimport { readFile, readFileOptional, directoryExists } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\nimport {\n CLAUDE_DIR,\n DIRS,\n PROJECT_ROOT,\n STANDARD_FILES,\n STANDARD_DIRS,\n type Provider,\n} from \"../consts\";\nimport { providerInUse, sourceFolderInUse, sourceFolderName } from \"./installation/install-layout\";\nimport { cliVersion } from \"./agents/agent-provenance\";\nimport \"./compile-seat.js\";\nimport type { AgentConfig, AgentName } from \"../types\";\n\n/**\n * What survives here is the half of a compile that reads the machine: the five `readFile`s that\n * fetch an agent's partials off disk, and the layered template roots a project can override. The\n * rendering itself — the template context, the injection sanitiser and the provenance stamp — is\n * `@workspace/compile/agent-source`, so the editor's output preview draws the bytes this writes\n * rather than a second implementation of them.\n */\nexport {\n buildAgentTemplateContext,\n sanitizeCompiledAgentData,\n sanitizeLiquidSyntax,\n} from \"@workspace/compile/agent-source\";\n\nasync function readAgentFiles(\n name: AgentName,\n agent: AgentConfig,\n projectRoot: string,\n): Promise<AgentFiles> {\n const agentSourceRoot = agent.sourceRoot || projectRoot;\n const agentBaseDir = agent.agentBaseDir || DIRS.agents;\n const agentPath = agent.path || name;\n const agentDir = path.join(agentSourceRoot, agentBaseDir, agentPath);\n\n const identity = await readFile(path.join(agentDir, STANDARD_FILES.IDENTITY_MD));\n const playbook = await readFile(path.join(agentDir, STANDARD_FILES.PLAYBOOK_MD));\n const criticalRequirementsTop = await readFileOptional(\n path.join(agentDir, STANDARD_FILES.CRITICAL_REQUIREMENTS_MD),\n \"\",\n );\n const criticalReminders = await readFileOptional(\n path.join(agentDir, STANDARD_FILES.CRITICAL_REMINDERS_MD),\n \"\",\n );\n\n const category = agentPath.split(\"/\")[0] || name;\n const categoryDir = path.join(agentSourceRoot, agentBaseDir, category);\n const output = await readOutputPartial(agentDir, categoryDir);\n\n return { identity, playbook, output, criticalRequirementsTop, criticalReminders };\n}\n\n/** The agent's own `output.md`, or its category's where the agent's is absent or empty. */\nasync function readOutputPartial(agentDir: string, categoryDir: string): Promise<string> {\n const own = await readFileOptional(path.join(agentDir, STANDARD_FILES.OUTPUT_MD), \"\");\n if (own) return own;\n return readFileOptional(path.join(categoryDir, STANDARD_FILES.OUTPUT_MD), \"\");\n}\n\n/**\n * Creates a Liquid template engine with a layered template root hierarchy.\n *\n * Template resolution order (first match wins):\n * 1. Project-local templates: `agents/_templates/` inside whichever source folder the project is\n * on — `.claude-src/` or `.agents-inc/<provider>/`, resolved by `sourceFolderInUse`\n * 2. Legacy templates: `{projectDir}/.claude/templates/`\n * 3. Built-in templates: `{PROJECT_ROOT}/src/agents/_templates/`\n *\n * The browser-side twin is `createEngineFromTemplates` in `@workspace/compile/engine`, which\n * builds the same engine over the vendored corpus instead of over directories. Every option\n * below is duplicated there, because a render that resolved filters or variables differently\n * would produce a different file from the same data.\n *\n * `globals.sourceFolder` is what makes a compiled agent's own text install-specific. A partial is\n * never rendered through this engine — it is prose, inlined verbatim — so `renderAgent` asks the\n * engine for that global and substitutes the one opted-in token itself, which is how\n * `agent-summoner` names the folder THIS project keeps its source in rather than a literal that is\n * wrong on every install of the other layout.\n *\n * @param projectDir - Optional project directory for local template overrides\n * @returns Configured Liquid engine with `.liquid` extension and strict filters\n */\nexport async function createLiquidEngine(projectDir?: string): Promise<Liquid> {\n const roots: string[] = [];\n // One read for both answers below: the templates this install overrides with, and the folder\n // name a compiled `agent-summoner` is told to author into, have to name one installation.\n const provider = providerInUse(projectDir);\n\n if (projectDir) {\n const srcTemplatesDir = path.join(\n sourceFolderInUse(projectDir, provider).dir,\n STANDARD_DIRS.AGENTS,\n path.basename(DIRS.templates),\n );\n if (await directoryExists(srcTemplatesDir)) {\n roots.push(srcTemplatesDir);\n verbose(`Using local templates from: ${srcTemplatesDir}`);\n }\n\n const legacyTemplatesDir = path.join(projectDir, CLAUDE_DIR, STANDARD_DIRS.TEMPLATES);\n if (await directoryExists(legacyTemplatesDir)) {\n roots.push(legacyTemplatesDir);\n verbose(`Using legacy templates from: ${legacyTemplatesDir}`);\n }\n }\n\n roots.push(path.join(PROJECT_ROOT, DIRS.templates));\n\n return new Liquid({\n root: roots,\n extname: \".liquid\",\n strictVariables: false,\n strictFilters: true,\n globals: { sourceFolder: sourceFolderName(projectDir, provider) },\n });\n}\n\n/**\n * One resolved sub-agent as the file `provider`'s host actually reads.\n *\n * **The two hosts share every byte of the prose and disagree about everything around it.** Claude\n * reads markdown under a frontmatter block; Codex reads an agent ROLE DEFINITION — TOML, three\n * required keys, and a deserializer that drops the WHOLE file on one unlisted key. So the split is\n * at the RENDER and not at the data: both start from the same\n * {@link buildAgentTemplateContext} context, and `renderAgentBody` is by construction the tail of\n * `renderAgent`, which is what makes a Codex sub-agent's instructions its Claude twin's byte for\n * byte.\n *\n * Nothing here decides WHICH sub-agents a host gets — `hostCompilesAgent` answers that, before the\n * write pass reaches this — and nothing here decides where the file goes.\n */\nexport async function compileAgentForHost(\n provider: Provider,\n name: AgentName,\n agent: AgentConfig,\n fallbackRoot: string,\n engine: Liquid,\n): Promise<string> {\n verbose(`Compiling agent: ${name} (${provider})`);\n\n const files = await readAgentFiles(name, agent, fallbackRoot);\n\n // Per-skill pluginRef attachment. Each skill's own `source` decides\n // whether it renders as `${id}:${id}` (plugin-installed) or bare id (ejected).\n // This correctly handles mixed-mode agents where some skills are plugin and\n // others are ejected. Missing `source` (user-authored local skills with no\n // SkillConfig entry) falls through to bare id — the expected case, not a\n // silent fallback.\n const data = buildAgentTemplateContext(name, agent, files, (skill) => ({\n ...skill,\n ...pluginRefFor(skill),\n }));\n const version = await cliVersion();\n\n switch (provider) {\n case \"claude\":\n return renderAgent(engine, data, version);\n case \"codex\":\n return renderAgentRoleToml(agent, await renderAgentBody(engine, data, version));\n default: {\n const _exhaustive: never = provider;\n return _exhaustive;\n }\n }\n}\n","import path from \"path\";\nimport { z } from \"zod\";\n\nimport { PROJECT_ROOT, STANDARD_FILES } from \"../../consts\";\nimport { readFile } from \"../../utils/fs\";\n\n/** The one field the marker needs from this CLI's own manifest. */\nconst ownPackageJsonSchema = z.object({ version: z.string().min(1) });\n\n/**\n * The provenance marker every compiled agent carries lives in `@workspace/compile/agent-source`,\n * beside the renderer that stamps it — the editor's output preview draws the same first body line\n * and must not compute it a second way. What stays here is the half that reads this CLI's own\n * manifest, which a browser has no equivalent of.\n */\nexport {\n hasProvenanceMarker,\n provenanceMarker,\n stampProvenanceMarker,\n} from \"@workspace/compile/agent-source\";\n\nlet ownVersion: Promise<string> | undefined;\n\n/**\n * This CLI's own published version, read once per process from the package manifest beside\n * the code — the same `PROJECT_ROOT` the bundled agent partials and templates resolve from.\n *\n * It reaches a compiled agent as the `version` argument of `renderAgent`, which hands it to the\n * template as `generatorVersion`; `agent.liquid` renders it in the trailing `<system-reminder>`\n * block. Deliberately not the provenance marker above it — that line's bytes are constant across\n * releases, so a version bump no longer rewrites the first cacheable byte of every compiled agent.\n */\nexport function cliVersion(): Promise<string> {\n ownVersion ??= readOwnVersion();\n return ownVersion;\n}\n\nasync function readOwnVersion(): Promise<string> {\n const manifestPath = path.join(PROJECT_ROOT, STANDARD_FILES.PACKAGE_JSON);\n const raw: unknown = JSON.parse(await readFile(manifestPath));\n\n const parsed = ownPackageJsonSchema.safeParse(raw);\n if (!parsed.success) {\n throw new Error(`Could not read this CLI's own version from ${manifestPath}`);\n }\n return parsed.data.version;\n}\n"],"mappings":"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","names":["partition","path","path","path","path","path","path","path","partition","agentName","path"]}
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ScrollAffordance
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PSX7CT6V.js";
|
|
5
5
|
import {
|
|
6
6
|
getStackName
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-LXXGZAD3.js";
|
|
8
8
|
import {
|
|
9
9
|
useMeasuredHeight
|
|
10
10
|
} from "./chunk-TSIU7DME.js";
|
|
11
11
|
import {
|
|
12
12
|
SkillAgentSummary
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-TRTVOVHR.js";
|
|
14
14
|
import {
|
|
15
15
|
useWizardStore
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-UBSSIIWD.js";
|
|
17
17
|
import {
|
|
18
18
|
ALL_SKILLS_EJECTED_LABEL,
|
|
19
19
|
CLI_COLORS,
|
|
20
20
|
DEFAULT_PUBLIC_SOURCE_NAME,
|
|
21
21
|
EJECT_SOURCE,
|
|
22
22
|
formatSourceDisplayName
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-AQXKAPCD.js";
|
|
24
24
|
|
|
25
25
|
// src/cli/components/wizard/summary-panel.tsx
|
|
26
26
|
import { Box, Text } from "ink";
|
|
@@ -95,8 +95,9 @@ var SummaryPanel = () => {
|
|
|
95
95
|
const skillConfigs = useWizardStore((s) => s.skillConfigs);
|
|
96
96
|
const agentConfigs = useWizardStore((s) => s.agentConfigs);
|
|
97
97
|
const selectedStackId = useWizardStore((s) => s.selectedStackId);
|
|
98
|
+
const setupMarketplace = useWizardStore((s) => s.setupMarketplace);
|
|
98
99
|
const { viewportRef, contentRef, contentMarginTop, hiddenAbove, hiddenBelow } = usePanelScroll();
|
|
99
|
-
const sourceNames = formatSkillMarketplaces(skillConfigs);
|
|
100
|
+
const sourceNames = setupMarketplace === null ? formatSkillMarketplaces(skillConfigs) : formatSourceDisplayName(setupMarketplace);
|
|
100
101
|
const stackName = getStackName(selectedStackId);
|
|
101
102
|
return /* @__PURE__ */ jsxs(
|
|
102
103
|
Box,
|
|
@@ -121,4 +122,4 @@ var SummaryPanel = () => {
|
|
|
121
122
|
export {
|
|
122
123
|
SummaryPanel
|
|
123
124
|
};
|
|
124
|
-
//# sourceMappingURL=chunk-
|
|
125
|
+
//# sourceMappingURL=chunk-VPJUCVU7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/components/wizard/summary-panel.tsx","../src/cli/components/hooks/use-panel-scroll.ts"],"sourcesContent":["import { Box, Text } from \"ink\";\nimport React from \"react\";\nimport { unique } from \"remeda\";\nimport {\n ALL_SKILLS_EJECTED_LABEL,\n CLI_COLORS,\n DEFAULT_PUBLIC_SOURCE_NAME,\n EJECT_SOURCE,\n formatSourceDisplayName,\n} from \"../../consts.js\";\nimport { useWizardStore } from \"../../stores/wizard-store.js\";\nimport type { SkillConfig } from \"../../types/config.js\";\nimport { usePanelScroll } from \"../hooks/use-panel-scroll.js\";\nimport { ScrollAffordance } from \"./scroll-affordance.js\";\nimport { SkillAgentSummary } from \"./skill-agent-summary.js\";\nimport { getStackName } from \"./utils.js\";\n\n/**\n * The Marketplace row's value: the distinct marketplaces the summarised skills come from.\n *\n * `SkillConfig.origin` is authoritative for where a skill came from, and holds exactly one\n * of two things: {@link EJECT_SOURCE}, meaning the files were copied locally, or the marketplace's\n * own name as its `marketplace.json` declares it — which is why a project initialised from a\n * different marketplace names that marketplace here without any extra plumbing.\n *\n * Three cases, and the two zero-marketplace ones are NOT the same state:\n * - **No skills at all.** Reachable — the `I` overlay opens from the stack step before anything is\n * selected. Nothing is claimed yet, so the default public marketplace stands in.\n * - **Skills, but every one ejected.** \"eject\" names no marketplace, so there is none to list;\n * naming the default would assert an origin none of those skills has.\n * - **Any marketplace-sourced skill.** Its marketplace is named. Ejected siblings contribute no\n * name but do not suppress the ones that do.\n *\n * Tombstoned (`excluded`) entries count. One still records a real global install and the\n * marketplace it came from, and `computeScopeDiff` renders it as a row in the summary below, so\n * dropping it here would leave the header disowning a row it sits above.\n *\n * Sorted, so the row cannot reorder between renders on config iteration order alone.\n */\nfunction formatSkillMarketplaces(skillConfigs: SkillConfig[]): string {\n if (skillConfigs.length === 0) return formatSourceDisplayName(DEFAULT_PUBLIC_SOURCE_NAME);\n\n const marketplaceNames = unique(\n skillConfigs.map((config) => config.origin).filter((origin) => origin !== EJECT_SOURCE),\n );\n if (marketplaceNames.length === 0) return ALL_SKILLS_EJECTED_LABEL;\n\n return marketplaceNames\n .map((name) => formatSourceDisplayName(name))\n .sort()\n .join(\" · \");\n}\n\ntype PanelHeaderProps = {\n sourceNames: string;\n stackName: string | undefined;\n};\n\n/**\n * Which marketplaces and which stack the summary below was built from.\n *\n * Rendered inside the scrolled content, so its `marginBottom` scrolls away\n * rather than permanently costing the viewport a row.\n *\n * `flexShrink={0}` is load-bearing: Ink defaults every box to `flexShrink: 1`,\n * so on a short terminal Yoga squeezes this block to fit instead of letting the\n * viewport clip it, and the Marketplace and Stack rows overprint into one\n * unreadable line. Clipping is the panel's job; the header must keep its height.\n */\nconst PanelHeader: React.FC<PanelHeaderProps> = ({ sourceNames, stackName }) => (\n <Box\n flexDirection=\"column\"\n flexShrink={0}\n borderStyle=\"single\"\n borderBottom={true}\n borderTop={false}\n borderLeft={false}\n borderRight={false}\n borderColor={CLI_COLORS.NEUTRAL}\n borderBottomDimColor\n paddingBottom={1}\n marginBottom={1}\n >\n <Box flexDirection=\"row\" columnGap={1}>\n <Text color={CLI_COLORS.WARNING} bold>\n Marketplace\n </Text>\n <Text color={CLI_COLORS.NEUTRAL}>{sourceNames}</Text>\n </Box>\n <Box flexDirection=\"row\" columnGap={1}>\n <Text color={CLI_COLORS.WARNING} bold>\n Stack\n </Text>\n <Text color={CLI_COLORS.NEUTRAL}>{stackName ?? \"none\"}</Text>\n </Box>\n </Box>\n);\n\n/**\n * Marketplace/stack header above a clipped, arrow-scrollable\n * `SkillAgentSummary` — the one panel both summary surfaces render: the `I`\n * overlay in `wizard-layout.tsx` and the confirm step.\n *\n * It owns the `↑`/`↓` keys through `usePanelScroll`. A host that needs other\n * keys adds its own `useInput` alongside it — `StepConfirm` claims `Enter` and\n * `Esc` that way.\n *\n * Horizontal padding only. Vertical padding is unshrinkable, so on a short\n * terminal it claims the last row the viewport has and the content measures 0\n * before it has ever been laid out — the panel then paints neither a summary\n * row nor the affordance that would say rows are missing.\n */\nexport const SummaryPanel: React.FC = () => {\n const skillConfigs = useWizardStore((s) => s.skillConfigs);\n const agentConfigs = useWizardStore((s) => s.agentConfigs);\n const selectedStackId = useWizardStore((s) => s.selectedStackId);\n const setupMarketplace = useWizardStore((s) => s.setupMarketplace);\n const { viewportRef, contentRef, contentMarginTop, hiddenAbove, hiddenBelow } = usePanelScroll();\n\n // A project set up from a marketplace named for it is set up from THAT one, whichever the\n // global install's skills it shows come from.\n const sourceNames =\n setupMarketplace === null\n ? formatSkillMarketplaces(skillConfigs)\n : formatSourceDisplayName(setupMarketplace);\n const stackName = getStackName(selectedStackId);\n\n return (\n <Box\n flexDirection=\"column\"\n flexGrow={1}\n flexBasis={0}\n borderStyle=\"single\"\n borderColor={CLI_COLORS.NEUTRAL}\n paddingX={2}\n >\n <Box ref={viewportRef} flexDirection=\"column\" flexGrow={1} flexBasis={0} overflow=\"hidden\">\n <Box ref={contentRef} flexDirection=\"column\" flexShrink={0} marginTop={contentMarginTop}>\n <PanelHeader sourceNames={sourceNames} stackName={stackName} />\n\n <Box width=\"100%\">\n <SkillAgentSummary skillConfigs={skillConfigs} agentConfigs={agentConfigs} />\n </Box>\n </Box>\n </Box>\n <ScrollAffordance hiddenAbove={hiddenAbove} hiddenBelow={hiddenBelow} />\n </Box>\n );\n};\n","import { useEffect, useRef, useState } from \"react\";\nimport type { Ref, RefObject } from \"react\";\nimport { type DOMElement, measureElement, useInput } from \"ink\";\nimport { useMeasuredHeight } from \"./use-measured-height.js\";\n\nexport type UsePanelScrollResult = {\n /** Attach to the clipping box — the one with `overflow=\"hidden\"`. */\n viewportRef: Ref<DOMElement>;\n /** Attach to the scrolled content box inside the viewport. */\n contentRef: RefObject<DOMElement | null>;\n /** Apply as the content box's `marginTop`; negative by the number of lines scrolled off the top. */\n contentMarginTop: number;\n /** Content lines scrolled off the top of the viewport. */\n hiddenAbove: number;\n /** Content lines clipped below the viewport. */\n hiddenBelow: number;\n};\n\n/**\n * Line scrolling for a single clipped viewport, driven by the `↑`/`↓` keys.\n *\n * The caller renders viewport, content and a `ScrollAffordance` sibling; this\n * owns the offset those three agree on. Distinct from `useSectionScroll`, which\n * moves a viewport to follow focus across variable-height sections — here there\n * is no focus, only the arrow keys.\n *\n * A host that needs other keys adds its own `useInput` alongside it, on keys\n * disjoint from `↑`/`↓`.\n */\nexport function usePanelScroll(): UsePanelScrollResult {\n // Measured on the clipping box itself, inside the border and padding, so it\n // is the height the content actually gets.\n const { ref: viewportRef, measuredHeight: viewportHeight } = useMeasuredHeight();\n const contentRef = useRef<DOMElement>(null);\n const [contentHeight, setContentHeight] = useState(0);\n const [scrollOffset, setScrollOffset] = useState(0);\n\n // Ink's measurement idiom: re-measure on every render, no dependency array\n // on purpose. The setState inside is guarded to fire only when the measured\n // height actually changed, so the chain converges instead of looping — a\n // dependency array here would skip re-measures after layout changes React\n // cannot see.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n useEffect(() => {\n if (!contentRef.current) return;\n const { height } = measureElement(contentRef.current);\n // Yoga under-reports a wrapped subtree measured inside a viewport too\n // short to lay it out, and reports 0 inside a zero-height one, so the\n // tallest reading is the trustworthy one. Believing a squeezed reading\n // would shrink the hidden-line count until the affordance disappeared,\n // which hands its row back and grows the count again — a render loop.\n setContentHeight((prev) => Math.max(prev, height));\n });\n\n // contentHeight only ever sizes the scroll range and the affordance count.\n // It must never gate the clip — which is why it is not returned: a viewport\n // that stops clipping when it looks too small grows to content height, which\n // makes it look big enough to stop clipping — a stable wrong answer that\n // paints the content over the border.\n const maxScroll = Math.max(0, contentHeight - viewportHeight);\n const hiddenBelow = Math.max(0, maxScroll - scrollOffset);\n\n useInput((_input, key) => {\n if (key.upArrow) setScrollOffset((prev) => Math.max(0, prev - 1));\n if (key.downArrow) setScrollOffset((prev) => Math.min(maxScroll, prev + 1));\n });\n\n return {\n viewportRef,\n contentRef,\n contentMarginTop: scrollOffset > 0 ? -scrollOffset : 0,\n hiddenAbove: scrollOffset,\n hiddenBelow,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
render
|
|
4
|
+
} from "./chunk-JFLVEFFP.js";
|
|
5
|
+
|
|
6
|
+
// src/cli/components/common/spinner.tsx
|
|
7
|
+
import "react";
|
|
8
|
+
import { Text } from "ink";
|
|
9
|
+
import { Spinner as InkSpinner } from "@inkjs/ui";
|
|
10
|
+
import { jsx } from "react/jsx-runtime";
|
|
11
|
+
var Spinner = ({ label }) => /* @__PURE__ */ jsx(InkSpinner, { label });
|
|
12
|
+
async function awaitUnderSpinner(label, load) {
|
|
13
|
+
const spinner = render(/* @__PURE__ */ jsx(Spinner, { label }));
|
|
14
|
+
try {
|
|
15
|
+
return await load();
|
|
16
|
+
} finally {
|
|
17
|
+
await takeDownWithoutTrace(spinner, label);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function takeDownWithoutTrace(spinner, label) {
|
|
21
|
+
spinner.rerender(/* @__PURE__ */ jsx(Text, { children: label }));
|
|
22
|
+
await spinner.waitUntilRenderFlush();
|
|
23
|
+
spinner.clear();
|
|
24
|
+
spinner.unmount();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
Spinner,
|
|
29
|
+
awaitUnderSpinner
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=chunk-VRXX4D6K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/components/common/spinner.tsx"],"sourcesContent":["import React from \"react\";\nimport { Text, type Instance } from \"ink\";\nimport { Spinner as InkSpinner } from \"@inkjs/ui\";\nimport { render } from \"../render.js\";\n\ntype SpinnerProps = {\n label: string;\n};\n\nexport const Spinner: React.FC<SpinnerProps> = ({ label }) => <InkSpinner label={label} />;\n\n/**\n * Awaits `load` behind a spinner and takes the spinner down whichever way the await ends, leaving\n * the terminal as it found it — what `init` and `edit` render next is a wizard sized to the\n * terminal's full height, so one line left above it pushes the wizard's top line off the screen.\n *\n * The cleanup is a `finally` because a load that refuses the run throws from inside this await,\n * and oclif would otherwise paint its error under an Ink tree still repainting over it. Never a\n * `catch`: the throw reaches the caller untouched, or both the error rendering and the exit code\n * change with it.\n */\nexport async function awaitUnderSpinner<T>(label: string, load: () => Promise<T>): Promise<T> {\n const spinner = render(<Spinner label={label} />);\n try {\n return await load();\n } finally {\n await takeDownWithoutTrace(spinner, label);\n }\n}\n\n/**\n * Stop the clock, paint what Ink is holding, and only then erase.\n *\n * Ink paints through a render throttle, and a spinner frame that arrives inside its window is\n * held rather than painted. `unmount()` paints whatever is held — so a spinner cleared with a\n * frame held was drawn again after the clear, where nothing erases it. A starved event loop is\n * what puts one there: a spinner tick that runs late is followed by the next one less than a\n * window later. Hence the order: the still line has no clock, so once it replaces the spinner no\n * new frame can be held; the flush paints anything already held; the clear then erases the last\n * thing painted, and `unmount()` finds nothing left to draw.\n */\nasync function takeDownWithoutTrace(spinner: Instance, label: string): Promise<void> {\n spinner.rerender(<Text>{label}</Text>);\n await spinner.waitUntilRenderFlush();\n spinner.clear();\n spinner.unmount();\n}\n"],"mappings":";;;;;;AAAA,OAAkB;AAClB,SAAS,YAA2B;AACpC,SAAS,WAAW,kBAAkB;AAOwB;AAAvD,IAAM,UAAkC,CAAC,EAAE,MAAM,MAAM,oBAAC,cAAW,OAAc;AAYxF,eAAsB,kBAAqB,OAAe,MAAoC;AAC5F,QAAM,UAAU,OAAO,oBAAC,WAAQ,OAAc,CAAE;AAChD,MAAI;AACF,WAAO,MAAM,KAAK;AAAA,EACpB,UAAE;AACA,UAAM,qBAAqB,SAAS,KAAK;AAAA,EAC3C;AACF;AAaA,eAAe,qBAAqB,SAAmB,OAA8B;AACnF,UAAQ,SAAS,oBAAC,QAAM,iBAAM,CAAO;AACrC,QAAM,QAAQ,qBAAqB;AACnC,UAAQ,MAAM;AACd,UAAQ,QAAQ;AAClB;","names":[]}
|
|
@@ -1,39 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
loadAgentDefs
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-42TNV2TK.js";
|
|
5
5
|
import {
|
|
6
6
|
pruneStaleCompiledAgents,
|
|
7
7
|
recompileAgents
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-V5CWACR5.js";
|
|
9
9
|
import {
|
|
10
|
+
agentCodec,
|
|
10
11
|
buildAgentScopeMap,
|
|
11
12
|
buildMarketplacePluginRef,
|
|
12
|
-
claudePluginUninstall,
|
|
13
13
|
copySkillsToLocalFlattened,
|
|
14
14
|
discoverAllPluginSkills,
|
|
15
15
|
ensureDir,
|
|
16
16
|
getErrorMessage,
|
|
17
|
+
hostAt,
|
|
18
|
+
installBaseDir,
|
|
17
19
|
isHomeDirectory,
|
|
18
|
-
|
|
20
|
+
loadInstalledConfig,
|
|
19
21
|
loadSkillsFromDir,
|
|
20
22
|
loadSkillsMatrixFromSource,
|
|
23
|
+
providerInUse,
|
|
21
24
|
remove,
|
|
22
25
|
removeDirIfEmpty,
|
|
23
26
|
resolveInstallPaths,
|
|
24
|
-
|
|
27
|
+
skillsDir,
|
|
28
|
+
skillsPathPrefix,
|
|
25
29
|
verbose
|
|
26
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-IM45LLQX.js";
|
|
27
31
|
import {
|
|
28
32
|
initializeMatrix,
|
|
29
33
|
matrix,
|
|
30
34
|
typedEntries,
|
|
31
35
|
typedKeys
|
|
32
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-GKPW33FP.js";
|
|
33
37
|
import {
|
|
34
|
-
LOCAL_SKILLS_PATH,
|
|
35
38
|
globalInstallRoot
|
|
36
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-AQXKAPCD.js";
|
|
37
40
|
|
|
38
41
|
// src/cli/lib/operations/project/remove-compiled-agents.ts
|
|
39
42
|
import path from "path";
|
|
@@ -51,7 +54,7 @@ async function removeCompiledAgents(options) {
|
|
|
51
54
|
};
|
|
52
55
|
}
|
|
53
56
|
async function pruneCompiledAgents(options) {
|
|
54
|
-
await pruneStaleCompiledAgents(options.agentsDir, options.keep);
|
|
57
|
+
await pruneStaleCompiledAgents(options.agentsDir, options.keep, options.codec);
|
|
55
58
|
await tidyEmptiedAgentsDir(options.agentsDir);
|
|
56
59
|
}
|
|
57
60
|
async function removeCompiledAgentFile(agentsDir, name) {
|
|
@@ -68,21 +71,7 @@ async function tidyEmptiedAgentsDir(agentsDir) {
|
|
|
68
71
|
|
|
69
72
|
// src/cli/lib/operations/project/compile-agents.ts
|
|
70
73
|
async function compileAgents(options) {
|
|
71
|
-
|
|
72
|
-
let resolvedAgentScopeMap = options.agentScopeMap;
|
|
73
|
-
if (options.scopeFilter) {
|
|
74
|
-
const loadedConfig = await loadProjectConfigFromDir(options.projectDir);
|
|
75
|
-
if (!resolvedAgentScopeMap && loadedConfig?.config) {
|
|
76
|
-
resolvedAgentScopeMap = buildAgentScopeMap(loadedConfig.config);
|
|
77
|
-
}
|
|
78
|
-
const filteredAgents = loadedConfig?.config.agents.filter((a) => !a.excluded && a.scope === options.scopeFilter).map((a) => a.name);
|
|
79
|
-
if (resolvedAgents && filteredAgents) {
|
|
80
|
-
const filterSet = new Set(filteredAgents);
|
|
81
|
-
resolvedAgents = resolvedAgents.filter((a) => filterSet.has(a));
|
|
82
|
-
} else if (filteredAgents) {
|
|
83
|
-
resolvedAgents = filteredAgents;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
74
|
+
const { agents: resolvedAgents, agentScopeMap: resolvedAgentScopeMap } = options.scopeFilter ? await rosterForScope(options, options.scopeFilter) : { agents: options.agents, agentScopeMap: options.agentScopeMap };
|
|
86
75
|
const recompileResult = await recompileAgents({
|
|
87
76
|
pluginDir: options.pluginDir ?? options.projectDir,
|
|
88
77
|
sourcePath: options.sourcePath,
|
|
@@ -100,25 +89,51 @@ async function compileAgents(options) {
|
|
|
100
89
|
warnings: recompileResult.warnings
|
|
101
90
|
};
|
|
102
91
|
}
|
|
92
|
+
async function rosterForScope(options, scope) {
|
|
93
|
+
const config = (await loadInstalledConfig(options.projectDir))?.config;
|
|
94
|
+
const activeAtScope = config?.agents.filter((agent) => !agent.excluded && agent.scope === scope).map((agent) => agent.name);
|
|
95
|
+
return {
|
|
96
|
+
agents: requestedAgentsAmong(options.agents, activeAtScope),
|
|
97
|
+
agentScopeMap: options.agentScopeMap ?? (config ? buildAgentScopeMap(config) : void 0)
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function requestedAgentsAmong(requested, activeAtScope) {
|
|
101
|
+
if (activeAtScope === void 0) return requested;
|
|
102
|
+
if (requested === void 0) return activeAtScope;
|
|
103
|
+
const atScope = new Set(activeAtScope);
|
|
104
|
+
return requested.filter((name) => atScope.has(name));
|
|
105
|
+
}
|
|
103
106
|
async function pruneStaleAgentsForPass(options, recompileResult) {
|
|
104
107
|
if (options.scopeFilter || !options.outputDir) return;
|
|
105
108
|
const compiledForDir = /* @__PURE__ */ new Set([
|
|
106
109
|
...recompileResult.compiled,
|
|
107
110
|
...recompileResult.failed
|
|
108
111
|
]);
|
|
109
|
-
await pruneCompiledAgents({
|
|
112
|
+
await pruneCompiledAgents({
|
|
113
|
+
agentsDir: options.outputDir,
|
|
114
|
+
keep: compiledForDir,
|
|
115
|
+
// The host this installation compiles for — a Codex role is a `.toml`, and a prune that only
|
|
116
|
+
// knew `.md` left a deselected role in place for Codex to keep offering (CLI-896's sibling).
|
|
117
|
+
codec: agentCodec(providerInUse(options.projectDir))
|
|
118
|
+
});
|
|
110
119
|
}
|
|
111
120
|
|
|
112
121
|
// src/cli/lib/operations/skills/discover-skills.ts
|
|
113
|
-
import path2 from "path";
|
|
114
122
|
var NO_LOCAL_SKILLS = { skills: {}, unusableMetadata: [] };
|
|
115
|
-
async function discoverLocalProjectSkills(
|
|
116
|
-
const
|
|
117
|
-
return loadSkillsFromDir(
|
|
118
|
-
pathPrefix:
|
|
123
|
+
async function discoverLocalProjectSkills(projectDir, scope) {
|
|
124
|
+
const provider = providerInUse(installBaseDir(projectDir, scope));
|
|
125
|
+
return loadSkillsFromDir(skillsDir(provider, scope, projectDir), {
|
|
126
|
+
pathPrefix: skillsPathPrefix(provider, scope, projectDir),
|
|
119
127
|
requireMetadata: true
|
|
120
128
|
});
|
|
121
129
|
}
|
|
130
|
+
function globalSkillsRoot() {
|
|
131
|
+
const home = globalInstallRoot();
|
|
132
|
+
return skillsDir(providerInUse(home), "global", home);
|
|
133
|
+
}
|
|
134
|
+
function projectSkillsRoot(projectDir, scope) {
|
|
135
|
+
return skillsDir(providerInUse(installBaseDir(projectDir, scope)), scope, projectDir);
|
|
136
|
+
}
|
|
122
137
|
function mergeSkills(...skillSources) {
|
|
123
138
|
const merged = {};
|
|
124
139
|
for (const source of skillSources) {
|
|
@@ -137,17 +152,20 @@ async function discoverInstalledSkills(projectDir) {
|
|
|
137
152
|
if (globalPluginSkillCount > 0) {
|
|
138
153
|
verbose(` Found ${globalPluginSkillCount} skills from global plugins`);
|
|
139
154
|
}
|
|
140
|
-
const globalLocal = isGlobalProject ? NO_LOCAL_SKILLS : await discoverLocalProjectSkills(
|
|
155
|
+
const globalLocal = isGlobalProject ? NO_LOCAL_SKILLS : await discoverLocalProjectSkills(projectDir, "global");
|
|
141
156
|
const globalLocalSkillCount = typedKeys(globalLocal.skills).length;
|
|
142
157
|
if (globalLocalSkillCount > 0) {
|
|
143
|
-
verbose(` Found ${globalLocalSkillCount} global local skills from
|
|
158
|
+
verbose(` Found ${globalLocalSkillCount} global local skills from ${globalSkillsRoot()}`);
|
|
144
159
|
}
|
|
145
160
|
const pluginSkills = await discoverAllPluginSkills(projectDir);
|
|
146
161
|
const pluginSkillCount = typedKeys(pluginSkills).length;
|
|
147
162
|
verbose(` Found ${pluginSkillCount} skills from installed plugins`);
|
|
148
|
-
const
|
|
163
|
+
const localScope = isGlobalProject ? "global" : "project";
|
|
164
|
+
const local = await discoverLocalProjectSkills(projectDir, localScope);
|
|
149
165
|
const localSkillCount = typedKeys(local.skills).length;
|
|
150
|
-
verbose(
|
|
166
|
+
verbose(
|
|
167
|
+
` Found ${localSkillCount} local skills from ${projectSkillsRoot(projectDir, localScope)}`
|
|
168
|
+
);
|
|
151
169
|
const allSkills = mergeSkills(globalPluginSkills, globalLocal.skills, pluginSkills, local.skills);
|
|
152
170
|
const totalSkillCount = typedKeys(allSkills).length;
|
|
153
171
|
return {
|
|
@@ -162,12 +180,12 @@ async function discoverInstalledSkills(projectDir) {
|
|
|
162
180
|
}
|
|
163
181
|
|
|
164
182
|
// src/cli/lib/operations/skills/copy-local-skills.ts
|
|
165
|
-
async function copyScopedLocalSkills(scopeSkills,
|
|
183
|
+
async function copyScopedLocalSkills(scopeSkills, skillsDir2, sourceResult) {
|
|
166
184
|
if (scopeSkills.length === 0) return [];
|
|
167
|
-
await ensureDir(
|
|
185
|
+
await ensureDir(skillsDir2);
|
|
168
186
|
return copySkillsToLocalFlattened(
|
|
169
187
|
scopeSkills.map((s) => s.id),
|
|
170
|
-
|
|
188
|
+
skillsDir2,
|
|
171
189
|
sourceResult
|
|
172
190
|
);
|
|
173
191
|
}
|
|
@@ -194,16 +212,18 @@ async function copyLocalSkills(skills, projectDir, sourceResult) {
|
|
|
194
212
|
}
|
|
195
213
|
|
|
196
214
|
// src/cli/lib/operations/skills/uninstall-plugin-skills.ts
|
|
215
|
+
var SCOPE_OF_AN_UNRECORDED_SKILL = "project";
|
|
197
216
|
async function uninstallPluginSkills(skillIds, oldSkills, marketplace, projectDir) {
|
|
217
|
+
const host = hostAt(projectDir);
|
|
198
218
|
const uninstalled = [];
|
|
199
219
|
const failed = [];
|
|
200
220
|
for (const skillId of skillIds) {
|
|
201
221
|
const oldSkill = oldSkills.find((s) => s.id === skillId);
|
|
202
|
-
const pluginScope =
|
|
222
|
+
const pluginScope = oldSkill?.scope ?? SCOPE_OF_AN_UNRECORDED_SKILL;
|
|
203
223
|
const pluginRef = buildMarketplacePluginRef(skillId, marketplace);
|
|
204
224
|
try {
|
|
205
|
-
await
|
|
206
|
-
uninstalled.push(skillId);
|
|
225
|
+
const outcome = await host.uninstallPlugin(pluginRef, pluginScope, projectDir);
|
|
226
|
+
if (outcome === "removed") uninstalled.push(skillId);
|
|
207
227
|
} catch (error) {
|
|
208
228
|
failed.push({ id: skillId, error: getErrorMessage(error) });
|
|
209
229
|
}
|
|
@@ -216,6 +236,7 @@ async function withCatalogueSeatedFor(projectDir, body) {
|
|
|
216
236
|
const callersCatalogue = matrix;
|
|
217
237
|
const { matrix: seated } = await loadSkillsMatrixFromSource({
|
|
218
238
|
projectDir,
|
|
239
|
+
purpose: "upkeep",
|
|
219
240
|
skipExtraSources: true,
|
|
220
241
|
matrixOnly: true
|
|
221
242
|
});
|
|
@@ -273,4 +294,4 @@ export {
|
|
|
273
294
|
recompileRegisteredProjectAgents,
|
|
274
295
|
recompilePropagatedProjectAgents
|
|
275
296
|
};
|
|
276
|
-
//# sourceMappingURL=chunk-
|
|
297
|
+
//# sourceMappingURL=chunk-WIFIAQZU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/lib/operations/project/remove-compiled-agents.ts","../src/cli/lib/operations/project/compile-agents.ts","../src/cli/lib/operations/skills/discover-skills.ts","../src/cli/lib/operations/skills/copy-local-skills.ts","../src/cli/lib/operations/skills/uninstall-plugin-skills.ts","../src/cli/lib/loading/catalogue-seat.ts","../src/cli/lib/operations/project/recompile-project-agents.ts"],"sourcesContent":["import path from \"path\";\nimport { pruneStaleCompiledAgents } from \"../../agents/list-compiled-agents.js\";\nimport type { AgentCodec } from \"../../installation/install-layout.js\";\nimport { getErrorMessage } from \"../../../utils/errors.js\";\nimport { remove, removeDirIfEmpty } from \"../../../utils/fs.js\";\nimport type { AgentName } from \"../../../types/index.js\";\n\nconst AGENT_FILE_EXTENSION = \".md\";\n\nexport type RemoveCompiledAgentsOptions = {\n /** The scope's `.claude/agents/` directory holding the compiled files. */\n agentsDir: string;\n agents: readonly AgentName[];\n};\n\nexport type PruneCompiledAgentsOptions = {\n agentsDir: string;\n /**\n * The COMPLETE set of agents that belong in `agentsDir` — every other\n * CLI-compiled agent there is stale. Only an authoritative pass can say this.\n */\n keep: ReadonlySet<AgentName>;\n /** The host's agent file format — Claude's `.md` when absent. */\n codec?: AgentCodec;\n};\n\nexport type RemoveCompiledAgentsResult = {\n /** Agents whose compiled file this call disposed of; an absent file counts. */\n removed: AgentName[];\n /** Agents whose file survived an error, for the caller to report. */\n failed: Array<{ name: AgentName; error: string }>;\n};\n\ntype RemovalOutcome = { name: AgentName; ok: true } | { name: AgentName; ok: false; error: string };\n\n/**\n * Deletes the named agents' compiled `.md` files from `agentsDir`, then takes the\n * directory itself when nothing at all is left in it — the shape `deleteLocalSkill`\n * models for skills.\n *\n * A scope's `.claude/agents/` is an artefact of what it holds, so the removal that\n * empties it owns the tidy and no caller has to remember. An agent with no file on\n * disk is nothing to do rather than an error.\n */\nexport async function removeCompiledAgents(\n options: RemoveCompiledAgentsOptions,\n): Promise<RemoveCompiledAgentsResult> {\n const outcomes = await Promise.all(\n options.agents.map((name) => removeCompiledAgentFile(options.agentsDir, name)),\n );\n await tidyEmptiedAgentsDir(options.agentsDir);\n\n return {\n removed: outcomes.flatMap((outcome) => (outcome.ok ? [outcome.name] : [])),\n failed: outcomes.flatMap((outcome) =>\n outcome.ok ? [] : [{ name: outcome.name, error: outcome.error }],\n ),\n };\n}\n\n/**\n * Deletes every CLI-compiled agent in `agentsDir` outside `keep`, then takes the\n * directory on the same terms as `removeCompiledAgents`.\n *\n * Hand-authored agents are preserved by `pruneStaleCompiledAgents`'s `isAgentName`\n * predicate, so a directory this leaves empty is one nothing at all remains in.\n */\nexport async function pruneCompiledAgents(options: PruneCompiledAgentsOptions): Promise<void> {\n await pruneStaleCompiledAgents(options.agentsDir, options.keep, options.codec);\n await tidyEmptiedAgentsDir(options.agentsDir);\n}\n\nasync function removeCompiledAgentFile(\n agentsDir: string,\n name: AgentName,\n): Promise<RemovalOutcome> {\n try {\n await remove(path.join(agentsDir, `${name}${AGENT_FILE_EXTENSION}`));\n return { name, ok: true };\n } catch (error) {\n return { name, ok: false, error: getErrorMessage(error) };\n }\n}\n\n/**\n * Emptiness is FILESYSTEM emptiness, never roster emptiness: a hand-authored agent\n * or any user-owned file keeps the directory alive whatever a config says. The\n * agents directory only — `.claude/` above it is uninstall's decision, not a\n * removal path's.\n */\nasync function tidyEmptiedAgentsDir(agentsDir: string): Promise<void> {\n await removeDirIfEmpty(agentsDir);\n}\n","import { pruneCompiledAgents } from \"./remove-compiled-agents.js\";\nimport { recompileAgents } from \"../../agents/index.js\";\nimport { loadInstalledConfig } from \"../../configuration/index.js\";\nimport { buildAgentScopeMap } from \"../../installation/index.js\";\nimport { agentCodec, providerInUse } from \"../../installation/install-layout.js\";\nimport type { AgentName, SkillDefinitionMap, SkillScope } from \"../../../types/index.js\";\n\nexport type CompileAgentsOptions = {\n projectDir: string;\n sourcePath: string;\n pluginDir?: string;\n skills?: SkillDefinitionMap;\n agentScopeMap?: Map<AgentName, SkillScope>;\n agents?: AgentName[];\n /** When set, loads config and filters agents to only those matching this scope. */\n scopeFilter?: SkillScope;\n outputDir?: string;\n};\n\nexport type CompilationResult = {\n compiled: AgentName[];\n /**\n * The subset of `compiled` whose file this pass actually wrote. A pass whose\n * `rewritten` is empty changed nothing on disk, which is exactly what the\n * recompile summary reports and what the count it replaced could not say.\n */\n rewritten: AgentName[];\n failed: AgentName[];\n warnings: string[];\n};\n\n/**\n * Compiles agent markdown files from templates + skill content.\n *\n * Thin wrapper around recompileAgents() that standardizes options.\n * The caller invokes this once (edit, update) or twice with scopeFilter (compile).\n */\nexport async function compileAgents(options: CompileAgentsOptions): Promise<CompilationResult> {\n const { agents: resolvedAgents, agentScopeMap: resolvedAgentScopeMap } = options.scopeFilter\n ? await rosterForScope(options, options.scopeFilter)\n : { agents: options.agents, agentScopeMap: options.agentScopeMap };\n\n const recompileResult = await recompileAgents({\n pluginDir: options.pluginDir ?? options.projectDir,\n sourcePath: options.sourcePath,\n ...(resolvedAgents !== undefined && { agents: resolvedAgents }),\n ...(options.skills !== undefined && { skills: options.skills }),\n projectDir: options.projectDir,\n ...(options.outputDir !== undefined && { outputDir: options.outputDir }),\n ...(resolvedAgentScopeMap !== undefined && { agentScopeMap: resolvedAgentScopeMap }),\n });\n\n await pruneStaleAgentsForPass(options, recompileResult);\n\n return {\n compiled: recompileResult.compiled,\n rewritten: recompileResult.rewritten,\n failed: recompileResult.failed,\n warnings: recompileResult.warnings,\n };\n}\n\n/**\n * What a scope-filtered pass compiles, read off the installed config: the requested agents\n * narrowed to those active at `scope` (all of those when none were requested), and the caller's\n * scope routing — auto-built from the config when the caller passed none. With no installed\n * config, both are the caller's as passed.\n */\nasync function rosterForScope(\n options: CompileAgentsOptions,\n scope: SkillScope,\n): Promise<{\n agents: AgentName[] | undefined;\n agentScopeMap: Map<AgentName, SkillScope> | undefined;\n}> {\n const config = (await loadInstalledConfig(options.projectDir))?.config;\n const activeAtScope = config?.agents\n .filter((agent) => !agent.excluded && agent.scope === scope)\n .map((agent) => agent.name);\n\n return {\n agents: requestedAgentsAmong(options.agents, activeAtScope),\n agentScopeMap: options.agentScopeMap ?? (config ? buildAgentScopeMap(config) : undefined),\n };\n}\n\n/** The requested agents that appear in `activeAtScope`; either list alone when the other is absent. */\nfunction requestedAgentsAmong(\n requested: AgentName[] | undefined,\n activeAtScope: AgentName[] | undefined,\n): AgentName[] | undefined {\n if (activeAtScope === undefined) return requested;\n if (requested === undefined) return activeAtScope;\n\n const atScope = new Set(activeAtScope);\n return requested.filter((name) => atScope.has(name));\n}\n\n/**\n * Compiled-agent writes are additive, so a deselected or stale agent's `.md`\n * lingers after recompile. An authoritative, scope-UNfiltered pass owns its\n * entire `outputDir` (its resolved roster is the full set for that directory),\n * so it prunes built-in agents no longer compiled there. A scope-FILTERED pass\n * (the hasBoth two-pass compile, or the registered-project recompile)\n * sees only one scope's agents and must never delete another scope's files, so\n * it skips pruning. Removing the stale files and tidying the directory they\n * emptied is one operation's job, not this pass's.\n */\nasync function pruneStaleAgentsForPass(\n options: CompileAgentsOptions,\n recompileResult: CompilationResult,\n): Promise<void> {\n if (options.scopeFilter || !options.outputDir) return;\n\n const compiledForDir = new Set<AgentName>([\n ...recompileResult.compiled,\n ...recompileResult.failed,\n ]);\n await pruneCompiledAgents({\n agentsDir: options.outputDir,\n keep: compiledForDir,\n // The host this installation compiles for — a Codex role is a `.toml`, and a prune that only\n // knew `.md` left a deselected role in place for Codex to keep offering (CLI-896's sibling).\n codec: agentCodec(providerInUse(options.projectDir)),\n });\n}\n","import { discoverAllPluginSkills } from \"../../plugins/index.js\";\nimport { isHomeDirectory } from \"../../installation/is-home-directory.js\";\n// loadSkillsFromDir lives in loading/ (a leaf) so both this module and\n// plugins/plugin-discovery can share it without an operations↔plugins cycle.\nimport { loadSkillsFromDir, type LoadedSkills } from \"../../loading/index.js\";\nimport { verbose } from \"../../../utils/logger.js\";\nimport { globalInstallRoot } from \"../../../consts.js\";\nimport {\n installBaseDir,\n providerInUse,\n skillsDir,\n skillsPathPrefix,\n} from \"../../installation/install-layout.js\";\nimport { typedEntries, typedKeys } from \"../../../utils/typed-object.js\";\nimport type { UnusableSkillMetadata } from \"../../loading/index.js\";\nimport type {\n SkillDefinition,\n SkillDefinitionMap,\n SkillId,\n SkillScope,\n} from \"../../../types/index.js\";\n\nexport type DiscoveredSkills = {\n allSkills: SkillDefinitionMap;\n totalSkillCount: number;\n pluginSkillCount: number;\n localSkillCount: number;\n globalPluginSkillCount: number;\n globalLocalSkillCount: number;\n /**\n * Installed skills whose metadata.yaml exists but describes no skill, from either\n * scope. Nothing was loaded for them; `compile` refuses the run over any entry\n * here rather than compile agents around a skill its metadata does not describe.\n */\n unusableMetadata: UnusableSkillMetadata[];\n};\n\n/** The result of a local-skill scan that was not performed. */\nconst NO_LOCAL_SKILLS: LoadedSkills = { skills: {}, unusableMetadata: [] };\n\n/**\n * Discovers the skills copied in rather than installed as plugins, at one scope of one project.\n *\n * **It composed `<rootDir>/.claude/skills` until 2026-09-22, for every provider**, which made this\n * the last Claude literal on the eject READ path: a Codex installation copies its skills to\n * `$CODEX_HOME/skills` and `<repo>/.agents/skills` — the WRITE path routes through the layout\n * already — and this read went on looking in a directory that host never writes to. Nothing failed:\n * the scan answered \"no skills\", which is the same answer an empty installation gives.\n *\n * The scope is a required argument rather than a defaulted one, for the reason C2 deleted\n * `DEFAULT_PROVIDER`: a default is invisible at the call site, and the two call sites below mean\n * different things by it. The provider is READ off the scope root the same way\n * `resolveInstallPaths` reads it, from one probe, so the directory and the prefix cannot name two\n * different installations.\n */\nexport async function discoverLocalProjectSkills(\n projectDir: string,\n scope: SkillScope,\n): Promise<LoadedSkills> {\n const provider = providerInUse(installBaseDir(projectDir, scope));\n\n return loadSkillsFromDir(skillsDir(provider, scope, projectDir), {\n pathPrefix: skillsPathPrefix(provider, scope, projectDir),\n requireMetadata: true,\n });\n}\n\n/** Where the global scope's copied-in skills are, for the diagnostic line that names them. */\nfunction globalSkillsRoot(): string {\n const home = globalInstallRoot();\n return skillsDir(providerInUse(home), \"global\", home);\n}\n\n/** The same, for whichever scope the project pass asked. */\nfunction projectSkillsRoot(projectDir: string, scope: SkillScope): string {\n return skillsDir(providerInUse(installBaseDir(projectDir, scope)), scope, projectDir);\n}\n\n/** Merges skill maps — later sources take precedence over earlier ones. */\nexport function mergeSkills(...skillSources: SkillDefinitionMap[]): SkillDefinitionMap {\n const merged: SkillDefinitionMap = {};\n\n for (const source of skillSources) {\n for (const [id, skill] of typedEntries<SkillId, SkillDefinition | undefined>(source)) {\n if (skill) {\n merged[id] = skill;\n }\n }\n }\n\n return merged;\n}\n\n/**\n * Discovers all installed skills for a project directory using a 4-way merge: the global scope's\n * plugins and copied-in skills, then the project's own of each, so the project wins on conflict.\n *\n * **Every one of the four is resolved per PROVIDER rather than spelled as a Claude path.** The\n * plugin halves go through `hostAt(...).listPlugins` and the copied-in halves through\n * {@link discoverLocalProjectSkills}; on Claude that is `~/.claude/{plugins,skills}` and\n * `<project>/.claude/{plugins,skills}` exactly as it always was, and on Codex it is the Codex\n * state root and the repository's own `.agents/skills`.\n *\n * Pure function — no user-facing logging. Callers add their own log messages.\n * Uses verbose() for diagnostic output only.\n */\nexport async function discoverInstalledSkills(projectDir: string): Promise<DiscoveredSkills> {\n const isGlobalProject = isHomeDirectory(projectDir);\n\n // 1. Global plugins\n const globalPluginSkills = isGlobalProject\n ? {}\n : await discoverAllPluginSkills(globalInstallRoot());\n const globalPluginSkillCount = typedKeys<SkillId>(globalPluginSkills).length;\n if (globalPluginSkillCount > 0) {\n verbose(` Found ${globalPluginSkillCount} skills from global plugins`);\n }\n\n // 2. Global local skills\n const globalLocal = isGlobalProject\n ? NO_LOCAL_SKILLS\n : await discoverLocalProjectSkills(projectDir, \"global\");\n const globalLocalSkillCount = typedKeys<SkillId>(globalLocal.skills).length;\n if (globalLocalSkillCount > 0) {\n verbose(` Found ${globalLocalSkillCount} global local skills from ${globalSkillsRoot()}`);\n }\n\n // 3. Project plugins\n const pluginSkills = await discoverAllPluginSkills(projectDir);\n const pluginSkillCount = typedKeys<SkillId>(pluginSkills).length;\n verbose(` Found ${pluginSkillCount} skills from installed plugins`);\n\n // 4. Project local skills.\n //\n // At the HOME root the two scopes are one installation, so the project pass asks the GLOBAL\n // scope — which is the same directory on Claude and is `$CODEX_HOME/skills` rather than\n // `<home>/.agents/skills` on Codex. Asking both and merging would double the local count on\n // every Claude run for no gain.\n const localScope: SkillScope = isGlobalProject ? \"global\" : \"project\";\n const local = await discoverLocalProjectSkills(projectDir, localScope);\n const localSkillCount = typedKeys<SkillId>(local.skills).length;\n verbose(\n ` Found ${localSkillCount} local skills from ${projectSkillsRoot(projectDir, localScope)}`,\n );\n\n // Merge: global first, project second — project wins on conflict\n const allSkills = mergeSkills(globalPluginSkills, globalLocal.skills, pluginSkills, local.skills);\n const totalSkillCount = typedKeys<SkillId>(allSkills).length;\n\n return {\n allSkills,\n totalSkillCount,\n pluginSkillCount: globalPluginSkillCount + pluginSkillCount,\n localSkillCount: globalLocalSkillCount + localSkillCount,\n globalPluginSkillCount,\n globalLocalSkillCount,\n unusableMetadata: [...globalLocal.unusableMetadata, ...local.unusableMetadata],\n };\n}\n","import { resolveInstallPaths } from \"../../installation/index.js\";\nimport { copySkillsToLocalFlattened, type CopiedSkill } from \"../../skills/index.js\";\nimport { ensureDir } from \"../../../utils/fs.js\";\nimport type { SkillConfig } from \"../../../types/config.js\";\nimport type { SourceLoadResult } from \"../../loading/source-loader.js\";\n\nexport type SkillCopyResult = {\n projectCopied: CopiedSkill[];\n globalCopied: CopiedSkill[];\n totalCopied: number;\n};\n\n/** Copies one scope's local skills into its skills directory. */\nasync function copyScopedLocalSkills(\n scopeSkills: SkillConfig[],\n skillsDir: string,\n sourceResult: SourceLoadResult,\n): Promise<CopiedSkill[]> {\n if (scopeSkills.length === 0) return [];\n\n await ensureDir(skillsDir);\n\n return copySkillsToLocalFlattened(\n scopeSkills.map((s) => s.id),\n skillsDir,\n sourceResult,\n );\n}\n\n/**\n * Copies local-source skills to their scope-appropriate directories.\n *\n * Splits skills by scope (project vs global), resolves install paths,\n * ensures directories exist, and copies from source.\n */\nexport async function copyLocalSkills(\n skills: SkillConfig[],\n projectDir: string,\n sourceResult: SourceLoadResult,\n): Promise<SkillCopyResult> {\n const projectLocalSkills = skills.filter((s) => s.scope !== \"global\");\n const globalLocalSkills = skills.filter((s) => s.scope === \"global\");\n\n const projectPaths = resolveInstallPaths(projectDir, \"project\");\n const globalPaths = resolveInstallPaths(projectDir, \"global\");\n\n const projectCopied = await copyScopedLocalSkills(\n projectLocalSkills,\n projectPaths.skillsDir,\n sourceResult,\n );\n const globalCopied = await copyScopedLocalSkills(\n globalLocalSkills,\n globalPaths.skillsDir,\n sourceResult,\n );\n\n return {\n projectCopied,\n globalCopied,\n totalCopied: projectCopied.length + globalCopied.length,\n };\n}\n","import { hostAt } from \"../../hosts/host-for.js\";\nimport { buildMarketplacePluginRef } from \"../../plugins/index.js\";\nimport { getErrorMessage } from \"../../../utils/errors.js\";\nimport type { SkillId } from \"../../../types/index.js\";\nimport type { SkillConfig, SkillScope } from \"../../../types/config.js\";\n\n/**\n * Where a plugin is looked for when the old config has no entry naming the skill at all.\n *\n * The scope translation swallowed this until C3 — an absent scope and a project one mapped to the\n * same Claude scope word — so the behaviour is unchanged and only the silence is gone. It is the\n * safe direction of the two: a project-scoped removal cannot take a plugin away from every other\n * project under one HOME.\n */\nconst SCOPE_OF_AN_UNRECORDED_SKILL: SkillScope = \"project\";\n\nexport type PluginUninstallResult = {\n /**\n * The skills whose plugin registration this run really dropped.\n *\n * OBSERVED rather than asked for: a host's `uninstallPlugin` answers `removed` or\n * `absent`, and until that answer was read this list held every id the caller named — so a\n * plugin the user had already removed themselves was reported as one `edit` had just taken\n * away. Neither is a failure, which is why the distinction cannot be an exception and has to be\n * the return value.\n */\n uninstalled: SkillId[];\n failed: Array<{ id: SkillId; error: string }>;\n};\n\n/**\n * Uninstalls skill plugins through the host this installation belongs to, using the scope the\n * old config filed each one under.\n *\n * Each plugin reference is qualified as `{skillId}@{marketplace}` to match\n * the form used at install time — bare skill IDs will not match the registry.\n */\nexport async function uninstallPluginSkills(\n skillIds: SkillId[],\n oldSkills: SkillConfig[],\n marketplace: string,\n projectDir: string,\n): Promise<PluginUninstallResult> {\n const host = hostAt(projectDir);\n const uninstalled: SkillId[] = [];\n const failed: PluginUninstallResult[\"failed\"] = [];\n\n for (const skillId of skillIds) {\n const oldSkill = oldSkills.find((s) => s.id === skillId);\n const pluginScope = oldSkill?.scope ?? SCOPE_OF_AN_UNRECORDED_SKILL;\n const pluginRef = buildMarketplacePluginRef(skillId, marketplace);\n try {\n const outcome = await host.uninstallPlugin(pluginRef, pluginScope, projectDir);\n if (outcome === \"removed\") uninstalled.push(skillId);\n } catch (error) {\n failed.push({ id: skillId, error: getErrorMessage(error) });\n }\n }\n\n return { uninstalled, failed };\n}\n","import type { MergedSkillsMatrix } from \"../../types\";\nimport { initializeMatrix, matrix } from \"../matrix/matrix-provider.js\";\nimport { loadSkillsMatrixFromSource } from \"./source-loader.js\";\n\n/**\n * Runs `body` with the skills catalogue seated for `projectDir`, hands it the catalogue\n * that was seated, and puts the caller's own seat back afterwards.\n *\n * A registered project's own local skills, and the skills of the marketplace ITS config\n * names, are in that project's catalogue and in nobody else's. Two things a fan-out does to\n * such a project read a catalogue, and both used to read the TRIGGERING command's:\n *\n * - The RENDER of a compiled agent, which reaches the catalogue through the module-level\n * singleton (`matrix` in `matrix-provider.ts`) that `statedUsageFor` and `liveCategoryOf`\n * in `stacks/stacks-loader.ts` read directly, so no argument reaches it. Rendered against\n * somebody else's seat, each of the project's own skills loses the `usageGuidance` it\n * states and falls back to the per-category placeholder.\n * - The DERIVATION of that project's `config-types.ts` unions, which looks each configured\n * skill's category up in the catalogue (`deriveCategories` / `deriveDomains`). Against\n * somebody else's, a skill only this project's catalogue carries resolves to nothing and\n * its category and domain leave the project's `Category` and `Domain` unions.\n *\n * Both have the same ending: the project's own next `compile` writes the value back from its\n * own catalogue, so the two commands undo each other for as long as they are both run.\n *\n * The catalogue is PASSED to the body as well as seated, so a derivation that can take one as\n * an argument does — a body reading the singleton it just asked to be seated cannot say which\n * seat it got, which is the distinction the whole helper exists to keep. `compile.ts`'s\n * `seatMatrixForPass` returns its catalogue for the same reason.\n *\n * The seat is restored rather than left standing because this is a side trip: the caller has\n * its own pass to finish — `init` and `edit` compile their own agents after the gate returns —\n * and would otherwise render them against the last registered project's catalogue.\n *\n * The load sits outside the `try` deliberately. It seats nothing when it throws, so there is\n * no seat to restore, and a project whose catalogue cannot be loaded is left to the caller's\n * own failure handling rather than silently processed against somebody else's — which is the\n * whole defect this exists to stop. Both callers count such a project as one they could not\n * reach and carry on with the rest.\n *\n * The load is an `\"upkeep\"` one: a fan-out rewrites a project that is already installed, so a\n * marketplace that has since lost its manifest does not leave the project naming global content\n * a change elsewhere just removed.\n */\nexport async function withCatalogueSeatedFor<T>(\n projectDir: string,\n body: (catalogue: MergedSkillsMatrix) => Promise<T>,\n): Promise<T> {\n const callersCatalogue = matrix;\n const { matrix: seated } = await loadSkillsMatrixFromSource({\n projectDir,\n purpose: \"upkeep\",\n skipExtraSources: true,\n matrixOnly: true,\n });\n try {\n return await body(seated);\n } finally {\n initializeMatrix(callersCatalogue);\n }\n}\n","import { compileAgents, type CompilationResult } from \"./compile-agents.js\";\nimport { loadAgentDefs } from \"./load-agent-defs.js\";\nimport { discoverInstalledSkills } from \"../skills/index.js\";\nimport { resolveInstallPaths } from \"../../installation/index.js\";\nimport { withCatalogueSeatedFor } from \"../../loading/catalogue-seat.js\";\nimport { getErrorMessage } from \"../../../utils/errors.js\";\n\n/**\n * Recompiles the PROJECT-scoped agents of a registered project after a global\n * config change was propagated into that project's `config.ts`.\n *\n * Propagation (`propagateGlobalChangesToProjects`) rewrites a registered\n * project's `config.ts` / `config-types.ts` but never touches its compiled\n * `.claude/agents/*.md`, so the compiled agents keep emitting whatever skill\n * reference form the OLD global data dictated. Run this per propagated project,\n * after propagation, to bring the compiled artifacts back in step.\n *\n * Scope: project only. The global agents were already recompiled by the\n * triggering operation's own pass; repeating a global pass per project would\n * rewrite `~/.claude/agents` once per registered project for no gain.\n *\n * Both inputs a compiled agent's bytes depend on are resolved from THIS\n * project's directory, and neither is inherited from the command that triggered\n * the fan-out: `skills` is passed explicitly, because without it\n * `recompileAgents` falls back to `discoverAllPluginSkills`, which sees plugin\n * skills only and would silently strip every global-local and project-local\n * skill; and the catalogue is seated by {@link withCatalogueSeatedFor}, because\n * the render reads it from a module-level singleton no argument reaches.\n *\n * Agent partials always come from the CLI itself (`getLocalAgentDefinitions`\n * returns `sourcePath: PROJECT_ROOT`), so no per-project marketplace source\n * resolution is needed.\n */\nexport async function recompileRegisteredProjectAgents(\n projectDir: string,\n): Promise<CompilationResult> {\n return withCatalogueSeatedFor(projectDir, async () => {\n const { allSkills } = await discoverInstalledSkills(projectDir);\n const { sourcePath } = await loadAgentDefs();\n\n return compileAgents({\n projectDir,\n sourcePath,\n skills: allSkills,\n scopeFilter: \"project\",\n outputDir: resolveInstallPaths(projectDir, \"project\").agentsDir,\n });\n });\n}\n\nexport type PropagatedRecompileSummary = {\n /** Projects where at least one compiled agent's content actually moved. */\n rewrittenCount: number;\n /**\n * Projects the fan-out reached whose agents all came back byte-identical. They\n * were visited and left alone, which is a different fact from being recompiled\n * and the one the old single count could not tell apart.\n */\n unchangedCount: number;\n failedCount: number;\n /** Per-project warnings in processing order — the caller surfaces them via warn(). */\n warnings: string[];\n};\n\n/**\n * Runs {@link recompileRegisteredProjectAgents} over every propagated project,\n * isolating per-project failures: one project's unreadable config or broken\n * template must not abort the loop or leave the remaining projects stale.\n * Projects are processed sequentially so the collected warnings keep a\n * deterministic per-project order.\n */\nexport async function recompilePropagatedProjectAgents(\n projectDirs: string[],\n): Promise<PropagatedRecompileSummary> {\n let rewrittenCount = 0;\n let unchangedCount = 0;\n let failedCount = 0;\n const warnings: string[] = [];\n\n for (const dir of projectDirs) {\n try {\n const result = await recompileRegisteredProjectAgents(dir);\n if (result.failed.length > 0) {\n failedCount++;\n warnings.push(...result.warnings);\n continue;\n }\n if (result.rewritten.length > 0) rewrittenCount++;\n else unchangedCount++;\n } catch (error) {\n failedCount++;\n warnings.push(`Could not recompile agents in ${dir}: ${getErrorMessage(error)}`);\n }\n }\n\n return { rewrittenCount, unchangedCount, failedCount, warnings };\n}\n"],"mappings":"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","names":["skillsDir"]}
|