agents-inc 0.165.0 → 0.166.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 +15 -0
- package/dist/{chunk-B5UUHBAM.js → chunk-25MPBIK3.js} +3 -3
- package/dist/{chunk-VPJUCVU7.js → chunk-2VUYFTUB.js} +6 -6
- package/dist/{chunk-BGU25XAY.js → chunk-4NDBUJHK.js} +8 -8
- package/dist/{chunk-5M3PQNXI.js → chunk-6L75SQZU.js} +2 -2
- package/dist/{chunk-YNOOZFIB.js → chunk-6LF4CQBY.js} +2 -2
- package/dist/{chunk-UBSSIIWD.js → chunk-7JNJ52CD.js} +4 -4
- package/dist/{chunk-IM45LLQX.js → chunk-A4KDXNOT.js} +47 -137
- package/dist/chunk-A4KDXNOT.js.map +1 -0
- package/dist/{chunk-6SW6SB7H.js → chunk-DAIEQJIF.js} +3 -3
- package/dist/{chunk-AQXKAPCD.js → chunk-ENGYJCTF.js} +1 -3
- package/dist/{chunk-AQXKAPCD.js.map → chunk-ENGYJCTF.js.map} +1 -1
- package/dist/{chunk-6AIZPSEI.js → chunk-G4JK7WVO.js} +15 -16
- package/dist/chunk-G4JK7WVO.js.map +1 -0
- package/dist/{chunk-WTANOSXK.js → chunk-G6UREQ4V.js} +9 -9
- package/dist/chunk-G6UREQ4V.js.map +1 -0
- package/dist/{chunk-V5CWACR5.js → chunk-GE3WKJ6Q.js} +4 -4
- package/dist/{chunk-V5CWACR5.js.map → chunk-GE3WKJ6Q.js.map} +1 -1
- package/dist/{chunk-JV4TKXEN.js → chunk-HHIYT3PI.js} +4 -4
- package/dist/{chunk-WIFIAQZU.js → chunk-HWEKHVLE.js} +6 -6
- package/dist/{chunk-5P26XBZO.js → chunk-JQRJQ4SS.js} +3 -3
- package/dist/{chunk-Y5SNGMB7.js → chunk-LFWLEDUG.js} +6 -6
- package/dist/{chunk-TRTVOVHR.js → chunk-MAWNVT2C.js} +4 -4
- package/dist/{chunk-57KU6SPH.js → chunk-MPTNC32T.js} +3 -3
- package/dist/{chunk-MBUGQI2M.js → chunk-NBXXFIIW.js} +3 -3
- package/dist/{chunk-42TNV2TK.js → chunk-PMJWD27C.js} +3 -3
- package/dist/{chunk-X2KUI7V3.js → chunk-PNB3524H.js} +5 -5
- package/dist/{chunk-IVNKU4DM.js → chunk-PXWG7KFO.js} +2 -2
- package/dist/chunk-PXWG7KFO.js.map +1 -0
- package/dist/{chunk-LXXGZAD3.js → chunk-QHDPLLK6.js} +3 -3
- package/dist/{chunk-TV5Y53R2.js → chunk-RAHSOJQ2.js} +2 -2
- package/dist/{chunk-CTUJO23Z.js → chunk-RZDXDMT6.js} +2 -2
- package/dist/{chunk-PSX7CT6V.js → chunk-SAYZWCH6.js} +2 -2
- package/dist/{chunk-MWDY76MT.js → chunk-SCOQ34YM.js} +2 -2
- package/dist/{chunk-4C7GL4F7.js → chunk-SXVHSOTH.js} +4 -4
- package/dist/{chunk-E6CHHXGY.js → chunk-T6UWJAB3.js} +2 -2
- package/dist/{chunk-F2ZWFWTP.js → chunk-TODCQFRB.js} +2 -2
- package/dist/{chunk-CEBDGZID.js → chunk-TTY6DPJN.js} +3 -3
- package/dist/{chunk-CBMAO6OP.js → chunk-TZ2YJJKK.js} +5 -5
- package/dist/{chunk-WSGX4IWC.js → chunk-V3I7VQ7N.js} +4 -4
- package/dist/{chunk-GKPW33FP.js → chunk-V5WXIM2F.js} +2 -2
- package/dist/{chunk-6CY6PW5X.js → chunk-VAPTVR3J.js} +3 -3
- package/dist/{chunk-CK5RM3DJ.js → chunk-WVUDLKIO.js} +2 -2
- package/dist/{chunk-HR4YDSJK.js → chunk-WWIFNPJG.js} +12 -12
- package/dist/{chunk-P3KJCYSR.js → chunk-XZEVWPX6.js} +7 -7
- package/dist/{chunk-AAWZED5M.js → chunk-Y3VIRVVE.js} +10 -58
- package/dist/chunk-Y3VIRVVE.js.map +1 -0
- package/dist/{chunk-XATIKM3P.js → chunk-Y47EV74W.js} +7 -7
- package/dist/{chunk-UR3RRTPO.js → chunk-ZR4YPKXQ.js} +4 -4
- package/dist/{chunk-UCORXS4D.js → chunk-ZXKBERQU.js} +2 -2
- package/dist/commands/build/marketplace.js +6 -6
- package/dist/commands/build/plugins.js +8 -8
- package/dist/commands/compile.js +14 -15
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/doctor.js +59 -156
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/edit.js +40 -41
- package/dist/commands/edit.js.map +1 -1
- package/dist/commands/eject.js +12 -13
- package/dist/commands/eject.js.map +1 -1
- package/dist/commands/init.js +39 -39
- package/dist/commands/list.js +9 -9
- package/dist/commands/new/marketplace.js +6 -6
- package/dist/commands/search.js +11 -11
- package/dist/commands/share.js +11 -12
- package/dist/commands/share.js.map +1 -1
- package/dist/commands/uninstall.js +39 -69
- 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/select-list.js +2 -2
- package/dist/components/wizard/category-grid.js +4 -4
- package/dist/components/wizard/checkbox-grid.js +4 -4
- package/dist/components/wizard/domain-selection.js +9 -9
- package/dist/components/wizard/run-wizard-session.js +26 -26
- package/dist/components/wizard/scroll-affordance.js +2 -2
- package/dist/components/wizard/skill-agent-summary.js +5 -5
- package/dist/components/wizard/source-grid.js +5 -5
- package/dist/components/wizard/stack-selection.js +7 -7
- package/dist/components/wizard/step-agents.js +8 -8
- package/dist/components/wizard/step-build.js +7 -7
- package/dist/components/wizard/step-confirm.js +9 -9
- package/dist/components/wizard/step-sources.js +8 -8
- package/dist/components/wizard/step-stack.js +8 -8
- package/dist/components/wizard/summary-panel.js +8 -8
- package/dist/components/wizard/toast.js +2 -2
- package/dist/components/wizard/wizard-layout.js +12 -12
- package/dist/components/wizard/wizard-tabs.js +2 -2
- package/dist/components/wizard/wizard.js +25 -25
- package/dist/hooks/init.js +50 -40
- package/dist/hooks/init.js.map +1 -1
- package/dist/load-agent-defs-3C27ENC7.js +14 -0
- package/dist/recompile-project-agents-DGNBCU3N.js +18 -0
- package/dist/{source-loader-RWZAYJ7E.js → source-loader-7KZ3MD5O.js} +4 -4
- package/dist/stores/wizard-store.js +4 -4
- package/package.json +1 -1
- package/src/schemas/project-config.schema.json +1 -1
- package/src/schemas/project-source-config.schema.json +1 -1
- package/dist/chunk-6AIZPSEI.js.map +0 -1
- package/dist/chunk-AAWZED5M.js.map +0 -1
- package/dist/chunk-IM45LLQX.js.map +0 -1
- package/dist/chunk-IVNKU4DM.js.map +0 -1
- package/dist/chunk-WTANOSXK.js.map +0 -1
- package/dist/load-agent-defs-JHLE6MAE.js +0 -14
- package/dist/recompile-project-agents-ZPP5NOLZ.js +0 -18
- /package/dist/{chunk-B5UUHBAM.js.map → chunk-25MPBIK3.js.map} +0 -0
- /package/dist/{chunk-VPJUCVU7.js.map → chunk-2VUYFTUB.js.map} +0 -0
- /package/dist/{chunk-BGU25XAY.js.map → chunk-4NDBUJHK.js.map} +0 -0
- /package/dist/{chunk-5M3PQNXI.js.map → chunk-6L75SQZU.js.map} +0 -0
- /package/dist/{chunk-YNOOZFIB.js.map → chunk-6LF4CQBY.js.map} +0 -0
- /package/dist/{chunk-UBSSIIWD.js.map → chunk-7JNJ52CD.js.map} +0 -0
- /package/dist/{chunk-6SW6SB7H.js.map → chunk-DAIEQJIF.js.map} +0 -0
- /package/dist/{chunk-JV4TKXEN.js.map → chunk-HHIYT3PI.js.map} +0 -0
- /package/dist/{chunk-WIFIAQZU.js.map → chunk-HWEKHVLE.js.map} +0 -0
- /package/dist/{chunk-5P26XBZO.js.map → chunk-JQRJQ4SS.js.map} +0 -0
- /package/dist/{chunk-Y5SNGMB7.js.map → chunk-LFWLEDUG.js.map} +0 -0
- /package/dist/{chunk-TRTVOVHR.js.map → chunk-MAWNVT2C.js.map} +0 -0
- /package/dist/{chunk-57KU6SPH.js.map → chunk-MPTNC32T.js.map} +0 -0
- /package/dist/{chunk-MBUGQI2M.js.map → chunk-NBXXFIIW.js.map} +0 -0
- /package/dist/{chunk-42TNV2TK.js.map → chunk-PMJWD27C.js.map} +0 -0
- /package/dist/{chunk-X2KUI7V3.js.map → chunk-PNB3524H.js.map} +0 -0
- /package/dist/{chunk-LXXGZAD3.js.map → chunk-QHDPLLK6.js.map} +0 -0
- /package/dist/{chunk-TV5Y53R2.js.map → chunk-RAHSOJQ2.js.map} +0 -0
- /package/dist/{chunk-CTUJO23Z.js.map → chunk-RZDXDMT6.js.map} +0 -0
- /package/dist/{chunk-PSX7CT6V.js.map → chunk-SAYZWCH6.js.map} +0 -0
- /package/dist/{chunk-MWDY76MT.js.map → chunk-SCOQ34YM.js.map} +0 -0
- /package/dist/{chunk-4C7GL4F7.js.map → chunk-SXVHSOTH.js.map} +0 -0
- /package/dist/{chunk-E6CHHXGY.js.map → chunk-T6UWJAB3.js.map} +0 -0
- /package/dist/{chunk-F2ZWFWTP.js.map → chunk-TODCQFRB.js.map} +0 -0
- /package/dist/{chunk-CEBDGZID.js.map → chunk-TTY6DPJN.js.map} +0 -0
- /package/dist/{chunk-CBMAO6OP.js.map → chunk-TZ2YJJKK.js.map} +0 -0
- /package/dist/{chunk-WSGX4IWC.js.map → chunk-V3I7VQ7N.js.map} +0 -0
- /package/dist/{chunk-GKPW33FP.js.map → chunk-V5WXIM2F.js.map} +0 -0
- /package/dist/{chunk-6CY6PW5X.js.map → chunk-VAPTVR3J.js.map} +0 -0
- /package/dist/{chunk-CK5RM3DJ.js.map → chunk-WVUDLKIO.js.map} +0 -0
- /package/dist/{chunk-HR4YDSJK.js.map → chunk-WWIFNPJG.js.map} +0 -0
- /package/dist/{chunk-P3KJCYSR.js.map → chunk-XZEVWPX6.js.map} +0 -0
- /package/dist/{chunk-XATIKM3P.js.map → chunk-Y47EV74W.js.map} +0 -0
- /package/dist/{chunk-UR3RRTPO.js.map → chunk-ZR4YPKXQ.js.map} +0 -0
- /package/dist/{chunk-UCORXS4D.js.map → chunk-ZXKBERQU.js.map} +0 -0
- /package/dist/{load-agent-defs-JHLE6MAE.js.map → load-agent-defs-3C27ENC7.js.map} +0 -0
- /package/dist/{recompile-project-agents-ZPP5NOLZ.js.map → recompile-project-agents-DGNBCU3N.js.map} +0 -0
- /package/dist/{source-loader-RWZAYJ7E.js.map → source-loader-7KZ3MD5O.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/commands/doctor.ts","../../src/cli/lib/installation/layout-findings.ts","../../src/cli/lib/source-validator.ts","../../src/cli/lib/content-validator.ts"],"sourcesContent":["import os from \"os\";\nimport path from \"path\";\nimport { BaseCommand } from \"../base-command\";\nimport { getErrorMessage } from \"../utils/errors\";\nimport { EXIT_CODES } from \"../lib/exit-codes\";\nimport {\n effectivelyExcludedSkillIds,\n findConfigLoadFailures,\n loadInstalledConfig,\n validateProjectConfig,\n SOURCE_ENV_VAR,\n type ResolvedConfig,\n} from \"../lib/configuration\";\nimport { loadSource, detectProject, type DetectedProject } from \"../lib/operations\";\nimport { matrix } from \"../lib/matrix/matrix-provider\";\nimport { discoverLocalSkills } from \"../lib/skills\";\nimport { getStackSkillIds } from \"../lib/stacks\";\nimport { filterExcludedEntries, listAgentFilesOf } from \"../lib/agents\";\nimport { getVerifiedPluginInstallPaths, parseMarketplacePluginRef } from \"../lib/plugins\";\nimport {\n declaresNoContent,\n isHomeDirectory,\n installBaseDir,\n resolveInstallPaths,\n} from \"../lib/installation\";\nimport {\n agentCodec,\n type AgentCodec,\n hostCompilesAgent,\n providerInUse,\n relativeConfigPath,\n resolveSourceDir,\n skillsDir,\n skillsPathPrefix,\n} from \"../lib/installation/install-layout\";\nimport { ambiguityFinding } from \"../lib/installation/provider-flag\";\nimport { unofferablePlacementsFound } from \"../lib/hosts/configured-placements\";\nimport { sourceScopesInPlay, type ScopeKind } from \"../lib/installation/source-scopes\";\nimport { layoutFindingFor, type LayoutFinding } from \"../lib/installation/layout-findings\";\nimport {\n REGISTRY_IS_NOT_AN_INVENTORY,\n recreateConfigFrom,\n scopeLabel,\n scopeNoun,\n} from \"../utils/messages\";\nimport { getInstalledConfigPath } from \"../lib/installation/install-base-dir\";\nimport { isSourceRepo } from \"../lib/source-validator\";\nimport {\n listInstalledArtifacts,\n validateInstalledAgents,\n validateInstalledPlugins,\n validateInstalledSkills,\n validateProjectConfigFile,\n validateRegisteredSources,\n type ContentIssue,\n type ContentValidation,\n} from \"../lib/content-validator\";\nimport type { SourceLoadResult } from \"../lib/loading\";\nimport type {\n AgentName,\n AgentScopeConfig,\n MergedSkillsMatrix,\n ProjectConfig,\n SkillConfig,\n SkillId,\n SkillScope,\n} from \"../types\";\nimport { fileExists, directoryExists } from \"../utils/fs\";\nimport { plural } from \"../utils/string\";\nimport {\n CLI_INVOKE_COMMAND,\n DEFAULT_BRANDING,\n EJECT_SOURCE,\n STANDARD_FILES,\n UI_SYMBOLS,\n type Provider,\n} from \"../consts\";\nimport { countBy, unique } from \"remeda\";\n\ntype CheckKind =\n | \"config\"\n | \"config-empty\"\n | \"layout\"\n | \"placements\"\n | \"skills\"\n | \"agents\"\n | \"orphans\"\n | \"orphans-unowned\"\n | \"installed\"\n | \"plugins\"\n | \"source\"\n | \"content-config\"\n | \"content-sources\"\n | \"content-plugins\"\n | \"content-skills\"\n | \"content-agents\";\n\ntype CheckResult = {\n kind: CheckKind;\n status: \"pass\" | \"fail\" | \"warn\" | \"skip\";\n message: string;\n details?: string[];\n};\n\ntype ConfigCheckOutput = {\n result: CheckResult;\n config: ProjectConfig | null;\n};\n\n/**\n * Which state the config these rows describe is in. `detectInstallation` answers \"is there an\n * installation here\", not \"is there a config here\", and hands back the same `null` for an absent\n * file and for one that loads while declaring neither skills nor agents — two states that need\n * different sentences. The state missing from this union is the one that cannot reach this layer:\n * a config that cannot be READ is the content layer's finding, and it skips the operational layer.\n *\n * A loaded config says whether it is this directory's own or the global one it inherits: a\n * directory with no config of its own under a global installation reads the global config, and a\n * row naming this directory's path for it describes a file that is not there.\n */\ntype ConfigState =\n | { kind: \"absent\" }\n | { kind: \"declares-nothing\"; config: ProjectConfig }\n | { kind: \"loaded\"; config: ProjectConfig; inherited: boolean };\n\n/**\n * The config in THIS directory, and what state it is in. Only this directory: the global fallback\n * is `detectInstallation`'s to make and it has already made it, so a project with no config of its\n * own is absent here whatever the home directory holds — `init` writes exactly the declares-nothing\n * shape as the blank global pair on every project setup, and it is not this project's config.\n *\n * The load cannot throw: `configDirsInPlay` is the same set the content layer just read, and any\n * failure in it skipped this layer entirely.\n */\nasync function resolveConfigState(\n detected: DetectedProject | null,\n projectDir: string,\n): Promise<ConfigState> {\n if (detected?.config) {\n return {\n kind: \"loaded\",\n config: detected.config,\n inherited: inheritsTheGlobalConfig(detected.installation.projectDir, projectDir),\n };\n }\n\n const loaded = await loadInstalledConfig(projectDir);\n if (!loaded) return { kind: \"absent\" };\n\n return declaresNoContent(loaded.config)\n ? { kind: \"declares-nothing\", config: loaded.config }\n : { kind: \"loaded\", config: loaded.config, inherited: false };\n}\n\n/** Whether the config detection read for `projectDir` is the home directory's, inherited. */\nfunction inheritsTheGlobalConfig(readFrom: string, projectDir: string): boolean {\n return isHomeDirectory(readFrom) && !isHomeDirectory(projectDir);\n}\n\n/**\n * `projectDir` rather than a module-level constant for the path this reports about: a scope's\n * source folder is resolved from disk, so nothing about it exists at module load, and every line\n * below has to name the folder this project is ACTUALLY on. A constant would tell a user on the\n * old name to go and edit a file that is not there.\n */\nfunction checkConfigValid(state: ConfigState, projectDir: string): ConfigCheckOutput {\n const theConfig = (verdict: string) => configRowMessage(state, projectDir, verdict);\n if (state.kind === \"absent\") {\n return {\n result: {\n kind: \"config\",\n status: \"fail\",\n message: theConfig(\"not found\"),\n details: [`Run '${CLI_INVOKE_COMMAND} init' to create a configuration`],\n },\n config: null,\n };\n }\n\n // Not a fault and nothing to repair — the file is valid, it just has nothing in it. The config\n // is still handed on: the rows below describe an empty install truthfully, where a `null` would\n // have them all report `Skipped (config invalid)` about a config that is valid.\n if (state.kind === \"declares-nothing\") {\n return {\n result: {\n kind: \"config-empty\",\n status: \"warn\",\n message: theConfig(\"is valid but declares no skills and no agents\"),\n },\n config: state.config,\n };\n }\n\n const { config } = state;\n const validation = validateProjectConfig(config);\n\n if (!validation.valid) {\n return {\n result: {\n kind: \"config\",\n status: \"fail\",\n message: theConfig(\"has errors\"),\n details: validation.errors,\n },\n config: null,\n };\n }\n\n if (validation.warnings.length > 0) {\n return {\n result: {\n kind: \"config\",\n status: \"warn\",\n message: theConfig(\"has warnings\"),\n details: validation.warnings,\n },\n config,\n };\n }\n\n return {\n result: {\n kind: \"config\",\n status: \"pass\",\n message: theConfig(\"is valid\"),\n },\n config,\n };\n}\n\n/**\n * What a Config Valid row says, led by whose config it is: this project's, or the global\n * installation's — the nouns the Layout rows lead with.\n *\n * A directory with no config of its own under a global installation reads the global one, and the\n * row says so, naming the file it actually read rather than this directory's path for a file that\n * is not there.\n */\nfunction configRowMessage(state: ConfigState, projectDir: string, verdict: string): string {\n if (state.kind === \"loaded\" && state.inherited)\n return `${theInheritedConfig()}, which ${verdict}`;\n return `${theOwnConfig(projectDir)} ${verdict}`;\n}\n\n/** `This project: .agents-inc/claude/config.ts`, or the global installation's from home. */\nfunction theOwnConfig(projectDir: string): string {\n return `${scopeLabel(scopeOf(projectDir))}: ${configNamedFrom(projectDir)}`;\n}\n\n/** A project with no config of its own, and the global one it reads in its place. */\nfunction theInheritedConfig(): string {\n return `${scopeLabel(\"project\")}: no ${STANDARD_FILES.CONFIG_TS}, using ${scopeNoun(\"global\")}'s (${configNamedFrom(os.homedir())})`;\n}\n\n/** The scope whose config a directory holds: the home directory's is the global one. */\nfunction scopeOf(dir: string): ScopeKind {\n return isHomeDirectory(dir) ? \"global\" : \"project\";\n}\n\n/**\n * A config file as a row names it: relative to the project it belongs to, and from home for the\n * global one — named relative to a project, the global config is a file that project does not have.\n */\nfunction configNamedFrom(root: string): string {\n const relPath = relativeConfigPath(root, providerInUse(root));\n return isHomeDirectory(root) ? `~/${relPath}` : relPath;\n}\n\nasync function checkSkillsResolved(\n config: ProjectConfig,\n matrix: MergedSkillsMatrix,\n projectDir: string,\n): Promise<CheckResult> {\n // config.ts declares skills both directly and through the stack. A global-only\n // install has a populated skills array and no stack at all, so keying this check\n // off the stack alone would report \"No skills configured\" for a real install.\n const excludedIds = effectivelyExcludedSkillIds(config.skills);\n const stackIds = config.stack ? getStackSkillIds(config.stack) : [];\n const uniqueSkills = unique([...stackIds, ...config.skills.map((s) => s.id)]).filter(\n (id) => !excludedIds.has(id),\n );\n\n if (uniqueSkills.length === 0) {\n return {\n kind: \"skills\",\n status: \"pass\",\n message: \"No skills configured\",\n };\n }\n\n const [localResult, globalResult] = await Promise.all([\n discoverLocalSkills(projectDir),\n !isHomeDirectory(projectDir) ? discoverLocalSkills(os.homedir()) : null,\n ]);\n const localSkillIds = new Set([\n ...(localResult?.skills.map((s) => s.id) ?? []),\n ...(globalResult?.skills.map((s) => s.id) ?? []),\n ]);\n\n const missingSkills = uniqueSkills.filter(\n (skillId) => !(skillId in matrix.skills) && !localSkillIds.has(skillId),\n );\n\n if (missingSkills.length > 0) {\n return {\n kind: \"skills\",\n status: \"fail\",\n message: `${ratio(uniqueSkills.length - missingSkills.length, uniqueSkills.length, \"skill\")} found`,\n details: missingSkills.map((s) => `- ${s} (not found)`),\n };\n }\n\n return {\n kind: \"skills\",\n status: \"pass\",\n message: `${ratio(uniqueSkills.length, uniqueSkills.length, \"skill\")} found`,\n };\n}\n\n/**\n * Whether every configured sub-agent is compiled where its own host reads it.\n *\n * **Two things here were Claude's and are the host's**, and both were found by running the\n * command on a healthy Codex installation rather than by reading it:\n *\n * - the FILENAME was `${name}.md`. Claude reads `.md` and Codex reads `.toml`, so on Codex this\n * looked for a file no release will ever write. {@link agentCodec} answers the extension, and\n * this is the extension ban's own census in action — the eslint config leaves `.md` unbanned\n * precisely because product modules like this one still spell it.\n * - the VERDICT was \"needs recompilation\", for a sub-agent {@link hostCompilesAgent} says this\n * host does not carry at all. Recompiling writes nothing, so the row asked for a remedy that\n * does not exist and turned an install working exactly as this release intends into a warning.\n * With C5 that is no longer a whole provider but two named sub-agents — `agent-summoner` and\n * `skill-summoner`, which Codex leaves out for v1 — so the row names them rather than the host.\n *\n * The host is read per SCOPE, the way `resolveInstallPaths` reads it beside this: two scopes can\n * be on two providers, and one answer for both is how a row reports about the wrong installation.\n */\nasync function checkAgentsCompiled(\n config: ProjectConfig,\n projectDir: string,\n): Promise<CheckResult> {\n const agents = config.agents;\n\n if (agents.length === 0) {\n return {\n kind: \"agents\",\n status: \"pass\",\n message: \"No agents configured\",\n };\n }\n\n const hosts = {\n global: providerInUse(installBaseDir(projectDir, \"global\")),\n project: providerInUse(installBaseDir(projectDir, \"project\")),\n };\n const dirs = {\n global: resolveInstallPaths(projectDir, \"global\").agentsDir,\n project: resolveInstallPaths(projectDir, \"project\").agentsDir,\n };\n\n const agentChecks = await Promise.all(\n agents.map((agent) => agentCompileCheck(agent, hosts, dirs)),\n );\n\n const unrenderable = agentChecks.filter((c) => c.state === \"unrenderable\");\n const missingAgents = agentChecks.filter((c) => c.state === \"missing\").map((c) => c.name);\n\n // The no-renderer answer leads when it is the whole story, because then nothing is wrong and\n // nothing can be done: a `warn` here would make every `doctor` on a Codex installation read as\n // a fault report about the one thing this release has already said it does not do yet. The\n // providers come off the rows themselves rather than off both scopes, so a project on one host\n // under a global on another names only the host whose sub-agents this is about.\n if (unrenderable.length === agents.length) {\n const hostsLeavingThemOut = unique(unrenderable.map((a) => a.provider)).join(\" or \");\n return {\n kind: \"agents\",\n status: \"pass\",\n message: `No sub-agent in this configuration is carried by ${hostsLeavingThemOut}`,\n details: unrenderable.map((a) => `- ${a.name} (left out of ${a.provider})`),\n };\n }\n\n if (missingAgents.length > 0) {\n return {\n kind: \"agents\",\n status: \"warn\",\n message: `${missingAgents.length} agent${missingAgents.length === 1 ? \"\" : \"s\"} need${missingAgents.length === 1 ? \"s\" : \"\"} recompilation`,\n details: missingAgents.map((a) => `- ${a} (missing)`),\n };\n }\n\n const renderable = agents.length - unrenderable.length;\n return {\n kind: \"agents\",\n status: \"pass\",\n message: `${ratio(renderable, renderable, \"agent\")} compiled`,\n };\n}\n\n/** One configured sub-agent as the Agents Compiled row sees it, and the host that decided it. */\ntype AgentCompileCheck = {\n name: AgentName;\n provider: Provider;\n state: \"unrenderable\" | \"compiled\" | \"missing\";\n};\n\n/**\n * Whether one sub-agent is left out by its scope's host, or else compiled or missing where that\n * host reads it — the extension and the directory are both the host's.\n */\nasync function agentCompileCheck(\n agent: AgentScopeConfig,\n hosts: Record<SkillScope, Provider>,\n agentsDirs: Record<SkillScope, string>,\n): Promise<AgentCompileCheck> {\n const provider = hosts[agent.scope];\n if (!hostCompilesAgent(provider, agent.name)) {\n return { name: agent.name, provider, state: \"unrenderable\" };\n }\n\n const file = `${agent.name}${agentCodec(provider).extension}`;\n const compiled = await fileExists(path.join(agentsDirs[agent.scope], file));\n return { name: agent.name, provider, state: compiled ? \"compiled\" : \"missing\" };\n}\n\nasync function checkNoOrphans(config: ProjectConfig, projectDir: string): Promise<CheckResult> {\n // The host's own agent files — `*.toml` roles on Codex, where a `*.md`-only listing found no\n // compiled agent and so no orphan either (the CLI-896 class, 2026-09-27).\n const codec = agentCodec(providerInUse(projectDir));\n const projectAgentsDir = resolveInstallPaths(projectDir, \"project\").agentsDir;\n const globalAgentsDir = resolveInstallPaths(projectDir, \"global\").agentsDir;\n\n // At home scope both scopes resolve to the same agents directory, so it is\n // listed once and its files are matched against both scopes' known names.\n const scopesShareAgentsDir = isHomeDirectory(projectDir);\n\n const [projectExists, globalExists] = await Promise.all([\n directoryExists(projectAgentsDir),\n !scopesShareAgentsDir ? directoryExists(globalAgentsDir) : false,\n ]);\n\n if (!projectExists && !globalExists) {\n return {\n kind: \"orphans\",\n status: \"pass\",\n message: \"No agents directory\",\n };\n }\n\n const projectMdFiles = projectExists ? await listAgentFilesOf(projectAgentsDir, codec) : [];\n const globalMdFiles = globalExists ? await listAgentFilesOf(globalAgentsDir, codec) : [];\n\n // Project files: only active project-scoped agents should have .md files here\n const activeProjectAgents: Set<string> = new Set(\n config.agents.filter((a) => a.scope === \"project\" && !a.excluded).map((a) => a.name),\n );\n // Global files: all global-scoped agents (including excluded) still serve other projects\n const knownGlobalAgents: Set<string> = new Set(\n config.agents.filter((a) => a.scope === \"global\").map((a) => a.name),\n );\n\n const knownProjectDirAgents = scopesShareAgentsDir\n ? new Set([...activeProjectAgents, ...knownGlobalAgents])\n : activeProjectAgents;\n\n const orphanedFiles = [\n ...orphanedAgentNames(projectMdFiles, knownProjectDirAgents, codec),\n ...orphanedAgentNames(globalMdFiles, knownGlobalAgents, codec),\n ];\n\n if (orphanedFiles.length > 0) {\n return {\n kind: \"orphans\",\n status: \"warn\",\n message: plural(orphanedFiles.length, \"orphaned agent file\"),\n details: orphanedFiles.map((f) => `- ${f}.md (not in config)`),\n };\n }\n\n return {\n kind: \"orphans\",\n status: \"pass\",\n message: \"No orphaned agent files\",\n };\n}\n\n/**\n * The agents whose compiled `.md` sits in a directory whose roster does not name them. The\n * roster differs per directory — a project directory knows only its own active agents, the\n * global one knows every global-scoped agent including the excluded — so it is a parameter.\n */\nfunction orphanedAgentNames(\n mdFiles: string[],\n knownAgents: ReadonlySet<string>,\n codec: AgentCodec,\n): string[] {\n return mdFiles\n .map((fileName) => path.basename(fileName, codec.extension))\n .filter((agentName) => !knownAgents.has(agentName));\n}\n\n/**\n * The same row when there is no configuration at all. Ownership is not unknown here, it is\n * settled: nothing declares any of it, so every artefact this CLI can prove it wrote is an\n * orphan and the row names it. What it CANNOT prove it wrote is left out — a skill directory\n * with no `forkedFrom` and an agent file with no provenance marker are somebody's own work,\n * and naming them here would offer `uninstall` files that command declines. See\n * {@link listInstalledArtifacts}, which asks exactly that question on both axes.\n *\n * A `fail`, where a stray file beside a config is a warning: that warning is earned by the next\n * `compile` pruning what it names, and nothing prunes these unattended. `compile` and `edit`\n * refuse without a config; `uninstall` does not need one, identifying skill directories by\n * their own `forkedFrom` and compiled agents by the marker each carries, which is why the tip\n * beneath this row can promise both halves.\n *\n * With nothing installed the row keeps the skip it has always printed. An empty directory with\n * no config is the state `init` exists for — there is nothing for a configuration to have owned.\n */\nasync function checkUnownedInstallation(projectDir: string): Promise<CheckResult> {\n const { skills, agents } = await listInstalledArtifacts(projectDir);\n if (skills.length === 0 && agents.length === 0) return skippedResult(\"orphans\");\n\n return {\n kind: \"orphans-unowned\",\n status: \"fail\",\n // The per-line \"(not in config)\" the other verdict repeats is said once here instead:\n // with no configuration it is true of every line, and there are as many lines as files.\n message: `${countedArtifacts(skills, agents)} installed here, and no configuration declares them`,\n details: [...skills, ...agents].map((artifact) => `- ${artifact}`),\n };\n}\n\n/** \"1/1 skill\", \"2/3 skills\" — the noun agrees with the count it is out of. */\nfunction ratio(found: number, total: number, noun: string): string {\n return `${found}/${plural(total, noun)}`;\n}\n\n/** \"7 skills and 2 agents\", dropping a half with nothing in it rather than saying \"0 agents\". */\nfunction countedArtifacts(skills: string[], agents: string[]): string {\n return [\n ...(skills.length > 0 ? [plural(skills.length, \"skill\")] : []),\n ...(agents.length > 0 ? [plural(agents.length, \"agent\")] : []),\n ].join(\" and \");\n}\n\n/**\n * Whether every eject-mode skill the configuration names is on disk where its own host reads it.\n *\n * **It composed `<scope root>/.claude/skills/<id>/SKILL.md` for every provider until\n * 2026-09-22**, which made a healthy Codex installation report every one of its skills missing\n * and told the reader to look in a directory that installation does not have. The write path had\n * already moved through the layout — `copyLocalSkills` writes `$CODEX_HOME/skills` and\n * `<repo>/.agents/skills` — so this row was the half that stayed behind, and it is the kind of\n * defect the host-path lint ban cannot report: the path was composed from a CONSTANT, and that\n * ban matches literals.\n *\n * The directory is asked per SKILL rather than once for the row, because scope decides it and a\n * configuration mixes scopes freely. The reader is told the directory that was actually looked\n * in, for the same reason — one sentence naming one folder cannot be true of two scopes on two\n * different hosts.\n */\nasync function checkSkillsInstalled(\n config: ProjectConfig,\n projectDir: string,\n): Promise<CheckResult> {\n const skills: SkillConfig[] = config.skills;\n const ejectSkills = skills.filter((s) => s.origin === EJECT_SOURCE);\n\n if (ejectSkills.length === 0) {\n return {\n kind: \"installed\",\n status: \"pass\",\n message: \"No eject-mode skills configured\",\n };\n }\n\n const skillChecks = await Promise.all(\n ejectSkills.map((skill) => ejectedSkillCheck(skill, projectDir)),\n );\n const missingSkills = skillChecks.filter((c) => !c.installed);\n\n if (missingSkills.length > 0) {\n return {\n kind: \"installed\",\n status: \"warn\",\n message: `${plural(missingSkills.length, \"skill\")} missing from disk`,\n details: missingSkills.map((s) => `- ${s.id} (not found in ${s.lookedIn}/)`),\n };\n }\n\n return {\n kind: \"installed\",\n status: \"pass\",\n message: `${ratio(ejectSkills.length, ejectSkills.length, \"eject-mode skill\")} installed`,\n };\n}\n\n/** One eject-mode skill as the Skills Installed row sees it, and where it was looked for. */\ntype EjectedSkillCheck = { id: SkillId; installed: boolean; lookedIn: string };\n\n/**\n * Whether one eject-mode skill's `SKILL.md` is where its scope's host reads it.\n *\n * The provider is read off the scope's OWN root, the way `resolveInstallPaths` and\n * `discoverLocalProjectSkills` read it: two scopes can be on two different providers, and one\n * probe per skill is what keeps the directory and the reported prefix naming one of them.\n */\nasync function ejectedSkillCheck(\n skill: SkillConfig,\n projectDir: string,\n): Promise<EjectedSkillCheck> {\n const provider = providerInUse(installBaseDir(projectDir, skill.scope));\n const dir = skillsDir(provider, skill.scope, projectDir);\n const installed = await fileExists(path.join(dir, skill.id, STANDARD_FILES.SKILL_MD));\n\n return {\n id: skill.id,\n installed,\n lookedIn: skillsPathPrefix(provider, skill.scope, projectDir),\n };\n}\n\n/**\n * Plugin-mode skills have no files under `.claude/skills/` — they live in the\n * Claude plugin registry. Verifying them means asking the registry for the scope\n * each skill was installed at, not looking on disk.\n */\nasync function checkPluginSkillsInstalled(\n config: ProjectConfig,\n projectDir: string,\n): Promise<CheckResult> {\n const pluginSkills: SkillConfig[] = config.skills.filter((s) => s.origin !== EJECT_SOURCE);\n\n if (pluginSkills.length === 0) {\n return {\n kind: \"plugins\",\n status: \"pass\",\n message: \"No plugin-mode skills configured\",\n };\n }\n\n const baseDirs = unique(pluginSkills.map((s) => installBaseDir(projectDir, s.scope)));\n const missingPerBaseDir = await Promise.all(\n baseDirs.map(async (baseDir) => {\n const installedIds = new Set(\n (await getVerifiedPluginInstallPaths(baseDir)).map((plugin) =>\n parseMarketplacePluginRef(plugin.pluginKey),\n ),\n );\n return pluginSkills\n .filter((skill) => installBaseDir(projectDir, skill.scope) === baseDir)\n .filter((skill) => !installedIds.has(skill.id))\n .map((skill) => skill.id);\n }),\n );\n const missingSkills = missingPerBaseDir.flat();\n\n if (missingSkills.length > 0) {\n return {\n kind: \"plugins\",\n status: \"warn\",\n message: `${plural(missingSkills.length, \"skill\")} not installed as plugins`,\n details: missingSkills.map((s) => `- ${s} (no enabled plugin found)`),\n };\n }\n\n return {\n kind: \"plugins\",\n status: \"pass\",\n message: `${ratio(pluginSkills.length, pluginSkills.length, \"plugin-mode skill\")} installed`,\n };\n}\n\n/**\n * How a marketplace came to be the one this run reads, in the words each rung earns.\n *\n * The `default` sentence is the one that had to exist. With no configuration anywhere the resolver\n * falls back to the public catalogue and this check FETCHES it, so the report a bare directory gets\n * describes a network round trip to a marketplace nobody named — which is the row doing its job,\n * since reachability is its whole subject, and it was doing it without saying so. The other four\n * rungs are here because the map is exhaustive against the union rather than a default with\n * exceptions; `doctor` takes no marketplace flag and reads no environment variable, so it reaches\n * the first two through nothing today.\n */\nconst MARKETPLACE_CHOSEN_BY = {\n flag: \"named by this run\",\n env: `named by ${SOURCE_ENV_VAR}`,\n project: \"named by this project's configuration\",\n global: \"named by the global configuration\",\n default: \"nothing here names one, so this is the default\",\n} as const satisfies Record<ResolvedConfig[\"sourceOrigin\"], string>;\n\n/** How this run got hold of that marketplace — off disk, or over the wire. */\nfunction howItWasReached(source: string, isLocal: boolean): string {\n return isLocal ? `Read ${source} from disk` : `Fetched ${source} over the network`;\n}\n\n/**\n * The row for a marketplace this run did reach.\n *\n * `message` names where the skills were read FROM, which for a remote marketplace is the cache\n * directory it was unpacked into; the provenance line beneath it names the marketplace itself.\n * Both, because neither answers the other's question: a cache path says nothing about whose\n * catalogue it holds, and a ref says nothing about what is on disk to inspect.\n *\n * The count is what that marketplace carries, not the matrix this run loaded: the matrix also\n * holds the skills ejected here and the global installation's, which may come from another\n * marketplace altogether.\n */\nfunction reachedMarketplace(result: SourceLoadResult): CheckResult {\n const { source, sourceOrigin } = result.sourceConfig;\n const skillCount = result.marketplaceSkillIds.size;\n const sourceLabel = result.isLocal ? \"local\" : \"remote\";\n\n return {\n kind: \"source\",\n status: \"pass\",\n message: `Connected to ${sourceLabel}: ${result.sourcePath}`,\n details: [\n `${plural(skillCount, \"skill\")} available`,\n `${howItWasReached(source, result.isLocal)} — ${MARKETPLACE_CHOSEN_BY[sourceOrigin]}`,\n ],\n };\n}\n\nasync function checkSourceReachable(projectDir: string): Promise<CheckResult> {\n try {\n const { sourceResult } = await loadSource({ projectDir });\n return reachedMarketplace(sourceResult);\n } catch (error) {\n const message = getErrorMessage(error);\n return {\n kind: \"source\",\n status: \"fail\",\n message: \"Failed to load marketplace\",\n details: [message],\n };\n }\n}\n\ntype ContentCheck = {\n kind: CheckKind;\n name: string;\n noun: string;\n run: (projectDir: string) => Promise<ContentValidation>;\n};\n\n/**\n * A content check plus the two gates it owns: whether it reads config.ts to know WHAT to validate\n * (see {@link CONFIG_CHECK}), and which operational rows its errors stand down.\n *\n * `blocks` names only the rows that READ what this pass validates, so their own verdict would be\n * this pass's finding in the row's words. A row absent from every `blocks` list reads none of the\n * content on disk and answers as truthfully as ever — an error here is not its business.\n */\ntype GatedContentCheck = ContentCheck & {\n readsConfig: boolean;\n blocks: readonly CheckKind[];\n};\n\n/**\n * The config file every other check is read against, so it is validated before any of them and\n * on its own: a file that exists and cannot be parsed is a finding about that file, and every\n * row underneath would be a cascade of it. It carries no gate of its own — it IS the gate.\n */\nconst CONFIG_CHECK: ContentCheck = {\n kind: \"content-config\",\n name: \"Config\",\n noun: \"config\",\n run: validateProjectConfigFile,\n};\n\n/**\n * The content layer: schema and file-level validation of what is on disk. It runs before the\n * operational layer so that a row whose inputs this layer has already reported broken can stand\n * down rather than re-report the same fault as a finding of its own.\n */\nconst CONTENT_CHECKS: GatedContentCheck[] = [\n {\n kind: \"content-sources\",\n name: \"Marketplaces\",\n noun: \"marketplace\",\n // The registered marketplaces are the ones config.ts names.\n readsConfig: true,\n // A marketplace whose content is broken is missing skills from the matrix every configured\n // id is resolved against, so the skills row would call ids \"not found\" that this row has\n // already accounted for.\n blocks: [\"skills\"],\n run: validateRegisteredSources,\n },\n {\n kind: \"content-plugins\",\n name: \"Plugins\",\n noun: \"plugin\",\n readsConfig: false,\n // `resolvePluginInstallPaths` swallows a registry it cannot parse and returns none, so every\n // plugin-mode skill would read \"no enabled plugin found\" — the registry's finding, once per\n // configured skill, wearing the row's words.\n blocks: [\"plugins\"],\n run: validateInstalledPlugins,\n },\n {\n kind: \"content-skills\",\n name: \"Skills\",\n noun: \"skill\",\n // `~/.claude/skills/` is shared with everything else that installs a skill there, so which of\n // its directories are this installation's is a question only the config can answer for the\n // ones carrying no provenance marker. A config nobody can read leaves that unanswerable.\n readsConfig: true,\n // `extractLocalSkill` DROPS a skill whose metadata.yaml is missing or unusable, so a \"not\n // found\" from the skills row would be this finding re-worded.\n blocks: [\"skills\"],\n run: validateInstalledSkills,\n },\n {\n kind: \"content-agents\",\n name: \"Agents\",\n noun: \"agent\",\n readsConfig: false,\n // Nothing downstream opens an agent .md. `Agents Compiled` asks whether the file is there and\n // `No Orphans` reads the names of the files that are — broken frontmatter changes neither.\n blocks: [],\n run: validateInstalledAgents,\n },\n];\n\nfunction contentStatus(errors: number, warnings: number): CheckResult[\"status\"] {\n if (errors > 0) return \"fail\";\n if (warnings > 0) return \"warn\";\n return \"pass\";\n}\n\nfunction contentMessage(noun: string, count: number, errors: number, warnings: number): string {\n // A pass that walked nothing can still report an issue — an unreadable plugin\n // registry is a finding about the directory, not about a plugin inside it.\n if (errors === 0 && warnings === 0) {\n return count === 0 ? `No ${noun}s to validate` : `${plural(count, noun)} validated`;\n }\n return `${plural(count, noun)}: ${plural(errors, \"error\")}, ${plural(warnings, \"warning\")}`;\n}\n\nfunction formatContentIssue(issue: ContentIssue): string {\n const marker = issue.severity === \"error\" ? \"ERROR\" : \"WARN\";\n return `- [${marker}] ${issue.file}: ${issue.message}`;\n}\n\nfunction toContentResult(check: ContentCheck, validation: ContentValidation): CheckResult {\n const errors = validation.issues.filter((issue) => issue.severity === \"error\").length;\n const warnings = validation.issues.length - errors;\n\n return {\n kind: check.kind,\n status: contentStatus(errors, warnings),\n message: contentMessage(check.noun, validation.count, errors, warnings),\n details: [\n ...validation.notes.map((note) => `- ${note}`),\n ...validation.issues.map(formatContentIssue),\n ],\n };\n}\n\n/**\n * The column every row's status symbol starts at. It has to clear the longest row name\n * the report prints — \"Marketplace Reachable\", 21 characters — or that row's name runs\n * straight into its own tick with no gap.\n */\nconst CHECK_WIDTH = 24;\n\n/** Section headings and the rows underneath them are indented one step apart. */\nconst SECTION_INDENT = \" \";\nconst ROW_INDENT = \" \";\n\nconst SECTION_CONTENT = \"Content checks\";\nconst SECTION_OPERATIONAL = \"Operational checks\";\nconst SKIP_NO_INSTALLATION = \"Skipped — no installation here (marketplace repository)\";\nconst SKIP_CONFIG_UNREADABLE = \"Skipped — the configuration that names them cannot be read\";\n\n/**\n * The whole operational layer standing down, for the one content finding that leaves every row\n * with nothing to say: a config nobody can read. It still says \"errors\" rather than naming that\n * file, because the config row is one of them and whatever else failed is above it too.\n */\nconst SKIP_AFTER_CONFIG_ERROR = \"Skipped — fix the content errors above first\";\n\n/**\n * The sentence a single row stands down with. Unlike its neighbours it names the finding rather\n * than the layer: which pass blocked it is the one thing separating a row that cannot answer from\n * the ones printing verdicts beside it.\n */\nfunction skipRestatingContent(nouns: string[]): string {\n return `Skipped — this row would only restate the ${nouns.join(\" and \")} errors above`;\n}\n\nfunction formatCheckName(name: string): string {\n return name.padEnd(CHECK_WIDTH);\n}\n\n/**\n * The glyph each row is headed with, every one of them from {@link UI_SYMBOLS}.\n *\n * The last two used to be literals here — `\"!\"` and `\"-\"` — and the second was the one that\n * mattered: `UI_SYMBOLS.SKIPPED` is an EN-DASH, so a stood-down row printed a different character\n * from the one the symbol table declares for exactly that state, while `UI_SYMBOLS.REMOVED` is\n * deliberately an ASCII hyphen for diff markers. Two glyphs a reader cannot tell apart, meaning\n * opposite things, and nothing pointing either literal at the table it belonged in.\n */\nfunction formatStatus(status: CheckResult[\"status\"]): string {\n switch (status) {\n case \"pass\":\n return UI_SYMBOLS.CHECK;\n case \"fail\":\n return UI_SYMBOLS.CROSS;\n case \"warn\":\n return UI_SYMBOLS.DISCOURAGED;\n case \"skip\":\n return UI_SYMBOLS.SKIPPED;\n default: {\n const _exhaustive: never = status;\n return _exhaustive;\n }\n }\n}\n\n/** The column a row's details start at, under its message. */\nconst DETAIL_INDENT = `${ROW_INDENT}${\" \".repeat(CHECK_WIDTH)} `;\n\nfunction formatCheckLine(name: string, result: CheckResult): string[] {\n const headerLine = `${ROW_INDENT}${formatCheckName(name)}${formatStatus(result.status)} ${result.message}`;\n const detailLines = (result.details ?? []).flatMap(indentedDetailLines);\n return [headerLine, ...detailLines];\n}\n\n/**\n * A detail can be a whole refusal — a loader's message, paragraphs and all — so every one of its\n * lines is indented, and its blank lines dropped. Indenting the first alone printed the rest at\n * column 0, outside the row they belong to and between the rows below it.\n */\nfunction indentedDetailLines(detail: string): string[] {\n return detail\n .split(\"\\n\")\n .filter((line) => line !== \"\")\n .map((line) => `${DETAIL_INDENT}${line}`);\n}\n\nfunction formatSummary(results: CheckResult[]): string {\n const counts = countBy(results, (r) => r.status);\n\n const parts = [\n `${counts.pass ?? 0} passed`,\n plural(counts.warn ?? 0, \"warning\"),\n plural(counts.fail ?? 0, \"error\"),\n ];\n\n return `${SECTION_INDENT}Summary: ${parts.join(\", \")}`;\n}\n\nconst TIPS: Array<{ kind: CheckKind; status: CheckResult[\"status\"]; tip: string }> = [\n {\n kind: \"agents\",\n status: \"warn\",\n tip: ` Tip: Run '${CLI_INVOKE_COMMAND} compile' to generate missing agent files`,\n },\n {\n kind: \"config\",\n status: \"fail\",\n tip: ` Tip: Run '${CLI_INVOKE_COMMAND} init' to create or fix configuration`,\n },\n {\n // The remedy for a valid config with nothing in it. Not the tip above: there is nothing to\n // create and nothing to fix. `init` on this config opens the wizard rather than the dashboard\n // — that is what the detection returning null is FOR — so it is the way to fill it in.\n kind: \"config-empty\",\n status: \"warn\",\n tip: ` Tip: Nothing is configured yet — run '${CLI_INVOKE_COMMAND} init' to choose skills and sub-agents`,\n },\n {\n // Printed beside the config tip above, which says how to get a configuration back and\n // nothing about the files that outlived the old one. Both halves are named with what they\n // actually do: `uninstall` matches skill directories by their own `forked-from` metadata and\n // compiled agents by the marker each one carries, so neither needs the configuration that is\n // gone — which is why the row can name both and this tip can promise both.\n kind: \"orphans-unowned\",\n status: \"fail\",\n tip: ` Tip: Nothing declares the files above — '${CLI_INVOKE_COMMAND} init' writes a configuration that can own them again, or '${CLI_INVOKE_COMMAND} uninstall' removes them, the compiled agents included: each file listed carries this CLI's own provenance, which is what that command reads when there is no configuration left`,\n },\n {\n kind: \"skills\",\n status: \"fail\",\n tip: \" Tip: Check skill IDs in config match available skills\",\n },\n {\n kind: \"installed\",\n status: \"warn\",\n // No command is named on purpose: 'eject skills --force' re-copies every skill in the\n // marketplace and always targets project scope, so it cannot repair a global-scoped\n // skill and it litters a plugin-mode project with local skill directories.\n tip: \" Tip: Re-eject the missing skills from the marketplace to restore their files\",\n },\n];\n\nfunction formatTips(results: CheckResult[]): string[] {\n return TIPS.filter((t) => results.some((r) => r.kind === t.kind && r.status === t.status)).map(\n (t) => t.tip,\n );\n}\n\n/**\n * The one remedy that applies to a config that cannot be read, once per such config, naming the\n * folder it is taken from. `init` does not clear such a file — it refuses it — so the config tip\n * would send the reader in a circle; this is the way out every refusing command names, worded the\n * same way and sending the reader to the same folder: the project, or the home directory for the\n * global config.\n *\n * Read again rather than threaded out of the content pass, and only once that pass has failed. A\n * failure it cannot classify is already that pass's own failed row, so it adds no tip of its own.\n */\nasync function recreateConfigTips(\n contentResults: CheckResult[],\n projectDir: string,\n): Promise<string[]> {\n if (!failedContentKinds(contentResults).has(CONFIG_CHECK.kind)) return [];\n\n const failures = await findConfigLoadFailures(projectDir).catch(() => []);\n return failures.map(\n (failure) =>\n ` Tip: There is no automatic repair — recreate the configuration by running ${recreateConfigFrom(failure.scopeRoot)}`,\n );\n}\n\n/** The row name every Layout row is printed under, one per scope. */\nconst LAYOUT_ROW_NAME = \"Layout\";\n\nfunction toLayoutResult(finding: LayoutFinding): CheckResult {\n return {\n kind: \"layout\",\n status: finding.status,\n message: finding.message,\n details: finding.details,\n };\n}\n\n/**\n * Registered projects whose own source folder is still the old one, as the registry spells them.\n *\n * Read from the global config's `projects` array and nothing else — which is exactly why the line\n * above the list says so. A config that cannot be loaded yields no list rather than a failed row:\n * the file itself is already this report's own finding, one row up.\n */\nasync function registeredProjectsOnTheOldFolder(home: string): Promise<string[]> {\n const loaded = await loadInstalledConfig(home).catch(() => null);\n return (loaded?.config.projects ?? []).filter(isOnTheLegacySourceFolder);\n}\n\n/**\n * Whether the installation in `dir` is read from the folder every pre-rename install carries.\n *\n * The provider is read off the directory rather than assumed: the retired name is Claude's, so a\n * registered project on another provider has only ever had one folder and is never on the old one.\n */\nfunction isOnTheLegacySourceFolder(dir: string): boolean {\n return resolveSourceDir(dir, providerInUse(dir)).legacy;\n}\n\n/** The row name the placement and installation-ambiguity findings are printed under. */\nconst PLACEMENTS_ROW_NAME = \"Placements Offered\";\n\n/**\n * Whether this installation is one every other command will act on — as a ROW, not a throw.\n *\n * **It carries the two findings that make the rest of this report a claim about nothing.** A\n * configuration asking for a mode/scope cell its host does not offer is refused by `compile`,\n * `edit` and `update`; a scope holding an installation of each provider is refused by `edit`,\n * `uninstall`, `share` and `eject`. Until this row existed, `doctor` — the one command whose job\n * is saying whether an installation is healthy — walked straight past both and pronounced a clean\n * bill of health on a configuration no command would touch. A diagnostic that disagrees with every\n * command it is diagnosing is worse than a missing one.\n *\n * **It reports and never refuses**, which is the same distinction `settleSourceLayoutBeforeWriting`\n * draws for the rival-folder refusal: a command that changes nothing must not refuse to LOOK,\n * because this is exactly the state a user needs to see in order to fix it.\n *\n * The message is the refusal's own, word for word, off the same host roster — so what a user is\n * told here and what they are told when `compile` stops are one sentence, not two that agree\n * today.\n *\n * **The summary counts FINDINGS, and said \"unusable installation\" until 2026-09-22.** The two\n * are not the same number in either direction: one installation with an unofferable placement AND\n * an ambiguous folder contributes two findings, while the ambiguity finding is itself about a\n * scope holding two installations. A count of one noun printed over a list of another is a\n * sentence a reader cannot check against the lines underneath it.\n */\nasync function checkOfferedPlacements(projectDir: string): Promise<CheckResult> {\n const findings = await everythingThatWouldStopACommandActing(projectDir);\n\n if (findings.length === 0) {\n return { kind: \"placements\", status: \"pass\", message: \"Every configured skill is placeable\" };\n }\n\n return {\n kind: \"placements\",\n status: \"fail\",\n message: `${plural(findings.length, \"finding\")} would stop a command acting here`,\n details: findings.map((finding) => `- ${finding}`),\n };\n}\n\n/**\n * The two findings a command would stop on, in one list: a placement no host can fill, and a scope\n * holding an installation of each provider.\n *\n * Asked together rather than in sequence because neither answer depends on the other, and reported\n * together because they are one question for a user — \"will anything act on this?\" — with two ways\n * of being answered no.\n */\nasync function everythingThatWouldStopACommandActing(projectDir: string): Promise<string[]> {\n const [unofferable, ambiguous] = await Promise.all([\n unofferablePlacementsFound(projectDir),\n ambiguityFinding(projectDir),\n ]);\n\n return ambiguous === null ? unofferable : [...unofferable, ambiguous];\n}\n\nfunction skippedResult(kind: CheckKind): CheckResult {\n return { kind, status: \"skip\", message: \"Skipped (config invalid)\" };\n}\n\n/**\n * The skills row when the marketplace never loaded. Distinct from {@link skippedResult}: the\n * config is fine, and what is missing is the matrix to resolve its skills against.\n */\nfunction sourceUnreachableSkillsResult(): CheckResult {\n return { kind: \"skills\", status: \"skip\", message: \"Skipped (marketplace unreachable)\" };\n}\n\nfunction skippedContentResult(kind: CheckKind): CheckResult {\n return { kind, status: \"skip\", message: SKIP_CONFIG_UNREADABLE };\n}\n\n/** The content passes that failed this run — the set every operational row is gated against. */\nfunction failedContentKinds(contentResults: CheckResult[]): ReadonlySet<CheckKind> {\n return new Set(contentResults.filter((r) => r.status === \"fail\").map((r) => r.kind));\n}\n\n/**\n * An operational row standing down because a content pass it reads through failed, naming what\n * blocked it. `null` — the answer for most rows on most runs — means nothing it reads is broken\n * and it can speak for itself.\n */\nfunction contentBlockedResult(\n row: CheckKind,\n failedContent: ReadonlySet<CheckKind>,\n): CheckResult | null {\n const blockingNouns = CONTENT_CHECKS.filter((check) => failedContent.has(check.kind))\n .filter((check) => check.blocks.includes(row))\n .map((check) => check.noun);\n\n if (blockingNouns.length === 0) return null;\n\n return { kind: row, status: \"skip\", message: skipRestatingContent(blockingNouns) };\n}\n\nfunction runContentCheck(check: ContentCheck, projectDir: string): Promise<CheckResult> {\n return safeCheck(check.kind, async () => toContentResult(check, await check.run(projectDir)));\n}\n\n/**\n * Wrap a check so a thrown exception becomes a `fail` result instead of crashing the\n * whole doctor run. Per-check isolation ensures one broken check does not mask others.\n */\nasync function safeCheck(kind: CheckKind, fn: () => Promise<CheckResult>): Promise<CheckResult> {\n try {\n return await fn();\n } catch (error) {\n return { kind, status: \"fail\", message: \"Check threw\", details: [getErrorMessage(error)] };\n }\n}\n\nexport default class Doctor extends BaseCommand {\n static summary = \"Diagnose common configuration issues\";\n\n static description = `Run diagnostic checks on your ${DEFAULT_BRANDING.NAME} configuration to identify issues with config validity, skill resolution, agent compilation, and marketplace connectivity.`;\n\n static examples = [\"<%= config.bin %> <%= command.id %>\"];\n\n static flags = {};\n\n async run(): Promise<void> {\n await this.parse(Doctor);\n const projectDir = process.cwd();\n\n // The shared `verbose()` logger stays OFF, and nothing here switches it on. `doctor` used to,\n // for the whole run — the mechanical residue of a `--verbose` flag whose removal was meant to\n // make each row's own DETAILS unconditional, which `formatCheckLine` does on its own. What the\n // logger added on top of that was the loaders' trace, spliced between the section headings and\n // the rows they head: for a directory holding nothing, 27 lines of it, every one restating a\n // row printed underneath it.\n this.printHeader(await this.resolveBrandingName(projectDir));\n const contentResults = await this.runContentChecks(projectDir);\n const operationalResults = await this.runOperationalChecks(projectDir, contentResults);\n const results = [...contentResults, ...operationalResults];\n\n this.printResults(results, await recreateConfigTips(contentResults, projectDir));\n\n if (results.some((r) => r.status === \"fail\")) {\n this.exit(EXIT_CODES.ERROR);\n }\n }\n\n private printHeader(brandingName: string): void {\n this.log(\"\");\n this.log(`${brandingName} Doctor`);\n this.log(\"\");\n this.log(`${SECTION_INDENT}Checking configuration health...`);\n this.log(\"\");\n }\n\n /**\n * The config is checked first and alone. A check that reads it to know what to validate would\n * otherwise ask a file already reported as unreadable — and asking is what emitted the loader's\n * own failure line once per read, spliced between these rows. Everything that walks installed\n * content on disk still runs: it says something true whatever the config is in.\n */\n private async runContentChecks(projectDir: string): Promise<CheckResult[]> {\n this.log(`${SECTION_INDENT}${SECTION_CONTENT}`);\n\n const configResult = await runContentCheck(CONFIG_CHECK, projectDir);\n const configUnreadable = configResult.status === \"fail\";\n\n const rows = await Promise.all(\n CONTENT_CHECKS.map(async (check) => ({\n name: check.name,\n result:\n check.readsConfig && configUnreadable\n ? skippedContentResult(check.kind)\n : await runContentCheck(check, projectDir),\n })),\n );\n\n const allRows = [{ name: CONFIG_CHECK.name, result: configResult }, ...rows];\n\n for (const row of allRows) {\n this.logCheck(row.name, row.result);\n }\n\n return allRows.map((row) => row.result);\n }\n\n /**\n * The whole layer stands down for the two findings that leave every row with nothing to say: a\n * config nobody can read — every row is read out of it, so all of them would be cascades of that\n * one file — and a marketplace repository with nothing installed, where a marketplace author has\n * no install for the layer to describe. Every other content error is scoped to the rows that\n * read what it is about; {@link GatedContentCheck} says which those are, per pass.\n */\n private async runOperationalChecks(\n projectDir: string,\n contentResults: CheckResult[],\n ): Promise<CheckResult[]> {\n this.log(\"\");\n this.log(`${SECTION_INDENT}${SECTION_OPERATIONAL}`);\n\n const failedContent = failedContentKinds(contentResults);\n if (failedContent.has(CONFIG_CHECK.kind)) {\n this.log(`${ROW_INDENT}${SKIP_AFTER_CONFIG_ERROR}`);\n return [];\n }\n\n const detected = await detectProject(projectDir);\n if (!detected && (await this.isUninstalledSourceRepo(projectDir))) {\n this.log(`${ROW_INDENT}${SKIP_NO_INSTALLATION}`);\n return [];\n }\n\n const configState = await resolveConfigState(detected, projectDir);\n return this.runAllChecks(projectDir, configState, failedContent);\n }\n\n /**\n * A config file that exists but failed to load also detects as \"no project\", and\n * that is a finding rather than an absence — so the skip requires no config file\n * at all, not merely no usable one.\n */\n private async isUninstalledSourceRepo(projectDir: string): Promise<boolean> {\n if (await fileExists(getInstalledConfigPath(projectDir))) return false;\n return isSourceRepo(projectDir);\n }\n\n private async runAllChecks(\n projectDir: string,\n configState: ConfigState,\n failedContent: ReadonlySet<CheckKind>,\n ): Promise<CheckResult[]> {\n const { result: configResult, config } = checkConfigValid(configState, projectDir);\n this.logCheck(\"Config Valid\", configResult);\n\n const layoutResults = await this.reportLayout(projectDir);\n\n // Directly under Layout, and above every row that reads content: Layout says which FOLDER this\n // report is about and this row says whether any command will act on what is in it.\n const placementsResult = await safeCheck(\"placements\", () =>\n checkOfferedPlacements(projectDir),\n );\n this.logCheck(PLACEMENTS_ROW_NAME, placementsResult);\n\n // loadSource (called by checkSourceReachable) populates the matrix. Run it\n // before checkSkillsResolved so skills lookups see a populated matrix; if\n // source fails, skip skills rather than reporting false \"not found\" errors.\n const sourceResult = await safeCheck(\"source\", () => checkSourceReachable(projectDir));\n\n const filteredConfig = config ? filterExcludedEntries(config) : null;\n\n const skillsResult = await this.resolveSkillsCheck(\n config,\n sourceResult,\n projectDir,\n failedContent,\n );\n this.logCheck(\"Skills Resolved\", skillsResult);\n\n const agentsResult = filteredConfig\n ? await safeCheck(\"agents\", () => checkAgentsCompiled(filteredConfig, projectDir))\n : skippedResult(\"agents\");\n this.logCheck(\"Agents Compiled\", agentsResult);\n\n const orphansResult = await this.resolveOrphansCheck(config, configState, projectDir);\n this.logCheck(\"No Orphans\", orphansResult);\n\n const installedResult = filteredConfig\n ? await safeCheck(\"installed\", () => checkSkillsInstalled(filteredConfig, projectDir))\n : skippedResult(\"installed\");\n this.logCheck(\"Skills Installed\", installedResult);\n\n const pluginsResult = await this.resolvePluginsCheck(filteredConfig, projectDir, failedContent);\n this.logCheck(\"Plugins Installed\", pluginsResult);\n\n this.logCheck(\"Marketplace Reachable\", sourceResult);\n\n return [\n configResult,\n ...layoutResults,\n placementsResult,\n skillsResult,\n agentsResult,\n orphansResult,\n installedResult,\n pluginsResult,\n sourceResult,\n ];\n }\n\n /**\n * One `Layout` row per scope in play, and — at the home directory — the registered projects\n * still on the old folder.\n *\n * Per scope rather than per machine: a project and the global installation can be on different\n * layouts, and one row for the two can only be right about one of them.\n *\n * Printed right under `Config Valid`, because every row below it is read out of a config file\n * whose folder this row is about — a scope with two rival folders has a config the rest of the\n * report may have read from either one.\n */\n private async reportLayout(projectDir: string): Promise<CheckResult[]> {\n const scopes = await sourceScopesInPlay(projectDir);\n const rows = await Promise.all(\n scopes.map(async (scope) => toLayoutResult(await layoutFindingFor(scope))),\n );\n const withRegistry = await this.appendRegistryList(projectDir, rows);\n\n for (const row of withRegistry) {\n this.logCheck(LAYOUT_ROW_NAME, row);\n }\n return withRegistry;\n }\n\n /**\n * The registered projects still on the old folder, added to the LAST row, and only at HOME.\n *\n * At home because that is where the registry lives, and the move a legacy project needs is made\n * by hand in that project — a list of other people's projects printed inside one of them is a\n * list nobody standing there can act on. The caveat travels with it: the registry holds a\n * fraction of the installations on a machine, so a list printed without it reads as an\n * inventory.\n */\n private async appendRegistryList(\n projectDir: string,\n rows: CheckResult[],\n ): Promise<CheckResult[]> {\n const last = rows.at(-1);\n if (!isHomeDirectory(projectDir) || last === undefined) return rows;\n\n const legacyProjects = await registeredProjectsOnTheOldFolder(projectDir);\n if (legacyProjects.length === 0) return rows;\n\n const detailed: CheckResult = {\n ...last,\n status: last.status === \"fail\" ? \"fail\" : \"warn\",\n details: [...(last.details ?? []), REGISTRY_IS_NOT_AN_INVENTORY, ...legacyProjects],\n };\n return [...rows.slice(0, -1), detailed];\n }\n\n /**\n * The orphan row, and which question it can answer. With a config it names the compiled\n * agents that config does not; with no config at all every installed file is unowned and it\n * names all of them. It skips for the one state left — a config that loads and fails\n * validation — because there the file that says who owns what has already been rejected, and\n * an installation must not be called stranded on the strength of one nobody can trust.\n */\n private async resolveOrphansCheck(\n config: ProjectConfig | null,\n configState: ConfigState,\n projectDir: string,\n ): Promise<CheckResult> {\n if (config) return safeCheck(\"orphans\", () => checkNoOrphans(config, projectDir));\n if (configState.kind === \"absent\") {\n return safeCheck(\"orphans\", () => checkUnownedInstallation(projectDir));\n }\n return skippedResult(\"orphans\");\n }\n\n /**\n * The skills row, and the three states in which it cannot be computed: no config to read the\n * skills out of, content it resolves against that the layer above has already reported broken,\n * and a marketplace that never loaded — so there is no populated matrix to resolve them\n * against, and every configured skill would be reported \"not found\".\n */\n private async resolveSkillsCheck(\n config: ProjectConfig | null,\n sourceResult: CheckResult,\n projectDir: string,\n failedContent: ReadonlySet<CheckKind>,\n ): Promise<CheckResult> {\n if (!config) return skippedResult(\"skills\");\n\n const blocked = contentBlockedResult(\"skills\", failedContent);\n if (blocked) return blocked;\n if (sourceResult.status === \"fail\") return sourceUnreachableSkillsResult();\n\n return safeCheck(\"skills\", () => checkSkillsResolved(config, matrix, projectDir));\n }\n\n /**\n * The plugin row, and the two states in which it cannot be computed: no config to read the\n * plugin-mode skills out of, and a plugin registry the layer above could not parse — which\n * resolves as no installs at all, so every configured skill would be reported missing.\n */\n private async resolvePluginsCheck(\n config: ProjectConfig | null,\n projectDir: string,\n failedContent: ReadonlySet<CheckKind>,\n ): Promise<CheckResult> {\n if (!config) return skippedResult(\"plugins\");\n\n const blocked = contentBlockedResult(\"plugins\", failedContent);\n if (blocked) return blocked;\n\n return safeCheck(\"plugins\", () => checkPluginSkillsInstalled(config, projectDir));\n }\n\n private logCheck(name: string, result: CheckResult): void {\n for (const line of formatCheckLine(name, result)) {\n this.log(line);\n }\n }\n\n private printResults(results: CheckResult[], recreateTips: string[]): void {\n this.log(\"\");\n this.log(formatSummary(results));\n\n const tips = [...formatTips(results), ...recreateTips];\n if (tips.length > 0) {\n this.log(\"\");\n for (const tip of tips) {\n this.log(tip);\n }\n }\n\n this.log(\"\");\n }\n}\n","import path from \"node:path\";\n\nimport { CLAUDE_DIR, STANDARD_DIRS } from \"../../consts.js\";\nimport { unusedSourceFolderName, type SourceFolderMove } from \"./install-layout.js\";\nimport { listAgentMdFiles } from \"../agents/list-compiled-agents.js\";\nimport { readFile } from \"../../utils/fs.js\";\nimport {\n bothSourceFoldersPresent,\n compiledAgentNamesTheOtherFolder,\n layoutIsCurrent,\n layoutIsLegacy,\n scopeLabel,\n} from \"../../utils/messages.js\";\nimport { folderBeingRead, type SourceScope } from \"./source-scopes.js\";\n\n/**\n * What `doctor`'s Layout row says about one scope.\n *\n * The row and the write commands' refusal read the same state through the same functions on\n * purpose: a collision one of them reports and the other does not is a state a user cannot get\n * out of. Reading is what `doctor` does, so it reports the state the refusal stops on rather than\n * stopping on it itself.\n *\n * One row per scope, never one for the machine. Two scopes can be on different layouts, and a\n * single row for both can only ever be wrong about one of them.\n */\n\n/** A row: how serious it is, the sentence it leads with, and what else is true of this scope. */\nexport type LayoutFinding = {\n status: \"pass\" | \"warn\" | \"fail\";\n message: string;\n details: string[];\n};\n\nexport async function layoutFindingFor(scope: SourceScope): Promise<LayoutFinding> {\n const { move } = scope;\n const label = scopeLabel(scope.kind);\n\n // A provider that never had a legacy folder has no move to describe, and until 2026-09-22 that\n // left the row printing the scope's NAME with no predicate after it — \"This project\", which is\n // not a sentence and says nothing about the layout the row exists to report. Both faults this\n // row can otherwise carry are properties of having two folder names: `both` is false by\n // construction for such a provider, and `unusedSourceFolderName` answers `null`, so there is\n // nothing to lead with and the layout statement is the whole row.\n if (move === null) {\n return { status: \"pass\", message: layoutIsCurrent(label, scope.relName), details: [] };\n }\n\n const whichLayout = layoutStatement(scope, move, label);\n const faults = await faultsFor(scope, move);\n const [worst, ...rest] = faults;\n\n if (worst === undefined) return { ...whichLayout, details: [] };\n\n // The fault leads and the layout becomes a detail: both are true, and a row can only carry one\n // status — so the line a reader must act on is the one at the top.\n return {\n status: \"fail\",\n message: `${label}: ${worst}`,\n details: [...rest.map((fault) => `${label}: ${fault}`), whichLayout.message],\n };\n}\n\n/** Which of the two folders this scope is on, and how much of a problem that is on its own. */\nfunction layoutStatement(\n scope: SourceScope,\n move: SourceFolderMove,\n label: string,\n): Pick<LayoutFinding, \"status\" | \"message\"> {\n if (!scope.legacy) return { status: \"pass\", message: layoutIsCurrent(label, move.to.relName) };\n return { status: \"warn\", message: layoutIsLegacy(label, move.from.relName, move.to.relName) };\n}\n\n/** Everything wrong with this scope's layout, most serious first. */\nasync function faultsFor(scope: SourceScope, move: SourceFolderMove): Promise<string[]> {\n const staleAgents = await compiledAgentsNamingTheOtherFolder(scope);\n return [...rivalFolderFault(scope, move), ...staleAgents];\n}\n\n/** Two folders on disk: whichever the resolver picked, the other one is being read by nobody. */\nfunction rivalFolderFault(scope: SourceScope, move: SourceFolderMove): string[] {\n if (!scope.both) return [];\n return [\n bothSourceFoldersPresent(move.from.relName, move.to.relName, folderBeingRead(scope, move)),\n ];\n}\n\n/**\n * Installed agents whose own prompt names the folder this scope is NOT on.\n *\n * `agent-summoner`'s playbook tells an agent where to author new sub-agents, so a copy compiled\n * under the other layout aims that instruction at a folder the resolver does not read: the files\n * it writes never compile, and nothing else in the product reports it. The remedy is a recompile,\n * which is what the message says.\n */\nasync function compiledAgentsNamingTheOtherFolder(scope: SourceScope): Promise<string[]> {\n const unused = unusedSourceFolderName(scope.root, scope.provider);\n if (unused === null) return [];\n\n const agentsDir = path.join(scope.root, CLAUDE_DIR, STANDARD_DIRS.AGENTS);\n const files = await listAgentMdFiles(agentsDir);\n const named = await Promise.all(\n files.map(async (file) => ({\n file,\n names: await namesFolder(path.join(agentsDir, file), unused),\n })),\n );\n\n return named\n .filter((entry) => entry.names)\n .map((entry) => compiledAgentNamesTheOtherFolder(entry.file, unused));\n}\n\n/** Whether one compiled agent's text names the folder. An unreadable file names nothing. */\nasync function namesFolder(agentPath: string, folder: string): Promise<boolean> {\n try {\n return (await readFile(agentPath)).includes(folder);\n } catch {\n return false;\n }\n}\n","import path from \"path\";\nimport { isRecord } from \"../utils/type-guards.js\";\nimport { parse as parseYaml } from \"yaml\";\nimport { z } from \"zod\";\nimport { glob, readFile, fileExists, directoryExists } from \"../utils/fs\";\nimport {\n DIRS,\n MARKETPLACE_JSON,\n PLUGIN_MANIFEST_DIR,\n SKILL_CATEGORIES_PATH,\n SKILL_RULES_PATH,\n SKILLS_DIR_PATH,\n STACKS_FILE_PATH,\n STANDARD_FILES,\n} from \"../consts\";\nimport {\n agentYamlGenerationSchema,\n metadataValidationSchema,\n splitMetadataValidationIssues,\n validateSkillMetadata,\n skillCategoriesFileSchema,\n skillRulesFileSchema,\n stackConfigValidationSchema,\n stacksConfigSchema,\n} from \"./schemas\";\nimport { parseFrontmatter } from \"./loading/loader\";\nimport { ConfigDefaultExportError, loadConfig, loadSourceRepoConfig } from \"./configuration\";\nimport { checkMatrixHealth, type MatrixHealthIssue } from \"./matrix\";\nimport {\n loadSkillsMatrixFromSource,\n MarketplaceManifestRequiredError,\n} from \"./loading/source-loader\";\nimport { MarketplaceNameRefusedError } from \"./loading/source-fetcher\";\nimport { matrix } from \"./matrix/matrix-provider\";\nimport { getErrorMessage } from \"../utils/errors\";\nimport { formatZodErrors } from \"./schema-validator\";\n\nexport type SourceValidationIssue = {\n severity: \"error\" | \"warning\";\n file: string;\n message: string;\n};\n\nexport type SourceValidationResult = {\n issues: SourceValidationIssue[];\n skillCount: number;\n errorCount: number;\n warningCount: number;\n};\n\n/**\n * Who this report is for, relative to the marketplace it is about.\n *\n * You are the `\"author\"` of the marketplace being validated when it IS the directory the command\n * ran in; a marketplace anywhere else — fetched, or on disk at another path — is one you are only\n * a `\"consumer\"` of. Not \"is there a marketplace under the cwd\": that says nothing about the OTHER\n * marketplace a config may point at, and the severity turns on the per-marketplace question.\n *\n * It changes one finding. A slug a marketplace's rules dangle is a typo its author can fix and\n * wants the run to fail on, and it is a defect a consumer cannot touch in a file they cannot open\n * — while every skill still installs and still resolves. Whether the reader holds push rights is\n * not knowable here and is not the test: a checkout under your cursor is a file you can edit.\n */\nexport type MarketplaceReader = \"author\" | \"consumer\";\n\n/**\n * True when `dir` is itself a skills source repository — it carries the skills tree\n * `validateSource` walks, at `src/skills/` or wherever its own source config points.\n * A marketplace author's checkout answers yes; a consumer project answers no.\n */\nexport async function isSourceRepo(dir: string): Promise<boolean> {\n // ABORT on an unreadable config, and it is caught rather than fatal at both callers: `doctor`\n // reaches this through `safeCheck`, so the throw becomes a failed row instead of an aborted\n // command, and its other caller asks only where no config file exists at all.\n const sourceConfig = await loadSourceRepoConfig(dir);\n return directoryExists(path.join(dir, sourceConfig?.skillsDir ?? SKILLS_DIR_PATH));\n}\n\n/** Checks if a key uses snake_case (has underscore between lowercase letters) */\nexport function isSnakeCase(key: string): boolean {\n return /[a-z]_[a-z]/.test(key);\n}\n\n/**\n * Checks top-level keys for snake_case usage (camelCase is the convention).\n * Non-object input yields no issues.\n */\nexport function checkSnakeCaseKeys(rawMetadata: unknown, relPath: string): SourceValidationIssue[] {\n if (!isRecord(rawMetadata)) return [];\n\n return Object.keys(rawMetadata)\n .filter(isSnakeCase)\n .map((key) => ({\n severity: \"error\" as const,\n file: relPath,\n message: `Key '${key}' uses snake_case — use camelCase instead`,\n }));\n}\n\n/**\n * Warns when a skill directory's name does not equal the skill's machine id\n * (SKILL.md frontmatter `name`) — the key the loader registers the skill under.\n * Warning, not error: the loader still loads the skill by its id, but the\n * directory no longer signals which skill it contains.\n */\nexport function checkDirNameMatchesSkillId(\n skillId: string,\n relPath: string,\n dirName: string,\n): SourceValidationIssue[] {\n if (skillId === dirName) return [];\n return [\n {\n severity: \"warning\",\n file: relPath,\n message: `Directory name '${dirName}' does not match skill id '${skillId}'`,\n },\n ];\n}\n\n/**\n * Reads a skill directory's SKILL.md frontmatter and checks the directory name\n * against the skill's machine id. When the id cannot be read (unreadable file or\n * missing/invalid frontmatter), reports that the check could not run.\n */\nasync function checkSkillDirName(\n skillsDir: string,\n skillsDirRelPath: string,\n skillDir: string,\n): Promise<SourceValidationIssue[]> {\n const dirName = path.basename(skillDir);\n const skillMdRelPath = path.join(skillsDirRelPath, skillDir, STANDARD_FILES.SKILL_MD);\n\n try {\n const content = await readFile(path.join(skillsDir, skillDir, STANDARD_FILES.SKILL_MD));\n const frontmatter = parseFrontmatter(content, skillMdRelPath);\n\n if (frontmatter === null) {\n return [\n {\n severity: \"warning\",\n file: skillMdRelPath,\n message: `Cannot verify directory name '${dirName}': missing or invalid ${STANDARD_FILES.SKILL_MD} frontmatter`,\n },\n ];\n }\n\n return checkDirNameMatchesSkillId(frontmatter.name, skillMdRelPath, dirName);\n } catch (error) {\n return [\n {\n severity: \"warning\",\n file: skillMdRelPath,\n message: `Cannot verify directory name '${dirName}': ${getErrorMessage(error)}`,\n },\n ];\n }\n}\n\n/**\n * Finds missing SKILL.md / metadata.yaml pairs (pure function, no I/O).\n *\n * Returns issues for:\n * - Directories with SKILL.md but no metadata.yaml\n * - Directories with metadata.yaml but no SKILL.md\n */\nexport function validateSkillFilePairs(\n skillMdDirs: Set<string>,\n metadataDirs: Set<string>,\n skillsDir: string,\n): SourceValidationIssue[] {\n const missingMetadata = [...skillMdDirs]\n .filter((dir) => !metadataDirs.has(dir))\n .map((dir) => ({\n severity: \"error\" as const,\n file: path.join(skillsDir, dir),\n message: `Missing ${STANDARD_FILES.METADATA_YAML} — skill directory has ${STANDARD_FILES.SKILL_MD} but no metadata`,\n }));\n\n const missingSkillMd = [...metadataDirs]\n .filter((dir) => !skillMdDirs.has(dir))\n .map((dir) => ({\n severity: \"error\" as const,\n file: path.join(skillsDir, dir),\n message: `Missing ${STANDARD_FILES.SKILL_MD} — skill directory has ${STANDARD_FILES.METADATA_YAML} but no SKILL.md`,\n }));\n\n return [...missingMetadata, ...missingSkillMd];\n}\n\n/**\n * Validates a skills source repository for metadata correctness.\n *\n * Checks:\n * 1. Every metadata.yaml against the strict validation schema\n * (over-length cliDescription is downgraded to a warning)\n * 2. Directory name equals the skill's machine id (SKILL.md frontmatter name)\n * 3. Cross-references resolve to existing skill IDs (via checkMatrixHealth)\n * 4. camelCase key convention (no snake_case)\n * 5. Every skill directory has both SKILL.md and metadata.yaml\n *\n * `reader` decides how loudly one of those findings is stated — see {@link MarketplaceReader}.\n * It defaults to `\"author\"`, the louder verdict: a caller that names nobody has said nothing\n * about who is reading, and under-reporting to the one person who can fix the file is the worse\n * of the two ways to be wrong.\n */\nexport async function validateSource(\n sourcePath: string,\n reader: MarketplaceReader = \"author\",\n): Promise<SourceValidationResult> {\n const resolvedPath = path.isAbsolute(sourcePath) ? sourcePath : path.resolve(sourcePath);\n\n if (!(await directoryExists(resolvedPath))) {\n return buildResult(\n [{ severity: \"error\", file: resolvedPath, message: \"Marketplace directory does not exist\" }],\n 0,\n );\n }\n\n // ABORT on an unreadable config, caught by `validateOneSource`, which turns it into an issue\n // against this marketplace. Defaulting past it would validate whatever sits at `src/skills/` and\n // call a marketplace whose skills live elsewhere empty.\n const sourceProjectConfig = await loadSourceRepoConfig(resolvedPath);\n const skillsDirRelPath = sourceProjectConfig?.skillsDir ?? SKILLS_DIR_PATH;\n const skillsDir = path.join(resolvedPath, skillsDirRelPath);\n\n if (!(await directoryExists(skillsDir))) {\n return buildResult(\n [{ severity: \"error\", file: skillsDir, message: \"Skills directory does not exist\" }],\n 0,\n );\n }\n\n // Phase 1: Check every skill directory has both SKILL.md and metadata.yaml\n const skillMdFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir);\n const metadataFiles = await glob(`**/${STANDARD_FILES.METADATA_YAML}`, skillsDir);\n\n const skillMdDirs = new Set(skillMdFiles.map((f) => path.dirname(f)));\n const metadataDirs = new Set(metadataFiles.map((f) => path.dirname(f)));\n\n const pairIssues = validateSkillFilePairs(skillMdDirs, metadataDirs, skillsDir);\n\n // Phase 2: Validate each metadata.yaml against strict schema and conventions.\n // Pair violations were already reported by phase 1 — validate only complete pairs.\n const validMetadataFiles = metadataFiles.filter((f) => skillMdDirs.has(path.dirname(f)));\n const skillIssues = await validateSkillsInTurn(skillsDir, skillsDirRelPath, validMetadataFiles);\n\n // Phase 3: Cross-reference validation via matrix health check\n const crossReferenceIssues = await checkCrossReferences(resolvedPath, reader);\n\n // Phases 4–6: optional source-repo targets — run in parallel\n // Phase 4: stack skill metadata + stack configs\n // Phase 5: agent metadata\n // Phase 6: config/*.ts runtime exports\n const extraIssues = await Promise.all([\n validateStacks(resolvedPath),\n validateAgents(resolvedPath),\n validateConfigFiles(resolvedPath),\n ]);\n\n return buildResult(\n [...pairIssues, ...skillIssues, ...crossReferenceIssues, ...extraIssues.flat()],\n validMetadataFiles.length,\n );\n}\n\n/** {@link validateOneSkill} over every complete pair, one skill at a time, in the order given. */\nasync function validateSkillsInTurn(\n skillsDir: string,\n skillsDirRelPath: string,\n metadataFiles: string[],\n): Promise<SourceValidationIssue[]> {\n const issues: SourceValidationIssue[] = [];\n for (const metadataFile of metadataFiles) {\n issues.push(...(await validateOneSkill(skillsDir, skillsDirRelPath, metadataFile)));\n }\n return issues;\n}\n\n/**\n * Why a YAML file could not be read, in the parser's own words. Two phases refuse the same\n * way — the skill-pair scan and the stack/agent scan — and they printed different sentences\n * for a while, one carrying the cause and one dropping it; naming the message once is what\n * stops them diverging again.\n */\nfunction yamlParseFailure(error: unknown): string {\n return `Failed to parse YAML: ${getErrorMessage(error)}`;\n}\n\n/**\n * One complete skill pair, judged four ways: its metadata.yaml parses, its keys are camelCase,\n * it satisfies the strict published-skill schema, and its directory is named after the id its\n * SKILL.md declares. A file nothing can be parsed out of reports that and stops — every check\n * after it reads the parsed value, so each would only restate the same fault.\n */\nasync function validateOneSkill(\n skillsDir: string,\n skillsDirRelPath: string,\n metadataFile: string,\n): Promise<SourceValidationIssue[]> {\n const relPath = path.join(skillsDirRelPath, metadataFile);\n\n let rawMetadata: unknown;\n try {\n rawMetadata = parseYaml(await readFile(path.join(skillsDir, metadataFile)));\n } catch (error) {\n return [{ severity: \"error\", file: relPath, message: yamlParseFailure(error) }];\n }\n\n return [\n ...checkSnakeCaseKeys(rawMetadata, relPath),\n ...checkMetadataSchema(rawMetadata, relPath),\n // Independent of metadata validity — the id lives in SKILL.md frontmatter\n ...(await checkSkillDirName(skillsDir, skillsDirRelPath, path.dirname(metadataFile))),\n ];\n}\n\n/**\n * The strict published-skill schema's verdict, as its hard errors plus the one advisory\n * (over-length cliDescription) `splitMetadataValidationIssues` separates out.\n */\nfunction checkMetadataSchema(rawMetadata: unknown, relPath: string): SourceValidationIssue[] {\n const result = validateSkillMetadata(rawMetadata);\n if (result.success) return [];\n\n const { errors, warnings } = splitMetadataValidationIssues(result.error, rawMetadata);\n return [\n ...errors.map((message) => ({ severity: \"error\" as const, file: relPath, message })),\n ...warnings.map((message) => ({ severity: \"warning\" as const, file: relPath, message })),\n ];\n}\n\n/**\n * The manifest as a reader of the report has to type it — the location half of the row\n * `doctor` prints, and therefore the file they open.\n */\nconst MARKETPLACE_MANIFEST_PATH = path.join(PLUGIN_MANIFEST_DIR, MARKETPLACE_JSON);\n\n/**\n * How loud a missing or unreadable manifest is, by who is reading: its author is one build from\n * the fix, and a consumer has nothing to load. Keyed on the whole union, so a new kind of reader\n * does not compile until someone decides this.\n */\nconst MANIFEST_REQUIRED_SEVERITY = {\n author: \"warning\",\n consumer: \"error\",\n} as const satisfies Record<MarketplaceReader, SourceValidationIssue[\"severity\"]>;\n\n/**\n * Whether every relationship the source declares resolves to a skill it actually holds.\n * A load that did not complete is reported by {@link matrixLoadFailure}, which decides\n * whether the failure is this marketplace's fault or merely this pass's.\n */\nasync function checkCrossReferences(\n resolvedPath: string,\n reader: MarketplaceReader,\n): Promise<SourceValidationIssue[]> {\n try {\n await loadSkillsMatrixFromSource({ sourceFlag: resolvedPath, skipExtraSources: true });\n return checkMatrixHealth(matrix).map((healthIssue) =>\n toSourceIssue(healthIssue, resolvedPath, reader),\n );\n } catch (error) {\n return [matrixLoadFailure(error, reader)];\n }\n}\n\n/**\n * A load this pass could not complete, as the finding a reader can act on.\n *\n * Three answers, because the failures are not one event. A marketplace with no valid\n * manifest cannot be loaded at all, so the finding is the manifest's, filed against the file\n * a reader opens rather than the categories file this pass happened to be reading, at the\n * severity {@link MANIFEST_REQUIRED_SEVERITY} gives its reader. A manifest naming the marketplace\n * something Claude Code registers no plugin under leaves nothing here installable, so it is\n * this marketplace's own ERROR, against the file that holds the name — without it the\n * Marketplaces row counted such a marketplace as validated and printed a tick beneath\n * `doctor`'s own warning about that same file, and a warning above a tick is what a reader\n * stops believing. Everything else only leaves this pass unable to run, so it stays a warning\n * against the file it was reading. Read off the throw's TYPE, so nothing here matches on a\n * sentence.\n */\nfunction matrixLoadFailure(error: unknown, reader: MarketplaceReader): SourceValidationIssue {\n if (error instanceof MarketplaceManifestRequiredError) {\n return {\n severity: MANIFEST_REQUIRED_SEVERITY[reader],\n file: MARKETPLACE_MANIFEST_PATH,\n message: getErrorMessage(error),\n };\n }\n\n if (error instanceof MarketplaceNameRefusedError) {\n return {\n severity: \"error\",\n file: MARKETPLACE_MANIFEST_PATH,\n message: getErrorMessage(error),\n };\n }\n\n return {\n severity: \"warning\",\n file: SKILL_CATEGORIES_PATH,\n message: `Cross-reference validation skipped: failed to load categories/rules: ${getErrorMessage(error)}`,\n };\n}\n\n/**\n * The audit manifest, as a path a reader can open. It is this CLI's own file rather than anything\n * in the marketplace being validated, and it has to be: the verdict a matrix contradicts is\n * recorded there and the marketplace holds no copy of it.\n */\nconst SKILL_AUDIT_PATH = \"src/cli/lib/configuration/skill-audit.ts\";\n\n/**\n * The file a finding's defect is written in — the one `doctor` renders as the location half of the\n * line, and therefore the one the reader opens.\n *\n * Each answer is the file the check actually reads, which is not always the file its name suggests:\n *\n * - a category and the `domain` it omits are both declared in the categories file, and a skill\n * naming a category nothing defines is the same file short an entry (its own metadata.yaml is\n * the other half of that fix, and the message already names the skill);\n * - every relation a resolved skill carries comes from the rules file — no metadata.yaml declares\n * one — so an unresolved reference and the dangling slug behind it are both written there;\n * - a verdict, recorded or missing, is the audit manifest's.\n *\n * Exhaustive on purpose: a seventh finding kind will not compile until someone decides where its\n * defect lives, which is the property one path shared by all six had already lost.\n */\nfunction fileHoldingDefect(finding: MatrixHealthIssue[\"finding\"]): string {\n switch (finding) {\n case \"category-missing-domain\":\n case \"skill-unknown-category\":\n return SKILL_CATEGORIES_PATH;\n case \"skill-unresolved-relation-ref\":\n case \"rule-unresolved-slug\":\n return SKILL_RULES_PATH;\n case \"audit-verdict-contradiction\":\n case \"skill-unaudited\":\n return SKILL_AUDIT_PATH;\n default: {\n const exhaustive: never = finding;\n return exhaustive;\n }\n }\n}\n\n/**\n * One health finding as this reader should hear it. Every kind but one is reported at the\n * severity the health check gave it — that severity is a property of the defect. The exception\n * is the slug a marketplace's own rules dangle, which is a property of the defect AND of who is\n * looking: a typo the author can fix, and a file the consumer cannot open.\n *\n * The reader moves the severity and the wording; it never moves the file, because which file holds\n * the defect is not a fact about who is reading.\n */\nfunction toSourceIssue(\n healthIssue: MatrixHealthIssue,\n marketplacePath: string,\n reader: MarketplaceReader,\n): SourceValidationIssue {\n const file = fileHoldingDefect(healthIssue.finding);\n\n if (reader === \"consumer\" && healthIssue.finding === \"rule-unresolved-slug\") {\n return {\n severity: \"warning\",\n file,\n message: consumedMarketplaceMessage(marketplacePath, healthIssue.details),\n };\n }\n\n return {\n severity: healthIssue.severity,\n file,\n message: healthIssue.details,\n };\n}\n\n/**\n * The same finding, addressed to someone who did not write the rule. It leads with the\n * marketplace because that is the part the slug alone does not tell them, and it says outright\n * that there is nothing here to fix — a warning that sends a reader hunting through their own\n * project for a typo in a file they do not own is worse than no warning at all.\n */\nfunction consumedMarketplaceMessage(marketplacePath: string, details: string): string {\n return `Marketplace '${marketplacePath}': ${details}. Nothing to fix here — the rule is that marketplace's, not this project's`;\n}\n\n/**\n * Validates stack-embedded skill metadata.yaml files and stack config.yaml files.\n * Skips silently when src/stacks/ does not exist.\n */\nasync function validateStacks(resolvedPath: string): Promise<SourceValidationIssue[]> {\n const stacksDir = path.join(resolvedPath, DIRS.stacks);\n if (!(await directoryExists(stacksDir))) return [];\n\n const [skillMetaIssues, configIssues] = await Promise.all([\n validateYamlFiles({\n baseDir: stacksDir,\n relBaseDir: DIRS.stacks,\n pattern: `**/skills/**/${STANDARD_FILES.METADATA_YAML}`,\n schema: metadataValidationSchema,\n }),\n validateYamlFiles({\n baseDir: stacksDir,\n relBaseDir: DIRS.stacks,\n pattern: `*/${STANDARD_FILES.CONFIG_YAML}`,\n schema: stackConfigValidationSchema,\n }),\n ]);\n return [...skillMetaIssues, ...configIssues];\n}\n\n/**\n * Validates each hand-authored agent `metadata.yaml` under `DIRS.agents` against\n * `agentYamlGenerationSchema` — compilation's INPUT schema rather than an output one, since\n * compiling an agent writes one `.md` file and no YAML at all. Skips silently when src/agents/\n * does not exist.\n */\nasync function validateAgents(resolvedPath: string): Promise<SourceValidationIssue[]> {\n const agentsDir = path.join(resolvedPath, DIRS.agents);\n if (!(await directoryExists(agentsDir))) return [];\n\n return validateYamlFiles({\n baseDir: agentsDir,\n relBaseDir: DIRS.agents,\n pattern: `**/${STANDARD_FILES.AGENT_METADATA_YAML}`,\n schema: agentYamlGenerationSchema,\n });\n}\n\n/**\n * Validates TypeScript config files (skill-categories.ts, skill-rules.ts, stacks.ts)\n * by runtime-loading them via loadConfig and validating the default export.\n * Skips silently when a file does not exist.\n */\nasync function validateConfigFiles(resolvedPath: string): Promise<SourceValidationIssue[]> {\n const results = await Promise.all([\n validateTsConfig(resolvedPath, SKILL_CATEGORIES_PATH, skillCategoriesFileSchema),\n validateTsConfig(resolvedPath, SKILL_RULES_PATH, skillRulesFileSchema),\n validateTsConfig(resolvedPath, STACKS_FILE_PATH, stacksConfigSchema),\n ]);\n return results.flat();\n}\n\n/** One kind of YAML file a source may carry: where it lives, and the schema each must satisfy. */\ntype YamlFileScan = {\n baseDir: string;\n /**\n * `baseDir` as issues report it, so their locations match the project-relative form used\n * elsewhere.\n */\n relBaseDir: string;\n pattern: string;\n schema: z.ZodType<unknown>;\n};\n\n/**\n * {@link validateYamlFile} over every file the scan's pattern matches, one file at a time, in\n * glob order.\n */\nasync function validateYamlFiles(scan: YamlFileScan): Promise<SourceValidationIssue[]> {\n const issues: SourceValidationIssue[] = [];\n const files = await glob(scan.pattern, scan.baseDir);\n\n for (const relFile of files) {\n issues.push(...(await validateYamlFile(scan, relFile)));\n }\n return issues;\n}\n\n/**\n * One YAML file judged against the scan's schema: its parse error if it cannot be read, otherwise\n * the schema's errors as field-path messages — none when it passes.\n */\nasync function validateYamlFile(\n scan: YamlFileScan,\n relFile: string,\n): Promise<SourceValidationIssue[]> {\n const displayPath = path.join(scan.relBaseDir, relFile);\n\n let parsed: unknown;\n try {\n parsed = parseYaml(await readFile(path.join(scan.baseDir, relFile)));\n } catch (error) {\n return [{ severity: \"error\", file: displayPath, message: yamlParseFailure(error) }];\n }\n\n const result = scan.schema.safeParse(parsed);\n if (result.success) return [];\n\n return formatZodErrors(result.error).map((message) => ({\n severity: \"error\" as const,\n file: displayPath,\n message,\n }));\n}\n\n/**\n * Runtime-loads a TypeScript config file via loadConfig and reports validation failures.\n * Absent files are not errors — only report when the file exists but fails to load or validate.\n *\n * The two ways a file can say nothing are reported apart, because the author's next move differs\n * and they used to arrive wearing each other's words. A `null` is a file that exported nothing at\n * all; a module that exported only named bindings raises {@link ConfigDefaultExportError}, which\n * is the one this file's own config files get wrong — `export const skillRules = {...}` reads to\n * `loadConfig` as a namespace, and validating THAT against the schema reports whichever field the\n * schema names first as missing from a file the author can see it in.\n */\nasync function validateTsConfig(\n resolvedPath: string,\n relConfigPath: string,\n schema: z.ZodType<unknown>,\n): Promise<SourceValidationIssue[]> {\n const absPath = path.join(resolvedPath, relConfigPath);\n if (!(await fileExists(absPath))) return [];\n\n try {\n const loaded = await loadConfig(absPath, schema);\n if (loaded === null) {\n return [{ severity: \"error\", file: relConfigPath, message: \"Config is empty\" }];\n }\n return [];\n } catch (error) {\n if (error instanceof ConfigDefaultExportError) {\n return [{ severity: \"error\", file: relConfigPath, message: \"Config has no default export\" }];\n }\n return [\n { severity: \"error\", file: relConfigPath, message: formatLoadError(relConfigPath, error) },\n ];\n }\n}\n\n/**\n * Normalizes a loadConfig error so the message references the relative config path,\n * not the absolute path embedded by loadConfig's own template string.\n */\nfunction formatLoadError(relConfigPath: string, error: unknown): string {\n const raw = getErrorMessage(error);\n const stripped = raw.replace(/Failed to load config from '[^']+':\\s*/, \"\");\n return `Failed to load ${relConfigPath}: ${stripped}`;\n}\n\nfunction buildResult(issues: SourceValidationIssue[], skillCount: number): SourceValidationResult {\n const errorCount = issues.filter((i) => i.severity === \"error\").length;\n const warningCount = issues.filter((i) => i.severity === \"warning\").length;\n return { issues, skillCount, errorCount, warningCount };\n}\n","import os from \"os\";\nimport path from \"path\";\nimport { partition } from \"remeda\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { PLUGIN_MANIFEST_DIR, STANDARD_FILES } from \"../consts.js\";\nimport { directoryExists, fileExists, listDirectories } from \"../utils/fs.js\";\nimport { listAgentMdFiles, splitAgentsByProvenance } from \"./agents/index.js\";\nimport { readSkillMetadata } from \"./loading/index.js\";\nimport {\n configDirsInPlay,\n findConfigLoadFailures,\n isLocalSource,\n loadInstalledConfig,\n resolvePrimarySourceEntry,\n type ConfigLoadError,\n type LoadedProjectConfig,\n type SourceEntry,\n} from \"./configuration/index.js\";\nimport { isHomeDirectory, resolveInstallPaths } from \"./installation/index.js\";\nimport { getInstalledConfigPath } from \"./installation/install-base-dir.js\";\nimport {\n getInstalledPluginsRegistryPath,\n getProjectPluginsDir,\n getUserPluginsDir,\n listRegisteredPluginInstalls,\n validateAgentFrontmatter,\n validateAllPlugins,\n validatePlugin,\n validateSkillFrontmatter,\n type ResolvedPlugin,\n} from \"./plugins/index.js\";\nimport { splitMetadataValidationIssues, validateSkillMetadata } from \"./schemas.js\";\nimport { readForkedFromMetadata } from \"./skills/index.js\";\nimport { isSourceRepo, validateSource, type MarketplaceReader } from \"./source-validator.js\";\nimport { invalidResult, mergeValidationResults, validResult } from \"./validation-result.js\";\nimport { scopeLabel } from \"../utils/messages.js\";\nimport { plural } from \"../utils/string.js\";\nimport type { ValidationResult } from \"../types/index.js\";\n\n/** How a source repository under the cwd is labelled when it is not a registered source. */\nconst CWD_SOURCE_NAME = \"current directory\";\n\n/** What a compiled agent is called on disk; `splitAgentsByProvenance` answers in bare names. */\nconst AGENT_FILE_EXTENSION = \".md\";\n\nexport type ContentIssue = {\n severity: \"error\" | \"warning\";\n /** The file or directory a reader has to open to fix the issue. */\n file: string;\n message: string;\n};\n\n/**\n * One content pass: what it walked, what it found, and what it deliberately did\n * not check. `notes` carries neither errors nor warnings — it is the record of\n * which sources were reached and which were skipped.\n */\nexport type ContentValidation = {\n count: number;\n issues: ContentIssue[];\n notes: string[];\n};\n\nconst NOTHING_VALIDATED: ContentValidation = { count: 0, issues: [], notes: [] };\n\nfunction mergeValidations(parts: ContentValidation[]): ContentValidation {\n return parts.reduce(\n (acc, part) => ({\n count: acc.count + part.count,\n issues: [...acc.issues, ...part.issues],\n notes: [...acc.notes, ...part.notes],\n }),\n NOTHING_VALIDATED,\n );\n}\n\n/** Render an absolute path with a `~/` prefix when it's under the user's home directory. */\nfunction displayDir(absolutePath: string): string {\n const home = os.homedir();\n if (absolutePath === home) return \"~\";\n if (absolutePath.startsWith(home + path.sep)) {\n return `~${path.sep}${path.relative(home, absolutePath)}`;\n }\n return absolutePath;\n}\n\nfunction toIssues(file: string, result: ValidationResult): ContentIssue[] {\n return [\n ...result.errors.map((message) => ({ severity: \"error\" as const, file, message })),\n ...result.warnings.map((message) => ({ severity: \"warning\" as const, file, message })),\n ];\n}\n\n/** Says which of the three load outcomes a config file is in, without restating the loader. */\nconst CONFIG_UNREADABLE = \"exists but could not be loaded\";\n\n/**\n * Validates the config files this run reads: the project's own and the global one it inlines.\n *\n * A config that exists and cannot be parsed is reported here rather than left to the operational\n * layer, for the reason that layer exists to be gated on — every operational row is read OUT of\n * this file, so each would be a cascade of the same fault. It is also the only report that can\n * carry the loader's reason: the layers below re-read the file per check, and their own\n * diagnostics for it are what used to arrive spliced between the rows.\n *\n * An ABSENT config is not a finding here. It is the legitimate state `init` exists for, and the\n * operational layer already names it and offers the remedy that fits it.\n */\nexport async function validateProjectConfigFile(projectDir: string): Promise<ContentValidation> {\n const [present, failures] = await Promise.all([\n countExistingConfigs(projectDir),\n findConfigLoadFailures(projectDir),\n ]);\n\n return { count: present, issues: failures.map(toUnreadableConfigIssue), notes: [] };\n}\n\n/**\n * Led by whose config it is, as every config row `doctor` prints is: from a project, the file can\n * be its own or the global one it inherits.\n */\nfunction toUnreadableConfigIssue(failure: ConfigLoadError): ContentIssue {\n return {\n severity: \"error\",\n file: `${scopeLabel(failure.scope)}'s ${displayDir(failure.configPath)}`,\n message: `${CONFIG_UNREADABLE}: ${failure.reason}`,\n };\n}\n\nasync function countExistingConfigs(projectDir: string): Promise<number> {\n const present = await Promise.all(\n configDirsInPlay(projectDir).map((dir) => fileExists(getInstalledConfigPath(dir))),\n );\n return present.filter(Boolean).length;\n}\n\n/**\n * Validates the marketplace this installation reads from when it is local, plus the current\n * directory when that is itself a skills source repository — that repository is the content a\n * marketplace author runs this against, and it is not the source they read skills from.\n * A remote marketplace is recorded as skipped rather than fetched.\n *\n * Each one is validated as the reader's own or as someone else's — see {@link readerFor}.\n */\nexport async function validateRegisteredSources(projectDir: string): Promise<ContentValidation> {\n // DEGRADE on an unreadable config. This pass declares `readsConfig: true`, so `doctor` stands it\n // down before it runs whenever the config check has already reported the file — and if it is\n // reached anyway, `safeCheck` turns the throw into a failed row rather than an aborted run. A\n // command whose job is naming what is wrong here must survive the thing it is naming.\n const primary = await resolvePrimarySourceEntry(projectDir);\n const registered = [primary];\n const remote = registered.filter((source) => !isLocalSource(source.url));\n const local = await localSourcesToValidate(projectDir, registered);\n\n const outcomes = await Promise.all(\n local.map((source) => validateOneSource(source, readerFor(projectDir, source))),\n );\n\n return {\n count: local.length,\n issues: outcomes.flatMap((outcome) => outcome.issues),\n notes: [\n ...outcomes.map((outcome) => outcome.note),\n ...remote.map((source) => `${source.name} (${source.url}) — skipped (remote)`),\n ],\n };\n}\n\nasync function localSourcesToValidate(\n projectDir: string,\n registered: SourceEntry[],\n): Promise<SourceEntry[]> {\n const local = registered.filter((source) => isLocalSource(source.url));\n const alreadyCovered = local.some((source) => isCwdMarketplace(projectDir, source));\n\n if (alreadyCovered || !(await isSourceRepo(projectDir))) return local;\n\n return [...local, { name: CWD_SOURCE_NAME, url: projectDir }];\n}\n\n/** Whether this marketplace IS the directory the command ran in, however the config spells it. */\nfunction isCwdMarketplace(projectDir: string, source: SourceEntry): boolean {\n return path.resolve(projectDir, source.url) === projectDir;\n}\n\n/**\n * Where this layer decides what the reader's relationship to a marketplace is — the one place\n * that holds both the directory the command ran in and the path of each marketplace it validates.\n * The appended `CWD_SOURCE_NAME` entry answers `\"author\"` by construction, which is the whole of\n * why a marketplace author's own repository is judged the way they need it to be.\n */\nfunction readerFor(projectDir: string, source: SourceEntry): MarketplaceReader {\n return isCwdMarketplace(projectDir, source) ? \"author\" : \"consumer\";\n}\n\ntype SourceOutcome = { note: string; issues: ContentIssue[] };\n\nasync function validateOneSource(\n source: SourceEntry,\n reader: MarketplaceReader,\n): Promise<SourceOutcome> {\n const label = `${source.name} (${source.url})`;\n try {\n const result = await validateSource(source.url, reader);\n return { note: `${label} — ${plural(result.skillCount, \"skill\")}`, issues: result.issues };\n } catch (error) {\n return {\n note: `${label} — failed`,\n issues: [{ severity: \"error\", file: source.url, message: getErrorMessage(error) }],\n };\n }\n}\n\n/** Validates every installed plugin bundle in the global and project plugin directories. */\nexport async function validateInstalledPlugins(projectDir: string): Promise<ContentValidation> {\n const dirs = [\n getUserPluginsDir(),\n ...(isHomeDirectory(projectDir) ? [] : [getProjectPluginsDir(projectDir)]),\n ];\n return mergeValidations(await Promise.all(dirs.map(validatePluginsDirectory)));\n}\n\nasync function validatePluginsDirectory(pluginsDir: string): Promise<ContentValidation> {\n if (!(await directoryExists(pluginsDir))) return NOTHING_VALIDATED;\n\n if (await fileExists(getInstalledPluginsRegistryPath(pluginsDir))) {\n const registryValidation = await validateRegistryPlugins(pluginsDir);\n if (registryValidation !== undefined) return registryValidation;\n }\n\n const pluginDirs = await findPluginDirectories(pluginsDir);\n if (pluginDirs.length === 0) return NOTHING_VALIDATED;\n\n const { results } = await validateAllPlugins(pluginsDir);\n\n return {\n count: results.length,\n issues: results.flatMap(({ name, result }) => toIssues(name, result)),\n notes: [],\n };\n}\n\n/**\n * Validates the installs recorded in the plugins directory's `installed_plugins.json`\n * (claude CLI >=2.1.220 cache layout). Returns undefined when the registry records no\n * installs so the caller can fall back to the direct-children scan (older/manual layouts).\n */\nasync function validateRegistryPlugins(pluginsDir: string): Promise<ContentValidation | undefined> {\n let installs: ResolvedPlugin[];\n try {\n installs = await listRegisteredPluginInstalls(pluginsDir);\n } catch (error) {\n return {\n count: 0,\n issues: [\n {\n severity: \"error\",\n file: displayDir(getInstalledPluginsRegistryPath(pluginsDir)),\n message: getErrorMessage(error),\n },\n ],\n notes: [],\n };\n }\n\n if (installs.length === 0) return undefined;\n\n // A recorded installPath that no longer exists surfaces as an invalid plugin\n // via validatePlugin's structure check, not a crash.\n const results = await Promise.all(\n installs.map(async ({ pluginKey, installPath }) =>\n toIssues(pluginKey, await validatePlugin(installPath)),\n ),\n );\n\n return { count: installs.length, issues: results.flat(), notes: [] };\n}\n\nasync function findPluginDirectories(pluginsDir: string): Promise<string[]> {\n const entries = await listDirectories(pluginsDir);\n const checks = await Promise.all(\n entries.map(async (name) => ({\n name,\n isPlugin: await directoryExists(path.join(pluginsDir, name, PLUGIN_MANIFEST_DIR)),\n })),\n );\n return checks.filter((check) => check.isPlugin).map((check) => check.name);\n}\n\n/**\n * Validates the SKILL.md and metadata.yaml of every installed skill this installation owns, both\n * scopes.\n *\n * Ownership has to be asked because `~/.claude/skills/` is CLAUDE CODE's directory, shared with\n * everything else that installs a skill there — not this CLI's private space. Judging every\n * directory in it against this CLI's schema reported a skill some other tool put there as a fault\n * in the user's install, in a file they never wrote and cannot fix from here.\n *\n * Two claims make a directory this installation's, and either is enough:\n *\n * - **The configuration in play names its id.** Both configs a run reads, because both scopes are\n * walked. This is the claim that keeps `doctor` loud where it matters most: the provenance\n * marker lives INSIDE metadata.yaml, so a skill whose metadata is missing or unparseable — the\n * plainest breakage there is — can carry no marker to be recognised by.\n * - **The directory carries `forkedFrom`.** The marker this CLI stamps into every skill directory\n * it writes, and the same question `uninstall` asks before removing anything. It answers for the\n * skills a configuration has stopped naming, or never got the chance to.\n *\n * A directory neither claim reaches is not judged and not counted. It IS named — see\n * {@link foreignSkillNote}: a check that quietly walks past a directory is indistinguishable from\n * one that missed it.\n */\nexport async function validateInstalledSkills(projectDir: string): Promise<ContentValidation> {\n const configuredIds = await configuredSkillIds(projectDir);\n return mergeValidations(\n await Promise.all(\n installedDirs(projectDir, \"skillsDir\").map((dir) =>\n validateSkillsDirectory(dir, configuredIds),\n ),\n ),\n );\n}\n\n/** Every skill id the configs this run reads name, across both scopes and both install modes. */\nasync function configuredSkillIds(projectDir: string): Promise<ReadonlySet<string>> {\n const loaded = await Promise.all(\n configDirsInPlay(projectDir).map((dir) => loadInstalledConfig(dir)),\n );\n return new Set(loaded.flatMap(configuredIdsOf));\n}\n\nfunction configuredIdsOf(loaded: LoadedProjectConfig | null): string[] {\n if (!loaded) return [];\n return loaded.config.skills.map((skill) => skill.id);\n}\n\n/** Validates the frontmatter of every compiled agent file, both scopes. */\nexport async function validateInstalledAgents(projectDir: string): Promise<ContentValidation> {\n return mergeValidations(\n await Promise.all(installedDirs(projectDir, \"agentsDir\").map(validateAgentsDirectory)),\n );\n}\n\n/**\n * The directories to walk for an installed-content pass. At the home directory both\n * scopes resolve to the same place, so it is walked once instead of twice.\n */\nfunction installedDirs(projectDir: string, key: \"skillsDir\" | \"agentsDir\"): string[] {\n const global = resolveInstallPaths(projectDir, \"global\")[key];\n if (isHomeDirectory(projectDir)) return [global];\n return [global, resolveInstallPaths(projectDir, \"project\")[key]];\n}\n\n/** What an installed-content pass walked, named rather than validated. */\nexport type InstalledArtifacts = {\n /** Every skill directory this CLI can prove it wrote, both scopes, as display paths. */\n skills: string[];\n /** Every compiled agent file this CLI can prove it wrote, both scopes, as display paths. */\n agents: string[];\n};\n\n/**\n * The same two walks {@link validateInstalledSkills} and {@link validateInstalledAgents} count,\n * naming what belongs to this installation instead of judging it. `doctor` reports this when no\n * configuration exists to compare the installation against: there is nothing to validate the\n * files AGAINST, and naming them is the whole finding.\n *\n * Each walk applies the same ownership question its own pass asks, minus the half that needs a\n * config to answer — see {@link listSkillDirsWithOurProvenance} and\n * {@link listAgentFilesWithOurProvenance}. Both are the question the command this row sends the\n * reader to asks before it removes anything, so what is named here is what `uninstall` takes.\n */\nexport async function listInstalledArtifacts(projectDir: string): Promise<InstalledArtifacts> {\n const [skills, agents] = await Promise.all([\n listAcrossScopes(installedDirs(projectDir, \"skillsDir\"), listSkillDirsWithOurProvenance),\n listAcrossScopes(installedDirs(projectDir, \"agentsDir\"), listAgentFilesWithOurProvenance),\n ]);\n return { skills, agents };\n}\n\n/**\n * The compiled agent files in one tree that this CLI can prove it wrote.\n *\n * The agent axis's counterpart to {@link listSkillDirsWithOurProvenance}, and deliberately\n * `splitAgentsByProvenance` rather than a second reading of the same marker: `uninstall` decides\n * what to remove with no configuration by asking that function, so a listing built any other way\n * could offer a file it then refused. It did — every `*.md` in the directory was named here while\n * the remover took only the marked ones, so a hand-authored agent was offered by one screen and\n * declined by the next.\n *\n * A file that cannot be read carries no marker and is not listed, which is that function's own\n * rule: \"cannot prove it is ours\" and \"is not ours\" call for the same answer. Nothing goes\n * unmentioned either way — {@link validateInstalledAgents} walks every agent file whatever state\n * the config is in.\n */\nasync function listAgentFilesWithOurProvenance(agentsDir: string): Promise<string[]> {\n const { marked } = await splitAgentsByProvenance(agentsDir);\n return marked.map((name) => `${name}${AGENT_FILE_EXTENSION}`);\n}\n\n/**\n * The skill directories in one tree that this CLI can prove it wrote.\n *\n * With no configuration left to name an id, the provenance marker is the only claim a directory\n * can carry — and it is the claim `uninstall` reads before removing anything, so every directory\n * listed here is one that command would actually remove. Listing a directory it would refuse is\n * the CLI recommending what it then declines to do.\n *\n * The marker lives INSIDE `metadata.yaml`, so a directory whose metadata is missing or unreadable\n * can carry no marker and is not listed. That is deliberate rather than a gap: \"cannot prove it is\n * ours\" and \"is not ours\" call for the same answer — leave it alone — which is the rule\n * `splitAgentsByProvenance` already applies to compiled agents. Nothing goes unmentioned either\n * way: {@link validateInstalledSkills} runs whatever state the config is in, and names every\n * directory it stepped over.\n */\nasync function listSkillDirsWithOurProvenance(skillsDir: string): Promise<string[]> {\n const names = await listDirectories(skillsDir);\n const judged = await Promise.all(\n names.map(async (name) => ({ name, ours: await carriesOurProvenance(skillsDir, name) })),\n );\n return judged.filter((entry) => entry.ours).map((entry) => entry.name);\n}\n\nasync function listAcrossScopes(\n dirs: string[],\n listEntries: (dir: string) => Promise<string[]>,\n): Promise<string[]> {\n const perDir = await Promise.all(dirs.map((dir) => listDisplayPaths(dir, listEntries)));\n return perDir.flat();\n}\n\n/** One directory's entries as display paths, or nothing when the directory is absent. */\nasync function listDisplayPaths(\n dir: string,\n listEntries: (dir: string) => Promise<string[]>,\n): Promise<string[]> {\n if (!(await directoryExists(dir))) return [];\n const entries = await listEntries(dir);\n return entries.map((entry) => displayDir(path.join(dir, entry)));\n}\n\nasync function validateSkillsDirectory(\n skillsDir: string,\n configuredIds: ReadonlySet<string>,\n): Promise<ContentValidation> {\n if (!(await directoryExists(skillsDir))) return NOTHING_VALIDATED;\n\n const skillDirs = await listDirectories(skillsDir);\n if (skillDirs.length === 0) return NOTHING_VALIDATED;\n\n const judged = await Promise.all(\n skillDirs.map(async (name) => ({\n name,\n ours: await isOurSkillDirectory(skillsDir, name, configuredIds),\n })),\n );\n const [ours, foreign] = partition(judged, (entry) => entry.ours);\n\n const results = await Promise.all(\n ours.map(async ({ name }) => {\n const skillDir = path.join(skillsDir, name);\n return toIssues(displayDir(skillDir), await validateInstalledSkill(skillDir));\n }),\n );\n\n return {\n count: ours.length,\n issues: results.flat(),\n notes: foreign.map(({ name }) => foreignSkillNote(skillsDir, name)),\n };\n}\n\n/** Either claim this installation has on a skill directory — see {@link validateInstalledSkills}. */\nasync function isOurSkillDirectory(\n skillsDir: string,\n name: string,\n configuredIds: ReadonlySet<string>,\n): Promise<boolean> {\n if (configuredIds.has(name)) return true;\n return carriesOurProvenance(skillsDir, name);\n}\n\n/**\n * The claim a skill directory carries on its own, independent of any configuration: the\n * `forkedFrom` block the copier stamps into every directory this CLI writes. One definition,\n * because the union above and the no-config listing are the same judgement with and without its\n * other half — and `uninstall` decides what to remove by asking exactly this.\n */\nasync function carriesOurProvenance(skillsDir: string, name: string): Promise<boolean> {\n return (await readForkedFromMetadata(path.join(skillsDir, name))) !== null;\n}\n\n/**\n * A directory this pass declined to judge, named so the count and the directory listing do not\n * silently disagree. Not a warning: nothing here is wrong, and a reader sent to fix a skill they\n * did not install is worse off than one told plainly that it is not this CLI's to police.\n */\nfunction foreignSkillNote(skillsDir: string, name: string): string {\n return `${displayDir(path.join(skillsDir, name))} — not installed by this CLI and named by no configuration here: not validated`;\n}\n\nasync function validateAgentsDirectory(agentsDir: string): Promise<ContentValidation> {\n if (!(await directoryExists(agentsDir))) return NOTHING_VALIDATED;\n\n const agentFiles = await listAgentMdFiles(agentsDir);\n if (agentFiles.length === 0) return NOTHING_VALIDATED;\n\n const results = await Promise.all(\n agentFiles.map(async (fileName) => {\n const agentPath = path.join(agentsDir, fileName);\n return toIssues(displayDir(agentPath), await validateAgentFrontmatter(agentPath));\n }),\n );\n\n return { count: agentFiles.length, issues: results.flat(), notes: [] };\n}\n\nasync function validateInstalledSkill(skillDir: string): Promise<ValidationResult> {\n const skillMd = await validateSkillFile(\n skillDir,\n STANDARD_FILES.SKILL_MD,\n validateSkillFrontmatter,\n );\n const metadata = await validateSkillFile(\n skillDir,\n STANDARD_FILES.METADATA_YAML,\n validateInstalledSkillMetadata,\n );\n\n return mergeValidationResults([skillMd, metadata]);\n}\n\n/** One of an installed skill's files judged by `validate`, or the error of its absence. */\nasync function validateSkillFile(\n skillDir: string,\n fileName: string,\n validate: (filePath: string) => Promise<ValidationResult>,\n): Promise<ValidationResult> {\n const filePath = path.join(skillDir, fileName);\n if (!(await fileExists(filePath))) return invalidResult(`Missing ${fileName}`);\n return validate(filePath);\n}\n\nasync function validateInstalledSkillMetadata(metadataPath: string): Promise<ValidationResult> {\n // Same judgment `compile` refuses on and local-skill discovery skips on: a file\n // that describes no skill is reported here as the error it is. The strict\n // published-skill checks below are layered on top of that verdict, never against\n // it — doctor reports what the other two passes refuse, and then some.\n const read = await readSkillMetadata(metadataPath);\n if (!read.usable) {\n return invalidResult(`${STANDARD_FILES.METADATA_YAML}: ${read.reason}`);\n }\n\n const metadata = read.metadata;\n const result = validateSkillMetadata(metadata);\n if (result.success) {\n return validResult();\n }\n\n // Over-length cliDescription is advisory — only hard schema violations invalidate the skill\n const { errors, warnings } = splitMetadataValidationIssues(result.error, metadata);\n return {\n valid: errors.length === 0,\n errors: errors.map((e) => `${STANDARD_FILES.METADATA_YAML}: ${e}`),\n warnings: warnings.map((w) => `${STANDARD_FILES.METADATA_YAML}: ${w}`),\n };\n}\n"],"mappings":"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","names":["os","path","path","path","skillsDir","path","path","skillsDir","os","matrix","path"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/commands/doctor.ts","../../src/cli/lib/source-validator.ts","../../src/cli/lib/content-validator.ts"],"sourcesContent":["import os from \"os\";\nimport path from \"path\";\nimport { BaseCommand } from \"../base-command\";\nimport { getErrorMessage } from \"../utils/errors\";\nimport { EXIT_CODES } from \"../lib/exit-codes\";\nimport {\n effectivelyExcludedSkillIds,\n findConfigLoadFailures,\n loadInstalledConfig,\n validateProjectConfig,\n SOURCE_ENV_VAR,\n type ResolvedConfig,\n} from \"../lib/configuration\";\nimport { loadSource, detectProject, type DetectedProject } from \"../lib/operations\";\nimport { matrix } from \"../lib/matrix/matrix-provider\";\nimport { discoverLocalSkills } from \"../lib/skills\";\nimport { getStackSkillIds } from \"../lib/stacks\";\nimport { filterExcludedEntries, listAgentFilesOf } from \"../lib/agents\";\nimport { getVerifiedPluginInstallPaths, parseMarketplacePluginRef } from \"../lib/plugins\";\nimport {\n declaresNoContent,\n isHomeDirectory,\n installBaseDir,\n resolveInstallPaths,\n} from \"../lib/installation\";\nimport {\n agentCodec,\n type AgentCodec,\n hostCompilesAgent,\n providerInUse,\n relativeConfigPath,\n skillsDir,\n skillsPathPrefix,\n} from \"../lib/installation/install-layout\";\nimport { ambiguityFinding } from \"../lib/installation/provider-flag\";\nimport { unofferablePlacementsFound } from \"../lib/hosts/configured-placements\";\nimport {\n sourceScopesInPlay,\n type ScopeKind,\n type SourceScope,\n} from \"../lib/installation/source-scopes\";\nimport { layoutIsCurrent, recreateConfigFrom, scopeLabel, scopeNoun } from \"../utils/messages\";\nimport { getInstalledConfigPath } from \"../lib/installation/install-base-dir\";\nimport { isSourceRepo } from \"../lib/source-validator\";\nimport {\n listInstalledArtifacts,\n validateInstalledAgents,\n validateInstalledPlugins,\n validateInstalledSkills,\n validateProjectConfigFile,\n validateRegisteredSources,\n type ContentIssue,\n type ContentValidation,\n} from \"../lib/content-validator\";\nimport type { SourceLoadResult } from \"../lib/loading\";\nimport type {\n AgentName,\n AgentScopeConfig,\n MergedSkillsMatrix,\n ProjectConfig,\n SkillConfig,\n SkillId,\n SkillScope,\n} from \"../types\";\nimport { fileExists, directoryExists } from \"../utils/fs\";\nimport { plural } from \"../utils/string\";\nimport {\n CLI_INVOKE_COMMAND,\n DEFAULT_BRANDING,\n EJECT_SOURCE,\n STANDARD_FILES,\n UI_SYMBOLS,\n type Provider,\n} from \"../consts\";\nimport { countBy, unique } from \"remeda\";\n\ntype CheckKind =\n | \"config\"\n | \"config-empty\"\n | \"layout\"\n | \"placements\"\n | \"skills\"\n | \"agents\"\n | \"orphans\"\n | \"orphans-unowned\"\n | \"installed\"\n | \"plugins\"\n | \"source\"\n | \"content-config\"\n | \"content-sources\"\n | \"content-plugins\"\n | \"content-skills\"\n | \"content-agents\";\n\ntype CheckResult = {\n kind: CheckKind;\n status: \"pass\" | \"fail\" | \"warn\" | \"skip\";\n message: string;\n details?: string[];\n};\n\ntype ConfigCheckOutput = {\n result: CheckResult;\n config: ProjectConfig | null;\n};\n\n/**\n * Which state the config these rows describe is in. `detectInstallation` answers \"is there an\n * installation here\", not \"is there a config here\", and hands back the same `null` for an absent\n * file and for one that loads while declaring neither skills nor agents — two states that need\n * different sentences. The state missing from this union is the one that cannot reach this layer:\n * a config that cannot be READ is the content layer's finding, and it skips the operational layer.\n *\n * A loaded config says whether it is this directory's own or the global one it inherits: a\n * directory with no config of its own under a global installation reads the global config, and a\n * row naming this directory's path for it describes a file that is not there.\n */\ntype ConfigState =\n | { kind: \"absent\" }\n | { kind: \"declares-nothing\"; config: ProjectConfig }\n | { kind: \"loaded\"; config: ProjectConfig; inherited: boolean };\n\n/**\n * The config in THIS directory, and what state it is in. Only this directory: the global fallback\n * is `detectInstallation`'s to make and it has already made it, so a project with no config of its\n * own is absent here whatever the home directory holds — `init` writes exactly the declares-nothing\n * shape as the blank global pair on every project setup, and it is not this project's config.\n *\n * The load cannot throw: `configDirsInPlay` is the same set the content layer just read, and any\n * failure in it skipped this layer entirely.\n */\nasync function resolveConfigState(\n detected: DetectedProject | null,\n projectDir: string,\n): Promise<ConfigState> {\n if (detected?.config) {\n return {\n kind: \"loaded\",\n config: detected.config,\n inherited: inheritsTheGlobalConfig(detected.installation.projectDir, projectDir),\n };\n }\n\n const loaded = await loadInstalledConfig(projectDir);\n if (!loaded) return { kind: \"absent\" };\n\n return declaresNoContent(loaded.config)\n ? { kind: \"declares-nothing\", config: loaded.config }\n : { kind: \"loaded\", config: loaded.config, inherited: false };\n}\n\n/** Whether the config detection read for `projectDir` is the home directory's, inherited. */\nfunction inheritsTheGlobalConfig(readFrom: string, projectDir: string): boolean {\n return isHomeDirectory(readFrom) && !isHomeDirectory(projectDir);\n}\n\n/**\n * `projectDir` rather than a module-level constant for the path this reports about: a scope's\n * source folder depends on the provider read off its disk, so nothing about it exists at module\n * load, and every line below has to name the folder this project is ACTUALLY on.\n */\nfunction checkConfigValid(state: ConfigState, projectDir: string): ConfigCheckOutput {\n const theConfig = (verdict: string) => configRowMessage(state, projectDir, verdict);\n if (state.kind === \"absent\") {\n return {\n result: {\n kind: \"config\",\n status: \"fail\",\n message: theConfig(\"not found\"),\n details: [`Run '${CLI_INVOKE_COMMAND} init' to create a configuration`],\n },\n config: null,\n };\n }\n\n // Not a fault and nothing to repair — the file is valid, it just has nothing in it. The config\n // is still handed on: the rows below describe an empty install truthfully, where a `null` would\n // have them all report `Skipped (config invalid)` about a config that is valid.\n if (state.kind === \"declares-nothing\") {\n return {\n result: {\n kind: \"config-empty\",\n status: \"warn\",\n message: theConfig(\"is valid but declares no skills and no agents\"),\n },\n config: state.config,\n };\n }\n\n const { config } = state;\n const validation = validateProjectConfig(config);\n\n if (!validation.valid) {\n return {\n result: {\n kind: \"config\",\n status: \"fail\",\n message: theConfig(\"has errors\"),\n details: validation.errors,\n },\n config: null,\n };\n }\n\n if (validation.warnings.length > 0) {\n return {\n result: {\n kind: \"config\",\n status: \"warn\",\n message: theConfig(\"has warnings\"),\n details: validation.warnings,\n },\n config,\n };\n }\n\n return {\n result: {\n kind: \"config\",\n status: \"pass\",\n message: theConfig(\"is valid\"),\n },\n config,\n };\n}\n\n/**\n * What a Config Valid row says, led by whose config it is: this project's, or the global\n * installation's — the nouns the Layout rows lead with.\n *\n * A directory with no config of its own under a global installation reads the global one, and the\n * row says so, naming the file it actually read rather than this directory's path for a file that\n * is not there.\n */\nfunction configRowMessage(state: ConfigState, projectDir: string, verdict: string): string {\n if (state.kind === \"loaded\" && state.inherited)\n return `${theInheritedConfig()}, which ${verdict}`;\n return `${theOwnConfig(projectDir)} ${verdict}`;\n}\n\n/** `This project: .agents-inc/claude/config.ts`, or the global installation's from home. */\nfunction theOwnConfig(projectDir: string): string {\n return `${scopeLabel(scopeOf(projectDir))}: ${configNamedFrom(projectDir)}`;\n}\n\n/** A project with no config of its own, and the global one it reads in its place. */\nfunction theInheritedConfig(): string {\n return `${scopeLabel(\"project\")}: no ${STANDARD_FILES.CONFIG_TS}, using ${scopeNoun(\"global\")}'s (${configNamedFrom(os.homedir())})`;\n}\n\n/** The scope whose config a directory holds: the home directory's is the global one. */\nfunction scopeOf(dir: string): ScopeKind {\n return isHomeDirectory(dir) ? \"global\" : \"project\";\n}\n\n/**\n * A config file as a row names it: relative to the project it belongs to, and from home for the\n * global one — named relative to a project, the global config is a file that project does not have.\n */\nfunction configNamedFrom(root: string): string {\n const relPath = relativeConfigPath(root, providerInUse(root));\n return isHomeDirectory(root) ? `~/${relPath}` : relPath;\n}\n\nasync function checkSkillsResolved(\n config: ProjectConfig,\n matrix: MergedSkillsMatrix,\n projectDir: string,\n): Promise<CheckResult> {\n // config.ts declares skills both directly and through the stack. A global-only\n // install has a populated skills array and no stack at all, so keying this check\n // off the stack alone would report \"No skills configured\" for a real install.\n const excludedIds = effectivelyExcludedSkillIds(config.skills);\n const stackIds = config.stack ? getStackSkillIds(config.stack) : [];\n const uniqueSkills = unique([...stackIds, ...config.skills.map((s) => s.id)]).filter(\n (id) => !excludedIds.has(id),\n );\n\n if (uniqueSkills.length === 0) {\n return {\n kind: \"skills\",\n status: \"pass\",\n message: \"No skills configured\",\n };\n }\n\n const [localResult, globalResult] = await Promise.all([\n discoverLocalSkills(projectDir),\n !isHomeDirectory(projectDir) ? discoverLocalSkills(os.homedir()) : null,\n ]);\n const localSkillIds = new Set([\n ...(localResult?.skills.map((s) => s.id) ?? []),\n ...(globalResult?.skills.map((s) => s.id) ?? []),\n ]);\n\n const missingSkills = uniqueSkills.filter(\n (skillId) => !(skillId in matrix.skills) && !localSkillIds.has(skillId),\n );\n\n if (missingSkills.length > 0) {\n return {\n kind: \"skills\",\n status: \"fail\",\n message: `${ratio(uniqueSkills.length - missingSkills.length, uniqueSkills.length, \"skill\")} found`,\n details: missingSkills.map((s) => `- ${s} (not found)`),\n };\n }\n\n return {\n kind: \"skills\",\n status: \"pass\",\n message: `${ratio(uniqueSkills.length, uniqueSkills.length, \"skill\")} found`,\n };\n}\n\n/**\n * Whether every configured sub-agent is compiled where its own host reads it.\n *\n * **Two things here were Claude's and are the host's**, and both were found by running the\n * command on a healthy Codex installation rather than by reading it:\n *\n * - the FILENAME was `${name}.md`. Claude reads `.md` and Codex reads `.toml`, so on Codex this\n * looked for a file no release will ever write. {@link agentCodec} answers the extension, and\n * this is the extension ban's own census in action — the eslint config leaves `.md` unbanned\n * precisely because product modules like this one still spell it.\n * - the VERDICT was \"needs recompilation\", for a sub-agent {@link hostCompilesAgent} says this\n * host does not carry at all. Recompiling writes nothing, so the row asked for a remedy that\n * does not exist and turned an install working exactly as this release intends into a warning.\n * With C5 that is no longer a whole provider but two named sub-agents — `agent-summoner` and\n * `skill-summoner`, which Codex leaves out for v1 — so the row names them rather than the host.\n *\n * The host is read per SCOPE, the way `resolveInstallPaths` reads it beside this: two scopes can\n * be on two providers, and one answer for both is how a row reports about the wrong installation.\n */\nasync function checkAgentsCompiled(\n config: ProjectConfig,\n projectDir: string,\n): Promise<CheckResult> {\n const agents = config.agents;\n\n if (agents.length === 0) {\n return {\n kind: \"agents\",\n status: \"pass\",\n message: \"No agents configured\",\n };\n }\n\n const hosts = {\n global: providerInUse(installBaseDir(projectDir, \"global\")),\n project: providerInUse(installBaseDir(projectDir, \"project\")),\n };\n const dirs = {\n global: resolveInstallPaths(projectDir, \"global\").agentsDir,\n project: resolveInstallPaths(projectDir, \"project\").agentsDir,\n };\n\n const agentChecks = await Promise.all(\n agents.map((agent) => agentCompileCheck(agent, hosts, dirs)),\n );\n\n const unrenderable = agentChecks.filter((c) => c.state === \"unrenderable\");\n const missingAgents = agentChecks.filter((c) => c.state === \"missing\").map((c) => c.name);\n\n // The no-renderer answer leads when it is the whole story, because then nothing is wrong and\n // nothing can be done: a `warn` here would make every `doctor` on a Codex installation read as\n // a fault report about the one thing this release has already said it does not do yet. The\n // providers come off the rows themselves rather than off both scopes, so a project on one host\n // under a global on another names only the host whose sub-agents this is about.\n if (unrenderable.length === agents.length) {\n const hostsLeavingThemOut = unique(unrenderable.map((a) => a.provider)).join(\" or \");\n return {\n kind: \"agents\",\n status: \"pass\",\n message: `No sub-agent in this configuration is carried by ${hostsLeavingThemOut}`,\n details: unrenderable.map((a) => `- ${a.name} (left out of ${a.provider})`),\n };\n }\n\n if (missingAgents.length > 0) {\n return {\n kind: \"agents\",\n status: \"warn\",\n message: `${missingAgents.length} agent${missingAgents.length === 1 ? \"\" : \"s\"} need${missingAgents.length === 1 ? \"s\" : \"\"} recompilation`,\n details: missingAgents.map((a) => `- ${a} (missing)`),\n };\n }\n\n const renderable = agents.length - unrenderable.length;\n return {\n kind: \"agents\",\n status: \"pass\",\n message: `${ratio(renderable, renderable, \"agent\")} compiled`,\n };\n}\n\n/** One configured sub-agent as the Agents Compiled row sees it, and the host that decided it. */\ntype AgentCompileCheck = {\n name: AgentName;\n provider: Provider;\n state: \"unrenderable\" | \"compiled\" | \"missing\";\n};\n\n/**\n * Whether one sub-agent is left out by its scope's host, or else compiled or missing where that\n * host reads it — the extension and the directory are both the host's.\n */\nasync function agentCompileCheck(\n agent: AgentScopeConfig,\n hosts: Record<SkillScope, Provider>,\n agentsDirs: Record<SkillScope, string>,\n): Promise<AgentCompileCheck> {\n const provider = hosts[agent.scope];\n if (!hostCompilesAgent(provider, agent.name)) {\n return { name: agent.name, provider, state: \"unrenderable\" };\n }\n\n const file = `${agent.name}${agentCodec(provider).extension}`;\n const compiled = await fileExists(path.join(agentsDirs[agent.scope], file));\n return { name: agent.name, provider, state: compiled ? \"compiled\" : \"missing\" };\n}\n\nasync function checkNoOrphans(config: ProjectConfig, projectDir: string): Promise<CheckResult> {\n // The host's own agent files — `*.toml` roles on Codex, where a `*.md`-only listing found no\n // compiled agent and so no orphan either (the CLI-896 class, 2026-09-27).\n const codec = agentCodec(providerInUse(projectDir));\n const projectAgentsDir = resolveInstallPaths(projectDir, \"project\").agentsDir;\n const globalAgentsDir = resolveInstallPaths(projectDir, \"global\").agentsDir;\n\n // At home scope both scopes resolve to the same agents directory, so it is\n // listed once and its files are matched against both scopes' known names.\n const scopesShareAgentsDir = isHomeDirectory(projectDir);\n\n const [projectExists, globalExists] = await Promise.all([\n directoryExists(projectAgentsDir),\n !scopesShareAgentsDir ? directoryExists(globalAgentsDir) : false,\n ]);\n\n if (!projectExists && !globalExists) {\n return {\n kind: \"orphans\",\n status: \"pass\",\n message: \"No agents directory\",\n };\n }\n\n const projectMdFiles = projectExists ? await listAgentFilesOf(projectAgentsDir, codec) : [];\n const globalMdFiles = globalExists ? await listAgentFilesOf(globalAgentsDir, codec) : [];\n\n // Project files: only active project-scoped agents should have .md files here\n const activeProjectAgents: Set<string> = new Set(\n config.agents.filter((a) => a.scope === \"project\" && !a.excluded).map((a) => a.name),\n );\n // Global files: all global-scoped agents (including excluded) still serve other projects\n const knownGlobalAgents: Set<string> = new Set(\n config.agents.filter((a) => a.scope === \"global\").map((a) => a.name),\n );\n\n const knownProjectDirAgents = scopesShareAgentsDir\n ? new Set([...activeProjectAgents, ...knownGlobalAgents])\n : activeProjectAgents;\n\n const orphanedFiles = [\n ...orphanedAgentNames(projectMdFiles, knownProjectDirAgents, codec),\n ...orphanedAgentNames(globalMdFiles, knownGlobalAgents, codec),\n ];\n\n if (orphanedFiles.length > 0) {\n return {\n kind: \"orphans\",\n status: \"warn\",\n message: plural(orphanedFiles.length, \"orphaned agent file\"),\n details: orphanedFiles.map((f) => `- ${f}.md (not in config)`),\n };\n }\n\n return {\n kind: \"orphans\",\n status: \"pass\",\n message: \"No orphaned agent files\",\n };\n}\n\n/**\n * The agents whose compiled `.md` sits in a directory whose roster does not name them. The\n * roster differs per directory — a project directory knows only its own active agents, the\n * global one knows every global-scoped agent including the excluded — so it is a parameter.\n */\nfunction orphanedAgentNames(\n mdFiles: string[],\n knownAgents: ReadonlySet<string>,\n codec: AgentCodec,\n): string[] {\n return mdFiles\n .map((fileName) => path.basename(fileName, codec.extension))\n .filter((agentName) => !knownAgents.has(agentName));\n}\n\n/**\n * The same row when there is no configuration at all. Ownership is not unknown here, it is\n * settled: nothing declares any of it, so every artefact this CLI can prove it wrote is an\n * orphan and the row names it. What it CANNOT prove it wrote is left out — a skill directory\n * with no `forkedFrom` and an agent file with no provenance marker are somebody's own work,\n * and naming them here would offer `uninstall` files that command declines. See\n * {@link listInstalledArtifacts}, which asks exactly that question on both axes.\n *\n * A `fail`, where a stray file beside a config is a warning: that warning is earned by the next\n * `compile` pruning what it names, and nothing prunes these unattended. `compile` and `edit`\n * refuse without a config; `uninstall` does not need one, identifying skill directories by\n * their own `forkedFrom` and compiled agents by the marker each carries, which is why the tip\n * beneath this row can promise both halves.\n *\n * With nothing installed the row keeps the skip it has always printed. An empty directory with\n * no config is the state `init` exists for — there is nothing for a configuration to have owned.\n */\nasync function checkUnownedInstallation(projectDir: string): Promise<CheckResult> {\n const { skills, agents } = await listInstalledArtifacts(projectDir);\n if (skills.length === 0 && agents.length === 0) return skippedResult(\"orphans\");\n\n return {\n kind: \"orphans-unowned\",\n status: \"fail\",\n // The per-line \"(not in config)\" the other verdict repeats is said once here instead:\n // with no configuration it is true of every line, and there are as many lines as files.\n message: `${countedArtifacts(skills, agents)} installed here, and no configuration declares them`,\n details: [...skills, ...agents].map((artifact) => `- ${artifact}`),\n };\n}\n\n/** \"1/1 skill\", \"2/3 skills\" — the noun agrees with the count it is out of. */\nfunction ratio(found: number, total: number, noun: string): string {\n return `${found}/${plural(total, noun)}`;\n}\n\n/** \"7 skills and 2 agents\", dropping a half with nothing in it rather than saying \"0 agents\". */\nfunction countedArtifacts(skills: string[], agents: string[]): string {\n return [\n ...(skills.length > 0 ? [plural(skills.length, \"skill\")] : []),\n ...(agents.length > 0 ? [plural(agents.length, \"agent\")] : []),\n ].join(\" and \");\n}\n\n/**\n * Whether every eject-mode skill the configuration names is on disk where its own host reads it.\n *\n * **It composed `<scope root>/.claude/skills/<id>/SKILL.md` for every provider until\n * 2026-09-22**, which made a healthy Codex installation report every one of its skills missing\n * and told the reader to look in a directory that installation does not have. The write path had\n * already moved through the layout — `copyLocalSkills` writes `$CODEX_HOME/skills` and\n * `<repo>/.agents/skills` — so this row was the half that stayed behind, and it is the kind of\n * defect the host-path lint ban cannot report: the path was composed from a CONSTANT, and that\n * ban matches literals.\n *\n * The directory is asked per SKILL rather than once for the row, because scope decides it and a\n * configuration mixes scopes freely. The reader is told the directory that was actually looked\n * in, for the same reason — one sentence naming one folder cannot be true of two scopes on two\n * different hosts.\n */\nasync function checkSkillsInstalled(\n config: ProjectConfig,\n projectDir: string,\n): Promise<CheckResult> {\n const skills: SkillConfig[] = config.skills;\n const ejectSkills = skills.filter((s) => s.origin === EJECT_SOURCE);\n\n if (ejectSkills.length === 0) {\n return {\n kind: \"installed\",\n status: \"pass\",\n message: \"No eject-mode skills configured\",\n };\n }\n\n const skillChecks = await Promise.all(\n ejectSkills.map((skill) => ejectedSkillCheck(skill, projectDir)),\n );\n const missingSkills = skillChecks.filter((c) => !c.installed);\n\n if (missingSkills.length > 0) {\n return {\n kind: \"installed\",\n status: \"warn\",\n message: `${plural(missingSkills.length, \"skill\")} missing from disk`,\n details: missingSkills.map((s) => `- ${s.id} (not found in ${s.lookedIn}/)`),\n };\n }\n\n return {\n kind: \"installed\",\n status: \"pass\",\n message: `${ratio(ejectSkills.length, ejectSkills.length, \"eject-mode skill\")} installed`,\n };\n}\n\n/** One eject-mode skill as the Skills Installed row sees it, and where it was looked for. */\ntype EjectedSkillCheck = { id: SkillId; installed: boolean; lookedIn: string };\n\n/**\n * Whether one eject-mode skill's `SKILL.md` is where its scope's host reads it.\n *\n * The provider is read off the scope's OWN root, the way `resolveInstallPaths` and\n * `discoverLocalProjectSkills` read it: two scopes can be on two different providers, and one\n * probe per skill is what keeps the directory and the reported prefix naming one of them.\n */\nasync function ejectedSkillCheck(\n skill: SkillConfig,\n projectDir: string,\n): Promise<EjectedSkillCheck> {\n const provider = providerInUse(installBaseDir(projectDir, skill.scope));\n const dir = skillsDir(provider, skill.scope, projectDir);\n const installed = await fileExists(path.join(dir, skill.id, STANDARD_FILES.SKILL_MD));\n\n return {\n id: skill.id,\n installed,\n lookedIn: skillsPathPrefix(provider, skill.scope, projectDir),\n };\n}\n\n/**\n * Plugin-mode skills have no files under `.claude/skills/` — they live in the\n * Claude plugin registry. Verifying them means asking the registry for the scope\n * each skill was installed at, not looking on disk.\n */\nasync function checkPluginSkillsInstalled(\n config: ProjectConfig,\n projectDir: string,\n): Promise<CheckResult> {\n const pluginSkills: SkillConfig[] = config.skills.filter((s) => s.origin !== EJECT_SOURCE);\n\n if (pluginSkills.length === 0) {\n return {\n kind: \"plugins\",\n status: \"pass\",\n message: \"No plugin-mode skills configured\",\n };\n }\n\n const baseDirs = unique(pluginSkills.map((s) => installBaseDir(projectDir, s.scope)));\n const missingPerBaseDir = await Promise.all(\n baseDirs.map(async (baseDir) => {\n const installedIds = new Set(\n (await getVerifiedPluginInstallPaths(baseDir)).map((plugin) =>\n parseMarketplacePluginRef(plugin.pluginKey),\n ),\n );\n return pluginSkills\n .filter((skill) => installBaseDir(projectDir, skill.scope) === baseDir)\n .filter((skill) => !installedIds.has(skill.id))\n .map((skill) => skill.id);\n }),\n );\n const missingSkills = missingPerBaseDir.flat();\n\n if (missingSkills.length > 0) {\n return {\n kind: \"plugins\",\n status: \"warn\",\n message: `${plural(missingSkills.length, \"skill\")} not installed as plugins`,\n details: missingSkills.map((s) => `- ${s} (no enabled plugin found)`),\n };\n }\n\n return {\n kind: \"plugins\",\n status: \"pass\",\n message: `${ratio(pluginSkills.length, pluginSkills.length, \"plugin-mode skill\")} installed`,\n };\n}\n\n/**\n * How a marketplace came to be the one this run reads, in the words each rung earns.\n *\n * The `default` sentence is the one that had to exist. With no configuration anywhere the resolver\n * falls back to the public catalogue and this check FETCHES it, so the report a bare directory gets\n * describes a network round trip to a marketplace nobody named — which is the row doing its job,\n * since reachability is its whole subject, and it was doing it without saying so. The other four\n * rungs are here because the map is exhaustive against the union rather than a default with\n * exceptions; `doctor` takes no marketplace flag and reads no environment variable, so it reaches\n * the first two through nothing today.\n */\nconst MARKETPLACE_CHOSEN_BY = {\n flag: \"named by this run\",\n env: `named by ${SOURCE_ENV_VAR}`,\n project: \"named by this project's configuration\",\n global: \"named by the global configuration\",\n default: \"nothing here names one, so this is the default\",\n} as const satisfies Record<ResolvedConfig[\"sourceOrigin\"], string>;\n\n/** How this run got hold of that marketplace — off disk, or over the wire. */\nfunction howItWasReached(source: string, isLocal: boolean): string {\n return isLocal ? `Read ${source} from disk` : `Fetched ${source} over the network`;\n}\n\n/**\n * The row for a marketplace this run did reach.\n *\n * `message` names where the skills were read FROM, which for a remote marketplace is the cache\n * directory it was unpacked into; the provenance line beneath it names the marketplace itself.\n * Both, because neither answers the other's question: a cache path says nothing about whose\n * catalogue it holds, and a ref says nothing about what is on disk to inspect.\n *\n * The count is what that marketplace carries, not the matrix this run loaded: the matrix also\n * holds the skills ejected here and the global installation's, which may come from another\n * marketplace altogether.\n */\nfunction reachedMarketplace(result: SourceLoadResult): CheckResult {\n const { source, sourceOrigin } = result.sourceConfig;\n const skillCount = result.marketplaceSkillIds.size;\n const sourceLabel = result.isLocal ? \"local\" : \"remote\";\n\n return {\n kind: \"source\",\n status: \"pass\",\n message: `Connected to ${sourceLabel}: ${result.sourcePath}`,\n details: [\n `${plural(skillCount, \"skill\")} available`,\n `${howItWasReached(source, result.isLocal)} — ${MARKETPLACE_CHOSEN_BY[sourceOrigin]}`,\n ],\n };\n}\n\nasync function checkSourceReachable(projectDir: string): Promise<CheckResult> {\n try {\n const { sourceResult } = await loadSource({ projectDir });\n return reachedMarketplace(sourceResult);\n } catch (error) {\n const message = getErrorMessage(error);\n return {\n kind: \"source\",\n status: \"fail\",\n message: \"Failed to load marketplace\",\n details: [message],\n };\n }\n}\n\ntype ContentCheck = {\n kind: CheckKind;\n name: string;\n noun: string;\n run: (projectDir: string) => Promise<ContentValidation>;\n};\n\n/**\n * A content check plus the two gates it owns: whether it reads config.ts to know WHAT to validate\n * (see {@link CONFIG_CHECK}), and which operational rows its errors stand down.\n *\n * `blocks` names only the rows that READ what this pass validates, so their own verdict would be\n * this pass's finding in the row's words. A row absent from every `blocks` list reads none of the\n * content on disk and answers as truthfully as ever — an error here is not its business.\n */\ntype GatedContentCheck = ContentCheck & {\n readsConfig: boolean;\n blocks: readonly CheckKind[];\n};\n\n/**\n * The config file every other check is read against, so it is validated before any of them and\n * on its own: a file that exists and cannot be parsed is a finding about that file, and every\n * row underneath would be a cascade of it. It carries no gate of its own — it IS the gate.\n */\nconst CONFIG_CHECK: ContentCheck = {\n kind: \"content-config\",\n name: \"Config\",\n noun: \"config\",\n run: validateProjectConfigFile,\n};\n\n/**\n * The content layer: schema and file-level validation of what is on disk. It runs before the\n * operational layer so that a row whose inputs this layer has already reported broken can stand\n * down rather than re-report the same fault as a finding of its own.\n */\nconst CONTENT_CHECKS: GatedContentCheck[] = [\n {\n kind: \"content-sources\",\n name: \"Marketplaces\",\n noun: \"marketplace\",\n // The registered marketplaces are the ones config.ts names.\n readsConfig: true,\n // A marketplace whose content is broken is missing skills from the matrix every configured\n // id is resolved against, so the skills row would call ids \"not found\" that this row has\n // already accounted for.\n blocks: [\"skills\"],\n run: validateRegisteredSources,\n },\n {\n kind: \"content-plugins\",\n name: \"Plugins\",\n noun: \"plugin\",\n readsConfig: false,\n // `resolvePluginInstallPaths` swallows a registry it cannot parse and returns none, so every\n // plugin-mode skill would read \"no enabled plugin found\" — the registry's finding, once per\n // configured skill, wearing the row's words.\n blocks: [\"plugins\"],\n run: validateInstalledPlugins,\n },\n {\n kind: \"content-skills\",\n name: \"Skills\",\n noun: \"skill\",\n // `~/.claude/skills/` is shared with everything else that installs a skill there, so which of\n // its directories are this installation's is a question only the config can answer for the\n // ones carrying no provenance marker. A config nobody can read leaves that unanswerable.\n readsConfig: true,\n // `extractLocalSkill` DROPS a skill whose metadata.yaml is missing or unusable, so a \"not\n // found\" from the skills row would be this finding re-worded.\n blocks: [\"skills\"],\n run: validateInstalledSkills,\n },\n {\n kind: \"content-agents\",\n name: \"Agents\",\n noun: \"agent\",\n readsConfig: false,\n // Nothing downstream opens an agent .md. `Agents Compiled` asks whether the file is there and\n // `No Orphans` reads the names of the files that are — broken frontmatter changes neither.\n blocks: [],\n run: validateInstalledAgents,\n },\n];\n\nfunction contentStatus(errors: number, warnings: number): CheckResult[\"status\"] {\n if (errors > 0) return \"fail\";\n if (warnings > 0) return \"warn\";\n return \"pass\";\n}\n\nfunction contentMessage(noun: string, count: number, errors: number, warnings: number): string {\n // A pass that walked nothing can still report an issue — an unreadable plugin\n // registry is a finding about the directory, not about a plugin inside it.\n if (errors === 0 && warnings === 0) {\n return count === 0 ? `No ${noun}s to validate` : `${plural(count, noun)} validated`;\n }\n return `${plural(count, noun)}: ${plural(errors, \"error\")}, ${plural(warnings, \"warning\")}`;\n}\n\nfunction formatContentIssue(issue: ContentIssue): string {\n const marker = issue.severity === \"error\" ? \"ERROR\" : \"WARN\";\n return `- [${marker}] ${issue.file}: ${issue.message}`;\n}\n\nfunction toContentResult(check: ContentCheck, validation: ContentValidation): CheckResult {\n const errors = validation.issues.filter((issue) => issue.severity === \"error\").length;\n const warnings = validation.issues.length - errors;\n\n return {\n kind: check.kind,\n status: contentStatus(errors, warnings),\n message: contentMessage(check.noun, validation.count, errors, warnings),\n details: [\n ...validation.notes.map((note) => `- ${note}`),\n ...validation.issues.map(formatContentIssue),\n ],\n };\n}\n\n/**\n * The column every row's status symbol starts at. It has to clear the longest row name\n * the report prints — \"Marketplace Reachable\", 21 characters — or that row's name runs\n * straight into its own tick with no gap.\n */\nconst CHECK_WIDTH = 24;\n\n/** Section headings and the rows underneath them are indented one step apart. */\nconst SECTION_INDENT = \" \";\nconst ROW_INDENT = \" \";\n\nconst SECTION_CONTENT = \"Content checks\";\nconst SECTION_OPERATIONAL = \"Operational checks\";\nconst SKIP_NO_INSTALLATION = \"Skipped — no installation here (marketplace repository)\";\nconst SKIP_CONFIG_UNREADABLE = \"Skipped — the configuration that names them cannot be read\";\n\n/**\n * The whole operational layer standing down, for the one content finding that leaves every row\n * with nothing to say: a config nobody can read. It still says \"errors\" rather than naming that\n * file, because the config row is one of them and whatever else failed is above it too.\n */\nconst SKIP_AFTER_CONFIG_ERROR = \"Skipped — fix the content errors above first\";\n\n/**\n * The sentence a single row stands down with. Unlike its neighbours it names the finding rather\n * than the layer: which pass blocked it is the one thing separating a row that cannot answer from\n * the ones printing verdicts beside it.\n */\nfunction skipRestatingContent(nouns: string[]): string {\n return `Skipped — this row would only restate the ${nouns.join(\" and \")} errors above`;\n}\n\nfunction formatCheckName(name: string): string {\n return name.padEnd(CHECK_WIDTH);\n}\n\n/**\n * The glyph each row is headed with, every one of them from {@link UI_SYMBOLS}.\n *\n * The last two used to be literals here — `\"!\"` and `\"-\"` — and the second was the one that\n * mattered: `UI_SYMBOLS.SKIPPED` is an EN-DASH, so a stood-down row printed a different character\n * from the one the symbol table declares for exactly that state, while `UI_SYMBOLS.REMOVED` is\n * deliberately an ASCII hyphen for diff markers. Two glyphs a reader cannot tell apart, meaning\n * opposite things, and nothing pointing either literal at the table it belonged in.\n */\nfunction formatStatus(status: CheckResult[\"status\"]): string {\n switch (status) {\n case \"pass\":\n return UI_SYMBOLS.CHECK;\n case \"fail\":\n return UI_SYMBOLS.CROSS;\n case \"warn\":\n return UI_SYMBOLS.DISCOURAGED;\n case \"skip\":\n return UI_SYMBOLS.SKIPPED;\n default: {\n const _exhaustive: never = status;\n return _exhaustive;\n }\n }\n}\n\n/** The column a row's details start at, under its message. */\nconst DETAIL_INDENT = `${ROW_INDENT}${\" \".repeat(CHECK_WIDTH)} `;\n\nfunction formatCheckLine(name: string, result: CheckResult): string[] {\n const headerLine = `${ROW_INDENT}${formatCheckName(name)}${formatStatus(result.status)} ${result.message}`;\n const detailLines = (result.details ?? []).flatMap(indentedDetailLines);\n return [headerLine, ...detailLines];\n}\n\n/**\n * A detail can be a whole refusal — a loader's message, paragraphs and all — so every one of its\n * lines is indented, and its blank lines dropped. Indenting the first alone printed the rest at\n * column 0, outside the row they belong to and between the rows below it.\n */\nfunction indentedDetailLines(detail: string): string[] {\n return detail\n .split(\"\\n\")\n .filter((line) => line !== \"\")\n .map((line) => `${DETAIL_INDENT}${line}`);\n}\n\nfunction formatSummary(results: CheckResult[]): string {\n const counts = countBy(results, (r) => r.status);\n\n const parts = [\n `${counts.pass ?? 0} passed`,\n plural(counts.warn ?? 0, \"warning\"),\n plural(counts.fail ?? 0, \"error\"),\n ];\n\n return `${SECTION_INDENT}Summary: ${parts.join(\", \")}`;\n}\n\nconst TIPS: Array<{ kind: CheckKind; status: CheckResult[\"status\"]; tip: string }> = [\n {\n kind: \"agents\",\n status: \"warn\",\n tip: ` Tip: Run '${CLI_INVOKE_COMMAND} compile' to generate missing agent files`,\n },\n {\n kind: \"config\",\n status: \"fail\",\n tip: ` Tip: Run '${CLI_INVOKE_COMMAND} init' to create or fix configuration`,\n },\n {\n // The remedy for a valid config with nothing in it. Not the tip above: there is nothing to\n // create and nothing to fix. `init` on this config opens the wizard rather than the dashboard\n // — that is what the detection returning null is FOR — so it is the way to fill it in.\n kind: \"config-empty\",\n status: \"warn\",\n tip: ` Tip: Nothing is configured yet — run '${CLI_INVOKE_COMMAND} init' to choose skills and sub-agents`,\n },\n {\n // Printed beside the config tip above, which says how to get a configuration back and\n // nothing about the files that outlived the old one. Both halves are named with what they\n // actually do: `uninstall` matches skill directories by their own `forked-from` metadata and\n // compiled agents by the marker each one carries, so neither needs the configuration that is\n // gone — which is why the row can name both and this tip can promise both.\n kind: \"orphans-unowned\",\n status: \"fail\",\n tip: ` Tip: Nothing declares the files above — '${CLI_INVOKE_COMMAND} init' writes a configuration that can own them again, or '${CLI_INVOKE_COMMAND} uninstall' removes them, the compiled agents included: each file listed carries this CLI's own provenance, which is what that command reads when there is no configuration left`,\n },\n {\n kind: \"skills\",\n status: \"fail\",\n tip: \" Tip: Check skill IDs in config match available skills\",\n },\n {\n kind: \"installed\",\n status: \"warn\",\n // No command is named on purpose: 'eject skills --force' re-copies every skill in the\n // marketplace and always targets project scope, so it cannot repair a global-scoped\n // skill and it litters a plugin-mode project with local skill directories.\n tip: \" Tip: Re-eject the missing skills from the marketplace to restore their files\",\n },\n];\n\nfunction formatTips(results: CheckResult[]): string[] {\n return TIPS.filter((t) => results.some((r) => r.kind === t.kind && r.status === t.status)).map(\n (t) => t.tip,\n );\n}\n\n/**\n * The one remedy that applies to a config that cannot be read, once per such config, naming the\n * folder it is taken from. `init` does not clear such a file — it refuses it — so the config tip\n * would send the reader in a circle; this is the way out every refusing command names, worded the\n * same way and sending the reader to the same folder: the project, or the home directory for the\n * global config.\n *\n * Read again rather than threaded out of the content pass, and only once that pass has failed. A\n * failure it cannot classify is already that pass's own failed row, so it adds no tip of its own.\n */\nasync function recreateConfigTips(\n contentResults: CheckResult[],\n projectDir: string,\n): Promise<string[]> {\n if (!failedContentKinds(contentResults).has(CONFIG_CHECK.kind)) return [];\n\n const failures = await findConfigLoadFailures(projectDir).catch(() => []);\n return failures.map(\n (failure) =>\n ` Tip: There is no automatic repair — recreate the configuration by running ${recreateConfigFrom(failure.scopeRoot)}`,\n );\n}\n\n/** The row name every Layout row is printed under, one per scope. */\nconst LAYOUT_ROW_NAME = \"Layout\";\n\n/** One scope's Layout row: which folder its installation is read from and written to. */\nfunction toLayoutResult(scope: SourceScope): CheckResult {\n return {\n kind: \"layout\",\n status: \"pass\",\n message: layoutIsCurrent(scopeLabel(scope.kind), scope.relName),\n };\n}\n\n/** The row name the placement and installation-ambiguity findings are printed under. */\nconst PLACEMENTS_ROW_NAME = \"Placements Offered\";\n\n/**\n * Whether this installation is one every other command will act on — as a ROW, not a throw.\n *\n * **It carries the two findings that make the rest of this report a claim about nothing.** A\n * configuration asking for a mode/scope cell its host does not offer is refused by `compile`,\n * `edit` and `update`; a scope holding an installation of each provider is refused by `edit`,\n * `uninstall`, `share` and `eject`. Until this row existed, `doctor` — the one command whose job\n * is saying whether an installation is healthy — walked straight past both and pronounced a clean\n * bill of health on a configuration no command would touch. A diagnostic that disagrees with every\n * command it is diagnosing is worse than a missing one.\n *\n * **It reports and never refuses**: a command that changes nothing must not refuse to LOOK,\n * because this is exactly the state a user needs to see in order to fix it.\n *\n * The message is the refusal's own, word for word, off the same host roster — so what a user is\n * told here and what they are told when `compile` stops are one sentence, not two that agree\n * today.\n *\n * **The summary counts FINDINGS, and said \"unusable installation\" until 2026-09-22.** The two\n * are not the same number in either direction: one installation with an unofferable placement AND\n * an ambiguous folder contributes two findings, while the ambiguity finding is itself about a\n * scope holding two installations. A count of one noun printed over a list of another is a\n * sentence a reader cannot check against the lines underneath it.\n */\nasync function checkOfferedPlacements(projectDir: string): Promise<CheckResult> {\n const findings = await everythingThatWouldStopACommandActing(projectDir);\n\n if (findings.length === 0) {\n return { kind: \"placements\", status: \"pass\", message: \"Every configured skill is placeable\" };\n }\n\n return {\n kind: \"placements\",\n status: \"fail\",\n message: `${plural(findings.length, \"finding\")} would stop a command acting here`,\n details: findings.map((finding) => `- ${finding}`),\n };\n}\n\n/**\n * The two findings a command would stop on, in one list: a placement no host can fill, and a scope\n * holding an installation of each provider.\n *\n * Asked together rather than in sequence because neither answer depends on the other, and reported\n * together because they are one question for a user — \"will anything act on this?\" — with two ways\n * of being answered no.\n */\nasync function everythingThatWouldStopACommandActing(projectDir: string): Promise<string[]> {\n const [unofferable, ambiguous] = await Promise.all([\n unofferablePlacementsFound(projectDir),\n ambiguityFinding(projectDir),\n ]);\n\n return ambiguous === null ? unofferable : [...unofferable, ambiguous];\n}\n\nfunction skippedResult(kind: CheckKind): CheckResult {\n return { kind, status: \"skip\", message: \"Skipped (config invalid)\" };\n}\n\n/**\n * The skills row when the marketplace never loaded. Distinct from {@link skippedResult}: the\n * config is fine, and what is missing is the matrix to resolve its skills against.\n */\nfunction sourceUnreachableSkillsResult(): CheckResult {\n return { kind: \"skills\", status: \"skip\", message: \"Skipped (marketplace unreachable)\" };\n}\n\nfunction skippedContentResult(kind: CheckKind): CheckResult {\n return { kind, status: \"skip\", message: SKIP_CONFIG_UNREADABLE };\n}\n\n/** The content passes that failed this run — the set every operational row is gated against. */\nfunction failedContentKinds(contentResults: CheckResult[]): ReadonlySet<CheckKind> {\n return new Set(contentResults.filter((r) => r.status === \"fail\").map((r) => r.kind));\n}\n\n/**\n * An operational row standing down because a content pass it reads through failed, naming what\n * blocked it. `null` — the answer for most rows on most runs — means nothing it reads is broken\n * and it can speak for itself.\n */\nfunction contentBlockedResult(\n row: CheckKind,\n failedContent: ReadonlySet<CheckKind>,\n): CheckResult | null {\n const blockingNouns = CONTENT_CHECKS.filter((check) => failedContent.has(check.kind))\n .filter((check) => check.blocks.includes(row))\n .map((check) => check.noun);\n\n if (blockingNouns.length === 0) return null;\n\n return { kind: row, status: \"skip\", message: skipRestatingContent(blockingNouns) };\n}\n\nfunction runContentCheck(check: ContentCheck, projectDir: string): Promise<CheckResult> {\n return safeCheck(check.kind, async () => toContentResult(check, await check.run(projectDir)));\n}\n\n/**\n * Wrap a check so a thrown exception becomes a `fail` result instead of crashing the\n * whole doctor run. Per-check isolation ensures one broken check does not mask others.\n */\nasync function safeCheck(kind: CheckKind, fn: () => Promise<CheckResult>): Promise<CheckResult> {\n try {\n return await fn();\n } catch (error) {\n return { kind, status: \"fail\", message: \"Check threw\", details: [getErrorMessage(error)] };\n }\n}\n\nexport default class Doctor extends BaseCommand {\n static summary = \"Diagnose common configuration issues\";\n\n static description = `Run diagnostic checks on your ${DEFAULT_BRANDING.NAME} configuration to identify issues with config validity, skill resolution, agent compilation, and marketplace connectivity.`;\n\n static examples = [\"<%= config.bin %> <%= command.id %>\"];\n\n static flags = {};\n\n async run(): Promise<void> {\n await this.parse(Doctor);\n const projectDir = process.cwd();\n\n // The shared `verbose()` logger stays OFF, and nothing here switches it on. `doctor` used to,\n // for the whole run — the mechanical residue of a `--verbose` flag whose removal was meant to\n // make each row's own DETAILS unconditional, which `formatCheckLine` does on its own. What the\n // logger added on top of that was the loaders' trace, spliced between the section headings and\n // the rows they head: for a directory holding nothing, 27 lines of it, every one restating a\n // row printed underneath it.\n this.printHeader(await this.resolveBrandingName(projectDir));\n const contentResults = await this.runContentChecks(projectDir);\n const operationalResults = await this.runOperationalChecks(projectDir, contentResults);\n const results = [...contentResults, ...operationalResults];\n\n this.printResults(results, await recreateConfigTips(contentResults, projectDir));\n\n if (results.some((r) => r.status === \"fail\")) {\n this.exit(EXIT_CODES.ERROR);\n }\n }\n\n private printHeader(brandingName: string): void {\n this.log(\"\");\n this.log(`${brandingName} Doctor`);\n this.log(\"\");\n this.log(`${SECTION_INDENT}Checking configuration health...`);\n this.log(\"\");\n }\n\n /**\n * The config is checked first and alone. A check that reads it to know what to validate would\n * otherwise ask a file already reported as unreadable — and asking is what emitted the loader's\n * own failure line once per read, spliced between these rows. Everything that walks installed\n * content on disk still runs: it says something true whatever the config is in.\n */\n private async runContentChecks(projectDir: string): Promise<CheckResult[]> {\n this.log(`${SECTION_INDENT}${SECTION_CONTENT}`);\n\n const configResult = await runContentCheck(CONFIG_CHECK, projectDir);\n const configUnreadable = configResult.status === \"fail\";\n\n const rows = await Promise.all(\n CONTENT_CHECKS.map(async (check) => ({\n name: check.name,\n result:\n check.readsConfig && configUnreadable\n ? skippedContentResult(check.kind)\n : await runContentCheck(check, projectDir),\n })),\n );\n\n const allRows = [{ name: CONFIG_CHECK.name, result: configResult }, ...rows];\n\n for (const row of allRows) {\n this.logCheck(row.name, row.result);\n }\n\n return allRows.map((row) => row.result);\n }\n\n /**\n * The whole layer stands down for the two findings that leave every row with nothing to say: a\n * config nobody can read — every row is read out of it, so all of them would be cascades of that\n * one file — and a marketplace repository with nothing installed, where a marketplace author has\n * no install for the layer to describe. Every other content error is scoped to the rows that\n * read what it is about; {@link GatedContentCheck} says which those are, per pass.\n */\n private async runOperationalChecks(\n projectDir: string,\n contentResults: CheckResult[],\n ): Promise<CheckResult[]> {\n this.log(\"\");\n this.log(`${SECTION_INDENT}${SECTION_OPERATIONAL}`);\n\n const failedContent = failedContentKinds(contentResults);\n if (failedContent.has(CONFIG_CHECK.kind)) {\n this.log(`${ROW_INDENT}${SKIP_AFTER_CONFIG_ERROR}`);\n return [];\n }\n\n const detected = await detectProject(projectDir);\n if (!detected && (await this.isUninstalledSourceRepo(projectDir))) {\n this.log(`${ROW_INDENT}${SKIP_NO_INSTALLATION}`);\n return [];\n }\n\n const configState = await resolveConfigState(detected, projectDir);\n return this.runAllChecks(projectDir, configState, failedContent);\n }\n\n /**\n * A config file that exists but failed to load also detects as \"no project\", and\n * that is a finding rather than an absence — so the skip requires no config file\n * at all, not merely no usable one.\n */\n private async isUninstalledSourceRepo(projectDir: string): Promise<boolean> {\n if (await fileExists(getInstalledConfigPath(projectDir))) return false;\n return isSourceRepo(projectDir);\n }\n\n private async runAllChecks(\n projectDir: string,\n configState: ConfigState,\n failedContent: ReadonlySet<CheckKind>,\n ): Promise<CheckResult[]> {\n const { result: configResult, config } = checkConfigValid(configState, projectDir);\n this.logCheck(\"Config Valid\", configResult);\n\n const layoutResults = await this.reportLayout(projectDir);\n\n // Directly under Layout, and above every row that reads content: Layout says which FOLDER this\n // report is about and this row says whether any command will act on what is in it.\n const placementsResult = await safeCheck(\"placements\", () =>\n checkOfferedPlacements(projectDir),\n );\n this.logCheck(PLACEMENTS_ROW_NAME, placementsResult);\n\n // loadSource (called by checkSourceReachable) populates the matrix. Run it\n // before checkSkillsResolved so skills lookups see a populated matrix; if\n // source fails, skip skills rather than reporting false \"not found\" errors.\n const sourceResult = await safeCheck(\"source\", () => checkSourceReachable(projectDir));\n\n const filteredConfig = config ? filterExcludedEntries(config) : null;\n\n const skillsResult = await this.resolveSkillsCheck(\n config,\n sourceResult,\n projectDir,\n failedContent,\n );\n this.logCheck(\"Skills Resolved\", skillsResult);\n\n const agentsResult = filteredConfig\n ? await safeCheck(\"agents\", () => checkAgentsCompiled(filteredConfig, projectDir))\n : skippedResult(\"agents\");\n this.logCheck(\"Agents Compiled\", agentsResult);\n\n const orphansResult = await this.resolveOrphansCheck(config, configState, projectDir);\n this.logCheck(\"No Orphans\", orphansResult);\n\n const installedResult = filteredConfig\n ? await safeCheck(\"installed\", () => checkSkillsInstalled(filteredConfig, projectDir))\n : skippedResult(\"installed\");\n this.logCheck(\"Skills Installed\", installedResult);\n\n const pluginsResult = await this.resolvePluginsCheck(filteredConfig, projectDir, failedContent);\n this.logCheck(\"Plugins Installed\", pluginsResult);\n\n this.logCheck(\"Marketplace Reachable\", sourceResult);\n\n return [\n configResult,\n ...layoutResults,\n placementsResult,\n skillsResult,\n agentsResult,\n orphansResult,\n installedResult,\n pluginsResult,\n sourceResult,\n ];\n }\n\n /**\n * One `Layout` row per scope in play.\n *\n * Per scope rather than per machine: a project and the global installation are two\n * installations, and one row for the two can only name one of their folders.\n *\n * Printed right under `Config Valid`, because every row below it is read out of a config file\n * in the folder this row names.\n */\n private async reportLayout(projectDir: string): Promise<CheckResult[]> {\n const rows = (await sourceScopesInPlay(projectDir)).map(toLayoutResult);\n\n for (const row of rows) {\n this.logCheck(LAYOUT_ROW_NAME, row);\n }\n return rows;\n }\n\n /**\n * The orphan row, and which question it can answer. With a config it names the compiled\n * agents that config does not; with no config at all every installed file is unowned and it\n * names all of them. It skips for the one state left — a config that loads and fails\n * validation — because there the file that says who owns what has already been rejected, and\n * an installation must not be called stranded on the strength of one nobody can trust.\n */\n private async resolveOrphansCheck(\n config: ProjectConfig | null,\n configState: ConfigState,\n projectDir: string,\n ): Promise<CheckResult> {\n if (config) return safeCheck(\"orphans\", () => checkNoOrphans(config, projectDir));\n if (configState.kind === \"absent\") {\n return safeCheck(\"orphans\", () => checkUnownedInstallation(projectDir));\n }\n return skippedResult(\"orphans\");\n }\n\n /**\n * The skills row, and the three states in which it cannot be computed: no config to read the\n * skills out of, content it resolves against that the layer above has already reported broken,\n * and a marketplace that never loaded — so there is no populated matrix to resolve them\n * against, and every configured skill would be reported \"not found\".\n */\n private async resolveSkillsCheck(\n config: ProjectConfig | null,\n sourceResult: CheckResult,\n projectDir: string,\n failedContent: ReadonlySet<CheckKind>,\n ): Promise<CheckResult> {\n if (!config) return skippedResult(\"skills\");\n\n const blocked = contentBlockedResult(\"skills\", failedContent);\n if (blocked) return blocked;\n if (sourceResult.status === \"fail\") return sourceUnreachableSkillsResult();\n\n return safeCheck(\"skills\", () => checkSkillsResolved(config, matrix, projectDir));\n }\n\n /**\n * The plugin row, and the two states in which it cannot be computed: no config to read the\n * plugin-mode skills out of, and a plugin registry the layer above could not parse — which\n * resolves as no installs at all, so every configured skill would be reported missing.\n */\n private async resolvePluginsCheck(\n config: ProjectConfig | null,\n projectDir: string,\n failedContent: ReadonlySet<CheckKind>,\n ): Promise<CheckResult> {\n if (!config) return skippedResult(\"plugins\");\n\n const blocked = contentBlockedResult(\"plugins\", failedContent);\n if (blocked) return blocked;\n\n return safeCheck(\"plugins\", () => checkPluginSkillsInstalled(config, projectDir));\n }\n\n private logCheck(name: string, result: CheckResult): void {\n for (const line of formatCheckLine(name, result)) {\n this.log(line);\n }\n }\n\n private printResults(results: CheckResult[], recreateTips: string[]): void {\n this.log(\"\");\n this.log(formatSummary(results));\n\n const tips = [...formatTips(results), ...recreateTips];\n if (tips.length > 0) {\n this.log(\"\");\n for (const tip of tips) {\n this.log(tip);\n }\n }\n\n this.log(\"\");\n }\n}\n","import path from \"path\";\nimport { isRecord } from \"../utils/type-guards.js\";\nimport { parse as parseYaml } from \"yaml\";\nimport { z } from \"zod\";\nimport { glob, readFile, fileExists, directoryExists } from \"../utils/fs\";\nimport {\n DIRS,\n MARKETPLACE_JSON,\n PLUGIN_MANIFEST_DIR,\n SKILL_CATEGORIES_PATH,\n SKILL_RULES_PATH,\n SKILLS_DIR_PATH,\n STACKS_FILE_PATH,\n STANDARD_FILES,\n} from \"../consts\";\nimport {\n agentYamlGenerationSchema,\n metadataValidationSchema,\n splitMetadataValidationIssues,\n validateSkillMetadata,\n skillCategoriesFileSchema,\n skillRulesFileSchema,\n stackConfigValidationSchema,\n stacksConfigSchema,\n} from \"./schemas\";\nimport { parseFrontmatter } from \"./loading/loader\";\nimport { ConfigDefaultExportError, loadConfig, loadSourceRepoConfig } from \"./configuration\";\nimport { checkMatrixHealth, type MatrixHealthIssue } from \"./matrix\";\nimport {\n loadSkillsMatrixFromSource,\n MarketplaceManifestRequiredError,\n} from \"./loading/source-loader\";\nimport { MarketplaceNameRefusedError } from \"./loading/source-fetcher\";\nimport { matrix } from \"./matrix/matrix-provider\";\nimport { getErrorMessage } from \"../utils/errors\";\nimport { formatZodErrors } from \"./schema-validator\";\n\nexport type SourceValidationIssue = {\n severity: \"error\" | \"warning\";\n file: string;\n message: string;\n};\n\nexport type SourceValidationResult = {\n issues: SourceValidationIssue[];\n skillCount: number;\n errorCount: number;\n warningCount: number;\n};\n\n/**\n * Who this report is for, relative to the marketplace it is about.\n *\n * You are the `\"author\"` of the marketplace being validated when it IS the directory the command\n * ran in; a marketplace anywhere else — fetched, or on disk at another path — is one you are only\n * a `\"consumer\"` of. Not \"is there a marketplace under the cwd\": that says nothing about the OTHER\n * marketplace a config may point at, and the severity turns on the per-marketplace question.\n *\n * It changes one finding. A slug a marketplace's rules dangle is a typo its author can fix and\n * wants the run to fail on, and it is a defect a consumer cannot touch in a file they cannot open\n * — while every skill still installs and still resolves. Whether the reader holds push rights is\n * not knowable here and is not the test: a checkout under your cursor is a file you can edit.\n */\nexport type MarketplaceReader = \"author\" | \"consumer\";\n\n/**\n * True when `dir` is itself a skills source repository — it carries the skills tree\n * `validateSource` walks, at `src/skills/` or wherever its own source config points.\n * A marketplace author's checkout answers yes; a consumer project answers no.\n */\nexport async function isSourceRepo(dir: string): Promise<boolean> {\n // ABORT on an unreadable config, and it is caught rather than fatal at both callers: `doctor`\n // reaches this through `safeCheck`, so the throw becomes a failed row instead of an aborted\n // command, and its other caller asks only where no config file exists at all.\n const sourceConfig = await loadSourceRepoConfig(dir);\n return directoryExists(path.join(dir, sourceConfig?.skillsDir ?? SKILLS_DIR_PATH));\n}\n\n/** Checks if a key uses snake_case (has underscore between lowercase letters) */\nexport function isSnakeCase(key: string): boolean {\n return /[a-z]_[a-z]/.test(key);\n}\n\n/**\n * Checks top-level keys for snake_case usage (camelCase is the convention).\n * Non-object input yields no issues.\n */\nexport function checkSnakeCaseKeys(rawMetadata: unknown, relPath: string): SourceValidationIssue[] {\n if (!isRecord(rawMetadata)) return [];\n\n return Object.keys(rawMetadata)\n .filter(isSnakeCase)\n .map((key) => ({\n severity: \"error\" as const,\n file: relPath,\n message: `Key '${key}' uses snake_case — use camelCase instead`,\n }));\n}\n\n/**\n * Warns when a skill directory's name does not equal the skill's machine id\n * (SKILL.md frontmatter `name`) — the key the loader registers the skill under.\n * Warning, not error: the loader still loads the skill by its id, but the\n * directory no longer signals which skill it contains.\n */\nexport function checkDirNameMatchesSkillId(\n skillId: string,\n relPath: string,\n dirName: string,\n): SourceValidationIssue[] {\n if (skillId === dirName) return [];\n return [\n {\n severity: \"warning\",\n file: relPath,\n message: `Directory name '${dirName}' does not match skill id '${skillId}'`,\n },\n ];\n}\n\n/**\n * Reads a skill directory's SKILL.md frontmatter and checks the directory name\n * against the skill's machine id. When the id cannot be read (unreadable file or\n * missing/invalid frontmatter), reports that the check could not run.\n */\nasync function checkSkillDirName(\n skillsDir: string,\n skillsDirRelPath: string,\n skillDir: string,\n): Promise<SourceValidationIssue[]> {\n const dirName = path.basename(skillDir);\n const skillMdRelPath = path.join(skillsDirRelPath, skillDir, STANDARD_FILES.SKILL_MD);\n\n try {\n const content = await readFile(path.join(skillsDir, skillDir, STANDARD_FILES.SKILL_MD));\n const frontmatter = parseFrontmatter(content, skillMdRelPath);\n\n if (frontmatter === null) {\n return [\n {\n severity: \"warning\",\n file: skillMdRelPath,\n message: `Cannot verify directory name '${dirName}': missing or invalid ${STANDARD_FILES.SKILL_MD} frontmatter`,\n },\n ];\n }\n\n return checkDirNameMatchesSkillId(frontmatter.name, skillMdRelPath, dirName);\n } catch (error) {\n return [\n {\n severity: \"warning\",\n file: skillMdRelPath,\n message: `Cannot verify directory name '${dirName}': ${getErrorMessage(error)}`,\n },\n ];\n }\n}\n\n/**\n * Finds missing SKILL.md / metadata.yaml pairs (pure function, no I/O).\n *\n * Returns issues for:\n * - Directories with SKILL.md but no metadata.yaml\n * - Directories with metadata.yaml but no SKILL.md\n */\nexport function validateSkillFilePairs(\n skillMdDirs: Set<string>,\n metadataDirs: Set<string>,\n skillsDir: string,\n): SourceValidationIssue[] {\n const missingMetadata = [...skillMdDirs]\n .filter((dir) => !metadataDirs.has(dir))\n .map((dir) => ({\n severity: \"error\" as const,\n file: path.join(skillsDir, dir),\n message: `Missing ${STANDARD_FILES.METADATA_YAML} — skill directory has ${STANDARD_FILES.SKILL_MD} but no metadata`,\n }));\n\n const missingSkillMd = [...metadataDirs]\n .filter((dir) => !skillMdDirs.has(dir))\n .map((dir) => ({\n severity: \"error\" as const,\n file: path.join(skillsDir, dir),\n message: `Missing ${STANDARD_FILES.SKILL_MD} — skill directory has ${STANDARD_FILES.METADATA_YAML} but no SKILL.md`,\n }));\n\n return [...missingMetadata, ...missingSkillMd];\n}\n\n/**\n * Validates a skills source repository for metadata correctness.\n *\n * Checks:\n * 1. Every metadata.yaml against the strict validation schema\n * (over-length cliDescription is downgraded to a warning)\n * 2. Directory name equals the skill's machine id (SKILL.md frontmatter name)\n * 3. Cross-references resolve to existing skill IDs (via checkMatrixHealth)\n * 4. camelCase key convention (no snake_case)\n * 5. Every skill directory has both SKILL.md and metadata.yaml\n *\n * `reader` decides how loudly one of those findings is stated — see {@link MarketplaceReader}.\n * It defaults to `\"author\"`, the louder verdict: a caller that names nobody has said nothing\n * about who is reading, and under-reporting to the one person who can fix the file is the worse\n * of the two ways to be wrong.\n */\nexport async function validateSource(\n sourcePath: string,\n reader: MarketplaceReader = \"author\",\n): Promise<SourceValidationResult> {\n const resolvedPath = path.isAbsolute(sourcePath) ? sourcePath : path.resolve(sourcePath);\n\n if (!(await directoryExists(resolvedPath))) {\n return buildResult(\n [{ severity: \"error\", file: resolvedPath, message: \"Marketplace directory does not exist\" }],\n 0,\n );\n }\n\n // ABORT on an unreadable config, caught by `validateOneSource`, which turns it into an issue\n // against this marketplace. Defaulting past it would validate whatever sits at `src/skills/` and\n // call a marketplace whose skills live elsewhere empty.\n const sourceProjectConfig = await loadSourceRepoConfig(resolvedPath);\n const skillsDirRelPath = sourceProjectConfig?.skillsDir ?? SKILLS_DIR_PATH;\n const skillsDir = path.join(resolvedPath, skillsDirRelPath);\n\n if (!(await directoryExists(skillsDir))) {\n return buildResult(\n [{ severity: \"error\", file: skillsDir, message: \"Skills directory does not exist\" }],\n 0,\n );\n }\n\n // Phase 1: Check every skill directory has both SKILL.md and metadata.yaml\n const skillMdFiles = await glob(`**/${STANDARD_FILES.SKILL_MD}`, skillsDir);\n const metadataFiles = await glob(`**/${STANDARD_FILES.METADATA_YAML}`, skillsDir);\n\n const skillMdDirs = new Set(skillMdFiles.map((f) => path.dirname(f)));\n const metadataDirs = new Set(metadataFiles.map((f) => path.dirname(f)));\n\n const pairIssues = validateSkillFilePairs(skillMdDirs, metadataDirs, skillsDir);\n\n // Phase 2: Validate each metadata.yaml against strict schema and conventions.\n // Pair violations were already reported by phase 1 — validate only complete pairs.\n const validMetadataFiles = metadataFiles.filter((f) => skillMdDirs.has(path.dirname(f)));\n const skillIssues = await validateSkillsInTurn(skillsDir, skillsDirRelPath, validMetadataFiles);\n\n // Phase 3: Cross-reference validation via matrix health check\n const crossReferenceIssues = await checkCrossReferences(resolvedPath, reader);\n\n // Phases 4–6: optional source-repo targets — run in parallel\n // Phase 4: stack skill metadata + stack configs\n // Phase 5: agent metadata\n // Phase 6: config/*.ts runtime exports\n const extraIssues = await Promise.all([\n validateStacks(resolvedPath),\n validateAgents(resolvedPath),\n validateConfigFiles(resolvedPath),\n ]);\n\n return buildResult(\n [...pairIssues, ...skillIssues, ...crossReferenceIssues, ...extraIssues.flat()],\n validMetadataFiles.length,\n );\n}\n\n/** {@link validateOneSkill} over every complete pair, one skill at a time, in the order given. */\nasync function validateSkillsInTurn(\n skillsDir: string,\n skillsDirRelPath: string,\n metadataFiles: string[],\n): Promise<SourceValidationIssue[]> {\n const issues: SourceValidationIssue[] = [];\n for (const metadataFile of metadataFiles) {\n issues.push(...(await validateOneSkill(skillsDir, skillsDirRelPath, metadataFile)));\n }\n return issues;\n}\n\n/**\n * Why a YAML file could not be read, in the parser's own words. Two phases refuse the same\n * way — the skill-pair scan and the stack/agent scan — and they printed different sentences\n * for a while, one carrying the cause and one dropping it; naming the message once is what\n * stops them diverging again.\n */\nfunction yamlParseFailure(error: unknown): string {\n return `Failed to parse YAML: ${getErrorMessage(error)}`;\n}\n\n/**\n * One complete skill pair, judged four ways: its metadata.yaml parses, its keys are camelCase,\n * it satisfies the strict published-skill schema, and its directory is named after the id its\n * SKILL.md declares. A file nothing can be parsed out of reports that and stops — every check\n * after it reads the parsed value, so each would only restate the same fault.\n */\nasync function validateOneSkill(\n skillsDir: string,\n skillsDirRelPath: string,\n metadataFile: string,\n): Promise<SourceValidationIssue[]> {\n const relPath = path.join(skillsDirRelPath, metadataFile);\n\n let rawMetadata: unknown;\n try {\n rawMetadata = parseYaml(await readFile(path.join(skillsDir, metadataFile)));\n } catch (error) {\n return [{ severity: \"error\", file: relPath, message: yamlParseFailure(error) }];\n }\n\n return [\n ...checkSnakeCaseKeys(rawMetadata, relPath),\n ...checkMetadataSchema(rawMetadata, relPath),\n // Independent of metadata validity — the id lives in SKILL.md frontmatter\n ...(await checkSkillDirName(skillsDir, skillsDirRelPath, path.dirname(metadataFile))),\n ];\n}\n\n/**\n * The strict published-skill schema's verdict, as its hard errors plus the one advisory\n * (over-length cliDescription) `splitMetadataValidationIssues` separates out.\n */\nfunction checkMetadataSchema(rawMetadata: unknown, relPath: string): SourceValidationIssue[] {\n const result = validateSkillMetadata(rawMetadata);\n if (result.success) return [];\n\n const { errors, warnings } = splitMetadataValidationIssues(result.error, rawMetadata);\n return [\n ...errors.map((message) => ({ severity: \"error\" as const, file: relPath, message })),\n ...warnings.map((message) => ({ severity: \"warning\" as const, file: relPath, message })),\n ];\n}\n\n/**\n * The manifest as a reader of the report has to type it — the location half of the row\n * `doctor` prints, and therefore the file they open.\n */\nconst MARKETPLACE_MANIFEST_PATH = path.join(PLUGIN_MANIFEST_DIR, MARKETPLACE_JSON);\n\n/**\n * How loud a missing or unreadable manifest is, by who is reading: its author is one build from\n * the fix, and a consumer has nothing to load. Keyed on the whole union, so a new kind of reader\n * does not compile until someone decides this.\n */\nconst MANIFEST_REQUIRED_SEVERITY = {\n author: \"warning\",\n consumer: \"error\",\n} as const satisfies Record<MarketplaceReader, SourceValidationIssue[\"severity\"]>;\n\n/**\n * Whether every relationship the source declares resolves to a skill it actually holds.\n * A load that did not complete is reported by {@link matrixLoadFailure}, which decides\n * whether the failure is this marketplace's fault or merely this pass's.\n */\nasync function checkCrossReferences(\n resolvedPath: string,\n reader: MarketplaceReader,\n): Promise<SourceValidationIssue[]> {\n try {\n await loadSkillsMatrixFromSource({ sourceFlag: resolvedPath, skipExtraSources: true });\n return checkMatrixHealth(matrix).map((healthIssue) =>\n toSourceIssue(healthIssue, resolvedPath, reader),\n );\n } catch (error) {\n return [matrixLoadFailure(error, reader)];\n }\n}\n\n/**\n * A load this pass could not complete, as the finding a reader can act on.\n *\n * Three answers, because the failures are not one event. A marketplace with no valid\n * manifest cannot be loaded at all, so the finding is the manifest's, filed against the file\n * a reader opens rather than the categories file this pass happened to be reading, at the\n * severity {@link MANIFEST_REQUIRED_SEVERITY} gives its reader. A manifest naming the marketplace\n * something Claude Code registers no plugin under leaves nothing here installable, so it is\n * this marketplace's own ERROR, against the file that holds the name — without it the\n * Marketplaces row counted such a marketplace as validated and printed a tick beneath\n * `doctor`'s own warning about that same file, and a warning above a tick is what a reader\n * stops believing. Everything else only leaves this pass unable to run, so it stays a warning\n * against the file it was reading. Read off the throw's TYPE, so nothing here matches on a\n * sentence.\n */\nfunction matrixLoadFailure(error: unknown, reader: MarketplaceReader): SourceValidationIssue {\n if (error instanceof MarketplaceManifestRequiredError) {\n return {\n severity: MANIFEST_REQUIRED_SEVERITY[reader],\n file: MARKETPLACE_MANIFEST_PATH,\n message: getErrorMessage(error),\n };\n }\n\n if (error instanceof MarketplaceNameRefusedError) {\n return {\n severity: \"error\",\n file: MARKETPLACE_MANIFEST_PATH,\n message: getErrorMessage(error),\n };\n }\n\n return {\n severity: \"warning\",\n file: SKILL_CATEGORIES_PATH,\n message: `Cross-reference validation skipped: failed to load categories/rules: ${getErrorMessage(error)}`,\n };\n}\n\n/**\n * The audit manifest, as a path a reader can open. It is this CLI's own file rather than anything\n * in the marketplace being validated, and it has to be: the verdict a matrix contradicts is\n * recorded there and the marketplace holds no copy of it.\n */\nconst SKILL_AUDIT_PATH = \"src/cli/lib/configuration/skill-audit.ts\";\n\n/**\n * The file a finding's defect is written in — the one `doctor` renders as the location half of the\n * line, and therefore the one the reader opens.\n *\n * Each answer is the file the check actually reads, which is not always the file its name suggests:\n *\n * - a category and the `domain` it omits are both declared in the categories file, and a skill\n * naming a category nothing defines is the same file short an entry (its own metadata.yaml is\n * the other half of that fix, and the message already names the skill);\n * - every relation a resolved skill carries comes from the rules file — no metadata.yaml declares\n * one — so an unresolved reference and the dangling slug behind it are both written there;\n * - a verdict, recorded or missing, is the audit manifest's.\n *\n * Exhaustive on purpose: a seventh finding kind will not compile until someone decides where its\n * defect lives, which is the property one path shared by all six had already lost.\n */\nfunction fileHoldingDefect(finding: MatrixHealthIssue[\"finding\"]): string {\n switch (finding) {\n case \"category-missing-domain\":\n case \"skill-unknown-category\":\n return SKILL_CATEGORIES_PATH;\n case \"skill-unresolved-relation-ref\":\n case \"rule-unresolved-slug\":\n return SKILL_RULES_PATH;\n case \"audit-verdict-contradiction\":\n case \"skill-unaudited\":\n return SKILL_AUDIT_PATH;\n default: {\n const exhaustive: never = finding;\n return exhaustive;\n }\n }\n}\n\n/**\n * One health finding as this reader should hear it. Every kind but one is reported at the\n * severity the health check gave it — that severity is a property of the defect. The exception\n * is the slug a marketplace's own rules dangle, which is a property of the defect AND of who is\n * looking: a typo the author can fix, and a file the consumer cannot open.\n *\n * The reader moves the severity and the wording; it never moves the file, because which file holds\n * the defect is not a fact about who is reading.\n */\nfunction toSourceIssue(\n healthIssue: MatrixHealthIssue,\n marketplacePath: string,\n reader: MarketplaceReader,\n): SourceValidationIssue {\n const file = fileHoldingDefect(healthIssue.finding);\n\n if (reader === \"consumer\" && healthIssue.finding === \"rule-unresolved-slug\") {\n return {\n severity: \"warning\",\n file,\n message: consumedMarketplaceMessage(marketplacePath, healthIssue.details),\n };\n }\n\n return {\n severity: healthIssue.severity,\n file,\n message: healthIssue.details,\n };\n}\n\n/**\n * The same finding, addressed to someone who did not write the rule. It leads with the\n * marketplace because that is the part the slug alone does not tell them, and it says outright\n * that there is nothing here to fix — a warning that sends a reader hunting through their own\n * project for a typo in a file they do not own is worse than no warning at all.\n */\nfunction consumedMarketplaceMessage(marketplacePath: string, details: string): string {\n return `Marketplace '${marketplacePath}': ${details}. Nothing to fix here — the rule is that marketplace's, not this project's`;\n}\n\n/**\n * Validates stack-embedded skill metadata.yaml files and stack config.yaml files.\n * Skips silently when src/stacks/ does not exist.\n */\nasync function validateStacks(resolvedPath: string): Promise<SourceValidationIssue[]> {\n const stacksDir = path.join(resolvedPath, DIRS.stacks);\n if (!(await directoryExists(stacksDir))) return [];\n\n const [skillMetaIssues, configIssues] = await Promise.all([\n validateYamlFiles({\n baseDir: stacksDir,\n relBaseDir: DIRS.stacks,\n pattern: `**/skills/**/${STANDARD_FILES.METADATA_YAML}`,\n schema: metadataValidationSchema,\n }),\n validateYamlFiles({\n baseDir: stacksDir,\n relBaseDir: DIRS.stacks,\n pattern: `*/${STANDARD_FILES.CONFIG_YAML}`,\n schema: stackConfigValidationSchema,\n }),\n ]);\n return [...skillMetaIssues, ...configIssues];\n}\n\n/**\n * Validates each hand-authored agent `metadata.yaml` under `DIRS.agents` against\n * `agentYamlGenerationSchema` — compilation's INPUT schema rather than an output one, since\n * compiling an agent writes one `.md` file and no YAML at all. Skips silently when src/agents/\n * does not exist.\n */\nasync function validateAgents(resolvedPath: string): Promise<SourceValidationIssue[]> {\n const agentsDir = path.join(resolvedPath, DIRS.agents);\n if (!(await directoryExists(agentsDir))) return [];\n\n return validateYamlFiles({\n baseDir: agentsDir,\n relBaseDir: DIRS.agents,\n pattern: `**/${STANDARD_FILES.AGENT_METADATA_YAML}`,\n schema: agentYamlGenerationSchema,\n });\n}\n\n/**\n * Validates TypeScript config files (skill-categories.ts, skill-rules.ts, stacks.ts)\n * by runtime-loading them via loadConfig and validating the default export.\n * Skips silently when a file does not exist.\n */\nasync function validateConfigFiles(resolvedPath: string): Promise<SourceValidationIssue[]> {\n const results = await Promise.all([\n validateTsConfig(resolvedPath, SKILL_CATEGORIES_PATH, skillCategoriesFileSchema),\n validateTsConfig(resolvedPath, SKILL_RULES_PATH, skillRulesFileSchema),\n validateTsConfig(resolvedPath, STACKS_FILE_PATH, stacksConfigSchema),\n ]);\n return results.flat();\n}\n\n/** One kind of YAML file a source may carry: where it lives, and the schema each must satisfy. */\ntype YamlFileScan = {\n baseDir: string;\n /**\n * `baseDir` as issues report it, so their locations match the project-relative form used\n * elsewhere.\n */\n relBaseDir: string;\n pattern: string;\n schema: z.ZodType<unknown>;\n};\n\n/**\n * {@link validateYamlFile} over every file the scan's pattern matches, one file at a time, in\n * glob order.\n */\nasync function validateYamlFiles(scan: YamlFileScan): Promise<SourceValidationIssue[]> {\n const issues: SourceValidationIssue[] = [];\n const files = await glob(scan.pattern, scan.baseDir);\n\n for (const relFile of files) {\n issues.push(...(await validateYamlFile(scan, relFile)));\n }\n return issues;\n}\n\n/**\n * One YAML file judged against the scan's schema: its parse error if it cannot be read, otherwise\n * the schema's errors as field-path messages — none when it passes.\n */\nasync function validateYamlFile(\n scan: YamlFileScan,\n relFile: string,\n): Promise<SourceValidationIssue[]> {\n const displayPath = path.join(scan.relBaseDir, relFile);\n\n let parsed: unknown;\n try {\n parsed = parseYaml(await readFile(path.join(scan.baseDir, relFile)));\n } catch (error) {\n return [{ severity: \"error\", file: displayPath, message: yamlParseFailure(error) }];\n }\n\n const result = scan.schema.safeParse(parsed);\n if (result.success) return [];\n\n return formatZodErrors(result.error).map((message) => ({\n severity: \"error\" as const,\n file: displayPath,\n message,\n }));\n}\n\n/**\n * Runtime-loads a TypeScript config file via loadConfig and reports validation failures.\n * Absent files are not errors — only report when the file exists but fails to load or validate.\n *\n * The two ways a file can say nothing are reported apart, because the author's next move differs\n * and they used to arrive wearing each other's words. A `null` is a file that exported nothing at\n * all; a module that exported only named bindings raises {@link ConfigDefaultExportError}, which\n * is the one this file's own config files get wrong — `export const skillRules = {...}` reads to\n * `loadConfig` as a namespace, and validating THAT against the schema reports whichever field the\n * schema names first as missing from a file the author can see it in.\n */\nasync function validateTsConfig(\n resolvedPath: string,\n relConfigPath: string,\n schema: z.ZodType<unknown>,\n): Promise<SourceValidationIssue[]> {\n const absPath = path.join(resolvedPath, relConfigPath);\n if (!(await fileExists(absPath))) return [];\n\n try {\n const loaded = await loadConfig(absPath, schema);\n if (loaded === null) {\n return [{ severity: \"error\", file: relConfigPath, message: \"Config is empty\" }];\n }\n return [];\n } catch (error) {\n if (error instanceof ConfigDefaultExportError) {\n return [{ severity: \"error\", file: relConfigPath, message: \"Config has no default export\" }];\n }\n return [\n { severity: \"error\", file: relConfigPath, message: formatLoadError(relConfigPath, error) },\n ];\n }\n}\n\n/**\n * Normalizes a loadConfig error so the message references the relative config path,\n * not the absolute path embedded by loadConfig's own template string.\n */\nfunction formatLoadError(relConfigPath: string, error: unknown): string {\n const raw = getErrorMessage(error);\n const stripped = raw.replace(/Failed to load config from '[^']+':\\s*/, \"\");\n return `Failed to load ${relConfigPath}: ${stripped}`;\n}\n\nfunction buildResult(issues: SourceValidationIssue[], skillCount: number): SourceValidationResult {\n const errorCount = issues.filter((i) => i.severity === \"error\").length;\n const warningCount = issues.filter((i) => i.severity === \"warning\").length;\n return { issues, skillCount, errorCount, warningCount };\n}\n","import os from \"os\";\nimport path from \"path\";\nimport { partition } from \"remeda\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport { PLUGIN_MANIFEST_DIR, STANDARD_FILES } from \"../consts.js\";\nimport { directoryExists, fileExists, listDirectories } from \"../utils/fs.js\";\nimport { listAgentMdFiles, splitAgentsByProvenance } from \"./agents/index.js\";\nimport { readSkillMetadata } from \"./loading/index.js\";\nimport {\n configDirsInPlay,\n findConfigLoadFailures,\n isLocalSource,\n loadInstalledConfig,\n resolvePrimarySourceEntry,\n type ConfigLoadError,\n type LoadedProjectConfig,\n type SourceEntry,\n} from \"./configuration/index.js\";\nimport { isHomeDirectory, resolveInstallPaths } from \"./installation/index.js\";\nimport { getInstalledConfigPath } from \"./installation/install-base-dir.js\";\nimport {\n getInstalledPluginsRegistryPath,\n getProjectPluginsDir,\n getUserPluginsDir,\n listRegisteredPluginInstalls,\n validateAgentFrontmatter,\n validateAllPlugins,\n validatePlugin,\n validateSkillFrontmatter,\n type ResolvedPlugin,\n} from \"./plugins/index.js\";\nimport { splitMetadataValidationIssues, validateSkillMetadata } from \"./schemas.js\";\nimport { readForkedFromMetadata } from \"./skills/index.js\";\nimport { isSourceRepo, validateSource, type MarketplaceReader } from \"./source-validator.js\";\nimport { invalidResult, mergeValidationResults, validResult } from \"./validation-result.js\";\nimport { scopeLabel } from \"../utils/messages.js\";\nimport { plural } from \"../utils/string.js\";\nimport type { ValidationResult } from \"../types/index.js\";\n\n/** How a source repository under the cwd is labelled when it is not a registered source. */\nconst CWD_SOURCE_NAME = \"current directory\";\n\n/** What a compiled agent is called on disk; `splitAgentsByProvenance` answers in bare names. */\nconst AGENT_FILE_EXTENSION = \".md\";\n\nexport type ContentIssue = {\n severity: \"error\" | \"warning\";\n /** The file or directory a reader has to open to fix the issue. */\n file: string;\n message: string;\n};\n\n/**\n * One content pass: what it walked, what it found, and what it deliberately did\n * not check. `notes` carries neither errors nor warnings — it is the record of\n * which sources were reached and which were skipped.\n */\nexport type ContentValidation = {\n count: number;\n issues: ContentIssue[];\n notes: string[];\n};\n\nconst NOTHING_VALIDATED: ContentValidation = { count: 0, issues: [], notes: [] };\n\nfunction mergeValidations(parts: ContentValidation[]): ContentValidation {\n return parts.reduce(\n (acc, part) => ({\n count: acc.count + part.count,\n issues: [...acc.issues, ...part.issues],\n notes: [...acc.notes, ...part.notes],\n }),\n NOTHING_VALIDATED,\n );\n}\n\n/** Render an absolute path with a `~/` prefix when it's under the user's home directory. */\nfunction displayDir(absolutePath: string): string {\n const home = os.homedir();\n if (absolutePath === home) return \"~\";\n if (absolutePath.startsWith(home + path.sep)) {\n return `~${path.sep}${path.relative(home, absolutePath)}`;\n }\n return absolutePath;\n}\n\nfunction toIssues(file: string, result: ValidationResult): ContentIssue[] {\n return [\n ...result.errors.map((message) => ({ severity: \"error\" as const, file, message })),\n ...result.warnings.map((message) => ({ severity: \"warning\" as const, file, message })),\n ];\n}\n\n/** Says which of the three load outcomes a config file is in, without restating the loader. */\nconst CONFIG_UNREADABLE = \"exists but could not be loaded\";\n\n/**\n * Validates the config files this run reads: the project's own and the global one it inlines.\n *\n * A config that exists and cannot be parsed is reported here rather than left to the operational\n * layer, for the reason that layer exists to be gated on — every operational row is read OUT of\n * this file, so each would be a cascade of the same fault. It is also the only report that can\n * carry the loader's reason: the layers below re-read the file per check, and their own\n * diagnostics for it are what used to arrive spliced between the rows.\n *\n * An ABSENT config is not a finding here. It is the legitimate state `init` exists for, and the\n * operational layer already names it and offers the remedy that fits it.\n */\nexport async function validateProjectConfigFile(projectDir: string): Promise<ContentValidation> {\n const [present, failures] = await Promise.all([\n countExistingConfigs(projectDir),\n findConfigLoadFailures(projectDir),\n ]);\n\n return { count: present, issues: failures.map(toUnreadableConfigIssue), notes: [] };\n}\n\n/**\n * Led by whose config it is, as every config row `doctor` prints is: from a project, the file can\n * be its own or the global one it inherits.\n */\nfunction toUnreadableConfigIssue(failure: ConfigLoadError): ContentIssue {\n return {\n severity: \"error\",\n file: `${scopeLabel(failure.scope)}'s ${displayDir(failure.configPath)}`,\n message: `${CONFIG_UNREADABLE}: ${failure.reason}`,\n };\n}\n\nasync function countExistingConfigs(projectDir: string): Promise<number> {\n const present = await Promise.all(\n configDirsInPlay(projectDir).map((dir) => fileExists(getInstalledConfigPath(dir))),\n );\n return present.filter(Boolean).length;\n}\n\n/**\n * Validates the marketplace this installation reads from when it is local, plus the current\n * directory when that is itself a skills source repository — that repository is the content a\n * marketplace author runs this against, and it is not the source they read skills from.\n * A remote marketplace is recorded as skipped rather than fetched.\n *\n * Each one is validated as the reader's own or as someone else's — see {@link readerFor}.\n */\nexport async function validateRegisteredSources(projectDir: string): Promise<ContentValidation> {\n // DEGRADE on an unreadable config. This pass declares `readsConfig: true`, so `doctor` stands it\n // down before it runs whenever the config check has already reported the file — and if it is\n // reached anyway, `safeCheck` turns the throw into a failed row rather than an aborted run. A\n // command whose job is naming what is wrong here must survive the thing it is naming.\n const primary = await resolvePrimarySourceEntry(projectDir);\n const registered = [primary];\n const remote = registered.filter((source) => !isLocalSource(source.url));\n const local = await localSourcesToValidate(projectDir, registered);\n\n const outcomes = await Promise.all(\n local.map((source) => validateOneSource(source, readerFor(projectDir, source))),\n );\n\n return {\n count: local.length,\n issues: outcomes.flatMap((outcome) => outcome.issues),\n notes: [\n ...outcomes.map((outcome) => outcome.note),\n ...remote.map((source) => `${source.name} (${source.url}) — skipped (remote)`),\n ],\n };\n}\n\nasync function localSourcesToValidate(\n projectDir: string,\n registered: SourceEntry[],\n): Promise<SourceEntry[]> {\n const local = registered.filter((source) => isLocalSource(source.url));\n const alreadyCovered = local.some((source) => isCwdMarketplace(projectDir, source));\n\n if (alreadyCovered || !(await isSourceRepo(projectDir))) return local;\n\n return [...local, { name: CWD_SOURCE_NAME, url: projectDir }];\n}\n\n/** Whether this marketplace IS the directory the command ran in, however the config spells it. */\nfunction isCwdMarketplace(projectDir: string, source: SourceEntry): boolean {\n return path.resolve(projectDir, source.url) === projectDir;\n}\n\n/**\n * Where this layer decides what the reader's relationship to a marketplace is — the one place\n * that holds both the directory the command ran in and the path of each marketplace it validates.\n * The appended `CWD_SOURCE_NAME` entry answers `\"author\"` by construction, which is the whole of\n * why a marketplace author's own repository is judged the way they need it to be.\n */\nfunction readerFor(projectDir: string, source: SourceEntry): MarketplaceReader {\n return isCwdMarketplace(projectDir, source) ? \"author\" : \"consumer\";\n}\n\ntype SourceOutcome = { note: string; issues: ContentIssue[] };\n\nasync function validateOneSource(\n source: SourceEntry,\n reader: MarketplaceReader,\n): Promise<SourceOutcome> {\n const label = `${source.name} (${source.url})`;\n try {\n const result = await validateSource(source.url, reader);\n return { note: `${label} — ${plural(result.skillCount, \"skill\")}`, issues: result.issues };\n } catch (error) {\n return {\n note: `${label} — failed`,\n issues: [{ severity: \"error\", file: source.url, message: getErrorMessage(error) }],\n };\n }\n}\n\n/** Validates every installed plugin bundle in the global and project plugin directories. */\nexport async function validateInstalledPlugins(projectDir: string): Promise<ContentValidation> {\n const dirs = [\n getUserPluginsDir(),\n ...(isHomeDirectory(projectDir) ? [] : [getProjectPluginsDir(projectDir)]),\n ];\n return mergeValidations(await Promise.all(dirs.map(validatePluginsDirectory)));\n}\n\nasync function validatePluginsDirectory(pluginsDir: string): Promise<ContentValidation> {\n if (!(await directoryExists(pluginsDir))) return NOTHING_VALIDATED;\n\n if (await fileExists(getInstalledPluginsRegistryPath(pluginsDir))) {\n const registryValidation = await validateRegistryPlugins(pluginsDir);\n if (registryValidation !== undefined) return registryValidation;\n }\n\n const pluginDirs = await findPluginDirectories(pluginsDir);\n if (pluginDirs.length === 0) return NOTHING_VALIDATED;\n\n const { results } = await validateAllPlugins(pluginsDir);\n\n return {\n count: results.length,\n issues: results.flatMap(({ name, result }) => toIssues(name, result)),\n notes: [],\n };\n}\n\n/**\n * Validates the installs recorded in the plugins directory's `installed_plugins.json`\n * (claude CLI >=2.1.220 cache layout). Returns undefined when the registry records no\n * installs so the caller can fall back to the direct-children scan (older/manual layouts).\n */\nasync function validateRegistryPlugins(pluginsDir: string): Promise<ContentValidation | undefined> {\n let installs: ResolvedPlugin[];\n try {\n installs = await listRegisteredPluginInstalls(pluginsDir);\n } catch (error) {\n return {\n count: 0,\n issues: [\n {\n severity: \"error\",\n file: displayDir(getInstalledPluginsRegistryPath(pluginsDir)),\n message: getErrorMessage(error),\n },\n ],\n notes: [],\n };\n }\n\n if (installs.length === 0) return undefined;\n\n // A recorded installPath that no longer exists surfaces as an invalid plugin\n // via validatePlugin's structure check, not a crash.\n const results = await Promise.all(\n installs.map(async ({ pluginKey, installPath }) =>\n toIssues(pluginKey, await validatePlugin(installPath)),\n ),\n );\n\n return { count: installs.length, issues: results.flat(), notes: [] };\n}\n\nasync function findPluginDirectories(pluginsDir: string): Promise<string[]> {\n const entries = await listDirectories(pluginsDir);\n const checks = await Promise.all(\n entries.map(async (name) => ({\n name,\n isPlugin: await directoryExists(path.join(pluginsDir, name, PLUGIN_MANIFEST_DIR)),\n })),\n );\n return checks.filter((check) => check.isPlugin).map((check) => check.name);\n}\n\n/**\n * Validates the SKILL.md and metadata.yaml of every installed skill this installation owns, both\n * scopes.\n *\n * Ownership has to be asked because `~/.claude/skills/` is CLAUDE CODE's directory, shared with\n * everything else that installs a skill there — not this CLI's private space. Judging every\n * directory in it against this CLI's schema reported a skill some other tool put there as a fault\n * in the user's install, in a file they never wrote and cannot fix from here.\n *\n * Two claims make a directory this installation's, and either is enough:\n *\n * - **The configuration in play names its id.** Both configs a run reads, because both scopes are\n * walked. This is the claim that keeps `doctor` loud where it matters most: the provenance\n * marker lives INSIDE metadata.yaml, so a skill whose metadata is missing or unparseable — the\n * plainest breakage there is — can carry no marker to be recognised by.\n * - **The directory carries `forkedFrom`.** The marker this CLI stamps into every skill directory\n * it writes, and the same question `uninstall` asks before removing anything. It answers for the\n * skills a configuration has stopped naming, or never got the chance to.\n *\n * A directory neither claim reaches is not judged and not counted. It IS named — see\n * {@link foreignSkillNote}: a check that quietly walks past a directory is indistinguishable from\n * one that missed it.\n */\nexport async function validateInstalledSkills(projectDir: string): Promise<ContentValidation> {\n const configuredIds = await configuredSkillIds(projectDir);\n return mergeValidations(\n await Promise.all(\n installedDirs(projectDir, \"skillsDir\").map((dir) =>\n validateSkillsDirectory(dir, configuredIds),\n ),\n ),\n );\n}\n\n/** Every skill id the configs this run reads name, across both scopes and both install modes. */\nasync function configuredSkillIds(projectDir: string): Promise<ReadonlySet<string>> {\n const loaded = await Promise.all(\n configDirsInPlay(projectDir).map((dir) => loadInstalledConfig(dir)),\n );\n return new Set(loaded.flatMap(configuredIdsOf));\n}\n\nfunction configuredIdsOf(loaded: LoadedProjectConfig | null): string[] {\n if (!loaded) return [];\n return loaded.config.skills.map((skill) => skill.id);\n}\n\n/** Validates the frontmatter of every compiled agent file, both scopes. */\nexport async function validateInstalledAgents(projectDir: string): Promise<ContentValidation> {\n return mergeValidations(\n await Promise.all(installedDirs(projectDir, \"agentsDir\").map(validateAgentsDirectory)),\n );\n}\n\n/**\n * The directories to walk for an installed-content pass. At the home directory both\n * scopes resolve to the same place, so it is walked once instead of twice.\n */\nfunction installedDirs(projectDir: string, key: \"skillsDir\" | \"agentsDir\"): string[] {\n const global = resolveInstallPaths(projectDir, \"global\")[key];\n if (isHomeDirectory(projectDir)) return [global];\n return [global, resolveInstallPaths(projectDir, \"project\")[key]];\n}\n\n/** What an installed-content pass walked, named rather than validated. */\nexport type InstalledArtifacts = {\n /** Every skill directory this CLI can prove it wrote, both scopes, as display paths. */\n skills: string[];\n /** Every compiled agent file this CLI can prove it wrote, both scopes, as display paths. */\n agents: string[];\n};\n\n/**\n * The same two walks {@link validateInstalledSkills} and {@link validateInstalledAgents} count,\n * naming what belongs to this installation instead of judging it. `doctor` reports this when no\n * configuration exists to compare the installation against: there is nothing to validate the\n * files AGAINST, and naming them is the whole finding.\n *\n * Each walk applies the same ownership question its own pass asks, minus the half that needs a\n * config to answer — see {@link listSkillDirsWithOurProvenance} and\n * {@link listAgentFilesWithOurProvenance}. Both are the question the command this row sends the\n * reader to asks before it removes anything, so what is named here is what `uninstall` takes.\n */\nexport async function listInstalledArtifacts(projectDir: string): Promise<InstalledArtifacts> {\n const [skills, agents] = await Promise.all([\n listAcrossScopes(installedDirs(projectDir, \"skillsDir\"), listSkillDirsWithOurProvenance),\n listAcrossScopes(installedDirs(projectDir, \"agentsDir\"), listAgentFilesWithOurProvenance),\n ]);\n return { skills, agents };\n}\n\n/**\n * The compiled agent files in one tree that this CLI can prove it wrote.\n *\n * The agent axis's counterpart to {@link listSkillDirsWithOurProvenance}, and deliberately\n * `splitAgentsByProvenance` rather than a second reading of the same marker: `uninstall` decides\n * what to remove with no configuration by asking that function, so a listing built any other way\n * could offer a file it then refused. It did — every `*.md` in the directory was named here while\n * the remover took only the marked ones, so a hand-authored agent was offered by one screen and\n * declined by the next.\n *\n * A file that cannot be read carries no marker and is not listed, which is that function's own\n * rule: \"cannot prove it is ours\" and \"is not ours\" call for the same answer. Nothing goes\n * unmentioned either way — {@link validateInstalledAgents} walks every agent file whatever state\n * the config is in.\n */\nasync function listAgentFilesWithOurProvenance(agentsDir: string): Promise<string[]> {\n const { marked } = await splitAgentsByProvenance(agentsDir);\n return marked.map((name) => `${name}${AGENT_FILE_EXTENSION}`);\n}\n\n/**\n * The skill directories in one tree that this CLI can prove it wrote.\n *\n * With no configuration left to name an id, the provenance marker is the only claim a directory\n * can carry — and it is the claim `uninstall` reads before removing anything, so every directory\n * listed here is one that command would actually remove. Listing a directory it would refuse is\n * the CLI recommending what it then declines to do.\n *\n * The marker lives INSIDE `metadata.yaml`, so a directory whose metadata is missing or unreadable\n * can carry no marker and is not listed. That is deliberate rather than a gap: \"cannot prove it is\n * ours\" and \"is not ours\" call for the same answer — leave it alone — which is the rule\n * `splitAgentsByProvenance` already applies to compiled agents. Nothing goes unmentioned either\n * way: {@link validateInstalledSkills} runs whatever state the config is in, and names every\n * directory it stepped over.\n */\nasync function listSkillDirsWithOurProvenance(skillsDir: string): Promise<string[]> {\n const names = await listDirectories(skillsDir);\n const judged = await Promise.all(\n names.map(async (name) => ({ name, ours: await carriesOurProvenance(skillsDir, name) })),\n );\n return judged.filter((entry) => entry.ours).map((entry) => entry.name);\n}\n\nasync function listAcrossScopes(\n dirs: string[],\n listEntries: (dir: string) => Promise<string[]>,\n): Promise<string[]> {\n const perDir = await Promise.all(dirs.map((dir) => listDisplayPaths(dir, listEntries)));\n return perDir.flat();\n}\n\n/** One directory's entries as display paths, or nothing when the directory is absent. */\nasync function listDisplayPaths(\n dir: string,\n listEntries: (dir: string) => Promise<string[]>,\n): Promise<string[]> {\n if (!(await directoryExists(dir))) return [];\n const entries = await listEntries(dir);\n return entries.map((entry) => displayDir(path.join(dir, entry)));\n}\n\nasync function validateSkillsDirectory(\n skillsDir: string,\n configuredIds: ReadonlySet<string>,\n): Promise<ContentValidation> {\n if (!(await directoryExists(skillsDir))) return NOTHING_VALIDATED;\n\n const skillDirs = await listDirectories(skillsDir);\n if (skillDirs.length === 0) return NOTHING_VALIDATED;\n\n const judged = await Promise.all(\n skillDirs.map(async (name) => ({\n name,\n ours: await isOurSkillDirectory(skillsDir, name, configuredIds),\n })),\n );\n const [ours, foreign] = partition(judged, (entry) => entry.ours);\n\n const results = await Promise.all(\n ours.map(async ({ name }) => {\n const skillDir = path.join(skillsDir, name);\n return toIssues(displayDir(skillDir), await validateInstalledSkill(skillDir));\n }),\n );\n\n return {\n count: ours.length,\n issues: results.flat(),\n notes: foreign.map(({ name }) => foreignSkillNote(skillsDir, name)),\n };\n}\n\n/** Either claim this installation has on a skill directory — see {@link validateInstalledSkills}. */\nasync function isOurSkillDirectory(\n skillsDir: string,\n name: string,\n configuredIds: ReadonlySet<string>,\n): Promise<boolean> {\n if (configuredIds.has(name)) return true;\n return carriesOurProvenance(skillsDir, name);\n}\n\n/**\n * The claim a skill directory carries on its own, independent of any configuration: the\n * `forkedFrom` block the copier stamps into every directory this CLI writes. One definition,\n * because the union above and the no-config listing are the same judgement with and without its\n * other half — and `uninstall` decides what to remove by asking exactly this.\n */\nasync function carriesOurProvenance(skillsDir: string, name: string): Promise<boolean> {\n return (await readForkedFromMetadata(path.join(skillsDir, name))) !== null;\n}\n\n/**\n * A directory this pass declined to judge, named so the count and the directory listing do not\n * silently disagree. Not a warning: nothing here is wrong, and a reader sent to fix a skill they\n * did not install is worse off than one told plainly that it is not this CLI's to police.\n */\nfunction foreignSkillNote(skillsDir: string, name: string): string {\n return `${displayDir(path.join(skillsDir, name))} — not installed by this CLI and named by no configuration here: not validated`;\n}\n\nasync function validateAgentsDirectory(agentsDir: string): Promise<ContentValidation> {\n if (!(await directoryExists(agentsDir))) return NOTHING_VALIDATED;\n\n const agentFiles = await listAgentMdFiles(agentsDir);\n if (agentFiles.length === 0) return NOTHING_VALIDATED;\n\n const results = await Promise.all(\n agentFiles.map(async (fileName) => {\n const agentPath = path.join(agentsDir, fileName);\n return toIssues(displayDir(agentPath), await validateAgentFrontmatter(agentPath));\n }),\n );\n\n return { count: agentFiles.length, issues: results.flat(), notes: [] };\n}\n\nasync function validateInstalledSkill(skillDir: string): Promise<ValidationResult> {\n const skillMd = await validateSkillFile(\n skillDir,\n STANDARD_FILES.SKILL_MD,\n validateSkillFrontmatter,\n );\n const metadata = await validateSkillFile(\n skillDir,\n STANDARD_FILES.METADATA_YAML,\n validateInstalledSkillMetadata,\n );\n\n return mergeValidationResults([skillMd, metadata]);\n}\n\n/** One of an installed skill's files judged by `validate`, or the error of its absence. */\nasync function validateSkillFile(\n skillDir: string,\n fileName: string,\n validate: (filePath: string) => Promise<ValidationResult>,\n): Promise<ValidationResult> {\n const filePath = path.join(skillDir, fileName);\n if (!(await fileExists(filePath))) return invalidResult(`Missing ${fileName}`);\n return validate(filePath);\n}\n\nasync function validateInstalledSkillMetadata(metadataPath: string): Promise<ValidationResult> {\n // Same judgment `compile` refuses on and local-skill discovery skips on: a file\n // that describes no skill is reported here as the error it is. The strict\n // published-skill checks below are layered on top of that verdict, never against\n // it — doctor reports what the other two passes refuse, and then some.\n const read = await readSkillMetadata(metadataPath);\n if (!read.usable) {\n return invalidResult(`${STANDARD_FILES.METADATA_YAML}: ${read.reason}`);\n }\n\n const metadata = read.metadata;\n const result = validateSkillMetadata(metadata);\n if (result.success) {\n return validResult();\n }\n\n // Over-length cliDescription is advisory — only hard schema violations invalidate the skill\n const { errors, warnings } = splitMetadataValidationIssues(result.error, metadata);\n return {\n valid: errors.length === 0,\n errors: errors.map((e) => `${STANDARD_FILES.METADATA_YAML}: ${e}`),\n warnings: warnings.map((w) => `${STANDARD_FILES.METADATA_YAML}: ${w}`),\n };\n}\n"],"mappings":"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","names":["os","path","skillsDir","path","path","skillsDir","os","matrix","path"]}
|