agents-inc 0.167.0 → 0.170.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 +31 -0
- package/README.md +18 -15
- package/dist/{chunk-ZR4YPKXQ.js → chunk-3G7X2WKI.js} +4 -4
- package/dist/{chunk-JOEA52NR.js → chunk-44TE3RKH.js} +46 -2
- package/dist/chunk-44TE3RKH.js.map +1 -0
- package/dist/{chunk-V5WXIM2F.js → chunk-52HDHJ7I.js} +11 -19
- package/dist/chunk-52HDHJ7I.js.map +1 -0
- package/dist/{chunk-V3I7VQ7N.js → chunk-6HNXYYVF.js} +39 -22
- package/dist/chunk-6HNXYYVF.js.map +1 -0
- package/dist/{chunk-MAWNVT2C.js → chunk-6ZRH5RHC.js} +10 -11
- package/dist/chunk-6ZRH5RHC.js.map +1 -0
- package/dist/{chunk-G6UREQ4V.js → chunk-7RDMTR64.js} +14 -34
- package/dist/chunk-7RDMTR64.js.map +1 -0
- package/dist/{chunk-GE3WKJ6Q.js → chunk-BMRUNMPV.js} +4 -4
- package/dist/{chunk-VAPTVR3J.js → chunk-CLPUWOSA.js} +4 -4
- package/dist/{chunk-VAPTVR3J.js.map → chunk-CLPUWOSA.js.map} +1 -1
- package/dist/{chunk-HWEKHVLE.js → chunk-F2BHT5C4.js} +9 -7
- package/dist/chunk-F2BHT5C4.js.map +1 -0
- package/dist/{chunk-ZXKBERQU.js → chunk-F5EBEATU.js} +2 -2
- package/dist/{chunk-PMJWD27C.js → chunk-HED2DAXZ.js} +3 -3
- package/dist/{chunk-SAYZWCH6.js → chunk-KOLDW6SF.js} +3 -3
- package/dist/{chunk-SAYZWCH6.js.map → chunk-KOLDW6SF.js.map} +1 -1
- package/dist/{chunk-A4KDXNOT.js → chunk-MQ6CGZNJ.js} +171 -522
- package/dist/chunk-MQ6CGZNJ.js.map +1 -0
- package/dist/{chunk-G4JK7WVO.js → chunk-NCWDK6L7.js} +115 -196
- package/dist/chunk-NCWDK6L7.js.map +1 -0
- package/dist/{chunk-VRXX4D6K.js → chunk-NEZ4OAOL.js} +1 -1
- package/dist/chunk-NEZ4OAOL.js.map +1 -0
- package/dist/chunk-NIHUOWE4.js +106 -0
- package/dist/chunk-NIHUOWE4.js.map +1 -0
- package/dist/{chunk-OI2H6M5I.js → chunk-NQW25DCF.js} +2 -2
- package/dist/chunk-NQW25DCF.js.map +1 -0
- package/dist/{chunk-25MPBIK3.js → chunk-OYJQUA3D.js} +3 -3
- package/dist/{chunk-25MPBIK3.js.map → chunk-OYJQUA3D.js.map} +1 -1
- package/dist/{chunk-ENGYJCTF.js → chunk-PJWGTNOX.js} +220 -254
- package/dist/chunk-PJWGTNOX.js.map +1 -0
- package/dist/{chunk-NBXXFIIW.js → chunk-RIPSGNLU.js} +3 -3
- package/dist/chunk-RIPSGNLU.js.map +1 -0
- package/dist/{chunk-Y47EV74W.js → chunk-SZYQISA2.js} +10 -13
- package/dist/chunk-SZYQISA2.js.map +1 -0
- package/dist/{chunk-TODCQFRB.js → chunk-USVBYQSQ.js} +2 -2
- package/dist/{chunk-Y3VIRVVE.js → chunk-V3KTSP3Q.js} +50 -42
- package/dist/chunk-V3KTSP3Q.js.map +1 -0
- package/dist/{chunk-PXWG7KFO.js → chunk-WTWMGH2C.js} +2 -2
- package/dist/commands/build/marketplace.js +23 -22
- package/dist/commands/build/marketplace.js.map +1 -1
- package/dist/commands/build/plugins.js +8 -9
- package/dist/commands/build/plugins.js.map +1 -1
- package/dist/commands/compile.js +19 -20
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/doctor.js +17 -18
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/edit.js +138 -488
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/eject.js +21 -33
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/init.js +17 -45
- package/dist/commands/list.js +19 -17
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/new/marketplace.js +11 -11
- package/dist/commands/new/marketplace.js.map +1 -1
- package/dist/commands/search.js +12 -13
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/share.js +11 -12
- package/dist/commands/share.js.map +1 -1
- package/dist/commands/uninstall.js +18 -20
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.js +8 -9
- package/dist/commands/update.js.map +1 -1
- package/dist/components/common/removal-plan-confirm.js +2 -2
- package/dist/components/common/scroll-affordance.js +9 -0
- package/dist/components/common/select-list.js +2 -3
- package/dist/components/common/skill-agent-summary.js +11 -0
- package/dist/components/common/spinner.js +1 -1
- package/dist/{chunk-2VUYFTUB.js → components/common/summary-panel.js} +57 -31
- package/dist/components/common/summary-panel.js.map +1 -0
- package/dist/config-exports.js +1 -1
- package/dist/hooks/init.js +18 -46
- package/dist/hooks/init.js.map +1 -1
- package/dist/load-agent-defs-YMDWBFDP.js +14 -0
- package/dist/recompile-project-agents-SZ76WNTW.js +18 -0
- package/dist/{source-loader-7KZ3MD5O.js → source-loader-C5SQXZXI.js} +5 -5
- package/dist/src/agents/meta/codex-keeper/playbook.md +2 -3
- package/package.json +6 -4
- package/src/agents/meta/codex-keeper/playbook.md +2 -3
- package/src/schemas/project-config.schema.json +1 -1
- package/assets/demo.tape +0 -266
- package/dist/chunk-2VUYFTUB.js.map +0 -1
- package/dist/chunk-4NDBUJHK.js +0 -237
- package/dist/chunk-4NDBUJHK.js.map +0 -1
- package/dist/chunk-6L75SQZU.js +0 -168
- package/dist/chunk-6L75SQZU.js.map +0 -1
- package/dist/chunk-6LF4CQBY.js +0 -51
- package/dist/chunk-6LF4CQBY.js.map +0 -1
- package/dist/chunk-7JNJ52CD.js +0 -1179
- package/dist/chunk-7JNJ52CD.js.map +0 -1
- package/dist/chunk-A4KDXNOT.js.map +0 -1
- package/dist/chunk-DAIEQJIF.js +0 -48
- package/dist/chunk-DAIEQJIF.js.map +0 -1
- package/dist/chunk-ENGYJCTF.js.map +0 -1
- package/dist/chunk-G4JK7WVO.js.map +0 -1
- package/dist/chunk-G6UREQ4V.js.map +0 -1
- package/dist/chunk-HHIYT3PI.js +0 -53
- package/dist/chunk-HHIYT3PI.js.map +0 -1
- package/dist/chunk-HWEKHVLE.js.map +0 -1
- package/dist/chunk-JOEA52NR.js.map +0 -1
- package/dist/chunk-JQRJQ4SS.js +0 -118
- package/dist/chunk-JQRJQ4SS.js.map +0 -1
- package/dist/chunk-LFWLEDUG.js +0 -59
- package/dist/chunk-LFWLEDUG.js.map +0 -1
- package/dist/chunk-MAWNVT2C.js.map +0 -1
- package/dist/chunk-MPTNC32T.js +0 -92
- package/dist/chunk-MPTNC32T.js.map +0 -1
- package/dist/chunk-NBXXFIIW.js.map +0 -1
- package/dist/chunk-OI2H6M5I.js.map +0 -1
- package/dist/chunk-PNB3524H.js +0 -197
- package/dist/chunk-PNB3524H.js.map +0 -1
- package/dist/chunk-QHDPLLK6.js +0 -35
- package/dist/chunk-QHDPLLK6.js.map +0 -1
- package/dist/chunk-RAHSOJQ2.js +0 -25
- package/dist/chunk-RAHSOJQ2.js.map +0 -1
- package/dist/chunk-RZDXDMT6.js +0 -23
- package/dist/chunk-RZDXDMT6.js.map +0 -1
- package/dist/chunk-SCOQ34YM.js +0 -17
- package/dist/chunk-SCOQ34YM.js.map +0 -1
- package/dist/chunk-SXVHSOTH.js +0 -268
- package/dist/chunk-SXVHSOTH.js.map +0 -1
- package/dist/chunk-T6UWJAB3.js +0 -28
- package/dist/chunk-T6UWJAB3.js.map +0 -1
- package/dist/chunk-TSIU7DME.js +0 -43
- package/dist/chunk-TSIU7DME.js.map +0 -1
- package/dist/chunk-TTY6DPJN.js +0 -27
- package/dist/chunk-TTY6DPJN.js.map +0 -1
- package/dist/chunk-TZ2YJJKK.js +0 -292
- package/dist/chunk-TZ2YJJKK.js.map +0 -1
- package/dist/chunk-V3I7VQ7N.js.map +0 -1
- package/dist/chunk-V5WXIM2F.js.map +0 -1
- package/dist/chunk-VRXX4D6K.js.map +0 -1
- package/dist/chunk-WTJ3AY33.js +0 -31
- package/dist/chunk-WTJ3AY33.js.map +0 -1
- package/dist/chunk-WVUDLKIO.js +0 -121
- package/dist/chunk-WVUDLKIO.js.map +0 -1
- package/dist/chunk-WWIFNPJG.js +0 -317
- package/dist/chunk-WWIFNPJG.js.map +0 -1
- package/dist/chunk-XQHZPINL.js +0 -89
- package/dist/chunk-XQHZPINL.js.map +0 -1
- package/dist/chunk-XZEVWPX6.js +0 -230
- package/dist/chunk-XZEVWPX6.js.map +0 -1
- package/dist/chunk-Y3VIRVVE.js.map +0 -1
- package/dist/chunk-Y47EV74W.js.map +0 -1
- package/dist/components/wizard/category-grid.js +0 -13
- package/dist/components/wizard/checkbox-grid.js +0 -12
- package/dist/components/wizard/domain-selection.js +0 -18
- package/dist/components/wizard/run-wizard-session.js +0 -40
- package/dist/components/wizard/run-wizard-session.js.map +0 -1
- package/dist/components/wizard/scroll-affordance.js +0 -9
- package/dist/components/wizard/skill-agent-summary.js +0 -13
- package/dist/components/wizard/source-grid.js +0 -16
- package/dist/components/wizard/source-grid.js.map +0 -1
- package/dist/components/wizard/stack-selection.js +0 -17
- package/dist/components/wizard/stack-selection.js.map +0 -1
- package/dist/components/wizard/step-agents.js +0 -18
- package/dist/components/wizard/step-agents.js.map +0 -1
- package/dist/components/wizard/step-build.js +0 -18
- package/dist/components/wizard/step-build.js.map +0 -1
- package/dist/components/wizard/step-confirm.js +0 -18
- package/dist/components/wizard/step-confirm.js.map +0 -1
- package/dist/components/wizard/step-sources.js +0 -19
- package/dist/components/wizard/step-sources.js.map +0 -1
- package/dist/components/wizard/step-stack.js +0 -18
- package/dist/components/wizard/step-stack.js.map +0 -1
- package/dist/components/wizard/summary-panel.js +0 -17
- package/dist/components/wizard/summary-panel.js.map +0 -1
- package/dist/components/wizard/toast.js +0 -9
- package/dist/components/wizard/toast.js.map +0 -1
- package/dist/components/wizard/wizard-layout.js +0 -22
- package/dist/components/wizard/wizard-layout.js.map +0 -1
- package/dist/components/wizard/wizard-tabs.js +0 -11
- package/dist/components/wizard/wizard-tabs.js.map +0 -1
- package/dist/components/wizard/wizard.js +0 -39
- package/dist/components/wizard/wizard.js.map +0 -1
- package/dist/load-agent-defs-3C27ENC7.js +0 -14
- package/dist/load-agent-defs-3C27ENC7.js.map +0 -1
- package/dist/recompile-project-agents-DGNBCU3N.js +0 -18
- package/dist/recompile-project-agents-DGNBCU3N.js.map +0 -1
- package/dist/source-loader-7KZ3MD5O.js.map +0 -1
- package/dist/stores/wizard-store.js +0 -24
- package/dist/stores/wizard-store.js.map +0 -1
- /package/assets/{demo.gif → legacy-wizard.gif} +0 -0
- /package/dist/{chunk-ZR4YPKXQ.js.map → chunk-3G7X2WKI.js.map} +0 -0
- /package/dist/{chunk-GE3WKJ6Q.js.map → chunk-BMRUNMPV.js.map} +0 -0
- /package/dist/{chunk-ZXKBERQU.js.map → chunk-F5EBEATU.js.map} +0 -0
- /package/dist/{chunk-PMJWD27C.js.map → chunk-HED2DAXZ.js.map} +0 -0
- /package/dist/{chunk-TODCQFRB.js.map → chunk-USVBYQSQ.js.map} +0 -0
- /package/dist/{chunk-PXWG7KFO.js.map → chunk-WTWMGH2C.js.map} +0 -0
- /package/dist/components/{wizard → common}/scroll-affordance.js.map +0 -0
- /package/dist/components/{wizard → common}/skill-agent-summary.js.map +0 -0
- /package/dist/{components/wizard/category-grid.js.map → load-agent-defs-YMDWBFDP.js.map} +0 -0
- /package/dist/{components/wizard/checkbox-grid.js.map → recompile-project-agents-SZ76WNTW.js.map} +0 -0
- /package/dist/{components/wizard/domain-selection.js.map → source-loader-C5SQXZXI.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/init.tsx","../src/cli/lib/permission-checker.tsx"],"sourcesContent":["import React from \"react\";\n\nimport { Flags, type Interfaces } from \"@oclif/core\";\nimport { Box, Text, useApp } from \"ink\";\nimport { render } from \"../components/render.js\";\n\nimport { BaseCommand } from \"../base-command.js\";\nimport { type WizardResultV2 } from \"../components/wizard/wizard.js\";\nimport { runWizardSession } from \"../components/wizard/run-wizard-session.js\";\nimport { useTerminalDimensions } from \"../components/hooks/use-terminal-dimensions.js\";\nimport { type SourceLoadResult } from \"../lib/loading/index.js\";\nimport {\n loadSource,\n sayCapturedWarnings,\n loadAgentDefs,\n copyLocalSkills,\n writeProjectConfig,\n compileAgentsAllScopes,\n compileProjectScope,\n type CompilationResult,\n type SkillCopyResult,\n discoverInstalledSkills,\n} from \"../lib/operations/index.js\";\nimport { movedGlobalAgentInputs, type GateReport } from \"../lib/config-gate/index.js\";\nimport { fetchSeedConfig } from \"../lib/seed/fetch-seed.js\";\nimport {\n registerExternalSkills,\n writeExternalSkills,\n type ExternalSkillInstall,\n} from \"../lib/seed/external-skills.js\";\nimport { seedToWizardResult, type SeedMapping } from \"../lib/seed/seed-to-wizard.js\";\nimport {\n holdInstalledGlobal,\n installPlanLines,\n notInstalledGlobally,\n readInstalledGlobal,\n type HeldGlobal,\n} from \"../lib/seed/seed-apply.js\";\nimport { getInstallationInfo } from \"../lib/plugins/plugin-info.js\";\nimport { loadProjectConfig } from \"../lib/configuration/project-config.js\";\nimport { isSameMarketplace, resolveBranding, resolveSource } from \"../lib/configuration/config.js\";\nimport {\n type InstallMode,\n detectInstallation,\n detectProjectInstallation,\n deriveInstallMode,\n holdsItsOwnInstallation,\n installBaseDir,\n resolveInstallPaths,\n buildAgentScopeMap,\n isHomeDirectory,\n INSTALL_MODE_LABELS,\n INSTALL_MODE_DESCRIPTIONS,\n} from \"../lib/installation/index.js\";\nimport { hostAt } from \"../lib/hosts/host-for.js\";\nimport {\n agentCodec,\n chooseProviderForThisRun,\n providerInUse,\n relativeConfigPath,\n} from \"../lib/installation/install-layout.js\";\nimport { hostCompileNotices } from \"../lib/hosts/host-compile-notices.js\";\nimport {\n providerFlag,\n providerNamedBy,\n providerNeedsAConfigurationToInstall,\n} from \"../lib/installation/provider-flag.js\";\nimport { checkPermissions } from \"../lib/permission-checker.js\";\nimport {\n ASCII_LOGO,\n CLI_INVOKE_COMMAND,\n DEFAULT_BRANDING,\n EDITOR_URL,\n EDIT_PROJECT_SETUP_FLAG,\n EDIT_PROJECT_SETUP_MARKETPLACE_FLAG,\n EJECT_SOURCE,\n editorConfigUrl,\n} from \"../consts.js\";\nimport { clearTerminalScreen } from \"../utils/terminal.js\";\nimport { SelectList, type SelectListItem } from \"../components/common/select-list.js\";\nimport { promptConfirm, promptValue } from \"../components/common/prompt-confirm.js\";\nimport { Confirm } from \"../components/common/confirm.js\";\nimport { awaitUnderSpinner } from \"../components/common/spinner.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { EXIT_CODES } from \"../lib/exit-codes.js\";\nimport { openUrl } from \"../utils/open-url.js\";\nimport { plural } from \"../utils/string.js\";\nimport type { AgentName, MergedSkillsMatrix, SkillId, SkillScope } from \"../types/index.js\";\nimport { type StartupMessage } from \"../utils/logger.js\";\nimport {\n INCOMPLETE_WORK_RECOVERY,\n SHARED_CONFIG_ARRIVALS,\n STATUS_MESSAGES,\n agentsNotCompiled,\n applySharedConfigHere,\n carriedSkillsWritten,\n editorLoadsItsOwnMarketplace,\n globalScopedAgentsHint,\n initSucceeded,\n keptAsInstalledGlobally,\n marketplaceFixedAtInstall,\n sharedConfigExistingInstall,\n skippedUnknownAgents,\n skippedUnknownSkills,\n wizardNeedsTerminal,\n} from \"../utils/messages.js\";\nimport type { SeedPayload } from \"@workspace/matrix/seed\";\n\nconst DASHBOARD_OPTIONS = [\n { label: \"Edit\", value: \"edit\" },\n { label: \"Compile\", value: \"compile\" },\n { label: \"Doctor\", value: \"doctor\" },\n { label: \"List\", value: \"list\" },\n] as const satisfies readonly SelectListItem<string>[];\n\n/** The commands offered by the project dashboard (single source: DASHBOARD_OPTIONS). */\nexport type DashboardCommand = (typeof DASHBOARD_OPTIONS)[number][\"value\"];\n\n/**\n * The dashboard's title: {@link ASCII_LOGO} for an installation resting on the shipped branding,\n * and the configured name in its place for one that white-labels it.\n *\n * **The logo gives way rather than sitting above the name, because it spells the shipped name.**\n * `branding.name` replaces a heading everywhere else it is read — every configured leg of\n * `commands/branding-name-reaches-headings` asserts {@link DEFAULT_BRANDING.NAME} is ABSENT — so\n * painting `AGENTS INC` in block capitals above `Northwind` would leave the vendor's name at the\n * most prominent surface the product has, which is the one thing white-labelling is for.\n *\n * The default path is untouched by construction: {@link resolveBranding} answers\n * {@link DEFAULT_BRANDING.NAME} for a configuration with no `branding` key, so an unbranded\n * install takes the logo arm. A configuration naming the shipped name explicitly takes it too —\n * it asked for the shipped name and gets the shipped presentation of it.\n */\nconst DashboardTitle: React.FC<{ name: string }> = ({ name }) =>\n name === DEFAULT_BRANDING.NAME ? <Text>{ASCII_LOGO}</Text> : <Text bold>{name}</Text>;\n\ntype DashboardProps = {\n /** Everything the screen paints, exactly as {@link formatDashboardText} is handed it. */\n data: DashboardData;\n onSelect: (command: DashboardCommand) => void;\n onCancel: () => void;\n};\n\nexport const Dashboard: React.FC<DashboardProps> = ({ data, onSelect, onCancel }) => {\n const { exit } = useApp();\n const { rows: terminalHeight } = useTerminalDimensions();\n\n return (\n <Box flexDirection=\"column\" height={terminalHeight}>\n <Box marginBottom={1}>\n <DashboardTitle name={data.name} />\n </Box>\n <Box marginBottom={1}>\n <Text>{dashboardCountLines(data).join(\"\\n\")}</Text>\n </Box>\n <SelectList\n items={DASHBOARD_OPTIONS}\n onSelect={(command) => {\n onSelect(command);\n exit();\n }}\n onCancel={() => {\n onCancel();\n exit();\n }}\n />\n </Box>\n );\n};\n\n/**\n * What this installation is, as the lines both dashboards paint between the title and the menu.\n *\n * **Shared because the two paths had diverged, not for reuse.** `showDashboard` branches on\n * `process.stdin.isTTY`, and only the text branch drew these — so the screen a person sits in\n * front of was less informative than the output they get by piping it, and no assertion could see\n * it, because each path was only ever compared against itself. One producer is what makes the two\n * unable to drift apart again.\n */\nexport function dashboardCountLines(data: DashboardData): string[] {\n const lines = [\n ` Skills: ${data.skillCount} installed`,\n ` Agents: ${data.agentCount} compiled`,\n ` Mode: ${INSTALL_MODE_LABELS[data.mode]}`,\n ];\n if (data.source) {\n lines.push(` Marketplace: ${data.source}`);\n }\n return lines;\n}\n\n/** Formats the dashboard summary as plain text lines (for non-interactive/test output). */\nexport function formatDashboardText(data: DashboardData): string {\n return [\n data.name,\n \"\",\n ...dashboardCountLines(data),\n \"\",\n ` [Edit] [Compile] [Doctor] [List]`,\n ].join(\"\\n\");\n}\n\n/**\n * What {@link showDashboard} answers when Ctrl+C ended it: a cancellation, as Ctrl+C is at every\n * other prompt — where Escape, which answers null, only steps back out of the dashboard.\n */\nconst DASHBOARD_INTERRUPTED = \"interrupted\";\n\n/** How the dashboard was left: a command chosen, Escape (null), or Ctrl+C. */\ntype DashboardAnswer = DashboardCommand | typeof DASHBOARD_INTERRUPTED | null;\n\n/**\n * Shows the project dashboard and returns the selected command, null when it was left with Escape,\n * or {@link DASHBOARD_INTERRUPTED} when Ctrl+C ended it.\n * In non-interactive environments (no TTY), prints the summary text and returns null.\n */\nexport async function showDashboard(\n projectDir: string,\n log?: (message: string) => void,\n): Promise<DashboardAnswer> {\n const data = await getDashboardData(projectDir);\n\n // Non-interactive: print text summary and exit (CI, piped, tests)\n if (!process.stdin.isTTY) {\n const output = log ?? console.log;\n output(formatDashboardText(data));\n return null;\n }\n\n // First-wins resolution via promptValue; clearOnResolve repaints a clean\n // terminal before unmount (dashboard occupies the full height).\n const selectedCommand = await promptValue<DashboardAnswer>(\n (resolve) => (\n <Dashboard\n data={data}\n onSelect={(command) => resolve(command)}\n onCancel={() => resolve(null)}\n />\n ),\n { onExit: DASHBOARD_INTERRUPTED, clearOnResolve: true },\n );\n\n clearTerminalScreen();\n\n return selectedCommand;\n}\n\n/**\n * Why the dashboard is on screen.\n *\n * `\"init\"` — the user ran `cc init` in an already-installed directory. That is a request\n * to set this project up, so choosing Edit continues the setup: it must materialise the\n * project even if the wizard changes nothing.\n *\n * `\"standalone\"` — the bare `cc` dashboard. Edit is just an editor there; a pass with no\n * changes is an inspection and must leave the filesystem untouched.\n */\nexport type DashboardOrigin = \"init\" | \"standalone\";\n\n/**\n * The extra argv a dashboard selection needs to carry its origin into the command — and, for an\n * `init` that named a marketplace, the marketplace the project setup reads its catalogue from.\n */\nfunction dashboardCommandArgv(\n command: DashboardCommand,\n origin: DashboardOrigin,\n setupMarketplace: string | undefined,\n): string[] {\n if (command !== \"edit\" || origin !== \"init\") return [];\n const marketplaceArgv =\n setupMarketplace === undefined\n ? []\n : [`--${EDIT_PROJECT_SETUP_MARKETPLACE_FLAG}`, setupMarketplace];\n return [`--${EDIT_PROJECT_SETUP_FLAG}`, ...marketplaceArgv];\n}\n\n/**\n * What a run offered the dashboard came to: no installation to show it for, shown and left or\n * acted on, or cancelled with Ctrl+C — which the caller ends the run on as cancelled.\n */\nexport type DashboardFlow = \"not-installed\" | \"shown\" | \"cancelled\";\n\n/**\n * Shared dashboard entry: when the project is already initialized, shows the\n * dashboard and runs the chosen command. Never exits the process — callers decide\n * from the {@link DashboardFlow} it answers (the init hook exits SUCCESS at its own\n * call site for a dashboard shown).\n *\n * `setupMarketplace` is what `init --marketplace` named. A project with no installation of its own\n * is shown the global installation's dashboard, and its Edit sets the project up — from the named\n * marketplace, as a bare `init` in an empty folder would, rather than from the one the global\n * installation stored.\n */\nexport async function runDashboardFlow(\n projectDir: string,\n config: Interfaces.Config,\n origin: DashboardOrigin,\n log?: (message: string) => void,\n setupMarketplace?: string,\n): Promise<DashboardFlow> {\n const installation = await detectInstallation(projectDir);\n if (!installation) return \"not-installed\";\n\n const selectedCommand = await showDashboard(projectDir, log);\n if (selectedCommand === DASHBOARD_INTERRUPTED) return \"cancelled\";\n if (selectedCommand) {\n await config.runCommand(\n selectedCommand,\n dashboardCommandArgv(selectedCommand, origin, setupMarketplace),\n );\n }\n return \"shown\";\n}\n\n/**\n * The marketplace this run was pointed at, as oclif hands it over.\n *\n * `marketplace` is a required key holding `string | undefined` rather than\n * `marketplace?: string` because that is what oclif produces: a non-required flag still gets\n * its key written on the parse result, set to `undefined` when it was not passed. Under\n * `exactOptionalPropertyTypes` the two are different types, and `?` would be describing a\n * producer that never omits the key.\n */\ntype SourceFlags = { marketplace: string | undefined };\n\n/**\n * What `init` decided to install, and the little each producer knows that the shared spine does\n * not. Keeping these on the value rather than branching on a flag downstream is what stops the\n * two paths growing separate copies of the install sequence.\n */\ntype Selection = {\n result: WizardResultV2;\n sourceResult: SourceLoadResult;\n /**\n * The marketplace this selection was actually loaded from, which the shared spine records in\n * the written config. Carried on the value because a shared id may name one the command line\n * did not, and the install has to record where its skills really came from either way.\n */\n sourceFlags: SourceFlags;\n /** Whether a person is at the terminal, so the permission notice may wait for them. */\n interactive: boolean;\n /**\n * Skills in the selection that are already installed where it puts them — a global install's\n * own, which a run from a project records as they are and never installs over.\n */\n installedAlready: ReadonlySet<SkillId>;\n};\n\n/**\n * Which marketplace a shared configuration installs from.\n *\n * A payload names the one its skills were fetched from, because a skill id carries whose skill it\n * is and never where that repository lives — so without the ref the load walks on to the default\n * public catalogue and installs a different repository's skill under the same id.\n *\n * `--marketplace` still outranks it: naming one is an instruction about THIS install, while the\n * payload's ref is a record of where the sharer's came from. A payload that names none leaves the\n * flag undefined, which puts the load back on the rungs it has always walked.\n */\nfunction sharedConfigSourceFlags(flags: SourceFlags, payload: SeedPayload): SourceFlags {\n return { marketplace: flags.marketplace ?? payload.marketplace };\n}\n\nexport default class Init extends BaseCommand {\n /**\n * The name this run prints itself under, resolved once on {@link install}'s spine because the\n * closing line is printed several calls below it. Same shape and same reason as `uninstall`'s\n * field: a plain `string` holding the shipped default rather than an optional every reader\n * would have to answer for.\n */\n private brandingName: string = DEFAULT_BRANDING.NAME;\n\n static summary = `Initialize ${DEFAULT_BRANDING.NAME} in this project`;\n static description =\n \"Interactive wizard to set up skills and agents. Supports Plugin Mode (native install) and Eject Mode (copy to .claude/).\";\n\n static examples = [\n {\n description: \"Start the setup wizard\",\n command: \"<%= config.bin %> <%= command.id %>\",\n },\n {\n description: \"Initialize from a custom marketplace\",\n command: \"<%= config.bin %> <%= command.id %> --marketplace github:org/marketplace\",\n },\n ];\n\n static flags = {\n marketplace: Flags.string({\n char: \"m\",\n description: \"Skills marketplace path or URL\",\n required: false,\n }),\n from: Flags.string({\n description: \"Install a configuration shared from agentsinc.sh by its id, without the wizard\",\n helpValue: \"<id>\",\n }),\n ui: Flags.boolean({\n description:\n \"Open agentsinc.sh/editor instead of the terminal — the id --from names, or the catalogue\",\n default: false,\n }),\n provider: providerFlag(),\n };\n\n /**\n * The command in two statements: do the work, then answer for it.\n *\n * {@link install} owns every path through the setup and returns however it likes; the exit\n * code is decided once, here, after the last of them. Deciding it inside would mean deciding\n * it at each of that method's endings, and an ending added later would inherit the exit-0\n * default silently — which is the defect this pair exists to close. `edit` is two statements\n * for the same reason.\n */\n async run(): Promise<void> {\n await this.install();\n this.exitIfWorkIncomplete();\n }\n\n /**\n * The editor, opened on whatever `--from` named — or on nothing when it named nothing.\n *\n * **One rule, both commands (owner ruling 2026-08-24): `--ui` opens the command's SUBJECT, and\n * `--from` is what supplies one.** `edit --ui` alone opens this installation because that is\n * `edit`'s subject; `init --ui` alone opens the catalogue because a fresh directory has none.\n * Given an id, either opens that id — which is what makes a shared configuration something a\n * recipient can look at rather than only apply blind.\n *\n * No publish happens here and none is needed. `edit --ui` mints an id because an installation\n * is not a configuration the store holds yet; an id already IS one, so this is\n * `editorConfigUrl` and nothing else — no network, no marketplace, no catalogue load.\n *\n * The link is PRINTED first and opened second. Over a pipe, in CI, and on a machine with no\n * desktop session there is no browser to be anybody's — a link nobody can copy is the whole\n * loss, so opening one is the convenience on top and a failure to open is a warning beside a\n * link that still works.\n */\n private async openEditor(id: string | undefined, projectDir: string): Promise<void> {\n const url = id === undefined ? EDITOR_URL : editorConfigUrl(id);\n\n this.log(id === undefined ? `Build it at ${url}` : `Open it at ${url}`);\n this.log(applySharedConfigHere(id, await holdsItsOwnInstallation(projectDir)));\n\n if (!process.stdin.isTTY) return;\n\n const opened = await openUrl(url);\n if (!opened.ok) this.warn(opened.error);\n }\n\n /**\n * One spine, two producers. The wizard and a shared id differ only in *where the selection\n * comes from* — everything after it (the install pipeline) is identical, so it lives here once\n * rather than being written twice and drifting. What each producer refuses, it refuses itself:\n * `--from` asks a question the wizard does not, and every refusal it makes comes before it.\n */\n private async install(): Promise<void> {\n const { flags } = await this.parse(Init);\n const projectDir = process.cwd();\n\n // Above everything, including `--ui`: `--provider` names which installation this run creates,\n // and a run that creates none has nothing for it to name. Refusing here is what leaves the\n // filesystem untouched at both scopes — an installation half-created by a run that then\n // refused is the state every later command has to guess about.\n this.settleTheProviderForThisRun(flags);\n\n // Above `ensureConfigReadable`, and above every read below it, because this route needs no\n // installation at all. A config too broken to load must not stop someone reaching the other\n // front door, and an id somebody shared is not this directory's business either.\n if (flags.ui) {\n this.refuseMarketplaceForTheEditor(flags.marketplace);\n return this.openEditor(flags.from, projectDir);\n }\n\n // Every route below reads the configs first — to show a dashboard, to refuse a shared id, or\n // to inline the global one. One that exists but cannot be read is recreated, not installed\n // over, and saying so here is what keeps the raw loader error off the screen.\n await this.ensureConfigReadable(projectDir);\n\n // Read once on the spine, because the closing line is printed several calls below it. The\n // degrade arm of `resolveBrandingName` cannot fire here: the line above has already refused\n // every config this would read.\n this.brandingName = await this.resolveBrandingName(projectDir);\n\n // Only a bare `init` is diverted to the dashboard. An id is an explicit instruction to install\n // *that* configuration, so it overrides an existing installation instead. A named marketplace\n // takes the same route: it is refused only where this folder's OWN installation was made from\n // another, and otherwise carried into the setup the dashboard's Edit runs.\n if (!flags.from) {\n await this.refuseMarketplaceOtherThanTheInstalledOne(projectDir, flags.marketplace);\n const dashboard = await this.showDashboardIfInitialized(projectDir, flags.marketplace);\n if (dashboard === \"cancelled\") this.error(\"Cancelled\", { exit: EXIT_CODES.CANCELLED });\n if (dashboard === \"shown\") return;\n }\n\n const selection = flags.from\n ? await this.selectionFromSharedConfig(flags.from, flags, projectDir)\n : await this.selectionFromWizard(flags, projectDir);\n\n if (!selection) this.exit(EXIT_CODES.CANCELLED);\n\n // On the spine rather than in either producer: a broken constraint is a fact about the\n // selection, not about where it came from, and `edit` reports the same fact the same way\n // the moment the wizard hands it one. Both producers run the same validator over the same\n // matrix — the wizard over what was chosen, `--from` over what survived the decode — so\n // what is said here is this catalog's verdict either way.\n this.reportValidationErrors(selection.result.validation);\n\n await this.handleInstallation(\n selection.result,\n selection.sourceResult,\n selection.sourceFlags,\n selection.interactive,\n selection.installedAlready,\n );\n }\n\n /**\n * Commits this run to the provider `--provider` named, or refuses a flag with nothing to name.\n *\n * **`--provider` is a LOCATION parameter, not a feature switch**, and the control that proves it\n * is `--provider claude`: naming the default out loud installs byte-for-byte what no flag\n * installs, because all the flag decides is which folder this run creates. Without that half\n * \"the flag selects an installation\" would be indistinguishable from \"the flag means Codex\".\n *\n * **And it is meaningless without `--from` (D15).** The provider is chosen in the web app, so\n * the only run that can be told one is the run that installs what the app produced. There is no\n * wizard step to choose a provider in and none is planned — which is what makes \"the interactive\n * flow is Claude-only\" provable rather than conventional. It also gives the placement refusal\n * something to refuse BEFORE anything is written: with `--from` required, a provider always\n * arrives beside a payload the pre-flight can read.\n */\n private settleTheProviderForThisRun(flags: Interfaces.InferredFlags<typeof Init.flags>): void {\n const named = providerNamedBy(flags.provider);\n if (named === undefined) return;\n\n if (flags.from === undefined) {\n this.error(providerNeedsAConfigurationToInstall(), { exit: EXIT_CODES.INVALID_ARGS });\n }\n\n chooseProviderForThisRun(named);\n }\n\n /** The interactive producer: load the source, run the wizard, return what was chosen. */\n private async selectionFromWizard(\n flags: SourceFlags,\n projectDir: string,\n ): Promise<Selection | null> {\n // The blank global config is created by writeProjectConfig() once the\n // wizard succeeds, never before it renders: a cancelled init must leave no\n // artifact that the next run could mistake for an existing installation.\n const isGlobalRoot = isHomeDirectory(projectDir);\n\n const { sourceResult, startupMessages } = await this.loadWizardInputsUnderSpinner(flags);\n\n // After the load, so a marketplace that cannot be loaded is still refused in its own words\n // over a pipe, and before the mount, which is the first thing here that needs a terminal.\n if (!process.stdin.isTTY) {\n this.error(wizardNeedsTerminal(\"init\"), { exit: EXIT_CODES.ERROR });\n }\n\n const result = await this.runWizard(startupMessages, isGlobalRoot);\n if (!result) return null;\n if (selectsNothing(result)) this.error(\"No skills selected\", { exit: EXIT_CODES.ERROR });\n\n return {\n result,\n sourceResult,\n sourceFlags: flags,\n // A person is already at the terminal, so the permission notice can wait for them.\n interactive: true,\n // The wizard opens only where nothing is installed at all — see `runWizard`.\n installedAlready: new Set(),\n };\n }\n\n /**\n * Everything the wizard needs, loaded behind a spinner that comes down whichever way the\n * await ends — a source that cannot be loaded refuses the run from inside it.\n */\n private async loadWizardInputsUnderSpinner(flags: SourceFlags): Promise<{\n sourceResult: SourceLoadResult;\n startupMessages: StartupMessage[];\n }> {\n return awaitUnderSpinner(STATUS_MESSAGES.LOADING_SKILLS, () => this.loadSourceOrFail(flags));\n }\n\n /**\n * The `--from <id>` producer: fetch and map, no wizard — and, at a terminal, one question.\n *\n * It installs into a clean directory, which is what the first refusal is: a shared\n * configuration is installed whole, so a directory already holding one has it applied with\n * `edit --from` instead, or is uninstalled first. The second is about the LOCATION rather than what is already in it — a global\n * installation holds only global-scoped content. Both fire before anything is written.\n *\n * A global installation ABOVE a project is not in the way. It is not this project's, so the run\n * adds to it what it lacks and states every entry it already holds as installed (see\n * `holdInstalledGlobal`).\n *\n * Before writing anything the run lists what goes into the project and what into the global\n * install. At a terminal it then asks, and a no writes nothing. With no terminal — a CI job or a\n * script — it prints the same lists and carries on: running headless is most of why the flag\n * exists.\n */\n private async selectionFromSharedConfig(\n id: string,\n flags: SourceFlags,\n projectDir: string,\n ): Promise<Selection | null> {\n await this.refuseInstalledProject(projectDir, id);\n\n this.log(`Fetching configuration ${id}...`);\n const fetched = await fetchSeedConfig(id);\n if (!fetched.ok) {\n this.error(fetched.error, { exit: EXIT_CODES.ERROR });\n }\n\n const sourceFlags = sharedConfigSourceFlags(flags, fetched.payload);\n const { sourceResult, startupMessages } = await this.loadSourceOrFail(sourceFlags);\n // Before the decode, because a skill the payload CARRIES answers to no catalogue: unseated,\n // its id is skipped like any other unknown one and its content is never read.\n const carriedSkills = this.registerExternalSkillsOrFail(\n fetched.payload,\n sourceResult.matrix,\n projectDir,\n );\n const { result, skippedSkillIds, skippedAgentNames } = this.decodeSeedOrFail(\n fetched.payload,\n sourceResult.matrix,\n );\n\n // The location refusal is `BaseCommand`'s, because `edit --from` reaches the same\n // contradiction through the other door and an invariant enforced on one producer is enforced\n // nowhere.\n this.refuseProjectScopedContentAtHome(result, projectDir);\n\n const held = holdInstalledGlobal(\n result,\n isHomeDirectory(projectDir) ? null : await readInstalledGlobal(projectDir),\n );\n const arrivingCarried = notInstalledGlobally(carriedSkills, held);\n\n // The last refusals, and still above the lists and the question: a yes installs what was\n // listed, so nothing listed may then be refused.\n await this.refuseSharedConfigBeforeAsking(\n held.result,\n arrivingCarried,\n projectDir,\n sourceResult,\n );\n\n // What the load held back for a wizard this run never mounts, directly above the skips it may\n // explain — a skill this catalogue \"does not know\" is often one it ships with a file that\n // will not parse.\n sayCapturedWarnings(startupMessages);\n\n // Named, not counted. \"3 skills were skipped\" cannot be acted on; the ids can, and this is the\n // one moment the user can tell whether what they shared is what they are getting. Worded once,\n // in `messages.ts`, because `edit --from` reports the same skips about the same wire.\n if (skippedSkillIds.length > 0) this.warn(skippedUnknownSkills(skippedSkillIds));\n if (skippedAgentNames.length > 0) this.warn(skippedUnknownAgents(skippedAgentNames));\n\n // Below the skips, which are its explanation, and above the lists: a configuration this\n // catalogue can place nothing of has nothing to list, and nothing to ask about.\n if (selectsNothing(held.result)) {\n this.error(`Configuration '${id}' contains no skills this catalog can install.`, {\n exit: EXIT_CODES.ERROR,\n });\n }\n\n this.logWhatGoesWhere(held);\n if (process.stdin.isTTY && !(await this.confirmInstall())) {\n this.log(\"Setup cancelled\");\n return null;\n }\n\n // After the question and before the install: every refusal this producer makes, and a no,\n // leave nothing written, and the copy step that follows finds these skills already where they\n // belong.\n await this.writeCarriedSkills(arrivingCarried);\n\n const arriving = notInstalledGlobally(held.result.skills, held);\n if (arriving.length > 0) {\n this.log(\n `Installing ${arriving.length} skill(s) across ${result.selectedAgents.length} sub-agent(s)\\n`,\n );\n }\n\n return {\n result: held.result,\n sourceResult,\n sourceFlags,\n interactive: false,\n installedAlready: new Set(held.installed.skillIds),\n };\n }\n\n /**\n * The two lists — what goes into this project, and what into the global install — then the\n * global skills skipped as already installed, and the global entries the configuration states\n * otherwise than they are installed, which stay.\n */\n private logWhatGoesWhere(held: HeldGlobal): void {\n for (const line of installPlanLines(held)) {\n this.log(line);\n }\n\n const { skillIds, agentNames } = held.keptAsInstalled;\n if (skillIds.length > 0 || agentNames.length > 0) {\n this.log(keptAsInstalledGlobally(skillIds, agentNames));\n }\n this.log(\"\");\n }\n\n /** The question under the lists, defaulting to no: nothing has been written yet. */\n private async confirmInstall(): Promise<boolean> {\n const outcome = await promptConfirm(({ onConfirm, onCancel }) => (\n <Confirm\n message={SHARED_CONFIG_ARRIVALS.INSTALL_CONFIRM}\n onConfirm={onConfirm}\n onCancel={onCancel}\n />\n ));\n return outcome === \"confirmed\";\n }\n\n /**\n * Refuses to install a shared configuration into a directory that already has one.\n *\n * Project-scoped detection rather than `detectInstallation`, whose global fallback would refuse\n * every clean project on a machine with a global install. A global install above the project is\n * added to rather than refused over — see {@link selectionFromSharedConfig}.\n *\n * This runs before the fetch: there is nothing to learn from the network about a directory that\n * is already spoken for.\n */\n private async refuseInstalledProject(projectDir: string, id: string): Promise<void> {\n const installation = await detectProjectInstallation(projectDir);\n if (!installation) return;\n\n this.error(sharedConfigExistingInstall(installation.configPath, id), {\n exit: EXIT_CODES.ERROR,\n });\n }\n\n /**\n * The decode refuses a payload the config model has nowhere to write (see `seedToWizardResult`).\n * That is a failure of this command, reported with this command's exit code rather than left to\n * surface as an unhandled throw.\n */\n private decodeSeedOrFail(payload: SeedPayload, matrix: MergedSkillsMatrix): SeedMapping {\n try {\n return seedToWizardResult(payload, matrix);\n } catch (error) {\n this.handleError(error);\n }\n }\n\n /**\n * Seats the catalogue entries the payload carries with it, or refuses the run.\n *\n * The refusals are `registerExternalSkills`' own — a carried skill asked for as a plugin, which\n * no marketplace serves, and a carried skill claiming an id the loaded catalogue already owns,\n * whose bytes would be written over that catalogue's own copy — and are reported with this\n * command's exit code for the same reason the decode's is.\n */\n private registerExternalSkillsOrFail(\n payload: SeedPayload,\n matrix: MergedSkillsMatrix,\n projectDir: string,\n ): ExternalSkillInstall[] {\n try {\n return registerExternalSkills(payload, matrix, projectDir);\n } catch (error) {\n this.handleError(error);\n }\n }\n\n /** Writes the skills the payload brought with it, and says which they were. */\n private async writeCarriedSkills(carried: ExternalSkillInstall[]): Promise<void> {\n if (carried.length === 0) return;\n\n await writeExternalSkills(carried);\n // Named rather than counted, like the skips: these are the entries no catalogue can explain,\n // so this line is the only place the user learns what arrived with the configuration itself.\n this.log(carriedSkillsWritten(carried.map((skill) => skill.id)));\n }\n\n private async showDashboardIfInitialized(\n projectDir: string,\n setupMarketplace: string | undefined,\n ): Promise<DashboardFlow> {\n return runDashboardFlow(\n projectDir,\n this.config,\n \"init\",\n (msg) => this.log(msg),\n setupMarketplace,\n );\n }\n\n /**\n * Refuses `--marketplace` beside `--ui`. The editor's address carries a configuration id and\n * nothing else, so the marketplace would be dropped on the way — and the editor loads\n * marketplaces itself.\n */\n private refuseMarketplaceForTheEditor(named: string | undefined): void {\n if (named === undefined) return;\n this.error(editorLoadsItsOwnMarketplace(named), { exit: EXIT_CODES.INVALID_ARGS });\n }\n\n /**\n * Refuses a marketplace other than the one this folder's OWN installation was made from.\n *\n * An installation's marketplace is chosen once, when it is made, and the dashboard every route\n * here ends on reads the one it stored — so naming another would be dropped, and a flag dropped\n * reads as honoured. Project-scoped detection, because a project with no installation of its\n * own is not refused for the global one above it: that is two installations, and the setup the\n * dashboard's Edit runs there takes the named marketplace. At the home directory the folder's\n * own installation IS the global one, and the same detection finds it.\n *\n * The stored marketplace is resolved the way every later command resolves it — this folder's\n * config, then the global one, then the default — so an installation that recorded none is\n * held to the one it has actually been reading. A folder named back by another spelling of its\n * path is that same marketplace.\n */\n private async refuseMarketplaceOtherThanTheInstalledOne(\n projectDir: string,\n named: string | undefined,\n ): Promise<void> {\n if (named === undefined) return;\n\n if (!(await detectProjectInstallation(projectDir))) return;\n\n const { source: stored } = await resolveSource({ caller: \"stored\", projectDir });\n if (isSameMarketplace(named, stored, projectDir)) return;\n\n this.error(marketplaceFixedAtInstall(stored, named), { exit: EXIT_CODES.INVALID_ARGS });\n }\n\n private async loadSourceOrFail(\n flags: SourceFlags,\n ): Promise<{ sourceResult: SourceLoadResult; startupMessages: StartupMessage[] }> {\n try {\n const loaded = await loadSource({\n // The one load that may CHOOSE a marketplace rather than read the stored one.\n caller: \"init\",\n ...(flags.marketplace !== undefined && { sourceFlag: flags.marketplace }),\n projectDir: process.cwd(),\n captureStartupMessages: true,\n });\n return { sourceResult: loaded.sourceResult, startupMessages: loaded.startupMessages };\n } catch (error) {\n this.error(getErrorMessage(error), {\n exit: EXIT_CODES.ERROR,\n });\n }\n }\n\n /**\n * `init`'s wizard opens on an empty selection, and can open on nothing else: it is reached\n * only once {@link showDashboardIfInitialized} has found no installation, and that check\n * falls back to the home directory — so a global roster to hydrate from would have diverted\n * this run to the dashboard before the wizard was built. Hydrating a saved selection is\n * `edit`'s job, and `Edit.runEditWizard` is where it happens.\n */\n private async runWizard(\n startupMessages: StartupMessage[],\n isGlobalRoot: boolean,\n ): Promise<WizardResultV2 | null> {\n return runWizardSession({\n hydrate: { isEditingFromGlobalScope: isGlobalRoot },\n props: {\n version: this.config.version,\n logo: ASCII_LOGO,\n startupMessages,\n },\n onCancel: () => this.log(\"Setup cancelled\"),\n clearTerminal: () => this.clearTerminal(),\n });\n }\n\n private async handleInstallation(\n result: WizardResultV2,\n sourceResult: SourceLoadResult,\n flags: SourceFlags,\n /**\n * Whether the caller can hold the terminal. The permission notice is an Ink app with no exit\n * of its own, so `waitUntilExit()` only ever resolves because a person is there to end it —\n * which is fine after the wizard and a hang everywhere else. `--from` sets this false: it has\n * to complete over a pipe and in CI.\n */\n interactive: boolean,\n /** Skills recorded as they are installed already, and so neither copied nor registered. */\n installedAlready: ReadonlySet<SkillId>,\n ): Promise<void> {\n const projectDir = process.cwd();\n const activeSkills = result.skills.filter((s) => !s.excluded);\n const toInstall = activeSkills.filter((s) => !installedAlready.has(s.id));\n const installMode = deriveInstallMode(toInstall);\n const ejectedSkills = toInstall.filter((s) => s.origin === EJECT_SOURCE);\n const pluginSkills = toInstall.filter((s) => s.origin !== EJECT_SOURCE);\n\n // The wizard's selection meets its placement refusal here. A shared configuration's has met\n // it already, above its lists and its question, and passes it again unchanged.\n this.refuseUnofferablePlacementsBeforeWriting(activeSkills, projectDir);\n this.logInstallPlan(installMode, ejectedSkills, pluginSkills);\n\n // Resolve marketplace up front — BEFORE any filesystem mutation. If\n // resolution fails in mixed mode, the hard-error must fire before\n // `copyEjectSkillsStep` copies anything to `.claude/skills/`, otherwise\n // we leave an orphaned half-install with no config.ts to recognise it.\n const resolvedMarketplace =\n pluginSkills.length > 0\n ? await this.requireMarketplaceOrExit(\n sourceResult,\n \"install plugin skills\",\n hostAt(projectDir),\n (marketplace) => this.log(`Registering marketplace \"${marketplace}\"...`),\n )\n : null;\n\n const copyResult =\n ejectedSkills.length > 0\n ? await this.copyEjectSkillsStep(ejectedSkills, projectDir, sourceResult, installMode)\n : null;\n\n if (resolvedMarketplace !== null) {\n await this.installPluginSkillsReported(\n pluginSkills,\n resolvedMarketplace,\n projectDir,\n sourceResult.matrix,\n );\n }\n // The shared install reporter hard-errors on any failure, so reaching here with\n // a resolved marketplace means plugin mode fully succeeded.\n const pluginModeSucceeded = resolvedMarketplace !== null;\n\n try {\n const { configResult, compileResult, agentScopeMap } = await this.writeConfigAndCompile(\n result,\n sourceResult,\n flags,\n );\n this.reportSuccess(\n configResult,\n compileResult,\n agentScopeMap,\n installMode,\n pluginModeSucceeded,\n copyResult,\n );\n\n await this.showPermissionNotice(projectDir, interactive);\n } catch (error) {\n this.handleError(error);\n }\n }\n\n /**\n * The permission notice, where there is one to show. It is an Ink app with no exit of its own,\n * so `waitUntilExit()` only ever resolves because a person is there to end it — which is fine\n * after the wizard and a hang everywhere else. Without a terminal to hold, one frame is\n * rendered and let go: the notice is information, not a prompt, so there is nothing to answer\n * and nothing to wait for.\n */\n private async showPermissionNotice(projectDir: string, interactive: boolean): Promise<void> {\n const permissionWarning = await checkPermissions(projectDir);\n if (!permissionWarning) return;\n\n if (!interactive) {\n const { unmount } = render(permissionWarning);\n unmount();\n return;\n }\n\n const { waitUntilExit } = render(permissionWarning);\n await waitUntilExit();\n }\n\n private logInstallPlan(\n installMode: InstallMode,\n ejectedSkills: WizardResultV2[\"skills\"],\n pluginSkills: WizardResultV2[\"skills\"],\n ): void {\n this.log(\"\\n\");\n this.log(`Selected ${plural(ejectedSkills.length + pluginSkills.length, \"skill\")}`);\n this.log(\n `Install mode: ${\n installMode === \"mixed\"\n ? `${INSTALL_MODE_LABELS.mixed} (${ejectedSkills.length} eject, ${pluginSkills.length} plugin)`\n : INSTALL_MODE_DESCRIPTIONS[installMode]\n }`,\n );\n }\n\n /**\n * Copies the ejected skills and says how many landed at each scope, in the words\n * {@link skillsCopiedLine} chooses.\n */\n private async copyEjectSkillsStep(\n localSkills: WizardResultV2[\"skills\"],\n projectDir: string,\n sourceResult: SourceLoadResult,\n installMode: InstallMode,\n ): Promise<SkillCopyResult> {\n this.log(\"Copying skills to local directory...\");\n const copyResult = await copyLocalSkills(localSkills, projectDir, sourceResult);\n this.log(skillsCopiedLine(copyResult, installMode, projectDir));\n return copyResult;\n }\n\n private async writeConfigAndCompile(\n result: WizardResultV2,\n sourceResult: SourceLoadResult,\n flags: SourceFlags,\n ): Promise<{\n configResult: Awaited<ReturnType<typeof writeProjectConfig>>;\n compileResult: CompilationResult;\n agentScopeMap: Map<AgentName, SkillScope>;\n }> {\n this.log(\"Generating configuration...\");\n const configResult = await writeProjectConfig({\n wizardResult: result,\n sourceResult,\n projectDir: process.cwd(),\n ...(flags.marketplace !== undefined && { sourceFlag: flags.marketplace }),\n });\n\n if (configResult.wasMerged) {\n this.log(`Merged with existing config at ${configResult.existingConfigPath}`);\n }\n\n this.log(`Configuration saved (${plural(configResult.config.agents.length, \"agent\")})\\n`);\n this.reportUnassignedSkills(configResult.config, result.skills);\n\n this.log(STATUS_MESSAGES.COMPILING_AGENTS);\n const cwd = process.cwd();\n const agentDefs = await loadAgentDefs();\n const { allSkills } = await discoverInstalledSkills(cwd);\n const agentScopeMap = buildAgentScopeMap(configResult.config);\n const compile = compilesGlobalScope(cwd, configResult.propagation)\n ? compileAgentsAllScopes\n : compileProjectScope;\n const compileResult = await compile({\n projectDir: cwd,\n sourcePath: agentDefs.sourcePath,\n skills: allSkills,\n agentScopeMap,\n });\n this.reportCompilation(compileResult);\n await this.reportWhatThisHostCannotCarry(cwd);\n\n this.reportFanOut(configResult.propagation);\n\n return { configResult, compileResult, agentScopeMap };\n }\n\n /**\n * What this host could not carry, after the compile summary and once per run.\n *\n * `init --from` is the door a configuration somebody ELSE wrote arrives through, so it is\n * precisely the run that must not be silent about the parts of it that did not survive the\n * translation. `compile` prints the same lines in the same order — see\n * {@link hostCompileNotices}, which is the one place either of them gets them from.\n *\n * A Claude install expresses every setting and carries every sub-agent, so it gets one line at\n * most: the trust dialog, while its project sub-agents' completion gate cannot run in this\n * folder.\n */\n private async reportWhatThisHostCannotCarry(cwd: string): Promise<void> {\n for (const notice of await hostCompileNotices(providerInUse(cwd), cwd)) {\n this.log(notice);\n }\n }\n\n /**\n * What the compile pass did, including the half this command used to drop.\n *\n * It read `compiled` and nothing else, so a pass that lost a sub-agent printed a number that\n * looked perfectly right — `compiled` counts successes, so it is correct either way — and the\n * failures reached no surface at all: no count, no reason, no exit code. A user's FIRST\n * install could silently land a roster missing a sub-agent.\n *\n * The compile is the last thing `init` does, so a failure here cannot be answered by aborting:\n * the skills are on disk and `config.ts` and `config-types.ts` describe them. What is owed is\n * the account, which {@link BaseCommand.exitIfWorkIncomplete} prints and answers for.\n */\n private reportCompilation(compileResult: CompilationResult): void {\n const { compiled, failed, warnings } = compileResult;\n const summary = `Compiled ${plural(compiled.length, \"agent\")}`;\n\n if (failed.length === 0) {\n this.log(`${summary}\\n`);\n return;\n }\n\n this.log(`${summary} (${failed.length} failed)\\n`);\n for (const warning of warnings) {\n this.warn(warning);\n }\n // Recorded off `failed` rather than off the warnings just printed: `warnings` also carries\n // entries that are not failures, and the ending must not file those as work owed.\n this.recordIncompleteWork(agentsNotCompiled(failed), INCOMPLETE_WORK_RECOVERY.RECOMPILE);\n }\n\n private reportSuccess(\n configResult: Awaited<ReturnType<typeof writeProjectConfig>>,\n compileResult: CompilationResult,\n agentScopeMap: Map<AgentName, SkillScope>,\n installMode: InstallMode,\n pluginModeSucceeded: boolean,\n copyResult: SkillCopyResult | null,\n ): void {\n // A run with work owed ends on the failure account instead — `initialized successfully!`\n // over a roster missing a sub-agent is the claim being withdrawn, not a line beside it.\n // Where the install landed is still reported below: that is what the account is about.\n if (!this.hasIncompleteWork) this.log(`${initSucceeded(this.brandingName)}\\n`);\n\n const isEjectOutput =\n installMode === \"eject\" || (installMode === \"mixed\" && !pluginModeSucceeded);\n if (isEjectOutput && copyResult && copyResult.totalCopied > 0) {\n this.reportSkillsCopied(copyResult);\n }\n this.reportAgentsCompiled(compileResult.compiled, agentScopeMap);\n this.reportConfiguration(configResult.configPath, agentScopeMap);\n }\n\n /**\n * Names the config that actually holds this install's assignments, and where a\n * recompile of them has to be run from.\n *\n * Split by the same `agentScopeMap` the two reporters above split on, because the\n * same scope decides both. A project config's `stack` is filtered down to\n * project-scoped agents on the way out, so a wholly GLOBAL install leaves it\n * carrying no assignment at all — naming it sends the user to a file with nothing\n * in it — and `compile` in this cwd runs the PROJECT pass, which recompiles no\n * global agent. At the home root both scopes resolve to one file and one pass, so\n * there is nothing to split and the project wording stands.\n */\n private reportConfiguration(\n projectConfigPath: string,\n agentScopeMap: Map<AgentName, SkillScope>,\n ): void {\n const cwd = process.cwd();\n const paths = {\n global: resolveInstallPaths(cwd, \"global\").configPath,\n project: projectConfigPath,\n };\n const split = splitAgentScopes(cwd, agentScopeMap);\n\n this.log(\"Configuration:\");\n for (const configPath of configsHoldingAssignments(split, paths)) {\n this.log(` ${configPath}`);\n }\n this.log(\"\");\n\n this.log(\"To customize agent-skill assignments:\");\n for (const [index, step] of customizationSteps(split, paths.global, cwd).entries()) {\n this.log(` ${index + 1}. ${step}`);\n }\n this.log(\"\");\n }\n\n /**\n * Reports where the skills actually landed. `copyLocalSkills` splits by scope,\n * so a default install driven from a project directory writes every skill\n * under HOME — reporting the project path there names a directory that was\n * never written.\n *\n * This block is a filesystem listing, so the entries are the on-disk directory\n * names — skill ids, not display names. `copySkillsToLocalFlattened` names each\n * destination directory after `skill.id`, so a user can copy any line and `cd`\n * into it.\n */\n private reportSkillsCopied(copyResult: SkillCopyResult): void {\n const cwd = process.cwd();\n const groups = isHomeDirectory(cwd)\n ? [\n {\n dir: resolveInstallPaths(cwd, \"project\").skillsDir,\n copied: [...copyResult.globalCopied, ...copyResult.projectCopied],\n },\n ]\n : [\n { dir: resolveInstallPaths(cwd, \"global\").skillsDir, copied: copyResult.globalCopied },\n { dir: resolveInstallPaths(cwd, \"project\").skillsDir, copied: copyResult.projectCopied },\n ].filter((group) => group.copied.length > 0);\n\n for (const group of groups) {\n this.log(\"Skills copied to:\");\n this.log(` ${group.dir}`);\n for (const copied of group.copied) {\n this.log(` ${copied.skillId}/`);\n }\n this.log(\"\");\n }\n }\n\n /**\n * Reports where the agents actually landed, mirroring the scope split\n * `compileAgentsAllScopes` performs: one pass at the home root, otherwise a\n * global pass under HOME and a project pass under the project directory.\n *\n * **The EXTENSION is the host's, per scope, and was Claude's literal `.md` until C5.** This\n * block describes the filesystem, so every line under a path header has to be a name a user can\n * copy out and open — and a Codex install printed `web-developer.md` above a directory holding\n * `web-developer.toml`. The two scopes are read separately because they can be on two hosts.\n */\n private reportAgentsCompiled(\n compiled: AgentName[],\n agentScopeMap: Map<AgentName, SkillScope>,\n ): void {\n const cwd = process.cwd();\n const groups = isHomeDirectory(cwd)\n ? [{ ...compiledInto(cwd, \"project\"), agents: compiled }]\n : [\n {\n ...compiledInto(cwd, \"global\"),\n agents: compiled.filter((name) => agentScopeMap.get(name) === \"global\"),\n },\n {\n ...compiledInto(cwd, \"project\"),\n agents: compiled.filter((name) => agentScopeMap.get(name) !== \"global\"),\n },\n ].filter((group) => group.agents.length > 0);\n\n for (const group of groups) {\n this.log(\"Agents compiled to:\");\n this.log(` ${group.dir}`);\n for (const agentName of group.agents) {\n this.log(` ${agentName}${group.extension}`);\n }\n this.log(\"\");\n }\n }\n}\n\n/**\n * Whether a selection holds nothing to install. A sub-agent is installable on its own — it has\n * front-matter, a prompt and a compiled file without owning a single skill — so only a selection\n * with neither is empty.\n *\n * Asked by each producer rather than on the spine, because each refuses it in its own words and\n * at its own moment: the wizard once the user has chosen nothing, and `--from` before it lists a\n * plan or asks about one.\n */\nfunction selectsNothing(result: WizardResultV2): boolean {\n return result.skills.length === 0 && result.selectedAgents.length === 0;\n}\n\n/**\n * Whether this run's compile covers the global install as well as the project — `edit`'s rule\n * (`compilesGlobalScope` in `commands/edit.tsx`), for the same reason.\n *\n * From a project, the global install is held as installed, so its sub-agents are recompiled only\n * when the write added something they are built from. Recompiled anyway, they would be built from\n * the catalogue THIS run loaded rather than the one the global install was made from — a skill\n * that catalogue does not ship is described by its fallback guidance instead of its own — and a\n * project run that added nothing to the global install would still have rewritten it. The wizard\n * reaches the same answer by the same test: it opens only where nothing is installed, so every\n * global entry it writes is an added one.\n */\nfunction compilesGlobalScope(cwd: string, written: GateReport): boolean {\n return isHomeDirectory(cwd) || movedGlobalAgentInputs(written);\n}\n\n/**\n * The line an eject copy ends on: how many skills landed, and where.\n *\n * A copy that landed at one scope names the directory `copyLocalSkills` wrote it into, read the\n * way that function reads it — `resolveInstallPaths` at the scope — so it is the folder the\n * `Skills copied to:` block beneath names too. The line spelled the directory from the scope ROOT\n * until 2026-10-03, so a run from a project that copied every skill under HOME printed\n * `.claude/skills/`, which, read where the command was typed, names the project's own empty\n * folder. A copy that landed at both scopes names no directory, because two scopes have two and\n * the block beneath lists each.\n *\n * In a mixed install the copies are its \"local\" half, beside the plugins installed after them.\n */\nfunction skillsCopiedLine(\n copyResult: SkillCopyResult,\n installMode: InstallMode,\n projectDir: string,\n): string {\n const isMixed = installMode === \"mixed\";\n const copied = plural(copyResult.totalCopied, isMixed ? \"local skill\" : \"skill\");\n const lineEnd = isMixed ? \"\" : \"\\n\";\n const projectCount = copyResult.projectCopied.length;\n const globalCount = copyResult.globalCopied.length;\n const landedAtBothScopes = projectCount > 0 && globalCount > 0;\n\n if (landedAtBothScopes) {\n return `Copied ${copied} (${projectCount} project, ${globalCount} global)${lineEnd}`;\n }\n const landedAt: SkillScope = globalCount > 0 ? \"global\" : \"project\";\n return `Copied ${copied} to ${resolveInstallPaths(projectDir, landedAt).skillsDir}/${lineEnd}`;\n}\n\n/**\n * Where one scope's sub-agents landed, and the extension the host that reads them writes.\n *\n * One read per scope, exactly as `resolveInstallPaths` reads one beside it: a project on Codex\n * under a global on Claude puts `.toml` in one block and `.md` in the other, and one answer for\n * both is how a path block names a file that is not there.\n */\nfunction compiledInto(cwd: string, scope: SkillScope): { dir: string; extension: string } {\n return {\n dir: resolveInstallPaths(cwd, scope).agentsDir,\n extension: agentCodec(providerInUse(installBaseDir(cwd, scope))).extension,\n };\n}\n\n/** The two config files an install can write, as the closing block refers to them. */\ntype ConfigPaths = { global: string; project: string };\n\n/** How this install's sub-agents fall across the two scopes, seen from the directory it ran in. */\ntype AgentScopeSplit = { globalAgentCount: number; isGlobalOnly: boolean };\n\n/**\n * At the home root the two scopes resolve to ONE config file and ONE compile pass, so there\n * is nothing to split and the split reports none — which is what leaves the project wording\n * standing there unchanged. Same fork `reportAgentsCompiled` makes over the same question.\n */\nfunction splitAgentScopes(cwd: string, agentScopeMap: Map<AgentName, SkillScope>): AgentScopeSplit {\n if (isHomeDirectory(cwd)) return { globalAgentCount: 0, isGlobalOnly: false };\n\n const scopes = [...agentScopeMap.values()];\n const globalAgentCount = scopes.filter((scope) => scope === \"global\").length;\n return {\n globalAgentCount,\n isGlobalOnly: globalAgentCount > 0 && globalAgentCount === scopes.length,\n };\n}\n\n/**\n * The config file(s) this install's assignments actually landed in. A wholly global install\n * has none in the project file — the writer filters its `stack` down to project-scoped\n * agents — so naming it would send the user to a file with nothing in it.\n */\nfunction configsHoldingAssignments(split: AgentScopeSplit, paths: ConfigPaths): string[] {\n if (split.isGlobalOnly) return [paths.global];\n if (split.globalAgentCount > 0) return [paths.global, paths.project];\n return [paths.project];\n}\n\n/**\n * What changing an assignment takes, in the order it takes it. The compile step is the half\n * that has to be scope-aware: `compile` run in a project directory performs the PROJECT pass\n * only, so it recompiles no global agent — which `globalScopedAgentsHint` already says, in\n * the words `compile` itself says it in when it lands in the mirror image of this state.\n */\nfunction customizationSteps(\n split: AgentScopeSplit,\n globalConfigPath: string,\n projectDir: string,\n): string[] {\n if (split.isGlobalOnly) {\n return [`Edit ${globalConfigPath}`, globalScopedAgentsHint(split.globalAgentCount)];\n }\n\n return [\n `Edit ${relativeConfigPath(projectDir, providerInUse(projectDir))}`,\n `Run '${CLI_INVOKE_COMMAND} compile' to regenerate agents`,\n ...(split.globalAgentCount > 0 ? [globalScopedAgentsHint(split.globalAgentCount)] : []),\n ];\n}\n\nexport type DashboardData = {\n /**\n * The name the dashboard is titled with — `branding.name` where the configuration supplies one\n * and {@link DEFAULT_BRANDING.NAME} otherwise. Carried on the data rather than read by either\n * consumer so {@link formatDashboardText} stays a pure function of what it is handed, and so\n * the one place that reaches a configuration is the one that already loads it.\n *\n * **Both of {@link showDashboard}'s branches read it, and that is the invariant to keep.** This\n * field was resolved for the piped branch alone for as long as it was, while the `Dashboard`\n * component was handed only its callbacks and painted {@link ASCII_LOGO} — so the dashboard a\n * person actually sees was the one surface `branding.name` never reached, and every spec on the\n * subject drove through a pipe and could not see it.\n */\n name: string;\n skillCount: number;\n agentCount: number;\n mode: InstallMode;\n source?: string;\n};\n\n/**\n * Gathers dashboard data from the installation and project config.\n *\n * Both counts come from the same scope-aware installation so the summary cannot\n * mix a project-only figure with a global one — a default install driven from a\n * project directory puts every skill and agent under HOME.\n */\nexport async function getDashboardData(projectDir: string): Promise<DashboardData> {\n // ABORT is the posture on `resolveBranding`, and it is the one already in force: the\n // `loadProjectConfig` beside it raises for the same file, so a configuration that cannot be\n // evaluated fails this call whether branding is read or not. Nothing is degraded here that was\n // not already, and the dashboard is only reached once an installation has been detected.\n const [info, loaded, branding] = await Promise.all([\n getInstallationInfo(),\n loadProjectConfig(projectDir),\n resolveBranding(projectDir),\n ]);\n\n const activeSkills = loaded?.config.skills.filter((s) => !s.excluded);\n const skillCount = info?.skillCount ?? 0;\n const agentCount = info?.agentCount ?? 0;\n const mode = info?.mode ?? (activeSkills ? deriveInstallMode(activeSkills) : \"eject\");\n const source = loaded?.config.marketplace;\n\n return {\n name: branding.name,\n skillCount,\n agentCount,\n mode,\n ...(source !== undefined && { source }),\n };\n}\n","import React from \"react\";\nimport { z } from \"zod\";\n\nimport { Text, Box } from \"ink\";\nimport path from \"path\";\n\nimport { CLI_COLORS, MAX_CONFIG_FILE_SIZE } from \"../consts\";\nimport {\n advisesOnPermissions,\n permissionFiles,\n providerInUse,\n} from \"./installation/install-layout.js\";\nimport { fileExists, readFileSafe } from \"../utils/fs\";\nimport { warn } from \"../utils/logger\";\nimport { settingsFileSchema } from \"./schemas\";\n\ntype SettingsFile = z.infer<typeof settingsFileSchema>;\ntype PermissionConfig = NonNullable<SettingsFile[\"permissions\"]>;\n\n/**\n * Reads one settings file's permissions block; undefined when absent, malformed, or empty.\n *\n * settings.json belongs to Claude Code, which adds keys on its own release schedule. This CLI\n * consumes `permissions` and owns nothing else in the file, so it stays silent about every other\n * field rather than warning about settings it has no standing to judge.\n */\nasync function readSettingsPermissions(filePath: string): Promise<PermissionConfig | undefined> {\n if (!(await fileExists(filePath))) return undefined;\n try {\n const content = await readFileSafe(filePath, MAX_CONFIG_FILE_SIZE);\n const raw: unknown = JSON.parse(content);\n const result = settingsFileSchema.safeParse(raw);\n const parsed: SettingsFile = result.success ? result.data : {};\n return parsed.permissions;\n } catch {\n warn(`Malformed settings file at '${filePath}' — skipping`);\n return undefined;\n }\n}\n\n/**\n * The files this host reads permissions from at project scope, in the host's own order: the base\n * file this CLI writes first, then any overlay that beats it.\n *\n * Composed from `CLAUDE_DIR` until 2026-09-22, which is why a Codex install read a `.claude/`\n * directory it had never written. {@link permissionFiles} is the one answer now, and this is its\n * first production reader.\n */\nfunction settingsFilesFor(projectRoot: string): string[] {\n return permissionFiles(providerInUse(projectRoot), \"project\", projectRoot);\n}\n\n/** Permissions from the highest-precedence settings file that defines them — the overlay wins. */\nasync function loadPermissions(files: readonly string[]): Promise<PermissionConfig | undefined> {\n for (const filePath of [...files].reverse()) {\n const permissions = await readSettingsPermissions(filePath);\n if (permissions) return permissions;\n }\n return undefined;\n}\n\n/**\n * One settings file as the notice names it: relative to the project, POSIX-separated.\n *\n * POSIX for `pathUnder`'s reason one module over — what comes back is a NAME shown to a person,\n * not a path this process opens.\n */\nfunction asUserWritesIt(projectRoot: string, file: string): string {\n return path.relative(projectRoot, file).split(path.sep).join(\"/\");\n}\n\n/**\n * The notice a run ends on when nothing has granted the agents any permissions — or `null` where\n * there is nothing true to say.\n *\n * **`null` for a host whose permission model this release cannot advise on**, which is\n * {@link advisesOnPermissions}'s whole subject. Every word below is Claude Code's: a\n * `permissions.allow` block, in JSON, in `settings.json`. Codex has neither the key nor the file,\n * and a Codex install printed this verbatim until 2026-09-22 — ending a run that had touched no\n * `.claude/` directory by telling the user to go and edit one.\n *\n * The file is NAMED from the layout rather than spelled, so the notice points at the file it\n * actually read.\n */\nexport async function checkPermissions(projectRoot: string): Promise<React.ReactElement | null> {\n if (!advisesOnPermissions(providerInUse(projectRoot))) return null;\n\n const files = settingsFilesFor(projectRoot);\n // The BASE file rather than the overlay: it is the one this CLI writes, and the one a user with\n // neither is being asked to create. A host with a notice to print has a file to name it in, so\n // an empty roster is a host that has nothing to say rather than a case to paper over.\n const [base] = files;\n if (base === undefined) return null;\n\n const permissions = await loadPermissions(files);\n const settingsFile = asUserWritesIt(projectRoot, base);\n\n if (!permissions) {\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor={CLI_COLORS.WARNING} padding={1}>\n <Text bold color={CLI_COLORS.WARNING}>\n Permission Notice\n </Text>\n <Text>No permissions configured in {settingsFile}</Text>\n <Text>Agents will prompt for approval on each tool use.</Text>\n <Text> </Text>\n <Text>For autonomous operation, add to {settingsFile}:</Text>\n <Text> </Text>\n <Text color={CLI_COLORS.DIM}>{\"{\"}</Text>\n <Text color={CLI_COLORS.DIM}>{' \"permissions\": {'}</Text>\n <Text color={CLI_COLORS.DIM}>{' \"allow\": ['}</Text>\n <Text color={CLI_COLORS.DIM}>{' \"Read(*)\",'}</Text>\n <Text color={CLI_COLORS.DIM}>{' \"Bash(git *)\",'}</Text>\n <Text color={CLI_COLORS.DIM}>{' \"Bash(bun *)\"'}</Text>\n <Text color={CLI_COLORS.DIM}>{\" ]\"}</Text>\n <Text color={CLI_COLORS.DIM}>{\" }\"}</Text>\n <Text color={CLI_COLORS.DIM}>{\"}\"}</Text>\n </Box>\n );\n }\n\n const hasRestrictiveBash = permissions.deny?.some(\n (rule) => rule === \"Bash(*)\" || rule === \"Bash\",\n );\n const hasNoAllows = !permissions.allow || permissions.allow.length === 0;\n\n if (hasRestrictiveBash || hasNoAllows) {\n return (\n <Box flexDirection=\"column\" borderStyle=\"round\" borderColor={CLI_COLORS.WARNING} padding={1}>\n <Text bold color={CLI_COLORS.WARNING}>\n Permission Warnings\n </Text>\n {hasRestrictiveBash && (\n <Text>\n ⚠ Bash is denied in permissions. Some agents require Bash for git, testing, and build\n commands.\n </Text>\n )}\n {hasNoAllows && (\n <Text>⚠ No allow rules configured. Agents will prompt for each tool use.</Text>\n )}\n </Box>\n );\n }\n\n return null;\n}\n"],"mappings":"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","names":["Box","Text","jsx","jsxs","Text","Box"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/lib/operations/source/load-source.ts","../src/cli/lib/operations/project/detect-project.ts","../src/cli/lib/operations/project/detect-both-installations.ts","../src/cli/lib/config-gate/index.ts","../src/cli/lib/config-gate/classify.ts","../src/cli/lib/config-gate/deps.ts","../src/cli/lib/config-gate/pair-writer.ts","../src/cli/lib/config-gate/propagate.ts","../../compile/src/global-config.ts","../src/cli/lib/config-gate/recompile.ts","../src/cli/lib/operations/project/write-project-config.ts","../src/cli/lib/operations/project/compile-agents-all-scopes.ts"],"sourcesContent":["import { loadSkillsMatrixFromSource, type SourceLoadResult } from \"../../loading/index.js\";\nimport type { SourceCaller } from \"../../configuration/index.js\";\nimport {\n enableBuffering,\n drainBuffer,\n disableBuffering,\n warn,\n type StartupMessage,\n} from \"../../../utils/logger.js\";\n\nexport type LoadSourceOptions = {\n /** Whether this load may reach the init-time source rungs — see {@link SourceCaller}. */\n caller?: SourceCaller;\n sourceFlag?: string;\n projectDir: string;\n /** When true, enables message buffering and captures startup messages. Default: false. */\n captureStartupMessages?: boolean;\n};\n\nexport type LoadedSource = {\n sourceResult: SourceLoadResult;\n /** Empty array when captureStartupMessages is false. */\n startupMessages: StartupMessage[];\n};\n\n/**\n * Loads the skills matrix from a resolved source.\n *\n * When `captureStartupMessages` is true, wraps the load in buffer mode so\n * warn() calls during loading are captured instead of written to stderr.\n * The caller (init/edit) hands them to the wizard, which paints them as a band\n * above the step — stderr does not survive the wizard clearing the terminal.\n *\n * @throws {Error} If source resolution or fetching fails.\n */\nexport async function loadSource(options: LoadSourceOptions): Promise<LoadedSource> {\n const { caller, sourceFlag, projectDir, captureStartupMessages } = options;\n\n if (captureStartupMessages) {\n enableBuffering();\n }\n\n let sourceResult: SourceLoadResult;\n try {\n sourceResult = await loadSkillsMatrixFromSource({\n ...(caller !== undefined && { caller }),\n ...(sourceFlag !== undefined && { sourceFlag }),\n projectDir,\n });\n } catch (error) {\n if (captureStartupMessages) {\n disableBuffering();\n }\n throw error;\n }\n\n let startupMessages: StartupMessage[] = [];\n if (captureStartupMessages) {\n startupMessages = drainBuffer();\n disableBuffering();\n }\n\n return { sourceResult, startupMessages };\n}\n\n/**\n * Says what a captured load held back, for a run that mounts no wizard to paint it.\n *\n * Capture exists for the wizard alone: its first repaint wipes stderr, so `warn()` is held and\n * painted as the startup band instead. A `--from` run loads through the same capture and mounts no\n * wizard, so what was held went nowhere — a skill skipped for a file that will not parse, two\n * folders claiming one id, a marketplace served from a stale cache. They are said here in the\n * words `compile` prints them in. Only the warnings: an `info` line is the band's own narration.\n */\nexport function sayCapturedWarnings(messages: readonly StartupMessage[]): void {\n for (const { level, text } of messages) {\n if (level === \"warn\") warn(text);\n }\n}\n","import { detectInstallation, type Installation } from \"../../installation/index.js\";\nimport { ConfigLoadError, loadProjectConfig } from \"../../configuration/index.js\";\nimport type { ProjectConfig } from \"../../../types/index.js\";\n\nexport type DetectedProject = {\n installation: Installation;\n config: ProjectConfig | null;\n configPath: string | null;\n};\n\n/**\n * Detects an existing CLI installation and loads its project config.\n *\n * Uses detectInstallation() which checks project-level first, then falls back\n * to global. Returns the installation metadata plus the loaded config.\n *\n * Does NOT throw. Returns null when no installation is found OR when the config\n * file exists but is corrupt — the caller (doctor, edit) reports the resulting\n * null as a config/installation problem to the user. `compile` deliberately\n * does NOT use this wrapper: it detects scopes directly so it can hard-error on\n * a corrupt config instead of degrading to null.\n */\nexport async function detectProject(projectDir?: string): Promise<DetectedProject | null> {\n const resolvedDir = projectDir ?? process.cwd();\n\n let installation: Installation | null;\n try {\n installation = await detectInstallation(resolvedDir);\n } catch (error) {\n if (error instanceof ConfigLoadError) return null;\n throw error;\n }\n\n if (!installation) {\n return null;\n }\n\n const loaded = await loadProjectConfig(installation.projectDir);\n\n return {\n installation,\n config: loaded?.config ?? null,\n configPath: loaded?.configPath ?? null,\n };\n}\n","import {\n detectGlobalInstallation,\n detectProjectInstallation,\n isHomeDirectory,\n type Installation,\n} from \"../../installation/index.js\";\n\nexport type BothInstallations = {\n global: Installation | null;\n project: Installation | null;\n hasBoth: boolean;\n};\n\n/**\n * Detects both global and project installations.\n *\n * Skips project detection when projectDir is the home directory\n * to avoid double-compile. Returns a convenience `hasBoth` flag\n * used by callers to set scopeFilter on compile passes.\n */\nexport async function detectBothInstallations(projectDir: string): Promise<BothInstallations> {\n const global = await detectGlobalInstallation();\n\n const project = isHomeDirectory(projectDir) ? null : await detectProjectInstallation(projectDir);\n return { global, project, hasBoth: !!global && !!project };\n}\n","import os from \"os\";\nimport path from \"path\";\nimport type { ProjectConfig, SkillId } from \"../../types\";\nimport type { SkillConfig } from \"../../types/config\";\nimport type { AuthoritativeScope } from \"../configuration/config-merger\";\nimport { CLI_INVOKE_COMMAND } from \"../../consts\";\nimport { ensureDir, writeFile } from \"../../utils/fs\";\nimport { verbose } from \"../../utils/logger\";\nimport { isHomeDirectory } from \"../installation/is-home-directory\";\nimport type { Provider } from \"../../consts\";\nimport { getInstalledConfigPath, getProjectConfigPath } from \"../installation/install-base-dir\";\nimport { providerInUse } from \"../installation/install-layout\";\nimport { loadProjectConfigFromDir } from \"../configuration/project-config\";\nimport { normalizeStackRecord } from \"../stacks/stacks-loader\";\nimport { isActiveAt } from \"../configuration/scope-predicates\";\nimport { splitConfigByScope } from \"../configuration/config-generator\";\n// The catalogue the renderers take as a parameter. It is the singleton this CLI seats at\n// startup, which is what `generateConfigSource` read directly before the renderers moved into\n// `@workspace/compile` and the editor became a second caller with a catalogue of its own.\nimport { matrix as activeMatrix } from \"../matrix/matrix-provider\";\nimport { generateConfigSource } from \"../configuration/config-writer\";\nimport {\n buildConfigTypesBackgroundData,\n regenerateConfigTypes,\n type ConfigTypesExtras,\n} from \"../configuration/config-types-writer\";\nimport {\n classifyGlobalChange,\n consequenceTier,\n tierPropagates,\n tierRegeneratesTypes,\n NO_CHANGES,\n type GlobalChangeSet,\n} from \"./classify.js\";\nimport { resolveGateDeps, type GateDeps, type LoadedGateDeps } from \"./deps.js\";\nimport { GlobalPairWriteViolation, withGateToken } from \"./gate-token.js\";\nimport {\n ensureBlankPair as ensureBlankPairInternal,\n writeGlobalConfigHalf,\n writeGlobalPair,\n writeGlobalTypesHalf,\n} from \"./pair-writer.js\";\nimport {\n buildProjectTypesExtras,\n mergeGlobalConfigs,\n normalizeProjectPath,\n propagateGlobalChangesToProjects,\n pruneGlobalEntriesFromRegisteredProjects,\n reconcileProjectSplitAgainstGlobal,\n resolveEffectiveGlobalConfig,\n writeProjectConfigPair,\n type PropagationResult,\n} from \"./propagate.js\";\nimport { NOTHING_RECOMPILED, recompilePropagated } from \"./recompile.js\";\nimport type { PropagatedRecompileSummary } from \"../operations/project/recompile-project-agents.js\";\n\nexport type { GateDeps } from \"./deps.js\";\nexport type { ConsequenceTier, GlobalChangeSet } from \"./classify.js\";\nexport { GlobalPairWriteViolation } from \"./gate-token.js\";\n\n/**\n * What one gated write did, for the caller to render.\n *\n * A write that propagates recompiles the propagated projects' agents itself:\n * both audited gaps in the previous contract (a project-context source\n * migration, a global uninstall) were instances of a caller forgetting to. A\n * report the caller may only log cannot go stale the same way, and per-project\n * failure isolation already lives in `recompilePropagatedProjectAgents`, so\n * internalizing the recompile adds no new failure mode.\n */\nexport type GateReport = {\n /** True when either half of the global pair was actually rewritten. */\n globalWritten: boolean;\n changes: GlobalChangeSet;\n propagated: PropagationResult;\n recompile: PropagatedRecompileSummary;\n};\n\n/**\n * The gate's public entry points are the ONLY code that mints the pair-write\n * privilege, and each one holds it across its whole consequence flow rather than\n * around the individual write.\n *\n * `pair-writer.ts` used to open the token inside each of its own functions, which\n * meant reaching that module by any route — a dynamic import, a re-export — came\n * with the privilege attached, and the runtime tripwire in `utils/fs.ts` had\n * nothing left to refuse. Moving the mint here inverts that: the private writers\n * REQUIRE a token they cannot produce, so authorization is a property of how the\n * write was ENTERED, which is exactly what the gate is meant to guarantee.\n *\n * The window is wider than the write itself — propagation and the recompile run\n * inside it too. That is accepted: everything in the window is the gate's own\n * code carrying out consequences the same entry already owes.\n *\n * Every entry below that writes or drives a write opens the token with\n * `withGateToken`, including `propagateGlobalRemoval`, which today reaches only\n * project pairs — the rule is \"a gate entry holds the privilege for its flow\",\n * not \"the ones whose current implementation happens to need it\".\n *\n * The one exception is `writeProjectPartial`. It refuses the home directory as its\n * first act and then writes a PROJECT's own config, which the tripwire never\n * guards; handing it the global-pair privilege would only blunt the refusal it\n * exists to make.\n */\n\n/** A write whose classification obliged nothing beyond the global pair. */\nconst NOTHING_PROPAGATED: PropagationResult = {\n updated: [],\n unreadable: [],\n gone: [],\n notOurs: [],\n failed: [],\n};\n\n/**\n * Whether a gated write moved anything the global install's compiled sub-agents are built from —\n * the question a caller asks before deciding its global compile pass has work to do.\n */\nexport function movedGlobalAgentInputs(written: GateReport): boolean {\n return tierRegeneratesTypes(consequenceTier(written.changes));\n}\n\nfunction report(\n globalWritten: boolean,\n changes: GlobalChangeSet,\n propagated: PropagationResult = NOTHING_PROPAGATED,\n recompile: PropagatedRecompileSummary = NOTHING_RECOMPILED,\n): GateReport {\n return { globalWritten, changes, propagated, recompile };\n}\n\n/**\n * Carries out a classified global change's consequences: fan the change out to\n * every registered project, then recompile the ones it rewrote.\n *\n * `currentProjectDir` is excluded from the fan-out — its own pair is written by\n * the call that triggered this, and its agents are compiled by the command.\n */\nasync function applyConsequences(\n globalConfig: ProjectConfig,\n changes: GlobalChangeSet,\n deps: LoadedGateDeps,\n provider: Provider,\n currentProjectDir?: string,\n): Promise<{ propagated: PropagationResult; recompile: PropagatedRecompileSummary }> {\n const tier = consequenceTier(changes);\n if (!tierPropagates(tier) || !globalConfig.projects?.length) {\n return { propagated: NOTHING_PROPAGATED, recompile: NOTHING_RECOMPILED };\n }\n\n const propagated = await propagateGlobalChangesToProjects(\n globalConfig,\n deps.agents,\n provider,\n currentProjectDir,\n { regenerateTypes: tierRegeneratesTypes(tier) },\n );\n if (propagated.updated.length > 0) {\n verbose(`Propagated global changes to ${propagated.updated.length} project(s)`);\n }\n\n const recompile = tierRegeneratesTypes(tier)\n ? await recompilePropagated(propagated.updated)\n : NOTHING_RECOMPILED;\n\n return { propagated, recompile };\n}\n\n/**\n * Loaders for a caller that holds no matrix of its own. Nothing is fetched\n * unless classification proves the write has consequences that read it, so a\n * registration-only mutation stays offline.\n *\n * `matrixOnly` + `skipExtraSources` are the offline policy the other config\n * readers already use: extra-source loading only annotates wizard UI tagging and\n * never changes the emitted config or types.\n */\nexport function lazyGateDeps(projectDir: string): GateDeps {\n return {\n loadMatrix: async () => {\n const { loadSkillsMatrixFromSource } = await import(\"../loading/source-loader.js\");\n const { matrix } = await loadSkillsMatrixFromSource({\n projectDir,\n skipExtraSources: true,\n matrixOnly: true,\n });\n return matrix;\n },\n loadAgents: async () => {\n const { loadAgentDefs } = await import(\"../operations/project/load-agent-defs.js\");\n return (await loadAgentDefs()).agents;\n },\n };\n}\n\nexport type WizardWriteArgs = {\n finalConfig: ProjectConfig;\n matrix: LoadedGateDeps[\"matrix\"];\n agents: LoadedGateDeps[\"agents\"];\n projectDir: string;\n projectConfigPath: string;\n projectInstallationExists: boolean;\n /**\n * How much of what it can see this session owns, from `mergeConfigs`' own vocabulary.\n *\n * Read by the PROJECT branch only, and only to decide whether the global config is made to\n * MATCH this session or merely absorb it. The home branch writes the whole global config from\n * `finalConfig` either way, and `undefined` (init) keeps the additive default.\n */\n authoritativeScope?: AuthoritativeScope;\n};\n\n/**\n * Writes config.ts and config-types.ts split by scope.\n * When installing into a project directory:\n * - Global config/types go to the home scope's source folder\n * - Project config/types go to the project's own (with import from global)\n * When installing from home directory, writes a single standalone config.\n */\nexport async function writeScopedFromWizard(args: WizardWriteArgs): Promise<GateReport> {\n return withGateToken(async () => {\n const { finalConfig, matrix, agents, projectDir, projectConfigPath } = args;\n // os.homedir() at call time, so the path agrees with getGlobalConfigImportPath(),\n // which reaches the same home through globalInstallRoot()\n const homeDir = os.homedir();\n const deps: LoadedGateDeps = { matrix, agents };\n\n if (!isHomeDirectory(projectDir)) {\n return writeFromProjectContext(args, homeDir, deps);\n }\n\n // Installing from ~/ — write directly to global config (no import preamble). The provider\n // is the one the home root's own installation is on: at this scope the project IS the\n // global installation.\n const provider = providerInUse(homeDir);\n const priorGlobal = await loadProjectConfigFromDir(homeDir, provider);\n const changes = classifyGlobalChange(priorGlobal?.config, finalConfig);\n const globalWritten = await writeGlobalPair(finalConfig, projectConfigPath, matrix, agents);\n\n const { propagated, recompile } = await applyConsequences(finalConfig, changes, deps, provider);\n return report(globalWritten, changes, propagated, recompile);\n });\n}\n\nasync function writeFromProjectContext(\n args: WizardWriteArgs,\n homeDir: string,\n deps: LoadedGateDeps,\n): Promise<GateReport> {\n const { finalConfig, projectDir } = args;\n\n // Installing from project — split by scope for project config generation.\n const { global: globalConfig, project: projectSplitConfig } = splitConfigByScope(finalConfig);\n // The PROJECT's provider decides which global installation this run inherits from and writes\n // to: each provider family inherits and propagates only within itself.\n const provider = providerInUse(projectDir);\n const globalConfigPath = getProjectConfigPath(homeDir, provider);\n\n // Merge new global-scoped items into the existing global config.\n // - Existing items are preserved (never removed from global during project init)\n // - New global items are added\n // - If no existing global config, write the full global split\n const existingGlobal = await loadProjectConfigFromDir(homeDir, provider);\n const effective = await resolveEffectiveGlobalConfig(\n globalConfig,\n existingGlobal?.config,\n projectDir,\n args.authoritativeScope,\n );\n const effectiveGlobalConfig = effective.config;\n\n const changes = classifyGlobalChange(existingGlobal?.config, effectiveGlobalConfig);\n const globalWritten = await writeGlobalPairWhenChanged(effective, globalConfigPath, deps);\n\n const { propagated, recompile } = await applyConsequences(\n effectiveGlobalConfig,\n changes,\n deps,\n provider,\n projectDir,\n );\n\n // Reconcile the project's own entries against the global config this write inlines.\n // Without it this branch hands the raw split straight to the inlining writer, so a\n // skill/agent the project owns at project scope AND a colliding live global install\n // both land as active entries in the same project config.\n const reconciledProjectConfig = reconcileProjectSplitAgainstGlobal(\n projectSplitConfig,\n effectiveGlobalConfig,\n deps.matrix,\n );\n await writeProjectPairWhenOwed(\n args,\n provider,\n reconciledProjectConfig,\n effectiveGlobalConfig,\n deps,\n );\n\n return report(globalWritten, changes, propagated, recompile);\n}\n\n/** Writes the resolved global pair when resolving it changed anything; answers whether it did. */\nasync function writeGlobalPairWhenChanged(\n effective: { config: ProjectConfig; changed: boolean },\n globalConfigPath: string,\n deps: LoadedGateDeps,\n): Promise<boolean> {\n if (!effective.changed) {\n verbose(\"Global config unchanged, skipping write\");\n return false;\n }\n\n await ensureDir(path.dirname(globalConfigPath));\n const written = await writeGlobalPair(\n effective.config,\n globalConfigPath,\n deps.matrix,\n deps.agents,\n );\n verbose(`Updated global config at ${globalConfigPath}`);\n return written;\n}\n\n/**\n * Writes the project's own pair if the project installation already exists OR if there is\n * anything of the project's own to record. Skips only when no existing project installation AND\n * nothing project-owned — creating an empty project config with just `import globalConfig` and\n * `{ ...globalConfig }` is pointless.\n */\nasync function writeProjectPairWhenOwed(\n project: Pick<WizardWriteArgs, \"projectDir\" | \"projectConfigPath\" | \"projectInstallationExists\">,\n provider: Provider,\n reconciledProjectConfig: ProjectConfig,\n effectiveGlobalConfig: ProjectConfig,\n deps: LoadedGateDeps,\n): Promise<void> {\n if (!project.projectInstallationExists && !holdsAnythingOfItsOwn(reconciledProjectConfig)) {\n verbose(\n \"Skipped project config — no existing project installation and no project-scoped items\",\n );\n return;\n }\n\n await ensureDir(path.dirname(project.projectConfigPath));\n await writeProjectConfigPair(\n project.projectDir,\n provider,\n reconciledProjectConfig,\n effectiveGlobalConfig,\n deps.matrix,\n deps.agents,\n );\n verbose(`Updated project config at ${project.projectConfigPath}`);\n}\n\n/** Whether the project's split carries anything of the project's own to record. */\nfunction holdsAnythingOfItsOwn(projectConfig: ProjectConfig): boolean {\n return projectConfig.skills.length > 0 || projectConfig.agents.length > 0;\n}\n\n/**\n * Regenerates a single scope's config-types.ts from its persisted config,\n * matching the wizard write path's writer selection exactly:\n * - global scope (home dir): standalone unions narrowed to the config's entries\n * - project scope: import-and-extend form (falls back to standalone when no\n * global config-types.ts exists)\n *\n * Writes no config half at either scope: the config on disk is the input.\n * Returns whether a file was actually rewritten (always false at project scope,\n * where the writer does not report).\n */\nexport async function writeScopeConfigTypes(\n projectDir: string,\n config: ProjectConfig,\n deps: LoadedGateDeps,\n extras?: ConfigTypesExtras,\n): Promise<boolean> {\n return withGateToken(async () => {\n if (isHomeDirectory(projectDir)) {\n return writeGlobalTypesHalf(\n config,\n getInstalledConfigPath(projectDir),\n deps.matrix,\n deps.agents,\n extras,\n );\n }\n\n await regenerateConfigTypes(\n projectDir,\n Promise.resolve(buildConfigTypesBackgroundData(deps.matrix, deps.agents)),\n extras ?? buildProjectTypesExtras(config, deps.matrix),\n );\n return false;\n });\n}\n\n/**\n * Regenerates a scope's config-types.ts from the config already on disk, which\n * is the truth and is never rewritten — the documented hand-edit workflow is\n * \"edit config.ts, then compile\", so the file the user edited must survive it.\n *\n * At the home directory the types are the standalone unions narrowed to that\n * config, and the fan-out is UNCONDITIONAL: a hand edit leaves no prior state to\n * diff against, so there is nothing to classify and the only safe assumption is\n * that every registered project's inlined copy is stale. At project and\n * marketplace directories the import-and-extend writer applies and nothing is\n * propagated — a project config is nobody else's input.\n */\nexport async function reconcileTypesFromDisk(\n projectDir: string,\n config: ProjectConfig,\n deps: LoadedGateDeps,\n options?: { extras?: ConfigTypesExtras; currentProjectDir?: string },\n): Promise<GateReport> {\n return withGateToken(async () => {\n const changes = classifyGlobalChange(undefined, config);\n const globalWritten = await writeScopeConfigTypes(projectDir, config, deps, options?.extras);\n\n if (!isHomeDirectory(projectDir)) return report(false, changes);\n if (!config.projects?.length) return report(globalWritten, changes);\n\n const propagated = await propagateGlobalChangesToProjects(\n config,\n deps.agents,\n providerInUse(projectDir),\n options?.currentProjectDir,\n );\n if (propagated.updated.length > 0) {\n verbose(`Propagated global changes to ${propagated.updated.length} project(s)`);\n }\n const recompile = await recompilePropagated(propagated.updated);\n\n return report(globalWritten, changes, propagated, recompile);\n });\n}\n\n/**\n * The transforms the gate will apply to the global config on a caller's behalf.\n *\n * A closed union rather than a caller-supplied function: every variant is\n * analysable and testable on its own, and no caller-authored transform runs\n * inside the only code allowed to write the pair.\n */\nexport type GlobalMutation =\n | { kind: \"migrate-skill-sources\"; sources: ReadonlyMap<SkillId, string> }\n | { kind: \"deregister-project\"; projectDir: string }\n | { kind: \"set-source\"; source: string; fallbackName: string };\n\n/** The migrated `origin` for an active-global entry, or the entry unchanged. */\nfunction withMigratedSource(\n skill: SkillConfig,\n migratedSources: ReadonlyMap<SkillId, string>,\n): SkillConfig {\n if (!isActiveAt(skill, \"global\")) return skill;\n const origin = migratedSources.get(skill.id);\n if (origin === undefined || origin === skill.origin) return skill;\n return { ...skill, origin };\n}\n\n/**\n * Rewrites `origin` on exactly the active-global entries listed in `migratedSources`,\n * returning every other entry — including global entries this session did not migrate —\n * identical by reference.\n */\nexport function applyMigratedGlobalSources(\n globalSkills: SkillConfig[],\n migratedSources: ReadonlyMap<SkillId, string>,\n): { skills: SkillConfig[]; changed: boolean } {\n const skills = globalSkills.map((skill) => withMigratedSource(skill, migratedSources));\n return { skills, changed: skills.some((skill, index) => skill !== globalSkills[index]) };\n}\n\n/**\n * The config a partial write commits: the scalars and arrays already on disk,\n * with the required fields filled. Mirrors what the partial writer has always\n * defaulted, so a scalar mutation cannot silently empty an install.\n */\nfunction fillRequiredFields(\n partial: Partial<ProjectConfig>,\n fallbackName: string | undefined,\n): ProjectConfig {\n const name = partial.name ?? fallbackName;\n if (!name) {\n throw new Error(\n `Cannot write config: no project config found. Run \\`${CLI_INVOKE_COMMAND} init\\` first.`,\n );\n }\n return { ...partial, name, skills: partial.skills ?? [], agents: partial.agents ?? [] };\n}\n\n/** Applies a mutation to the config on disk, or null when it is a no-op. */\nfunction applyMutation(\n mutation: GlobalMutation,\n current: Partial<ProjectConfig>,\n): Partial<ProjectConfig> | null {\n switch (mutation.kind) {\n case \"migrate-skill-sources\": {\n const { skills, changed } = applyMigratedGlobalSources(\n current.skills ?? [],\n mutation.sources,\n );\n return changed ? { ...current, skills } : null;\n }\n case \"deregister-project\": {\n const projects = current.projects ?? [];\n if (projects.length === 0) return null;\n const normalized = normalizeProjectPath(mutation.projectDir);\n const filtered = projects.filter((p) => p !== normalized);\n return filtered.length === projects.length ? null : { ...current, projects: filtered };\n }\n case \"set-source\":\n return { ...current, marketplace: mutation.source };\n default: {\n const _exhaustive: never = mutation;\n return _exhaustive;\n }\n }\n}\n\n/** The name a mutation invents when the global config has none yet. */\nfunction fallbackNameFor(mutation: GlobalMutation): string | undefined {\n return mutation.kind === \"set-source\" ? mutation.fallbackName : undefined;\n}\n\n/**\n * Applies a typed transform to the global config and carries out whatever its\n * classification obliges. No variant is exempt from classification by\n * construction: a source change propagates because project configs inline the\n * global scalars, and a deregistration does not because nothing inlines the\n * registration list.\n */\nexport async function mutateGlobal(mutation: GlobalMutation, deps: GateDeps): Promise<GateReport> {\n return withGateToken(async () => {\n const homeDir = os.homedir();\n // The full loader, not the scalar one: it normalizes every stack assignment\n // back to `SkillAssignment[]`. The writer compacts an exclusive category to its\n // bare value on the way out, and re-emitting that bare form without\n // normalization drops the category — so a scalar mutation would silently strip\n // every exclusive category from the global stack.\n const provider = providerInUse(homeDir);\n const loadedGlobal = await loadProjectConfigFromDir(homeDir, provider);\n if (!loadedGlobal) return report(false, NO_CHANGES);\n\n const current = loadedGlobal.config;\n const mutated = applyMutation(mutation, current);\n if (!mutated) return report(false, NO_CHANGES);\n\n const prior = fillRequiredFields(current, fallbackNameFor(mutation));\n const next = fillRequiredFields(mutated, fallbackNameFor(mutation));\n const changes = classifyGlobalChange(prior, next);\n\n const globalWritten = await writeGlobalConfigHalf(\n next,\n getProjectConfigPath(homeDir, provider),\n );\n if (!next.projects?.length) return report(globalWritten, changes);\n\n const loaded = await resolveGateDeps(deps, consequenceTier(changes));\n if (!loaded) return report(globalWritten, changes);\n\n const { propagated, recompile } = await applyConsequences(next, changes, loaded, provider);\n return report(globalWritten, changes, propagated, recompile);\n });\n}\n\n/**\n * Prunes the CLI-inlined global entries from every registered project after a\n * GLOBAL uninstall, and recompiles their agents.\n *\n * Writes no pair: the pair this would derive from has just been deleted. That is\n * the reason this is its own entry rather than a flag on a writing one — the\n * removal case must not be reachable from a code path whose job is to write.\n *\n * Call AFTER the global manifest has been removed so the regenerated project\n * types fall back to the standalone form instead of importing from the\n * now-deleted global config-types.ts.\n *\n * `provider` is the installation that was removed, and it is a parameter rather than a read:\n * the folder that would answer it has just been deleted, so by the time this runs the disk no\n * longer records which installation the pruning belongs to.\n */\nexport async function propagateGlobalRemoval(\n preRemovalGlobalConfig: ProjectConfig,\n deps: LoadedGateDeps,\n provider: Provider,\n): Promise<GateReport> {\n return withGateToken(async () => {\n const changes = classifyGlobalChange(preRemovalGlobalConfig, {\n ...preRemovalGlobalConfig,\n skills: [],\n agents: [],\n });\n\n const propagated = await pruneGlobalEntriesFromRegisteredProjects(\n preRemovalGlobalConfig,\n deps.agents,\n provider,\n );\n const recompile = await recompilePropagated(propagated.updated);\n\n return report(false, changes, propagated, recompile);\n });\n}\n\n/**\n * Creates the blank global pair when none exists. No classification and no\n * fan-out: the registration list propagation reads lives in the file that was\n * just found absent, so a pair created here has no registered projects.\n */\nexport async function ensureBlankPair(provider: Provider): Promise<boolean> {\n return withGateToken(() => ensureBlankPairInternal(provider));\n}\n\nexport type WriteProjectPartialOptions = {\n /** Name to invent when the partial has none. Absent → a missing name is an error. */\n fallbackName?: string;\n};\n\n/**\n * The partial with every stack assignment normalized back to `SkillAssignment[]`.\n *\n * Every caller of this entry reads the config with the LENIENT loader\n * (`loadProjectSourceConfig`), overlays one scalar, and hands the result back.\n * That loader passes the on-disk stack through untouched, and the writer emits an\n * exclusive category in its BARE form (`\"web-framework\": \"web-framework-react\"`,\n * no array) — so on a load / re-emit round trip `compactCategories`, which keeps\n * only non-empty arrays, drops the category and the user silently loses an\n * assignment they never touched.\n *\n * Normalizing at this boundary rather than inside the writer keeps the fix on the\n * one path that can present a denormalized stack: the full loader\n * (`loadProjectConfigFromDir`) already runs `normalizeStackRecord`, so every other\n * writer input arrives normalized.\n */\nfunction withNormalizedStack(partial: Partial<ProjectConfig>): Partial<ProjectConfig> {\n if (!partial.stack) return partial;\n return { ...partial, stack: normalizeStackRecord(partial.stack) };\n}\n\n/**\n * Writes a partial config to a PROJECT's own `config.ts`, filling\n * required defaults. Refuses the home directory: the global pair's config half\n * may never be written without its types sibling and without classification.\n */\nexport async function writeProjectPartial(\n projectDir: string,\n partial: Partial<ProjectConfig>,\n options: WriteProjectPartialOptions = {},\n): Promise<void> {\n if (isHomeDirectory(projectDir)) {\n throw new GlobalPairWriteViolation(getInstalledConfigPath(projectDir));\n }\n\n const configPath = getInstalledConfigPath(projectDir);\n await ensureDir(path.dirname(configPath));\n await writeFile(\n configPath,\n generateConfigSource(\n fillRequiredFields(withNormalizedStack(partial), options.fallbackName),\n activeMatrix,\n ),\n );\n}\n\n/**\n * `normalizeProjectPath` is the single rule for any value compared against the\n * global config's `projects` array, so a reader that only wants to MATCH a\n * registration (never write one) gets it from here rather than reimplementing\n * `fs.realpathSync` and drifting from the rule the registrations were stored\n * under. It writes nothing, so exporting it hands out no privilege.\n */\nexport { mergeGlobalConfigs, normalizeProjectPath };\n","import { isDeepEqual } from \"remeda\";\nimport type { AgentName, ProjectConfig, SkillId } from \"../../types\";\nimport type { AgentScopeConfig, SkillConfig } from \"../../types/config\";\n\n/**\n * The work one global config write owes, decided from what actually moved\n * between the config on disk and the config being written.\n *\n * T1 — the generated type unions or the compiled agents are derived from what\n * changed: rewrite both halves of the pair, propagate to every registered\n * project, recompile those projects' agents.\n * T2 — a scalar the unions do not encode changed. Project configs inline the\n * global scalars verbatim, so the config half propagates; no union moves,\n * so no types are regenerated and no agent is recompiled.\n * T3 — only the `projects[]` registration list changed. Nothing is propagated,\n * and the matrix / agent definitions propagation would need are never\n * loaded.\n * T4 — nothing moved. No file is touched.\n */\nexport type ConsequenceTier = \"T1\" | \"T2\" | \"T3\" | \"T4\";\n\nexport type GlobalChangeSet = {\n skills: {\n added: SkillId[];\n removed: SkillId[];\n /**\n * Ids whose `origin` moved — the field's name on `SkillConfig`; this one keeps the\n * older word. Separate from `otherChanged` because the per-skill value decides the\n * reference form a compiled agent emits\n * (`<id>:<id>` for a marketplace-sourced skill, the bare id for an ejected\n * one), so a source change that skipped the recompile leaves every\n * registered project's agents naming a reference that no longer resolves.\n */\n sourceChanged: SkillId[];\n otherChanged: SkillId[];\n };\n agents: {\n added: AgentName[];\n removed: AgentName[];\n changed: AgentName[];\n };\n stackChanged: boolean;\n selectedDomainsChanged: boolean;\n /** Names of the inlined scalar fields that moved, in config key order. */\n scalarsChanged: string[];\n projectsChanged: boolean;\n};\n\n/** The identity element of {@link GlobalChangeSet} — nothing moved, so T4. */\nexport const NO_CHANGES: GlobalChangeSet = {\n skills: { added: [], removed: [], sourceChanged: [], otherChanged: [] },\n agents: { added: [], removed: [], changed: [] },\n stackChanged: false,\n selectedDomainsChanged: false,\n scalarsChanged: [],\n projectsChanged: false,\n};\n\n/** The four fields the config writer extracts into typed named variables. */\nconst EXTRACTED_FIELDS = new Set([\"skills\", \"agents\", \"stack\", \"selectedDomains\"]);\n\n/** The registration list — bookkeeping, never emitted into a project config. */\nconst REGISTRATION_FIELD = \"projects\";\n\n/**\n * Normalizes a config the way the writer does immediately before emission: a\n * JSON round-trip, so an explicitly-`undefined` field and an absent one compare\n * equal. Without it every write that merely reconstructs an object produces\n * phantom diffs.\n */\nfunction normalize(config: ProjectConfig): Record<string, unknown> {\n // Boundary cast: `JSON.parse` is typed `any`, and round-tripping an object\n // always yields one — the whole point of the call is that the result is the\n // same record with its undefined-valued keys gone.\n return JSON.parse(JSON.stringify(config)) as Record<string, unknown>;\n}\n\n/** Field value as the writer would emit it, or undefined when absent. */\nfunction field(config: Record<string, unknown>, key: string): unknown {\n return config[key];\n}\n\nfunction skillsById(config: Record<string, unknown>): Map<SkillId, SkillConfig> {\n const skills = (config.skills ?? []) as SkillConfig[];\n return new Map(skills.map((skill) => [skill.id, skill]));\n}\n\nfunction agentsByName(config: Record<string, unknown>): Map<AgentName, AgentScopeConfig> {\n const agents = (config.agents ?? []) as AgentScopeConfig[];\n return new Map(agents.map((agent) => [agent.name, agent]));\n}\n\n/** The scalar keys either side carries, in `next`-first order with `prev` extras appended. */\nfunction scalarKeys(prev: Record<string, unknown>, next: Record<string, unknown>): string[] {\n const isScalarKey = (key: string): boolean =>\n !EXTRACTED_FIELDS.has(key) && key !== REGISTRATION_FIELD;\n const nextKeys = Object.keys(next).filter(isScalarKey);\n const extraPrevKeys = Object.keys(prev).filter(\n (key) => isScalarKey(key) && !nextKeys.includes(key),\n );\n return [...nextKeys, ...extraPrevKeys];\n}\n\nfunction diffSkills(\n prev: Record<string, unknown>,\n next: Record<string, unknown>,\n): GlobalChangeSet[\"skills\"] {\n const before = skillsById(prev);\n const after = skillsById(next);\n\n const added = [...after.keys()].filter((id) => !before.has(id));\n const removed = [...before.keys()].filter((id) => !after.has(id));\n\n const sourceChanged: SkillId[] = [];\n const otherChanged: SkillId[] = [];\n for (const [id, afterEntry] of after) {\n const beforeEntry = before.get(id);\n if (!beforeEntry || isDeepEqual(beforeEntry, afterEntry)) continue;\n if (beforeEntry.origin !== afterEntry.origin) sourceChanged.push(id);\n else otherChanged.push(id);\n }\n\n return { added, removed, sourceChanged, otherChanged };\n}\n\nfunction diffAgents(\n prev: Record<string, unknown>,\n next: Record<string, unknown>,\n): GlobalChangeSet[\"agents\"] {\n const before = agentsByName(prev);\n const after = agentsByName(next);\n\n return {\n added: [...after.keys()].filter((name) => !before.has(name)),\n removed: [...before.keys()].filter((name) => !after.has(name)),\n changed: [...after.keys()].filter((name) => {\n const beforeEntry = before.get(name);\n return beforeEntry !== undefined && !isDeepEqual(beforeEntry, after.get(name));\n }),\n };\n}\n\n/**\n * Diffs the config on disk against the config about to be written. `prev` is\n * undefined when no global config exists yet, which every field reads as a\n * change from absent.\n */\nexport function classifyGlobalChange(\n prev: ProjectConfig | undefined,\n next: ProjectConfig,\n): GlobalChangeSet {\n const before = prev ? normalize(prev) : {};\n const after = normalize(next);\n const moved = (key: string): boolean => !isDeepEqual(field(before, key), field(after, key));\n\n return {\n skills: diffSkills(before, after),\n agents: diffAgents(before, after),\n stackChanged: moved(\"stack\"),\n selectedDomainsChanged: moved(\"selectedDomains\"),\n scalarsChanged: scalarKeys(before, after).filter(moved),\n projectsChanged: moved(REGISTRATION_FIELD),\n };\n}\n\n/** True when something the type unions or the compiled agents derive from moved. */\nfunction movesTypesOrCompiledAgents(changes: GlobalChangeSet): boolean {\n const { skills, agents } = changes;\n return (\n skills.added.length > 0 ||\n skills.removed.length > 0 ||\n skills.sourceChanged.length > 0 ||\n skills.otherChanged.length > 0 ||\n agents.added.length > 0 ||\n agents.removed.length > 0 ||\n agents.changed.length > 0 ||\n changes.stackChanged ||\n changes.selectedDomainsChanged\n );\n}\n\n/** The work tier a change set obliges. */\nexport function consequenceTier(changes: GlobalChangeSet): ConsequenceTier {\n if (movesTypesOrCompiledAgents(changes)) return \"T1\";\n if (changes.scalarsChanged.length > 0) return \"T2\";\n if (changes.projectsChanged) return \"T3\";\n return \"T4\";\n}\n\n/** True when the tier's consequences need the matrix and agent definitions. */\nexport function tierNeedsDeps(tier: ConsequenceTier): boolean {\n return tier === \"T1\" || tier === \"T2\";\n}\n\n/** True when the tier regenerates the types half and recompiles propagated agents. */\nexport function tierRegeneratesTypes(tier: ConsequenceTier): boolean {\n return tier === \"T1\";\n}\n\n/** True when the tier fans the change out to every registered project. */\nexport function tierPropagates(tier: ConsequenceTier): boolean {\n return tier === \"T1\" || tier === \"T2\";\n}\n","import type { AgentDefinition, AgentName, MergedSkillsMatrix } from \"../../types\";\nimport { tierNeedsDeps, tierRegeneratesTypes, type ConsequenceTier } from \"./classify.js\";\n\n/** The matrix and agent definitions propagation and type generation read. */\nexport type LoadedGateDeps = {\n matrix: MergedSkillsMatrix;\n agents: Partial<Record<AgentName, AgentDefinition>>;\n};\n\n/**\n * What a gate entry needs to carry out a write's consequences.\n *\n * The lazy form exists so a caller that cannot cheaply produce the matrix — an\n * offline uninstall, a deregistration — hands over loaders instead. Whether they\n * run is decided by classification: a registration-only or no-op write has no\n * consequences to carry out, so nothing is loaded and the write stays offline.\n */\nexport type GateDeps =\n | LoadedGateDeps\n | {\n loadMatrix: () => Promise<MergedSkillsMatrix>;\n loadAgents: () => Promise<Partial<Record<AgentName, AgentDefinition>>>;\n };\n\nfunction isLoaded(deps: GateDeps): deps is LoadedGateDeps {\n return \"matrix\" in deps;\n}\n\n/**\n * Resolves `deps` only when `tier` has consequences that read them. Returns null\n * otherwise, and the lazy loaders are never called.\n */\nexport async function resolveGateDeps(\n deps: GateDeps,\n tier: ConsequenceTier,\n): Promise<LoadedGateDeps | null> {\n if (!tierNeedsDeps(tier)) return null;\n if (isLoaded(deps)) return deps;\n\n // Agent definitions are read only where the types half is regenerated, so a\n // config-half-only tier never pays for loading them.\n const [matrix, agents] = await Promise.all([\n deps.loadMatrix(),\n tierRegeneratesTypes(tier) ? deps.loadAgents() : NO_AGENTS,\n ]);\n return { matrix, agents };\n}\n\n/** Agent definitions a tier that regenerates no types never reads. */\nconst NO_AGENTS: Promise<Partial<Record<AgentName, AgentDefinition>>> = Promise.resolve({});\n","import os from \"os\";\nimport path from \"path\";\nimport type { AgentDefinition, AgentName, MergedSkillsMatrix, ProjectConfig } from \"../../types\";\nimport type { Provider } from \"../../consts\";\nimport { STANDARD_FILES } from \"../../consts\";\nimport { sourceFolderInUse } from \"../installation/install-layout\";\nimport { ensureDir, fileExists, readFile, writeFile } from \"../../utils/fs\";\nimport { verbose } from \"../../utils/logger\";\nimport {\n generateBlankGlobalConfigSource,\n generateConfigSource,\n} from \"../configuration/config-writer\";\nimport {\n buildConfigTypesBackgroundData,\n generateBlankGlobalConfigTypesSource,\n generateConfigTypesSource,\n type ConfigTypesExtras,\n} from \"../configuration/config-types-writer\";\n// The catalogue the renderers take as a parameter. It is the singleton this CLI seats at\n// startup, which is what `generateConfigSource` read directly before the renderers moved into\n// `@workspace/compile` and the editor became a second caller with a catalogue of its own.\nimport { matrix as activeMatrix } from \"../matrix/matrix-provider\";\nimport { assertGateToken } from \"./gate-token.js\";\n\n/**\n * Nothing in this module MINTS the write privilege — every function below\n * REQUIRES it, and `writeIfChanged` is where that requirement is asserted. The\n * token is opened by the gate's public entry points in `index.ts`, each of which\n * wraps its whole consequence flow.\n *\n * That split is the point (D-309): a callee that opens the privilege on its own\n * behalf hands it to whoever reached it, so a caller that got past module\n * privacy — a dynamic import, a re-export added for convenience — arrived\n * already authorized and the runtime tripwire in `utils/fs.ts` waved it through.\n * Requiring the token instead makes reaching the private writer useless on its\n * own: the privilege belongs to the entry points, not to the writers.\n */\n\n/** Absolute paths of the two halves of the global config pair. */\nexport type GlobalPairPaths = {\n configPath: string;\n typesPath: string;\n};\n\n/**\n * Resolves `provider`'s global pair paths. `os.homedir()` at call time, so they agree\n * with every other home-dir reader and with test home mocks.\n *\n * The provider is the asking run's, because the home root can hold a global installation of\n * each and the pair belongs to exactly one of them.\n */\nexport function globalPairPaths(provider: Provider): GlobalPairPaths {\n const dir = sourceFolderInUse(os.homedir(), provider).dir;\n return {\n configPath: path.join(dir, STANDARD_FILES.CONFIG_TS),\n typesPath: path.join(dir, STANDARD_FILES.CONFIG_TYPES_TS),\n };\n}\n\n/** The types sibling of a config path — the two always share a directory. */\nexport function typesPathFor(configPath: string): string {\n return path.join(path.dirname(configPath), STANDARD_FILES.CONFIG_TYPES_TS);\n}\n\n/**\n * Writes `source` to `filePath` unless the file already holds exactly those\n * bytes. Both halves are derived from the same config on every write, so pair\n * coherence holds by derivation and the comparison only suppresses the mtime\n * churn a projects-only or scalar-only change would otherwise inflict on every\n * consumer watching these files. Returns true when the file was written.\n */\nasync function writeIfChanged(filePath: string, source: string): Promise<boolean> {\n assertGateToken(filePath);\n if ((await fileExists(filePath)) && (await readFile(filePath)) === source) return false;\n await writeFile(filePath, source);\n return true;\n}\n\n/**\n * Standalone (non-importing) type unions narrowed to `config`'s own entries,\n * widened by `extras` — the literals a just-created skill or agent needs before\n * it reaches the config.\n */\nfunction renderStandaloneTypes(\n config: ProjectConfig,\n matrix: MergedSkillsMatrix,\n agents: Partial<Record<AgentName, AgentDefinition>>,\n extras?: ConfigTypesExtras,\n): string {\n const data = buildConfigTypesBackgroundData(matrix, agents);\n return generateConfigTypesSource(\n data.matrix,\n data.agentNames,\n data.customAgentNames,\n extras,\n config,\n );\n}\n\n/**\n * Writes the config half of the pair at `configPath`. The global config never\n * takes the project writer's import/inline options — it is the file those\n * options resolve against.\n *\n * The one renderer call left reading the singleton rather than taking a\n * catalogue: its sole caller is `mutateGlobal`, which writes this half BEFORE\n * `resolveGateDeps` runs, deliberately — a mutation with no registered projects\n * must not pay for the catalogue load. There is no catalogue in scope to pass.\n */\nexport async function writeGlobalConfigHalf(\n config: ProjectConfig,\n configPath: string,\n): Promise<boolean> {\n return writeIfChanged(configPath, generateConfigSource(config, activeMatrix));\n}\n\n/** Writes the types half of the pair beside `configPath`. */\nexport async function writeGlobalTypesHalf(\n config: ProjectConfig,\n configPath: string,\n matrix: MergedSkillsMatrix,\n agents: Partial<Record<AgentName, AgentDefinition>>,\n extras?: ConfigTypesExtras,\n): Promise<boolean> {\n return writeIfChanged(\n typesPathFor(configPath),\n renderStandaloneTypes(config, matrix, agents, extras),\n );\n}\n\n/**\n * Writes both halves from one config AND one catalogue, so they cannot disagree.\n *\n * The catalogue is the caller's `matrix` for both halves. It was the caller's for\n * the types half and the module singleton for the config half, which is the\n * disagreement this function's name says cannot happen: the two are the same\n * object on every production path today, and the whole reason the renderers take\n * a catalogue parameter is that a second caller seats a different one.\n */\nexport async function writeGlobalPair(\n config: ProjectConfig,\n configPath: string,\n matrix: MergedSkillsMatrix,\n agents: Partial<Record<AgentName, AgentDefinition>>,\n): Promise<boolean> {\n const configWritten = await writeIfChanged(configPath, generateConfigSource(config, matrix));\n const typesWritten = await writeIfChanged(\n typesPathFor(configPath),\n renderStandaloneTypes(config, matrix, agents),\n );\n return configWritten || typesWritten;\n}\n\n/**\n * Creates a blank global pair in `provider`'s home-scope source folder when none exists: `config.ts`\n * with empty arrays and `config-types.ts` with `never` unions. Returns true when\n * the files were created, false when they already existed.\n */\nexport async function ensureBlankPair(provider: Provider): Promise<boolean> {\n const { configPath, typesPath } = globalPairPaths(provider);\n\n if (await fileExists(configPath)) {\n verbose(\"Global config already exists, skipping blank creation\");\n return false;\n }\n\n await ensureDir(path.dirname(configPath));\n\n await writeIfChanged(configPath, generateBlankGlobalConfigSource());\n await writeIfChanged(typesPath, generateBlankGlobalConfigTypesSource());\n\n verbose(`Created blank global config at ${path.dirname(configPath)}`);\n return true;\n}\n","import fs from \"fs\";\nimport { isDeepEqual, unique } from \"remeda\";\nimport type {\n AgentDefinition,\n AgentName,\n Category,\n MergedSkillsMatrix,\n ProjectConfig,\n SkillAssignment,\n SkillId,\n StackAgentConfig,\n} from \"../../types\";\nimport type { AgentScopeConfig, SkillConfig } from \"../../types/config\";\nimport { PROVIDERS, type Provider } from \"../../consts\";\nimport { getInstalledConfigPath, getProjectConfigPath } from \"../installation/install-base-dir\";\nimport { providerInUse, sourceFolderInUse } from \"../installation/install-layout\";\nimport { ConfigLoadError, loadInstalledConfig } from \"../configuration/project-config\";\nimport { mergeConfigs, type AuthoritativeScope } from \"../configuration/config-merger\";\nimport {\n activeProjectAgentNames,\n isActiveAt,\n isGlobalTombstone,\n type ScopedEntry,\n} from \"../configuration/scope-predicates\";\nimport { generateConfigSource, type ConfigSourceOptions } from \"../configuration/config-writer\";\nimport {\n buildConfigTypesBackgroundData,\n type ConfigTypesExtras,\n deriveCategories,\n deriveDomains,\n regenerateConfigTypes,\n} from \"../configuration/config-types-writer\";\n// The catalogue the renderers take as a parameter. It is the singleton this CLI seats at\n// startup, which is what `generateConfigSource` read directly before the renderers moved into\n// `@workspace/compile` and the editor became a second caller with a catalogue of its own.\n// Inside the fan-out it is the seat `withCatalogueSeatedFor` has put the current project's\n// catalogue in, which is why the config half needs no catalogue argument to follow the types\n// half onto the right one.\nimport { matrix as activeMatrix } from \"../matrix/matrix-provider\";\nimport { withCatalogueSeatedFor } from \"../loading/catalogue-seat.js\";\nimport { fileExists, writeFile } from \"../../utils/fs\";\nimport { getErrorMessage } from \"../../utils/errors\";\nimport { verbose } from \"../../utils/logger\";\nimport { typedEntries, typedKeys } from \"../../utils/typed-object\";\nimport { LOCAL_PSEUDO_CATEGORY } from \"../../consts\";\n// The merge a project install resolves the global config through lives in `@workspace/compile`,\n// because the editor's output preview resolves the global config it draws through it too.\nimport { addSessionToGlobal, type ResolvedGlobalConfig } from \"@workspace/compile/global-config\";\n\nexport { mergeGlobalConfigs } from \"@workspace/compile/global-config\";\n\nexport async function writeConfigFile(\n config: ProjectConfig,\n configPath: string,\n options?: ConfigSourceOptions,\n): Promise<void> {\n const source = generateConfigSource(config, activeMatrix, options);\n await writeFile(configPath, source);\n}\n\n/**\n * The single normalization for every value compared against the global config's\n * `projects` array — the one written by {@link registerProjectPath}, the one the\n * gate's `deregister-project` mutation filters against, and the current-project\n * skip in {@link propagateGlobalChangesToProjects}. Symlinks are resolved, so an entry\n * stored under one of them matches byte-for-byte under the others. The two ends\n * normalizing differently is what left symlinked layouts (macOS `/tmp`, a\n * `~/dev/repo` pointing at `/data/repo`) registered forever after an uninstall.\n *\n * Throws when the directory does not exist. That is deliberate: a path that\n * cannot be resolved is an error, not a cue to fall back to a weaker\n * normalization — a second tier would reintroduce exactly the asymmetry this\n * helper exists to remove. The one caller that must survive it (`uninstall`'s\n * deregistration) already wraps the call in a warn-and-continue guard.\n */\nexport function normalizeProjectPath(projectDir: string): string {\n return fs.realpathSync(projectDir);\n}\n\n/**\n * Registers a project directory in the global config's `projects` array.\n * Paths are normalized via {@link normalizeProjectPath} to resolve symlinks.\n * Filters stale entries (where the project's config.ts no longer exists).\n */\nasync function registerProjectPath(\n globalConfig: ProjectConfig,\n projectDir: string,\n): Promise<{ config: ProjectConfig; changed: boolean }> {\n const normalizedPath = normalizeProjectPath(projectDir);\n const existing = globalConfig.projects ?? [];\n\n // Filter stale entries\n const staleChecks = await Promise.all(\n existing.map(async (p) => ({\n path: p,\n hasConfig: await fileExists(getInstalledConfigPath(p)),\n })),\n );\n const valid = staleChecks.filter((c) => c.hasConfig).map((c) => c.path);\n\n if (valid.includes(normalizedPath)) {\n const changed = valid.length !== existing.length;\n return { config: changed ? { ...globalConfig, projects: valid } : globalConfig, changed };\n }\n\n return { config: { ...globalConfig, projects: [...valid, normalizedPath] }, changed: true };\n}\n\nfunction isProjectScopedEntry(entry: ScopedEntry): boolean {\n return entry.scope === \"project\";\n}\n\n/** True when the global config still has an active (non-excluded) skill for this id. */\nfunction globalHasActiveSkill(globalConfig: ProjectConfig, id: SkillId): boolean {\n return globalConfig.skills.some((s) => s.id === id && isActiveAt(s, \"global\"));\n}\n\n/** True when the global config still has an active (non-excluded) agent for this name. */\nfunction globalHasActiveAgent(globalConfig: ProjectConfig, name: AgentName): boolean {\n return globalConfig.agents.some((a) => a.name === name && isActiveAt(a, \"global\"));\n}\n\n/**\n * Keeps a project's own skill entries when re-inlining fresh global data, dropping\n * tombstones that no longer correspond to a real global install.\n *\n * A tombstone (`scope === \"global\" && excluded`) only has meaning while the global entry\n * it masks still exists. Once the skill has been removed from the global config, the\n * tombstone is stale — carrying it forward would leave the project showing a masked\n * global item that no longer exists. Project-scoped entries are always retained.\n */\nfunction retainProjectOwnedSkills(\n skills: SkillConfig[],\n globalConfig: ProjectConfig,\n): SkillConfig[] {\n return skills.filter(\n (entry) =>\n isProjectScopedEntry(entry) ||\n (isGlobalTombstone(entry) && globalHasActiveSkill(globalConfig, entry.id)),\n );\n}\n\n/** Agent mirror of {@link retainProjectOwnedSkills}. */\nfunction retainProjectOwnedAgents(\n agents: AgentScopeConfig[],\n globalConfig: ProjectConfig,\n): AgentScopeConfig[] {\n return agents.filter(\n (entry) =>\n isProjectScopedEntry(entry) ||\n (isGlobalTombstone(entry) && globalHasActiveAgent(globalConfig, entry.name)),\n );\n}\n\n/**\n * Category a skill belongs to according to the MERGED matrix. `undefined` when the\n * matrix has no entry for the id (a user-authored local skill carries no matrix\n * record) or when the entry sits in the `local` pseudo-category — neither\n * participates in category rules, and neither may throw.\n */\nfunction categoryOfSkill(id: SkillId, matrix: MergedSkillsMatrix): Category | undefined {\n const category = matrix.skills[id]?.category;\n if (category === undefined || category === LOCAL_PSEUDO_CATEGORY) return undefined;\n return category;\n}\n\n/**\n * True when the merged matrix DECLARES this category as holding at most one skill.\n * Read from the matrix passed in rather than `defaultCategories` so a source repo's\n * category overrides are honoured.\n *\n * A category the matrix does not declare is deliberately NOT treated as exclusive.\n * The wizard's toggle handler defaults an undeclared one to exclusive\n * (`use-build-step-props.ts` uses `matrix.categories[categoryId]?.exclusive ?? true`),\n * but a rule that MASKS persisted entries must only fire on a flag the data\n * actually carries.\n */\nfunction isExclusiveCategory(category: Category, matrix: MergedSkillsMatrix): boolean {\n return matrix.categories[category]?.exclusive === true;\n}\n\n/** Categories occupied by an active project-scoped skill, per the merged matrix. */\nfunction activeProjectCategories(\n projectOwnedSkills: SkillConfig[],\n matrix: MergedSkillsMatrix,\n): Set<Category> {\n return new Set(\n projectOwnedSkills\n .filter((s) => isActiveAt(s, \"project\"))\n .map((s) => categoryOfSkill(s.id, matrix))\n .filter((category): category is Category => category !== undefined),\n );\n}\n\n/**\n * The collision that justifies masking a live global skill. Two kinds, both read from the\n * project's OWN entries: IDENTITY — the project owns the same id at project scope; CATEGORY —\n * the project owns a different active skill in the same category and the matrix declares that\n * category exclusive.\n *\n * Shared by the mask producer ({@link maskCollidingGlobalSkills}) and the self-heal that\n * removes a mask once its collision clears ({@link dropOrphanedDerivedMasks}), so the two can\n * never disagree about what a mask means.\n */\nfunction buildProjectCollisionTest(\n projectOwnedSkills: SkillConfig[],\n matrix: MergedSkillsMatrix,\n): (id: SkillId) => boolean {\n const ownedIds = new Set(\n projectOwnedSkills.filter((s) => isActiveAt(s, \"project\")).map((s) => s.id),\n );\n const occupiedExclusiveCategories = new Set(\n [...activeProjectCategories(projectOwnedSkills, matrix)].filter((category) =>\n isExclusiveCategory(category, matrix),\n ),\n );\n\n return (id) => {\n if (ownedIds.has(id)) return true;\n const category = categoryOfSkill(id, matrix);\n return category !== undefined && occupiedExclusiveCategories.has(category);\n };\n}\n\n/**\n * Drops a derived mask that no longer masks anything, so the global install becomes\n * visible again once the collision that produced it is gone.\n *\n * A derived mask and a user-authored tombstone are BYTE-IDENTICAL in config.ts — both are\n * `{ id, scope: \"global\", excluded: true }` — but the wizard can no longer mint\n * the second kind on its own: a project-scope deselect of a globally-installed skill is\n * refused, and a domain deselect only drops what the project owns. The one user route to a\n * global tombstone is the `s` scope toggle (G→P), which always pairs the tombstone with an\n * active project entry for the same id — an IDENTITY collision. So every bare mask is\n * machine-derived, and a single retention test suffices: keep it iff the collision that\n * would re-derive it is still there.\n */\nfunction dropOrphanedDerivedMasks(\n projectOwnedSkills: SkillConfig[],\n matrix: MergedSkillsMatrix,\n): SkillConfig[] {\n const collidesWithProjectOwnership = buildProjectCollisionTest(projectOwnedSkills, matrix);\n return projectOwnedSkills.filter(\n (entry) => !isGlobalTombstone(entry) || collidesWithProjectOwnership(entry.id),\n );\n}\n\n/**\n * Agent mirror of {@link dropOrphanedDerivedMasks}, identity collisions only: agents\n * have no categories, so the project-scoped sibling with the same name is the only thing a\n * mask can be justified by.\n */\nfunction dropOrphanedDerivedAgentMasks(projectOwnedAgents: AgentScopeConfig[]): AgentScopeConfig[] {\n const ownedNames = new Set(activeProjectAgentNames(projectOwnedAgents));\n return projectOwnedAgents.filter(\n (entry) => !isGlobalTombstone(entry) || ownedNames.has(entry.name),\n );\n}\n\n/**\n * Builds the tombstones that mask live global skills this project cannot show\n * alongside what it already owns at project scope. Two collision kinds, both keyed\n * against the SAME live global config:\n *\n * 1. IDENTITY — the project owns the same id at project scope. Without the\n * mask, `partitionInlinedConfigEntries` re-inlines the global copy as a SECOND\n * active entry, leaving one id active at BOTH scopes instead of rendering the\n * dual-scope `[P][G]` pair.\n * 2. CATEGORY — the project owns a DIFFERENT active skill in the same category and\n * the matrix declares that category exclusive. Reconciliation keyed on identity\n * alone cannot see this, so a project owning Vue plus a global install of React\n * ends up with two active skills in a category that permits one.\n *\n * The project-owned skill wins locally. This is DELIBERATELY ASYMMETRIC with the `s`\n * round trip, where a user-initiated radio swap in `toggleTechnology` refuses to displace a\n * globally-locked skill: there the user is actively trying to drop a shared install,\n * whereas here the collision is PUSHED IN by a global install landing on pre-existing\n * project state. Letting global win would silently uninstall the user's own skill.\n *\n * Tombstones are spread from the global entry so they carry the global install's\n * `origin`. A skill the project merely inherits (no active project-scope entry, no\n * exclusive-category collision) is skipped — it stays a single active global entry.\n * A skill the project already tombstones is skipped so re-running is idempotent.\n */\nfunction maskCollidingGlobalSkills(\n projectOwnedSkills: SkillConfig[],\n globalConfig: ProjectConfig,\n matrix: MergedSkillsMatrix,\n): SkillConfig[] {\n const collidesWithProjectOwnership = buildProjectCollisionTest(projectOwnedSkills, matrix);\n const alreadyTombstoned = new Set(projectOwnedSkills.filter(isGlobalTombstone).map((s) => s.id));\n\n return globalConfig.skills\n .filter(\n (globalEntry) =>\n isActiveAt(globalEntry, \"global\") &&\n collidesWithProjectOwnership(globalEntry.id) &&\n !alreadyTombstoned.has(globalEntry.id),\n )\n .map((globalEntry) => ({ ...globalEntry, excluded: true }));\n}\n\n/**\n * Agent mirror of {@link maskCollidingGlobalSkills}, identity collisions only.\n * Agents have no categories, so there is no grouping dimension to reconcile.\n */\nfunction maskCollidingGlobalAgents(\n projectOwnedAgents: AgentScopeConfig[],\n globalConfig: ProjectConfig,\n): AgentScopeConfig[] {\n const ownedNames = new Set(activeProjectAgentNames(projectOwnedAgents));\n const alreadyTombstoned = new Set(\n projectOwnedAgents.filter(isGlobalTombstone).map((a) => a.name),\n );\n\n return globalConfig.agents\n .filter(\n (globalEntry) =>\n isActiveAt(globalEntry, \"global\") &&\n ownedNames.has(globalEntry.name) &&\n !alreadyTombstoned.has(globalEntry.name),\n )\n .map((globalEntry) => ({ ...globalEntry, excluded: true }));\n}\n\n/**\n * Reconciles a project's OWN entries against the live global config, immediately\n * before the inlining writer merges the two.\n *\n * Applied at BOTH sites that write a project `config.ts` with `globalConfig` inlined:\n * `propagateGlobalChangesToProjects` (a global change fanning out to registered\n * projects) and the project-scope save branch of `writeScopedFromWizard` (an ordinary\n * project `init`/`edit` performed while the colliding skill is already active\n * globally). Either site alone can produce the malformed shape, so both must run it.\n *\n * Self-heal runs BEFORE masking on BOTH axes so a mask whose collision has cleared is\n * removed rather than immediately re-derived, and so masking's `alreadyTombstoned` guard\n * only sees tombstones that are still warranted.\n *\n * Masking is PROJECT-LOCAL: it is applied to the project split only. The global\n * config passed in is read, never rewritten — tombstones never belong in the\n * global config.ts.\n */\nexport function reconcileProjectSplitAgainstGlobal(\n projectSplit: ProjectConfig,\n globalConfig: ProjectConfig,\n matrix: MergedSkillsMatrix,\n): ProjectConfig {\n const healedSkills = dropOrphanedDerivedMasks(projectSplit.skills, matrix);\n const healedAgents = dropOrphanedDerivedAgentMasks(projectSplit.agents);\n return {\n ...projectSplit,\n skills: [...healedSkills, ...maskCollidingGlobalSkills(healedSkills, globalConfig, matrix)],\n agents: [...healedAgents, ...maskCollidingGlobalAgents(healedAgents, globalConfig)],\n };\n}\n\n/**\n * Computes the skill ids that this project inherited from global scope but that are\n * no longer active at global scope after the change being propagated — and that the\n * project does not own at project scope. These are the only ids that should be pruned\n * from the project's stack: a project-scoped agent may legitimately reference a\n * globally-installed skill, and once that skill is removed at global scope the\n * reference becomes dangling.\n *\n * `priorProjectSkills` is the project's pre-reconciliation (on-disk, inlined) skills.\n * A global skill that was just removed still appears here as a `scope: \"global\"`,\n * non-excluded entry, which is the signal we key on. Project-scoped skills and\n * user-authored local skills (which carry no SkillConfig entry at all) are never in\n * this set, so they are always preserved.\n */\nfunction computeRemovedGlobalSkillIds(\n priorProjectSkills: SkillConfig[],\n globalConfig: ProjectConfig,\n): Set<SkillId> {\n const activeGlobalIds = new Set(\n globalConfig.skills.filter((s) => isActiveAt(s, \"global\")).map((s) => s.id),\n );\n const projectOwnedIds = new Set(\n priorProjectSkills.filter((s) => isActiveAt(s, \"project\")).map((s) => s.id),\n );\n return new Set(\n priorProjectSkills\n .filter(\n (s) =>\n s.scope === \"global\" &&\n !s.excluded &&\n !activeGlobalIds.has(s.id) &&\n !projectOwnedIds.has(s.id),\n )\n .map((s) => s.id),\n );\n}\n\n/**\n * Prunes stack references to global skills that were just removed at global scope,\n * so a project-scoped agent stops referencing a skill that no longer exists anywhere.\n * Only ids in `removedGlobalSkillIds` are dropped — every other assignment is kept\n * verbatim, in order, with its `preloaded` flag untouched. Categories and agents left\n * empty by the pruning are removed. When nothing was removed, the stack is returned\n * unchanged so unaffected projects produce byte-identical config output.\n */\nfunction retainReconciledStack(\n stack: Record<string, StackAgentConfig> | undefined,\n removedGlobalSkillIds: Set<SkillId>,\n): Record<string, StackAgentConfig> | undefined {\n if (!stack || removedGlobalSkillIds.size === 0) return stack;\n\n const reconciled: Record<string, StackAgentConfig> = {};\n for (const [agent, agentStack] of Object.entries(stack)) {\n const reconciledAgentStack = withoutRemovedAssignments(agentStack, removedGlobalSkillIds);\n if (typedKeys<Category>(reconciledAgentStack).length > 0) {\n reconciled[agent] = reconciledAgentStack;\n }\n }\n return reconciled;\n}\n\n/**\n * One agent's stack with the removed ids' assignments dropped, keeping every other assignment in\n * order. A category the pruning empties is left out.\n */\nfunction withoutRemovedAssignments(\n agentStack: StackAgentConfig,\n removedGlobalSkillIds: Set<SkillId>,\n): StackAgentConfig {\n const reconciledAgentStack: StackAgentConfig = {};\n for (const [category, assignments] of typedEntries<Category, SkillAssignment[]>(agentStack)) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- typedEntries/Object.entries launders the `| undefined` a Partial<Record> admits out of its result type, so this guard reads as dead while still covering an explicitly-undefined slot\n if (!assignments) continue;\n const kept = assignments.filter((assignment) => !removedGlobalSkillIds.has(assignment.id));\n if (kept.length > 0) reconciledAgentStack[category] = kept;\n }\n return reconciledAgentStack;\n}\n\n/**\n * The registered projects one fan-out rewrote, and the ones it left alone, by why. Each project is\n * in exactly one, so a run can say which and why for each.\n */\nexport type PropagationResult = {\n updated: string[];\n /** Its `config.ts` is there and cannot be loaded. */\n unreadable: string[];\n /** Nothing is installed at the registered path any more. */\n gone: string[];\n /** It holds another provider's installation, not this one's. */\n notOurs: string[];\n /** Its rewrite threw: its catalogue would not load, or its pair could not be written. */\n failed: string[];\n};\n\nexport type PropagationOptions = {\n /**\n * Whether each project's config-types.ts is rewritten alongside its config.ts.\n * False for a change that moves no type union — a project inlines the global\n * scalars into its config half, but no union is derived from them.\n */\n regenerateTypes?: boolean;\n};\n\n/**\n * The single writer for a project's config pair, used by BOTH sites that emit\n * one: the per-project propagation step below and the project branch of the\n * wizard write.\n *\n * `buildProjectTypesExtras` is fed the EFFECTIVE config — the project's own\n * reconciled rows plus the active global rows the writer is about to inline —\n * at both sites. The emitted config.ts names every one of those rows, so extras\n * derived from the project split alone declare fewer literals than the sibling\n * config.ts uses, and the pair stops type-checking against itself.\n */\nexport async function writeProjectConfigPair(\n projectDir: string,\n provider: Provider,\n reconciledSplit: ProjectConfig,\n effectiveGlobal: ProjectConfig,\n matrix: MergedSkillsMatrix,\n agents: Partial<Record<AgentName, AgentDefinition>>,\n options: PropagationOptions = {},\n): Promise<void> {\n const configPath = refuseAWriteOutsideTheInstallation(projectDir, provider);\n\n // Reconcile-before-both-writes: the two halves are derived from the same\n // reconciled data in the same call, so neither can describe a config the other\n // does not.\n await writeConfigFile(reconciledSplit, configPath, {\n isProjectConfig: true,\n globalConfig: effectiveGlobal,\n });\n\n if (options.regenerateTypes === false) return;\n\n // Uses the same global-aware path for project types — emits\n // `import type { SkillId as GlobalSkillId, ... }` and extends with any\n // project-scoped items the project owns. Without this, a global-scope install\n // would overwrite the project's import-form types with the standalone/inlined\n // form.\n await regenerateConfigTypes(\n projectDir,\n Promise.resolve(buildConfigTypesBackgroundData(matrix, agents)),\n buildProjectTypesExtras(inlinedProjectView(reconciledSplit, effectiveGlobal), matrix),\n );\n}\n\n/**\n * The rows the inlining writer draws a project's config.ts from: the project's\n * own reconciled entries plus everything the global config contributes. Union\n * membership is decided downstream by `buildProjectTypesExtras`, which reads the\n * active entries — a tombstone's id always arrives via the live global row it\n * masks.\n */\nfunction inlinedProjectView(\n reconciledSplit: ProjectConfig,\n effectiveGlobal: ProjectConfig,\n): ProjectConfig {\n return {\n ...reconciledSplit,\n skills: [...effectiveGlobal.skills, ...reconciledSplit.skills],\n agents: [...effectiveGlobal.agents, ...reconciledSplit.agents],\n selectedDomains: unique([\n ...(effectiveGlobal.selectedDomains ?? []),\n ...(reconciledSplit.selectedDomains ?? []),\n ]),\n stack: { ...(effectiveGlobal.stack ?? {}), ...(reconciledSplit.stack ?? {}) },\n };\n}\n\n/**\n * The project's OWN entries, reconciled against the now-current global config — the\n * half the inlining writer is about to merge fresh global data into.\n *\n * Project-scoped entries, plus tombstones that still correspond to a live global\n * install. Tombstones whose global entry has been removed are dropped here so the\n * project stops referencing a global item that no longer exists. The stack is\n * reconciled against the same now-current global data so a project-scoped agent stops\n * referencing a global skill that was just removed at global scope.\n *\n * `onDisk.skills` (pre-reconciliation) is what detects the removed globals, because a\n * skill the global scope just lost is still present there as a `scope: \"global\"`\n * reference.\n */\nfunction reconcileAgainstGlobal(\n onDisk: ProjectConfig,\n globalConfig: ProjectConfig,\n matrix: MergedSkillsMatrix,\n): ProjectConfig {\n const removedGlobalSkillIds = computeRemovedGlobalSkillIds(onDisk.skills, globalConfig);\n const reconciledStack = retainReconciledStack(onDisk.stack, removedGlobalSkillIds);\n\n return reconcileProjectSplitAgainstGlobal(\n {\n ...onDisk,\n skills: retainProjectOwnedSkills(onDisk.skills, globalConfig),\n agents: retainProjectOwnedAgents(onDisk.agents, globalConfig),\n ...(reconciledStack !== undefined && { stack: reconciledStack }),\n },\n globalConfig,\n matrix,\n );\n}\n\n/**\n * The config path this project's pair is written to, checked against the installation that is\n * actually in that directory.\n *\n * C2's compile-time proof does not reach here. Propagation resolves its destination from a\n * config-derived STRING — a path out of the global config's `projects` array — so deleting the\n * provider default finds every typed caller and none of these. The assertion is the runtime half\n * of the same guarantee: a pair written into the source folder of a provider the directory does\n * not hold is a write into another installation, and the one thing this step must never do\n * quietly.\n *\n * **The two sides are two independent reads, and that is the whole of the fix.** It compared\n * `getInstalledConfigPath(projectDir)` against `sourceFolderInUse(projectDir, providerInUse(...))`\n * until 2026-09-21 — one `providerInUse` answer joined with `config.ts` on one side and left bare\n * on the other, so the comparison was `join(folder, \"config.ts\").startsWith(folder)` and no disk\n * state could make it fail. What the caller BELIEVES now arrives as `provider`; what the\n * installation IS is read here.\n */\nfunction refuseAWriteOutsideTheInstallation(projectDir: string, provider: Provider): string {\n const installed = providerInUse(projectDir);\n const configPath = getProjectConfigPath(projectDir, provider);\n\n if (installed !== provider) {\n throw new Error(\n `Refusing to write ${configPath}: ${projectDir} holds the ${installed} installation at ` +\n `${sourceFolderInUse(projectDir, installed).dir}, not a ${provider} one.`,\n );\n }\n return configPath;\n}\n\n/**\n * Rewrites ONE registered project's pair against the now-current global config.\n * Answers false when the project has a config file the loader returns nothing for,\n * which is the caller's cue to record it as unreached.\n *\n * Every catalogue lookup this makes is against the catalogue\n * {@link withCatalogueSeatedFor} loads for THIS project — its own local skills, and the\n * skills of the marketplace its own config names — rather than the one belonging to the\n * command that triggered the fan-out. Three readers depend on it and none of them can be\n * satisfied by the triggering command's: {@link reconcileAgainstGlobal} decides which of\n * the project's entries survive by their categories, `buildProjectTypesExtras` derives\n * the project's `Category` and `Domain` unions from them, and the config half's writer\n * reads the seated singleton directly. A skill only this project's catalogue carries used\n * to resolve to nothing in all three.\n */\nasync function propagateToProject(\n projectPath: string,\n provider: Provider,\n globalConfig: ProjectConfig,\n agents: Partial<Record<AgentName, AgentDefinition>>,\n options: PropagationOptions,\n): Promise<boolean> {\n // Before the catalogue: its load reads this config too, and refuses it in words no caller can\n // tell from any other failure, where this raises a ConfigLoadError.\n const existingProject = await loadInstalledConfig(projectPath);\n if (!existingProject?.config) return false;\n\n return withCatalogueSeatedFor(projectPath, async (catalogue) => {\n const projectSplit = reconcileAgainstGlobal(existingProject.config, globalConfig, catalogue);\n\n await writeProjectConfigPair(\n projectPath,\n provider,\n projectSplit,\n globalConfig,\n catalogue,\n agents,\n { regenerateTypes: options.regenerateTypes ?? true },\n );\n return true;\n });\n}\n\n/**\n * Propagates global config changes to all registered project configs.\n * Updates each project's config-types.ts (type unions) and config.ts (inlined global data).\n * Skips stale project paths, the current project being installed, and any registered project\n * that belongs to a DIFFERENT provider's installation.\n *\n * There is deliberately NO catalogue parameter. Each project's own is loaded per project by\n * {@link propagateToProject}, and a parameter beside that could only ever be the wrong one —\n * which is what every caller used to pass. A project whose catalogue cannot be loaded lands\n * in `failed` through the same catch as any other failure: leaving it stale is recoverable\n * by a run from inside it, where rewriting its types from another installation's catalogue\n * is the fight this exists to end.\n */\nexport async function propagateGlobalChangesToProjects(\n globalConfig: ProjectConfig,\n agents: Partial<Record<AgentName, AgentDefinition>>,\n provider: Provider,\n currentProjectDir?: string,\n options: PropagationOptions = {},\n): Promise<PropagationResult> {\n const projects = globalConfig.projects ?? [];\n const result: PropagationResult = {\n updated: [],\n unreadable: [],\n gone: [],\n notOurs: [],\n failed: [],\n };\n if (projects.length === 0) return result;\n\n const currentNormalized = currentProjectDir ? normalizeProjectPath(currentProjectDir) : null;\n\n // One project at a time, never `Promise.all`: each propagation seats its project's catalogue\n // in the module-wide matrix singleton and restores the previous one after, so two in flight\n // would render against each other's.\n for (const projectPath of projects) {\n // Skip the project currently being installed (it's already being written)\n if (currentNormalized && projectPath === currentNormalized) continue;\n\n const outcome = await propagateToRegisteredProject(\n projectPath,\n provider,\n globalConfig,\n agents,\n options,\n );\n result[outcome].push(projectPath);\n }\n\n return result;\n}\n\n/**\n * Brings one registered project's pair up to date with the global config, or leaves it alone and\n * answers why: nothing installed there any more, another provider's installation, a config that is\n * there and cannot be loaded, or a propagation that threw — loading its catalogue as much as\n * writing its pair. None of them stops the fan-out.\n *\n * Whether anything is installed there is asked first, of every provider, because the provider\n * policy cannot answer it: the layout reads a folder holding nothing as the provider a NEW\n * installation is made under, so to a global on the other provider a deleted project reads as\n * another provider's.\n */\nasync function propagateToRegisteredProject(\n projectPath: string,\n provider: Provider,\n globalConfig: ProjectConfig,\n agents: Partial<Record<AgentName, AgentDefinition>>,\n options: PropagationOptions,\n): Promise<keyof PropagationResult> {\n if (!(await holdsAnInstallation(projectPath))) {\n verbose(`Skipped propagation to ${projectPath} (nothing installed there)`);\n return \"gone\";\n }\n\n const notOurs = await whyThisProjectIsNotOurs(projectPath, provider);\n if (notOurs !== null) {\n verbose(`Skipped propagation to ${projectPath} (${notOurs})`);\n return \"notOurs\";\n }\n\n try {\n if (!(await propagateToProject(projectPath, provider, globalConfig, agents, options))) {\n return \"gone\";\n }\n verbose(`Propagated global changes to ${projectPath}`);\n return \"updated\";\n } catch (error) {\n verbose(`Failed to propagate to ${projectPath}: ${getErrorMessage(error)}`);\n return error instanceof ConfigLoadError ? \"unreadable\" : \"failed\";\n }\n}\n\n/** Whether any provider's installation is at `projectPath`, this fan-out's or another's. */\nasync function holdsAnInstallation(projectPath: string): Promise<boolean> {\n const configs = await Promise.all(\n PROVIDERS.map((installed) => fileExists(getProjectConfigPath(projectPath, installed))),\n );\n return configs.includes(true);\n}\n\n/**\n * Why a registered project that holds an installation is not this fan-out's to rewrite, or `null`\n * when it is.\n *\n * A global installation fans out only within its own provider family: `projects[]` is a list of\n * directories with nothing in it saying which installation each belongs to, so a project on the\n * other provider is a different installation that happens to be registered here, and rewriting\n * its pair from this global's data is the cross-installation write the folders exist to prevent.\n * A root the layout reads as this provider's while it holds no config of this provider's — a run\n * whose `--provider` named it — holds only the other provider's, so it is not ours either.\n *\n * **A refusal is a reason to skip, never a reason to abort the fan-out.** `providerInUse` refuses\n * a root whose installation this release has no host for, and a registered project it cannot act\n * on is one to leave alone — exactly as a project on the other provider is. The refusal's own\n * sentence becomes the reason, so the folder that produced it reaches the verbose log rather than\n * being replaced by a summary of it.\n */\nasync function whyThisProjectIsNotOurs(\n projectPath: string,\n provider: Provider,\n): Promise<string | null> {\n const notOurs = `not a ${provider} installation`;\n try {\n if (providerInUse(projectPath) !== provider) return notOurs;\n } catch (error) {\n return getErrorMessage(error);\n }\n return (await fileExists(getProjectConfigPath(projectPath, provider))) ? null : notOurs;\n}\n\n/**\n * Prunes CLI-inlined global-scoped entries from every registered project after a\n * GLOBAL uninstall. Reuses {@link propagateGlobalChangesToProjects} with an\n * emptied global config so every global skill/agent reads as removed: project-\n * scoped entries are retained verbatim, inlined global rows and tombstones are\n * dropped, per-agent stack refs lose their global-only ids, and each project's\n * config-types.ts is regenerated.\n *\n * Call AFTER the global config manifest has been removed so the regenerated\n * project types fall back to the standalone form instead of importing from the\n * now-deleted global config-types.ts. Unreachable project dirs are reported by why,\n * never thrown.\n *\n * The per-project catalogue seat comes with the reuse, and this is the case that needs it\n * most: the standalone form these projects fall back to declares its unions from the\n * catalogue outright rather than extending an imported one, so a project's own skills are\n * the ONLY thing keeping their categories in the file. Derived from the uninstalling\n * command's catalogue, a global uninstall would take a project's own taxonomy with it.\n */\nexport async function pruneGlobalEntriesFromRegisteredProjects(\n globalConfig: ProjectConfig,\n agents: Partial<Record<AgentName, AgentDefinition>>,\n provider: Provider,\n): Promise<PropagationResult> {\n const emptiedGlobal: ProjectConfig = {\n ...globalConfig,\n skills: [],\n agents: [],\n };\n return propagateGlobalChangesToProjects(emptiedGlobal, agents, provider);\n}\n\n/**\n * Resolves the global config a project install should write, and registers this project's path.\n * `globalDataChanged` gates propagation; `changed` gates the write itself.\n *\n * `authority` is `mergeConfigs`' own word for how much of what it can see the session owns, and\n * it selects between two resolutions: `matchGlobalToSession` below, and `addSessionToGlobal` from\n * `@workspace/compile`. Absent, or `\"owned\"`, keeps the standing additive behaviour.\n */\nexport async function resolveEffectiveGlobalConfig(\n globalSplit: ProjectConfig,\n existingGlobalConfig: ProjectConfig | undefined,\n projectDir: string,\n authority?: AuthoritativeScope,\n): Promise<{ config: ProjectConfig; globalDataChanged: boolean; changed: boolean }> {\n const merged =\n authority === \"all\"\n ? matchGlobalToSession(globalSplit, existingGlobalConfig)\n : addSessionToGlobal(globalSplit, existingGlobalConfig, activeMatrix);\n\n const registration = await registerProjectPath(merged.config, projectDir);\n return {\n config: registration.config,\n globalDataChanged: merged.changed,\n changed: merged.changed || registration.changed,\n };\n}\n\n/**\n * The resolution for a session that owns every scope it can see: the global config is made to\n * MATCH it, so a global entry the session left out is REMOVED rather than preserved.\n *\n * One caller reaches this — a confirmed `edit --from` — and it is not acting on its own\n * initiative: it states a whole roster, its plan showed every global removal under its own\n * heading, named every other registered project the removal reaches, and somebody answered yes.\n * Nothing else in the CLI may write the global config from a project, and this does not widen\n * that: the word arrives from the command, not from the shape of the data.\n *\n * `mergeConfigs` is what does it rather than a merge of this module's own, because \"absent from\n * the session means deselected\" is exactly what `authoritativeScope: \"all\"` already means at the\n * home root. It carries the global installation's identity, its stack fallback and its\n * `projects[]` registry across — a session's split says nothing about who the global\n * installation is or which projects read it, and the fan-out walks that registry.\n *\n * An empty session is a real answer here, so there is no `hasGlobalItems` shortcut: a\n * configuration that installs nothing globally removes what was.\n */\nfunction matchGlobalToSession(\n globalSplit: ProjectConfig,\n existingGlobalConfig: ProjectConfig | undefined,\n): ResolvedGlobalConfig {\n if (!existingGlobalConfig) return { config: globalSplit, changed: true };\n\n const config = mergeConfigs(globalSplit, existingGlobalConfig, { authoritativeScope: \"all\" });\n return { config, changed: !isDeepEqual(config, existingGlobalConfig) };\n}\n\n/** Category keys the emitted stack holds, across every agent it configures. */\nfunction stackCategories(stack: ProjectConfig[\"stack\"]): Category[] {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- typedEntries/Object.entries launders the `| undefined` a Partial<Record> admits out of its result type, so this guard reads as dead while still covering an explicitly-undefined slot\n return Object.values(stack ?? {}).flatMap((agentStack) => typedKeys<Category>(agentStack ?? {}));\n}\n\n/**\n * Derives the extras for regenerateConfigTypes so a project's config-types.ts declares every\n * literal its sibling config.ts holds — the two are written from the same config and must not\n * be able to disagree.\n *\n * ALL active (non-excluded) entries, not just project-scoped ones:\n * generateProjectConfigWithInlinedGlobal writes the active global rows into the project's own\n * config.ts verbatim, and the imported Global* unions cover those rows only for as long as the\n * global config still happens to contain them. A later global-scope run narrows those unions\n * and a project nobody touched stops type-checking (TS2322 on a skill id or domain, TS2353 on a\n * stack's category key).\n *\n * Domains and stack categories are read off the config's own arrays as well as derived from its\n * skill rows: `selectedDomains` is a wizard preference no skill row has to back, and neither it\n * nor a stack entry is pruned when the last skill that would derive it leaves.\n */\nexport function buildProjectTypesExtras(\n finalConfig: ProjectConfig,\n matrix: MergedSkillsMatrix,\n): Required<ConfigTypesExtras> {\n const activeSkills = finalConfig.skills.filter((s) => !s.excluded);\n const activeAgents = finalConfig.agents.filter((a) => !a.excluded);\n\n const extraSkillIds = unique(activeSkills.map((s) => s.id));\n const extraAgentNames = unique(activeAgents.map((a) => a.name));\n\n const extraCategories = unique([\n ...deriveCategories(extraSkillIds, matrix),\n ...stackCategories(finalConfig.stack),\n ]);\n const extraDomains = unique([\n ...deriveDomains(extraCategories, matrix),\n ...(finalConfig.selectedDomains ?? []),\n ]);\n\n return { extraSkillIds, extraAgentNames, extraDomains, extraCategories };\n}\n","import { isDeepEqual } from \"remeda\"\n\nimport { skillDomain, type CompileCatalog } from \"./catalog.js\"\nimport { GLOBAL_CONFIG_NAME } from \"./paths.js\"\nimport { typedEntries } from \"./typed-object.js\"\nimport type {\n AgentName,\n Category,\n ProjectConfig,\n SkillAssignment,\n StackAgentConfig,\n} from \"./types.js\"\n\n/**\n * THE GLOBAL CONFIG A PROJECT INSTALL WRITES.\n *\n * Run from a project directory, `init` never writes the session's global half\n * as it stands: it merges that half into the global config already on disk,\n * which on a clean machine is the blank pair `ensureBlankPair` has just\n * written. The CLI's `config-gate/propagate.ts` resolves the file it writes\n * through {@link addSessionToGlobal}, and the editor's output preview resolves\n * the file it draws through the same call over {@link blankGlobalConfig} — so\n * the fields this merge carries across are the fields the drawn global\n * `config.ts` holds, and a second copy would be a second answer to what that\n * file says.\n */\n\n/**\n * The configuration the CLI's blank global `config.ts` template\n * (`generateBlankGlobalConfigSource`) holds, as a loader reads it back.\n *\n * What a project install on a machine with nothing installed merges its global\n * half into. The editor's output preview has no file to read, so it starts from\n * this; the CLI's `project-config.test.ts` loads the template and holds it\n * against this, which is what keeps the two one claim.\n */\nexport function blankGlobalConfig(): ProjectConfig {\n return {\n name: GLOBAL_CONFIG_NAME,\n skills: [],\n agents: [],\n selectedDomains: [],\n }\n}\n\n/**\n * Deep-additive stack merge: appends any (agent, category, skill) triple present in\n * `incoming` but missing in `existing`. Never removes or overwrites existing entries\n * (including their `preloaded` flags). Returns a fresh stack object — inputs are not\n * mutated. `changed` is true iff at least one new agent, category, or skill assignment\n * was appended.\n */\nfunction additiveMergeStack(\n existing: Partial<Record<AgentName, StackAgentConfig>> | undefined,\n incoming: Partial<Record<AgentName, StackAgentConfig>> | undefined\n): { stack: Partial<Record<AgentName, StackAgentConfig>>; changed: boolean } {\n const merged: Partial<Record<AgentName, StackAgentConfig>> = existing\n ? structuredClone(existing)\n : {}\n if (!incoming) return { stack: merged, changed: false }\n\n let changed = false\n for (const [agentName, incomingAgentStack] of typedEntries<\n AgentName,\n StackAgentConfig\n >(incoming)) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- typedEntries/Object.entries launders the `| undefined` a Partial<Record> admits out of its result type, so this guard reads as dead while still covering an explicitly-undefined slot\n if (!incomingAgentStack) continue\n\n const existingAgentStack = merged[agentName]\n if (!existingAgentStack) {\n merged[agentName] = structuredClone(incomingAgentStack)\n changed = true\n continue\n }\n\n if (mergeAgentCategories(existingAgentStack, incomingAgentStack)) {\n changed = true\n }\n }\n\n return { stack: merged, changed }\n}\n\n/**\n * Mutates `existingAgentStack` in place by appending any category or skill assignment\n * from `incomingAgentStack` that is not already present. Returns true if anything was\n * appended. Caller must pass a cloned `existingAgentStack` — this function is only\n * called on the merged copy, never on the original input.\n */\nfunction mergeAgentCategories(\n existingAgentStack: StackAgentConfig,\n incomingAgentStack: StackAgentConfig\n): boolean {\n let changed = false\n for (const [category, incomingAssignments] of typedEntries<\n Category,\n SkillAssignment[]\n >(incomingAgentStack)) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- typedEntries/Object.entries launders the `| undefined` a Partial<Record> admits out of its result type, so this guard reads as dead while still covering an explicitly-undefined slot\n if (!incomingAssignments) continue\n\n const existingAssignments = existingAgentStack[category]\n if (!existingAssignments) {\n existingAgentStack[category] = incomingAssignments.map((a) => ({ ...a }))\n changed = true\n continue\n }\n\n if (appendMissingAssignments(existingAssignments, incomingAssignments)) {\n changed = true\n }\n }\n return changed\n}\n\n/**\n * Mutates `existingAssignments` in place by appending a copy of each incoming assignment whose id\n * it does not already hold — an id repeated within `incomingAssignments` is appended once. Returns\n * true if anything was appended. Called on the merged copy only, like\n * {@link mergeAgentCategories}.\n */\nfunction appendMissingAssignments(\n existingAssignments: SkillAssignment[],\n incomingAssignments: SkillAssignment[]\n): boolean {\n const existingIds = new Set(existingAssignments.map((a) => a.id))\n let changed = false\n for (const assignment of incomingAssignments) {\n if (existingIds.has(assignment.id)) continue\n existingAssignments.push({ ...assignment })\n existingIds.add(assignment.id)\n changed = true\n }\n return changed\n}\n\n/**\n * Merges new global-scoped items into an existing global config.\n * Adds skills/agents that don't already exist. Never removes existing items.\n *\n * The CLI's unit tests for it are the `mergeGlobalConfigs` describe block in\n * `local-installer.test.ts`, which reach it through `config-gate/propagate.ts`.\n */\nexport function mergeGlobalConfigs(\n existing: ProjectConfig,\n incoming: ProjectConfig\n): { config: ProjectConfig; changed: boolean } {\n const existingSkillIds = new Set(existing.skills.map((s) => s.id))\n const existingAgentNames = new Set(existing.agents.map((a) => a.name))\n\n const incomingActiveSkills = incoming.skills.filter((s) => !s.excluded)\n const incomingActiveAgents = incoming.agents.filter((a) => !a.excluded)\n const newSkills = incomingActiveSkills.filter(\n (s) => !existingSkillIds.has(s.id)\n )\n const newAgents = incomingActiveAgents.filter(\n (a) => !existingAgentNames.has(a.name)\n )\n\n const mergedSkills = [...existing.skills, ...newSkills]\n const mergedAgents = [...existing.agents, ...newAgents]\n\n // Per-agent stack merge policy: deep-additive. Project-context edits must NEVER remove\n // or overwrite global state; individual projects express their local view via tombstones\n // in the PROJECT config, not by rewriting the GLOBAL config (see commit 403df46:\n // \"never modify global config from project-level operations\").\n //\n // Merge rule per triple (agent, category, skill):\n // - agent absent in existing -> add from incoming\n // - category absent in existing -> add from incoming\n // - skill id absent in existing -> append from incoming\n // - everything already present -> keep existing as-is (including its preloaded flag)\n // Anything present only in `existing` is left untouched.\n const { stack: mergedStack, changed: stackChanged } = additiveMergeStack(\n existing.stack,\n incoming.stack\n )\n\n // Merge selected domains (union, no duplicates)\n const mergedSelectedDomains = [\n ...new Set([\n ...(existing.selectedDomains ?? []),\n ...(incoming.selectedDomains ?? []),\n ]),\n ]\n\n // Marketplace identity (`marketplace`, `marketplaceName`) travels on the global partition of\n // `splitConfigByScope` but was previously lost here, leaving the global config with no\n // record of where its plugins came from. `uninstall` reads `config.marketplaceName` to build\n // the `<id>@<marketplace name>` registry key (getCliInstalledPluginKeys) — without it a global\n // uninstall silently owns nothing and leaves registered plugins behind.\n //\n // Precedence is FILL-ONLY: existing wins, incoming is used solely when the global config\n // has no value yet. Both fields are scalar but the merged config is multi-marketplace by\n // construction — this merge never removes skills, so after a second project init from a\n // different marketplace the skills array holds plugins from BOTH, and whichever label is\n // recorded orphans the other's registry key. Repointing is therefore never a strict\n // improvement, and doing it from a project context would silently rewrite global state on\n // behalf of every other registered project (commit 403df46). This also matches\n // `mergeConfigs`, which preserves `existingConfig.marketplaceName` on the home-root install\n // path. Changing global marketplace identity stays an explicit global-scope operation\n // (`init` run from ~), which writes the global config directly and bypasses this merge.\n const addsEntries = newSkills.length > 0 || newAgents.length > 0\n\n // No term for the marketplace identity: it is filled only alongside an added entry, and\n // `addsEntries` already marks the merge dirty — which is what `writeGlobalPairWhenChanged` (the\n // CLI's lib/config-gate/index.ts) tests before writing, so the fill is never dropped unwritten.\n const changed =\n addsEntries ||\n stackChanged ||\n !isDeepEqual(existing.selectedDomains ?? [], mergedSelectedDomains)\n\n return {\n config: {\n ...existing,\n skills: mergedSkills,\n agents: mergedAgents,\n stack: mergedStack,\n selectedDomains: mergedSelectedDomains,\n ...marketplaceIdentityFilledFrom(existing, addsEntries ? incoming : null),\n },\n changed,\n }\n}\n\n/**\n * The marketplace identity the global config records after a merge: its own, with each field it\n * lacks filled from `arriving` — and `arriving` is `null` unless the merge ADDS an entry.\n *\n * The entries are what came from that marketplace. A project session inlines every global entry\n * it holds, so the incoming half names the project's marketplace even when each of those entries\n * is the global install's own; filling from it then would name, in the one config every project\n * inherits, a catalogue none of its entries came from.\n */\nfunction marketplaceIdentityFilledFrom(\n existing: ProjectConfig,\n arriving: ProjectConfig | null\n): Pick<ProjectConfig, \"marketplace\" | \"marketplaceName\"> {\n const marketplaceName = existing.marketplaceName ?? arriving?.marketplaceName\n const marketplace = existing.marketplace ?? arriving?.marketplace\n return {\n ...(marketplaceName !== undefined && { marketplaceName }),\n ...(marketplace !== undefined && { marketplace }),\n }\n}\n\n/** What one resolution decided: the config to commit, and whether the global data changed. */\nexport type ResolvedGlobalConfig = { config: ProjectConfig; changed: boolean }\n\n/**\n * The standing resolution, and the one every caller but `edit --from` gets: the session's global\n * items are ADDED and nothing is taken away.\n *\n * A project install has asked nobody about the machine, so it may not decide for it (commit\n * 403df46, \"never modify global config from project-level operations\"). The `hasGlobalItems`\n * shortcut is part of that: a session carrying nothing global is not a statement that the global\n * install should be empty. So is {@link withTheDomainsItAdds}: a domain the session picked for the\n * project alone is not one the global install takes on.\n */\nexport function addSessionToGlobal(\n globalSplit: ProjectConfig,\n existingGlobalConfig: ProjectConfig | undefined,\n catalog: CompileCatalog\n): ResolvedGlobalConfig {\n const hasGlobalItems =\n globalSplit.skills.length > 0 || globalSplit.agents.length > 0\n if (!hasGlobalItems) {\n return {\n config: existingGlobalConfig ?? {\n name: GLOBAL_CONFIG_NAME,\n skills: [],\n agents: [],\n },\n changed: false,\n }\n }\n\n const arriving = withTheDomainsItAdds(\n globalSplit,\n existingGlobalConfig,\n catalog\n )\n if (!existingGlobalConfig) return { config: arriving, changed: true }\n return mergeGlobalConfigs(existingGlobalConfig, arriving)\n}\n\n/**\n * The session's global half, with its domains narrowed to those a global skill it ADDS comes from.\n *\n * A split hands its global half the session's whole domain selection, the project's own picks\n * included, because a domain selection belongs to the config it is written in. Merged into the\n * global install as it stands, a project that picked an API skill for itself would widen the\n * domains every project inherits — and rewrite the global config while adding nothing to it.\n */\nfunction withTheDomainsItAdds(\n globalSplit: ProjectConfig,\n existingGlobalConfig: ProjectConfig | undefined,\n catalog: CompileCatalog\n): ProjectConfig {\n const installed = new Set(\n existingGlobalConfig?.skills.map((skill) => skill.id)\n )\n const arrivingDomains = new Set(\n globalSplit.skills\n .filter((skill) => !skill.excluded && !installed.has(skill.id))\n .map((skill) => skillDomain(catalog, skill.id))\n )\n return {\n ...globalSplit,\n selectedDomains: (globalSplit.selectedDomains ?? []).filter((domain) =>\n arrivingDomains.has(domain)\n ),\n }\n}\n","import type { PropagatedRecompileSummary } from \"../operations/project/recompile-project-agents.js\";\n\n/** The summary of a fan-out that recompiled nothing, so callers render one shape. */\nexport const NOTHING_RECOMPILED: PropagatedRecompileSummary = {\n rewrittenCount: 0,\n unchangedCount: 0,\n failedCount: 0,\n warnings: [],\n};\n\n/**\n * Recompiles the agents of every project a global write propagated into.\n *\n * Imported lazily: `recompile-project-agents` lives in the operations layer,\n * which imports back into `installation`, so a static import here would form a\n * load-time cycle.\n * Per-project failure isolation lives in `recompilePropagatedProjectAgents` and\n * is unchanged by running it from inside the write.\n */\nexport async function recompilePropagated(\n projectDirs: string[],\n): Promise<PropagatedRecompileSummary> {\n if (projectDirs.length === 0) return NOTHING_RECOMPILED;\n\n const { recompilePropagatedProjectAgents } =\n await import(\"../operations/project/recompile-project-agents.js\");\n return recompilePropagatedProjectAgents(projectDirs);\n}\n","import path from \"path\";\nimport {\n buildAndMergeConfig,\n resolveInstallPaths,\n isHomeDirectory,\n} from \"../../installation/index.js\";\nimport type { SourceLoadResult } from \"../../loading/index.js\";\nimport type { AuthoritativeScope } from \"../../configuration/index.js\";\nimport {\n ensureBlankPair,\n writeScopedFromWizard,\n type GateReport,\n} from \"../../config-gate/index.js\";\nimport { providerInUse } from \"../../installation/install-layout.js\";\nimport { loadAgentDefs, type AgentDefs } from \"./load-agent-defs.js\";\nimport { ensureDir } from \"../../../utils/fs.js\";\nimport type { ProjectConfig } from \"../../../types/index.js\";\nimport type { WizardResultV2 } from \"../../../components/wizard/wizard.js\";\n\nexport type ConfigWriteOptions = {\n wizardResult: WizardResultV2;\n sourceResult: SourceLoadResult;\n projectDir: string;\n sourceFlag?: string;\n /**\n * What {@link loadAgentDefs} answered, for a caller that has already asked it. Omitted, this\n * function asks for itself.\n *\n * It takes the WHOLE value rather than the roster inside it, so the only thing a caller can\n * hand over is what that one function produced. This used to be a bare\n * `Partial<Record<AgentName, AgentDefinition>>` — any map at all — which made \"a roster\n * different from the one the CLI would load\" a representable argument, and this function emits\n * the `AgentName` / `SelectedAgentName` unions from whatever it is given. Deleting the option\n * instead would cost `edit` a second uncached walk-and-parse of `src/agents/` per run, since\n * it needs `sourcePath` off the same value for its compile pass — one fact loaded twice, where\n * the second load is the one that can disagree.\n */\n agentDefs?: AgentDefs;\n /**\n * Authority of `cc edit`'s newConfig over absent entries:\n * `\"all\"` (global edit) drops any deselected entry, `\"owned\"` (project edit) drops deselected\n * project-owned entries only, `undefined` (init) keeps additive union-preserve.\n */\n authoritativeScope?: AuthoritativeScope;\n};\n\nexport type ConfigWriteResult = {\n config: ProjectConfig;\n configPath: string;\n wasMerged: boolean;\n existingConfigPath?: string;\n /**\n * What the gated write did: what moved in the global config, which registered\n * projects it fanned out to, and the recompile it drove in them. The caller\n * renders it; the work is already done.\n */\n propagation: GateReport;\n};\n\n/**\n * Builds, merges, and writes project configuration files.\n *\n * Handles the full config pipeline:\n * 1. buildAndMergeConfig() — generates config from wizard result, merges with existing\n * 2. loadAgentDefs() — the CLI's own sub-agent definitions, for config-types generation\n * 3. ensureBlankPair() — ensures the global config pair exists (when in project context)\n * 4. writeScopedFromWizard() — writes config.ts and config-types.ts split by scope,\n * fans global changes out to registered projects and recompiles their agents\n */\nexport async function writeProjectConfig(options: ConfigWriteOptions): Promise<ConfigWriteResult> {\n const { wizardResult, sourceResult, projectDir, sourceFlag } = options;\n const projectPaths = resolveInstallPaths(projectDir, \"project\");\n\n await ensureDir(path.dirname(projectPaths.configPath));\n\n // The marketplace is deliberately NOT consulted for this roster. It names the sub-agents the\n // emitted unions declare, and a marketplace-defined name in `AgentName` is one no compile pass\n // can honour: agent partials resolve through `getLocalAgentDefinitions`, which answers the\n // CLI's own root, and the generated `AGENT_NAMES` is built from that same directory. Reading\n // `sourceResult.sourcePath` here is what made `init` and `edit` emit two different files from\n // one config (owner ruling 2026-08-21).\n const { agents } = options.agentDefs ?? (await loadAgentDefs());\n\n const mergeResult = await buildAndMergeConfig(\n wizardResult,\n sourceResult,\n projectDir,\n sourceFlag,\n options.authoritativeScope,\n );\n const finalConfig = mergeResult.config;\n\n const isProjectContext = !isHomeDirectory(projectDir);\n\n if (isProjectContext) {\n // The blank global pair belongs to the installation THIS project is on: a project\n // inherits only from the global of its own provider, so creating the other provider's\n // pair would leave the one it actually reads absent.\n await ensureBlankPair(providerInUse(projectDir));\n }\n\n const propagation = await writeScopedFromWizard({\n finalConfig,\n matrix: sourceResult.matrix,\n agents,\n projectDir,\n projectConfigPath: projectPaths.configPath,\n projectInstallationExists: isProjectContext,\n // The same word the merger above took. It decides the config ROW there and the GLOBAL\n // config a project write commits here, and the two files disagree the moment it does not\n // reach both: a row preserved for a skill whose directory the removal diff deleted.\n ...(options.authoritativeScope !== undefined && {\n authoritativeScope: options.authoritativeScope,\n }),\n });\n\n return {\n config: finalConfig,\n configPath: projectPaths.configPath,\n wasMerged: mergeResult.merged,\n ...(mergeResult.existingConfigPath !== undefined && {\n existingConfigPath: mergeResult.existingConfigPath,\n }),\n propagation,\n };\n}\n","import os from \"os\";\nimport { compileAgents, type CompilationResult } from \"./compile-agents.js\";\nimport { isHomeDirectory, resolveInstallPaths } from \"../../installation/index.js\";\nimport type { AgentName, SkillDefinitionMap, SkillScope } from \"../../../types/index.js\";\n\nexport type CompileAllScopesOptions = {\n /** Working directory of the command — a project root, or home for global context. */\n projectDir: string;\n sourcePath: string;\n skills: SkillDefinitionMap;\n agentScopeMap: Map<AgentName, SkillScope>;\n};\n\n/**\n * Compiles agents for every scope the current context owns.\n *\n * Home context: a single pass writing to the home agents directory.\n * Project context: a global pass (from home) then a project pass, each\n * filtered to its own scope so the project pass cannot overwrite global\n * agents with zero-skill versions. Results merge in pass order.\n */\nexport async function compileAgentsAllScopes(\n options: CompileAllScopesOptions,\n): Promise<CompilationResult> {\n const { projectDir, sourcePath, skills, agentScopeMap } = options;\n const base = { sourcePath, skills, agentScopeMap };\n\n if (isHomeDirectory(projectDir)) {\n return compileAgents({\n ...base,\n projectDir,\n outputDir: resolveInstallPaths(projectDir, \"project\").agentsDir,\n });\n }\n\n const globalResult = await compileAgents({\n ...base,\n projectDir: os.homedir(),\n outputDir: resolveInstallPaths(os.homedir(), \"global\").agentsDir,\n scopeFilter: \"global\",\n });\n const projectResult = await compileProjectScope(options);\n return mergeCompilationResults(globalResult, projectResult);\n}\n\n/**\n * The project pass alone, filtered to the project's own scope — what a project run compiles when\n * it leaves the global install exactly as installed, so its global pass would have nothing to\n * write but the drift it was never asked to repair.\n */\nexport async function compileProjectScope(\n options: CompileAllScopesOptions,\n): Promise<CompilationResult> {\n const { projectDir, sourcePath, skills, agentScopeMap } = options;\n return compileAgents({\n sourcePath,\n skills,\n agentScopeMap,\n projectDir,\n outputDir: resolveInstallPaths(projectDir, \"project\").agentsDir,\n scopeFilter: \"project\",\n });\n}\n\nfunction mergeCompilationResults(...results: CompilationResult[]): CompilationResult {\n return {\n compiled: results.flatMap((r) => r.compiled),\n rewritten: results.flatMap((r) => r.rewritten),\n failed: results.flatMap((r) => r.failed),\n warnings: results.flatMap((r) => r.warnings),\n };\n}\n"],"mappings":"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","names":["os","path","matrix","matrix","isDeepEqual","isDeepEqual","typedEntries","isDeepEqual","matrix","isDeepEqual","recompilePropagatedProjectAgents","loadSkillsMatrixFromSource","matrix","loadAgentDefs","os","path","ensureBlankPair","path","path","ensureBlankPair","os","os"]}
|
package/dist/chunk-HHIYT3PI.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
Wizard
|
|
4
|
-
} from "./chunk-WWIFNPJG.js";
|
|
5
|
-
import {
|
|
6
|
-
promptValue
|
|
7
|
-
} from "./chunk-4AAOOAIN.js";
|
|
8
|
-
import {
|
|
9
|
-
hydrateWizardStore
|
|
10
|
-
} from "./chunk-7JNJ52CD.js";
|
|
11
|
-
import {
|
|
12
|
-
disableBuffering,
|
|
13
|
-
drainBuffer,
|
|
14
|
-
enableBuffering
|
|
15
|
-
} from "./chunk-A4KDXNOT.js";
|
|
16
|
-
|
|
17
|
-
// src/cli/components/wizard/run-wizard-session.tsx
|
|
18
|
-
import "react";
|
|
19
|
-
import { jsx } from "react/jsx-runtime";
|
|
20
|
-
function hydrateIntoStartupBand(hydrate, loaded = []) {
|
|
21
|
-
enableBuffering();
|
|
22
|
-
try {
|
|
23
|
-
hydrateWizardStore(hydrate);
|
|
24
|
-
return [...loaded, ...drainBuffer()];
|
|
25
|
-
} finally {
|
|
26
|
-
disableBuffering();
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
async function runWizardSession(options) {
|
|
30
|
-
const startupMessages = hydrateIntoStartupBand(options.hydrate, options.props.startupMessages);
|
|
31
|
-
const result = await promptValue(
|
|
32
|
-
(resolve) => /* @__PURE__ */ jsx(
|
|
33
|
-
Wizard,
|
|
34
|
-
{
|
|
35
|
-
...options.props,
|
|
36
|
-
startupMessages,
|
|
37
|
-
onComplete: resolve,
|
|
38
|
-
onCancel: () => {
|
|
39
|
-
options.onCancel();
|
|
40
|
-
resolve(null);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
),
|
|
44
|
-
{ onExit: null, clearOnResolve: true }
|
|
45
|
-
);
|
|
46
|
-
options.clearTerminal();
|
|
47
|
-
return !result || result.cancelled ? null : result;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export {
|
|
51
|
-
runWizardSession
|
|
52
|
-
};
|
|
53
|
-
//# sourceMappingURL=chunk-HHIYT3PI.js.map
|