buddy-agent-harness 0.8.0 → 0.9.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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cli.mjs +19036 -1
- package/dist/dist-ktNZ1sJo.mjs +2966 -0
- package/dist/esm-BMqwcD7W.mjs +87 -0
- package/dist/esm-CVacKtt6.mjs +64 -0
- package/dist/index.d.mts +15 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2734 -1
- package/dist/index.mjs.map +1 -0
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/doctor/SKILL.md +1 -1
- package/skills/enhance/SKILL.md +54 -10
- package/skills/enhance/references/delegation.history.md +15 -0
- package/skills/enhance/references/delegation.md +63 -0
- package/skills/init/SKILL.md +1 -1
- package/dist/cli-4ekejn5W.mjs +0 -2735
- package/dist/cli-4ekejn5W.mjs.map +0 -1
- package/dist/cli.d.mts +0 -15
- package/dist/cli.d.mts.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli-4ekejn5W.mjs","names":["readJson","parseToml","inspect"],"sources":["../src/is-record/is-record.ts","../src/command-output/command-output.ts","../src/dep-plugins/dep-plugins.ts","../src/dep-plugins/resolve.ts","../src/dep-plugins/generate.ts","../src/dep-plugins/harness-plugins.ts","../src/dep-plugins/reconcile.ts","../src/dep-plugins/dep-plugins.command.ts","../src/diagnose-bridges/directory-files.ts","../src/harness-registry/harness-registry.ts","../src/diagnose-bridges/locator.ts","../src/diagnose-bridges/doctor-guidance.ts","../src/diagnose-configuration/diagnose-configuration.ts","../src/diagnose-bridges/json-with-comments.ts","../src/diagnose-mcp/mcp-model.ts","../src/diagnose-mcp/mcp-sources.ts","../src/diagnose-mcp/mcp-baseline.ts","../src/diagnose-mcp/mcp-secrets.ts","../src/diagnose-mcp/diagnose-mcp.ts","../src/diagnose-nonstandard/diagnose-nonstandard.ts","../src/skill-projection/skill-projection.ts","../src/diagnose-bridges/agents-files.ts","../src/diagnose-bridges/diagnose-instructions.ts","../src/diagnose-bridges/git-bridge-state.ts","../src/diagnose-bridges/diagnose-bridges.ts","../src/diagnose-bridges/doctor.command.ts","../src/initialize-harnesses/initialize-harnesses.ts","../src/initialize-harnesses/init.command.ts","../src/cli.ts"],"sourcesContent":["/**\n * Whether a value is a record: an object with named keys, and not an array.\n *\n * Everything this package reads is a file someone else wrote — a golden MCP set, a harness config,\n * a projection record — so every parse lands on `unknown` and every reader narrows it here before\n * asking for a key. Four modules had their own copy of the same three clauses; a predicate that\n * decides what counts as a parsed object is one fact, and a copy of it that drifts changes what a\n * file is read to say.\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","import { sep } from 'node:path'\nimport { encode } from '@toon-format/toon'\nimport { isRecord } from '../is-record/is-record.ts'\n\nexport type OutputFormat = 'json' | 'toon' | 'text'\n\nconst formats: readonly OutputFormat[] = ['toon', 'json', 'text']\n\n/** Rejects anything but the supported formats so an unknown value never falls back silently. */\nexport function parseFormat(value: string | undefined): OutputFormat {\n\tif (!formats.includes(value as OutputFormat)) throw new Error('--format must be toon, json, or text.')\n\treturn value as OutputFormat\n}\n\n/** The single stdout boundary: internal logic stays on plain objects, encoding happens here. */\nexport function writeResult(value: object, format: OutputFormat): void {\n\tprocess.stdout.write(`${encodeResult(value, format)}\\n`)\n}\n\nfunction encodeResult(value: object, format: OutputFormat): string {\n\tif (format === 'json') return JSON.stringify(value)\n\treturn format === 'text' ? renderText(value) : encode(value)\n}\n\nfunction cell(value: unknown): string {\n\treturn value === undefined ? '' : String(value)\n}\n\n/** A header row plus one row per record, every column padded to its widest cell. */\nfunction table(rows: Record<string, unknown>[]): string[] {\n\tconst columns = [...new Set(rows.flatMap((row) => Object.keys(row)))]\n\tconst widths = columns.map((column) => Math.max(column.length, ...rows.map((row) => cell(row[column]).length)))\n\tconst line = (values: string[]) =>\n\t\t` ${values.map((value, index) => value.padEnd(widths[index] as number)).join(' ')}`.trimEnd()\n\treturn [line(columns), ...rows.map((row) => line(columns.map((column) => cell(row[column]))))]\n}\n\n/**\n * The same result rendered for a person rather than for an agent: scalars as `key: value`, records\n * as an aligned table, and everything else as a bulleted list. TOON stays the default because it is\n * what an agent parses; this is for reading over someone's shoulder.\n */\nexport function renderText(value: object): string {\n\tconst blocks = Object.entries(value).map(([key, item]) => {\n\t\tif (!Array.isArray(item)) return [`${key}: ${isRecord(item) ? JSON.stringify(item) : String(item)}`]\n\t\tif (!item.length) return [`${key}: (none)`]\n\t\tconst body = item.every(isRecord) ? table(item) : item.map((entry) => ` - ${String(entry)}`)\n\t\treturn [`${key}:`, ...body]\n\t})\n\n\t// A blank line wherever a multi-line block meets its neighbour, so a following scalar does not\n\t// read as another row of the table above it.\n\treturn blocks\n\t\t.flatMap((block, index) => {\n\t\t\tconst previous = blocks[index - 1]\n\t\t\treturn previous && (previous.length > 1 || block.length > 1) ? ['', ...block] : block\n\t\t})\n\t\t.join('\\n')\n}\n\n/** AXI §10: the executable's absolute path, with the user's home directory collapsed to `~`. */\nexport function binPath(home: string, executable: string | undefined): string {\n\tif (!executable) return 'buddy-agent-harness'\n\treturn home && executable.startsWith(home + sep) ? `~${executable.slice(home.length)}` : executable\n}\n","/**\n * Dependency-provided plugins: the catalog half.\n *\n * A dependency that ships an Agent Plugins manifest at its package root is a plugin the consuming\n * repository can install through its harness. This module turns the set of such dependencies into a\n * marketplace catalog; installing from that catalog is the runtime's own job, and the lifecycle half\n * drives it.\n *\n * Everything here is pure. Resolution against `node_modules` lives in `resolve.ts`.\n */\n\n/** One dependency that ships a plugin manifest. */\nexport type DepPlugin = {\n\t/** The npm package name — what a consumer declared, and what the catalog installs by. */\n\tpackage: string\n\t/** The plugin's own name from its manifest, which need not match the package name. */\n\tplugin: string\n\t/** The version actually installed, read from the dependency's own `package.json`. */\n\tversion: string\n\tdescription?: string\n}\n\n/**\n * A catalog entry's `npm` source. Deliberately not a path: a path source is resolved against the\n * marketplace root and may not escape it, so a path-sourced catalog would have to sit at the\n * repository root — the one place the repository's own public catalog already occupies. An `npm`\n * source carries no path, so this catalog can live in its own directory and stay separate from\n * whatever the repository publishes for its own consumers.\n */\nexport type NpmSource = {\n\tsource: 'npm'\n\tpackage: string\n\tversion: string\n}\n\nexport type CatalogEntry = {\n\tname: string\n\tsource: NpmSource\n\tdescription?: string\n}\n\n/**\n * The Claude-shaped catalog, which Codex, Copilot CLI, and Cursor also read. `owner` is an object\n * whose `name` is required — a string there is a schema error rather than a tolerated shorthand.\n */\nexport type Catalog = {\n\tname: string\n\towner: { name: string }\n\tdescription: string\n\tplugins: CatalogEntry[]\n}\n\n/**\n * Marketplace names generic enough that two repositories are likely to pick the same one. The\n * collision is silent and costly: registering a second marketplace under a name already in use\n * replaces the first, and the install that follows reports \"already installed\" and does nothing —\n * so the repository silently runs the other one's plugin versions.\n */\nconst GENERIC_NAMES = new Set(['deps', 'dependencies', 'local', 'plugins', 'marketplace', 'plugin', 'default'])\n\nexport function isGenericName(name: string): boolean {\n\treturn GENERIC_NAMES.has(name.toLowerCase())\n}\n\n/**\n * A marketplace name derived from the consuming package's own name, so two repositories do not\n * collide by default. A scope is folded into the name rather than dropped (`@acme/web` → `acme-web`)\n * because the unscoped half is the part most likely to repeat across organizations.\n */\nexport function marketplaceName(packageName: string): string {\n\tconst folded = packageName.replace(/^@/, '').replace(/\\//g, '-')\n\tconst cleaned = folded\n\t\t.replace(/[^a-zA-Z0-9-_.]/g, '-')\n\t\t.replace(/-+/g, '-')\n\t\t.replace(/^-|-$/g, '')\n\treturn cleaned || 'agent-plugins'\n}\n\n/**\n * Dependency names a repository declares, both runtime and development.\n *\n * Development dependencies are included on purpose: a plugin that supplies a project's testing or\n * release workflow is a development dependency, and excluding them would drop most of the cases this\n * exists for. Transitive dependencies are never included — see `resolve.ts`.\n */\nexport function declaredDependencies(manifest: unknown): string[] {\n\tif (!manifest || typeof manifest !== 'object') return []\n\tconst record = manifest as Record<string, unknown>\n\tconst names = new Set<string>()\n\tfor (const field of ['dependencies', 'devDependencies']) {\n\t\tconst group = record[field]\n\t\tif (!group || typeof group !== 'object') continue\n\t\tfor (const name of Object.keys(group as Record<string, unknown>)) names.add(name)\n\t}\n\treturn [...names].sort()\n}\n\nexport type BuildCatalogOptions = {\n\tname: string\n\towner: string\n\tplugins: readonly DepPlugin[]\n}\n\n/**\n * The catalog is derived in full on every run and never merged into what is already on disk. That is\n * what makes the three cases a consumer cares about — a plugin added, a plugin removed, a version\n * moved — fall out of regeneration instead of needing reconciliation logic of their own.\n *\n * Entries are sorted by name so that a regeneration whose inputs did not change produces a\n * byte-identical file, which is what lets a check mode compare rather than interpret.\n */\nexport function buildCatalog({ name, owner, plugins }: BuildCatalogOptions): Catalog {\n\tconst entries = [...plugins]\n\t\t.sort((a, b) => a.plugin.localeCompare(b.plugin))\n\t\t.map(({ plugin, package: pkg, version, description }): CatalogEntry => {\n\t\t\tconst source: NpmSource = { source: 'npm', package: pkg, version }\n\t\t\treturn description ? { name: plugin, source, description } : { name: plugin, source }\n\t\t})\n\n\treturn {\n\t\tname,\n\t\towner: { name: owner },\n\t\tdescription: 'Plugins provided by this repository’s dependencies. Generated — do not edit.',\n\t\tplugins: entries,\n\t}\n}\n\n/** Serialized the way the repository formats JSON, with the trailing newline a text file needs. */\nexport function serializeCatalog(catalog: Catalog): string {\n\treturn `${JSON.stringify(catalog, null, '\\t')}\\n`\n}\n","/**\n * Locating a declared dependency on disk, and reading the plugin manifest it may ship.\n *\n * Resolution rather than scanning. Walking `node_modules` finds transitive dependencies nobody\n * vetted, and a skill or plugin is instruction text that steers an agent — an unvetted transitive\n * dependency is a supply-chain surface, not a convenience. Asking the module resolver where a\n * *declared* dependency lives answers the question that was actually asked, and it is correct under\n * npm, pnpm, yarn with the node-modules linker, and bun alike, because all four put a real directory\n * on disk. Only Yarn PnP does not, and it is reported rather than guessed at.\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { dirname, join } from 'node:path'\nimport type { DepPlugin } from './dep-plugins.ts'\n\n/** Why a declared dependency yielded no plugin, when the caller needs to explain itself. */\nexport type SkipReason = 'unresolved' | 'no-manifest' | 'unnamed'\n\nexport type ResolveOutcome = { ok: true; plugin: DepPlugin } | { ok: false; package: string; reason: SkipReason }\n\nfunction readJson(path: string): unknown {\n\ttry {\n\t\treturn JSON.parse(readFileSync(path, 'utf8'))\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\n/**\n * Every ancestor `node_modules/<pkg>` from `startDir` upward. The fallback path only — see\n * {@link packageDir}.\n */\nfunction ancestorCandidates(startDir: string, pkg: string): string[] {\n\tconst segments = pkg.split('/')\n\tconst candidates: string[] = []\n\tlet dir = startDir\n\tfor (;;) {\n\t\tcandidates.push(join(dir, 'node_modules', ...segments))\n\t\tconst parent = dirname(dir)\n\t\tif (parent === dir) break\n\t\tdir = parent\n\t}\n\treturn candidates\n}\n\n/**\n * The directory a declared dependency was installed into, or `undefined`.\n *\n * Two mechanisms, in order, because neither alone is sufficient:\n *\n * 1. **`require.resolve('<pkg>/package.json')`, anchored at the declaring manifest.** The anchor\n * matters: under pnpm's isolated store only the package that declares a dependency can resolve it,\n * so resolving from a workspace root would miss what a workspace member declared.\n * 2. **A walk up `node_modules`.** Roughly one package in seven declares an `exports` map without a\n * `./package.json` entry, and those refuse the first mechanism with `ERR_PACKAGE_PATH_NOT_EXPORTED`\n * however plainly they are installed. Resolving a deeper file instead is not an escape — the same\n * map blocks that too.\n */\nexport function packageDir(pkg: string, fromManifest: string): string | undefined {\n\ttry {\n\t\tconst require = createRequire(fromManifest)\n\t\treturn dirname(require.resolve(`${pkg}/package.json`))\n\t} catch {\n\t\t// fall through to the walk\n\t}\n\tfor (const candidate of ancestorCandidates(dirname(fromManifest), pkg)) {\n\t\tif (existsSync(join(candidate, 'package.json'))) return candidate\n\t}\n\treturn undefined\n}\n\n/**\n * Reads the plugin a dependency ships, if it ships one.\n *\n * The version comes from the dependency's own `package.json` rather than from a lockfile. It is the\n * same number — the version actually installed — and reading it needs no knowledge of which of the\n * four lockfile formats this repository uses.\n *\n * A manifest at the package root is what the Agent Plugins Specification pins, and it is the form a\n * harness accepts as a marketplace source without any vendor-specific manifest alongside it.\n */\nexport function readDepPlugin(pkg: string, fromManifest: string): ResolveOutcome {\n\tconst dir = packageDir(pkg, fromManifest)\n\tif (!dir) return { ok: false, package: pkg, reason: 'unresolved' }\n\n\tconst manifest = readJson(join(dir, 'plugin.json'))\n\tif (!manifest || typeof manifest !== 'object') return { ok: false, package: pkg, reason: 'no-manifest' }\n\n\tconst record = manifest as Record<string, unknown>\n\tconst name = typeof record['name'] === 'string' ? record['name'] : undefined\n\tif (!name) return { ok: false, package: pkg, reason: 'unnamed' }\n\n\t// The installed version, not the manifest's: the catalog installs the package from npm, so the\n\t// number that must match is the one npm published the tree under.\n\tconst installed = readJson(join(dir, 'package.json')) as { version?: unknown } | undefined\n\tconst version = typeof installed?.version === 'string' ? installed.version : undefined\n\tif (!version) return { ok: false, package: pkg, reason: 'no-manifest' }\n\n\tconst description = typeof record['description'] === 'string' ? record['description'] : undefined\n\treturn {\n\t\tok: true,\n\t\tplugin: description\n\t\t\t? { package: pkg, plugin: name, version, description }\n\t\t\t: { package: pkg, plugin: name, version },\n\t}\n}\n\n/**\n * True when the process is running under Yarn's Plug'n'Play resolver, where packages live inside zip\n * archives rather than as directories. A marketplace source must be a real directory and an npm\n * source must be installable, so a PnP project needs `nodeLinker: node-modules` or the affected\n * dependencies unplugged. Detected rather than worked around, so the report can say so.\n */\nexport function isPnp(): boolean {\n\treturn Boolean((process.versions as Record<string, string | undefined>)['pnp'])\n}\n","/**\n * Deriving a repository's dependency-plugin catalog, and deciding whether what is on disk matches.\n */\n\nimport { mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport {\n\tbuildCatalog,\n\ttype Catalog,\n\ttype DepPlugin,\n\tdeclaredDependencies,\n\tisGenericName,\n\tmarketplaceName,\n\tserializeCatalog,\n} from './dep-plugins.ts'\nimport { isPnp, readDepPlugin, type SkipReason } from './resolve.ts'\n\n/**\n * The catalog's home, relative to the repository root.\n *\n * Under this tool's own directory, beside the other files it owns there — the same place as\n * `mcp.toml` and `mcp.projected.json`, and the same shape as `.agents/repobuddy/` and\n * `.agents/cyberlegion/`. A directory named for what it holds rather than for who writes it would be\n * a second convention in a tree that already has one.\n *\n * Not the repository root's `.claude-plugin/`, which is where a repository publishes its *own*\n * plugins for its *own* consumers. Merging the two would leak every dependency-provided plugin into\n * what a consumer sees on adding the published marketplace, and no harness has a notion of a private\n * catalog entry to prevent that. Being a separate directory also makes this one a marketplace root\n * in its own right, which is what lets its entries be resolved without reaching outside it.\n */\nexport const CATALOG_DIR: string = join('.agents', 'buddy-agent-harness')\nexport const CATALOG_PATH: string = join(CATALOG_DIR, '.claude-plugin', 'marketplace.json')\n\n/** A declared dependency that yielded no plugin, kept so a report can say why rather than go quiet. */\nexport type Skipped = { package: string; reason: SkipReason }\n\nexport type DerivedCatalog = {\n\tcatalog: Catalog\n\tplugins: DepPlugin[]\n\t/** Only the dependencies that failed to resolve at all; a dependency with no plugin is not news. */\n\tunresolved: Skipped[]\n\t/** Set when the marketplace name is one two repositories are likely to share. */\n\tgenericName: boolean\n\t/** Set under Yarn PnP, where no dependency has a directory for a harness to install from. */\n\tpnp: boolean\n}\n\nfunction readManifest(path: string): Record<string, unknown> {\n\treturn JSON.parse(readFileSync(path, 'utf8')) as Record<string, unknown>\n}\n\n/**\n * Derives the catalog from the repository's declared dependencies.\n *\n * Derived in full every time. A plugin newly added to `package.json` appears, one removed disappears,\n * and a version that moved follows — all three without a line of reconciliation logic, because there\n * is nothing to reconcile against. That is the same rule the vendor manifests already follow: the\n * artifact is derived, never authored.\n */\nexport function deriveCatalog(root: string): DerivedCatalog {\n\tconst manifestPath = join(root, 'package.json')\n\tconst manifest = readManifest(manifestPath)\n\n\tconst plugins: DepPlugin[] = []\n\tconst unresolved: Skipped[] = []\n\tfor (const pkg of declaredDependencies(manifest)) {\n\t\tconst outcome = readDepPlugin(pkg, manifestPath)\n\t\tif (outcome.ok) plugins.push(outcome.plugin)\n\t\telse if (outcome.reason === 'unresolved') unresolved.push({ package: pkg, reason: outcome.reason })\n\t}\n\n\tconst owner = typeof manifest['name'] === 'string' ? manifest['name'] : 'unknown'\n\tconst name = marketplaceName(owner)\n\n\treturn {\n\t\tcatalog: buildCatalog({ name, owner, plugins }),\n\t\tplugins,\n\t\tunresolved,\n\t\tgenericName: isGenericName(name),\n\t\tpnp: isPnp(),\n\t}\n}\n\n/** What a write did, or would do, so a caller can report a no-op as a no-op rather than as a change. */\nexport type WriteOutcome = 'created' | 'updated' | 'unchanged'\n\n/**\n * What writing the catalog would do, without touching the disk.\n *\n * Comparing the serialized form rather than the parsed one is deliberate: a byte comparison is what\n * lets a check mode be a comparison instead of an interpretation, and `buildCatalog` sorts its\n * entries so that unchanged inputs serialize identically.\n */\nexport function catalogStatus(root: string, catalog: Catalog): WriteOutcome {\n\tlet current: string | undefined\n\ttry {\n\t\tcurrent = readFileSync(join(root, CATALOG_PATH), 'utf8')\n\t} catch {\n\t\tcurrent = undefined\n\t}\n\tif (current === serializeCatalog(catalog)) return 'unchanged'\n\treturn current === undefined ? 'created' : 'updated'\n}\n\n/** Writes the catalog, reporting whether it moved. A write that would change nothing writes nothing. */\nexport function writeCatalog(root: string, catalog: Catalog): WriteOutcome {\n\tconst outcome = catalogStatus(root, catalog)\n\tif (outcome === 'unchanged') return outcome\n\n\tconst path = join(root, CATALOG_PATH)\n\tmkdirSync(dirname(path), { recursive: true })\n\twriteFileSync(path, serializeCatalog(catalog))\n\treturn outcome\n}\n","/**\n * What each harness does with a marketplace catalog, and how to read what it currently has.\n *\n * The runtimes diverge more than they look, and every difference below was established by running\n * the shipped CLI rather than by reading a document:\n *\n * - **Claude Code** refreshes with `plugin update`; re-running `plugin install` is a no-op that\n * reports \"already installed\" and leaves the stale copy in place. Every verb defaults to user\n * scope, so each one has to name `--scope project` explicitly.\n * - **Codex** has no update verb and no scope. Re-running `plugin add` re-reads the catalog and\n * replaces the cached copy, so the same command installs and refreshes. It records no version in\n * its config — the installed version is only in the cache directory's name.\n * - **Copilot CLI** accepts a path source but **rejects an `npm` source**, so it cannot consume this\n * catalog at all. See {@link COPILOT_LIMITATION}.\n * - **Cursor** exposes no plugin subcommand from a terminal.\n *\n * A consumer should not have to know any of that, which is why it is here rather than in a document.\n */\n\nimport { existsSync, readdirSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport type PluginRuntimeName = 'claude-code' | 'codex'\n\nexport type InstalledPlugin = {\n\t/** `<plugin>@<marketplace>` — the identity a harness installs and uninstalls by. */\n\tid: string\n\tversion: string\n\tscope: string\n\t/** Present for project- and local-scoped installs; absent where a runtime has no scopes. */\n\tprojectPath?: string\n}\n\nexport type RegisteredMarketplace = { name: string; path: string }\n\n/**\n * The state a harness keeps outside the repository. Read, never written: the install is the\n * runtime's own job, and driving it through its own CLI is what keeps that true.\n */\nexport type HarnessPluginState = {\n\tinstalled: InstalledPlugin[]\n\tmarketplaces: RegisteredMarketplace[]\n}\n\n/**\n * One runtime's half of the contract: where its state lives, how to read it, and the commands that\n * move it toward a catalog.\n */\nexport type PluginRuntime = {\n\tname: PluginRuntimeName\n\t/** Whether this runtime is set up on this machine at all. */\n\tpresent(home: string, env?: NodeJS.ProcessEnv): boolean\n\tstate(home: string, env?: NodeJS.ProcessEnv): HarnessPluginState\n\tregister(catalogDir: string): string\n\tinstall(id: string): string\n\tupdate(id: string): string\n\tuninstall(id: string): string\n\t/** Empty where a runtime re-reads the catalog on install and needs no separate refresh. */\n\trefreshMarketplace(marketplace: string): string\n\t/**\n\t * True where an install belongs to one repository rather than the whole machine. Where false,\n\t * every repository on the machine shares one install and the reconciliation cannot isolate them.\n\t */\n\tscoped: boolean\n}\n\n/**\n * Why Copilot CLI is absent from the table. Recorded rather than dropped, so the reason travels with\n * the code: `copilot plugin marketplace add` rejects this catalog with\n * `Invalid marketplace.json: plugins.0.source: Invalid input`, while the same catalog with a\n * `./path` source is accepted. Supporting it needs a second, path-sourced catalog — and a path source\n * cannot escape its marketplace root, which is the constraint the `npm` source was chosen to avoid.\n */\nexport const COPILOT_LIMITATION =\n\t'Copilot CLI rejects an npm marketplace source, so it cannot install from this catalog. It needs a path-sourced catalog, which cannot reach node_modules from a subdirectory.'\n\nfunction readJson(path: string): unknown {\n\ttry {\n\t\treturn JSON.parse(readFileSync(path, 'utf8'))\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nfunction directories(path: string): string[] {\n\ttry {\n\t\treturn readdirSync(path, { withFileTypes: true })\n\t\t\t.filter((entry) => entry.isDirectory())\n\t\t\t.map((entry) => entry.name)\n\t} catch {\n\t\treturn []\n\t}\n}\n\n// ── Claude Code ──\n\n/**\n * `CLAUDE_CONFIG_DIR` wins where set, which is also what keeps a test off a developer's real\n * configuration.\n */\nexport function claudeConfigDir(home: string, env: NodeJS.ProcessEnv = process.env): string {\n\treturn env['CLAUDE_CONFIG_DIR'] || join(home, '.claude')\n}\n\n/**\n * Reads what Claude Code has installed.\n *\n * The file keys each plugin by `<plugin>@<marketplace>` and holds an *array* per key, because the\n * same plugin can be installed at more than one scope and, at project scope, once per project.\n */\nexport function readClaudeState(configDir: string): HarnessPluginState {\n\tconst installedFile = readJson(join(configDir, 'plugins', 'installed_plugins.json')) as\n\t\t| { plugins?: Record<string, unknown> }\n\t\t| undefined\n\tconst installed: InstalledPlugin[] = []\n\tfor (const [id, entries] of Object.entries(installedFile?.plugins ?? {})) {\n\t\tif (!Array.isArray(entries)) continue\n\t\tfor (const entry of entries) {\n\t\t\tif (!entry || typeof entry !== 'object') continue\n\t\t\tconst record = entry as Record<string, unknown>\n\t\t\tconst version = typeof record['version'] === 'string' ? record['version'] : undefined\n\t\t\tconst scope = typeof record['scope'] === 'string' ? record['scope'] : undefined\n\t\t\tif (!version || !scope) continue\n\t\t\tconst projectPath = typeof record['projectPath'] === 'string' ? record['projectPath'] : undefined\n\t\t\tinstalled.push(projectPath ? { id, version, scope, projectPath } : { id, version, scope })\n\t\t}\n\t}\n\n\tconst knownFile = readJson(join(configDir, 'plugins', 'known_marketplaces.json')) as\n\t\t| Record<string, { source?: { path?: unknown } }>\n\t\t| undefined\n\tconst marketplaces: RegisteredMarketplace[] = []\n\tfor (const [name, value] of Object.entries(knownFile ?? {})) {\n\t\tmarketplaces.push({ name, path: typeof value?.source?.path === 'string' ? value.source.path : '' })\n\t}\n\n\treturn { installed, marketplaces }\n}\n\nexport const claudeCode: PluginRuntime = {\n\tname: 'claude-code',\n\tscoped: true,\n\tpresent: (home, env = process.env) => existsSync(claudeConfigDir(home, env)),\n\tstate: (home, env = process.env) => readClaudeState(claudeConfigDir(home, env)),\n\tregister: (catalogDir) => `claude plugin marketplace add ./${catalogDir} --scope project`,\n\tinstall: (id) => `claude plugin install ${id} --scope project`,\n\t/**\n\t * Not `install`: re-running that on an already-installed plugin changes nothing and says so, which\n\t * reads as success. `--scope project` is required rather than cosmetic — every verb defaults to\n\t * user scope, so an update omitting it fails with \"not installed at scope user\" against a plugin\n\t * that is plainly installed.\n\t */\n\tupdate: (id) => `claude plugin update ${id} --scope project`,\n\tuninstall: (id) => `claude plugin uninstall ${id} --scope project`,\n\trefreshMarketplace: (marketplace) => `claude plugin marketplace update ${marketplace}`,\n}\n\n// ── Codex ──\n\nexport function codexHome(home: string, env: NodeJS.ProcessEnv = process.env): string {\n\treturn env['CODEX_HOME'] || join(home, '.codex')\n}\n\n/**\n * Reads what Codex has installed.\n *\n * The installed version comes from the cache layout, `plugins/cache/<marketplace>/<plugin>/<version>`,\n * because Codex's `config.toml` records only that a plugin is enabled — never which version.\n *\n * Registered marketplaces are read from `config.toml` by scanning for its `[marketplaces.<name>]`\n * tables. A deliberately narrow read rather than a TOML parser: only two keys are needed, and the\n * cost of being wrong is a report that suggests re-registering, which is harmless to re-run.\n */\nexport function readCodexState(home: string): HarnessPluginState {\n\tconst cacheRoot = join(home, 'plugins', 'cache')\n\tconst installed: InstalledPlugin[] = []\n\tfor (const marketplace of directories(cacheRoot)) {\n\t\tfor (const plugin of directories(join(cacheRoot, marketplace))) {\n\t\t\tfor (const version of directories(join(cacheRoot, marketplace, plugin))) {\n\t\t\t\tinstalled.push({ id: `${plugin}@${marketplace}`, version, scope: 'user' })\n\t\t\t}\n\t\t}\n\t}\n\n\tconst marketplaces: RegisteredMarketplace[] = []\n\tlet config = ''\n\ttry {\n\t\tconfig = readFileSync(join(home, 'config.toml'), 'utf8')\n\t} catch {\n\t\tconfig = ''\n\t}\n\t// The entry itself is carried rather than its name, so assigning a source needs no index lookup\n\t// and no guard against one that cannot fail.\n\tlet current: RegisteredMarketplace | undefined\n\tfor (const line of config.split('\\n')) {\n\t\tconst header = /^\\s*\\[marketplaces\\.(?:\"([^\"]+)\"|([^\\]]+))\\]\\s*$/.exec(line)\n\t\tif (header) {\n\t\t\tcurrent = { name: (header[1] ?? header[2]) as string, path: '' }\n\t\t\tmarketplaces.push(current)\n\t\t\tcontinue\n\t\t}\n\t\tif (/^\\s*\\[/.test(line)) {\n\t\t\tcurrent = undefined\n\t\t\tcontinue\n\t\t}\n\t\tconst source = current ? /^\\s*source\\s*=\\s*\"([^\"]*)\"\\s*$/.exec(line) : undefined\n\t\tif (source?.[1] && current) current.path = source[1]\n\t}\n\n\treturn { installed, marketplaces }\n}\n\nexport const codex: PluginRuntime = {\n\tname: 'codex',\n\t/**\n\t * Codex installs into `CODEX_HOME` with no notion of scope, so one install is shared by every\n\t * repository on the machine. Two repositories whose catalogs share a marketplace name cannot be\n\t * told apart here, which is what makes the marketplace name derived from the package name matter.\n\t */\n\tscoped: false,\n\tpresent: (home, env = process.env) => existsSync(codexHome(home, env)),\n\tstate: (home, env = process.env) => readCodexState(codexHome(home, env)),\n\tregister: (catalogDir) => `codex plugin marketplace add ./${catalogDir}`,\n\tinstall: (id) => `codex plugin add ${id}`,\n\t/** No update verb: re-running `add` re-reads the catalog and replaces the cached copy. */\n\tupdate: (id) => `codex plugin add ${id}`,\n\tuninstall: (id) => `codex plugin remove ${id}`,\n\t/** `marketplace upgrade` refreshes git snapshots only; `add` already re-reads a local catalog. */\n\trefreshMarketplace: () => '',\n}\n\nexport const runtimes: readonly PluginRuntime[] = [claudeCode, codex]\n","/**\n * Comparing a derived catalog against what a harness actually has installed.\n *\n * The harness reconciles nothing on its own. A catalog entry that appears is not installed; one that\n * disappears leaves the plugin installed and failing to load, with `prune` declining to remove it\n * because it was never an auto-installed dependency. So the delta has to be computed, and every\n * conclusion here comes from that asymmetry.\n *\n * Pure. Reading harness state lives in `harness-plugins.ts`.\n */\n\nimport type { Catalog } from './dep-plugins.ts'\nimport type { InstalledPlugin, RegisteredMarketplace } from './harness-plugins.ts'\n\nexport type ActionKind = 'register' | 'install' | 'update' | 'uninstall'\n\nexport type Action = {\n\tkind: ActionKind\n\t/** `<plugin>@<marketplace>`, or the marketplace name for `register`. */\n\tsubject: string\n\t/** Why, in the terms a reader can act on: the version moving, or the entry that went away. */\n\tdetail: string\n}\n\nexport type Reconciliation = {\n\tactions: Action[]\n\t/**\n\t * Set when a marketplace of this name is registered against a different directory — the silent\n\t * collision, which produces no error from the harness at any point.\n\t */\n\tcollision?: string\n\t/** Set when the catalog's marketplace is not registered with the harness at all. */\n\tunregistered: boolean\n}\n\nexport type ReconcileOptions = {\n\tcatalog: Catalog\n\t/** Where the catalog lives, relative to the repository root. */\n\tcatalogDir: string\n\t/** Absolute repository root, used to keep another project's installs out of the comparison. */\n\troot: string\n\tinstalled: readonly InstalledPlugin[]\n\tmarketplaces: readonly RegisteredMarketplace[]\n\t/**\n\t * Whether this runtime installs per repository. Where it does not — Codex keeps one install per\n\t * machine — every install under this marketplace is this repository's by default, because there\n\t * is nothing to tell two repositories apart.\n\t */\n\tscoped?: boolean\n}\n\n/**\n * Installs belonging to this repository and this catalog.\n *\n * On a scoped runtime a user-scoped install is deliberately excluded rather than counted: it is\n * shared with every other repository on the machine, so treating it as satisfying this repository's\n * catalog would let one project's pin silently stand in for another's.\n */\nfunction ours(\n\tinstalled: readonly InstalledPlugin[],\n\tmarketplace: string,\n\troot: string,\n\tscoped: boolean,\n): InstalledPlugin[] {\n\tconst suffix = `@${marketplace}`\n\treturn installed.filter((entry) => entry.id.endsWith(suffix) && (!scoped || entry.projectPath === root))\n}\n\nexport function reconcile({\n\tcatalog,\n\tcatalogDir,\n\troot,\n\tinstalled,\n\tmarketplaces,\n\tscoped = true,\n}: ReconcileOptions): Reconciliation {\n\tconst registration = marketplaces.find((entry) => entry.name === catalog.name)\n\tconst actions: Action[] = []\n\n\t// A registration pointing somewhere else is the collision case. Reported rather than repaired:\n\t// re-registering would take the name from whichever repository currently holds it, trading one\n\t// silent breakage for another.\n\tconst expected = catalogDir.split(/[\\\\/]/).filter(Boolean)\n\tconst pointsHere = registration ? expected.every((segment) => registration.path.includes(segment)) : false\n\tconst collision = registration && !pointsHere ? registration.path : undefined\n\n\tif (!registration) {\n\t\tactions.push({\n\t\t\tkind: 'register',\n\t\t\tsubject: catalog.name,\n\t\t\tdetail: 'marketplace is not registered with this harness',\n\t\t})\n\t}\n\n\tconst mine = ours(installed, catalog.name, root, scoped)\n\tconst byId = new Map(mine.map((entry) => [entry.id, entry]))\n\n\tfor (const entry of catalog.plugins) {\n\t\tconst id = `${entry.name}@${catalog.name}`\n\t\tconst current = byId.get(id)\n\t\tif (!current) {\n\t\t\tactions.push({ kind: 'install', subject: id, detail: `not installed; catalog pins ${entry.source.version}` })\n\t\t\tcontinue\n\t\t}\n\t\tif (current.version !== entry.source.version) {\n\t\t\tactions.push({\n\t\t\t\tkind: 'update',\n\t\t\t\tsubject: id,\n\t\t\t\tdetail: `installed ${current.version}, catalog pins ${entry.source.version}`,\n\t\t\t})\n\t\t}\n\t}\n\n\t// Installed but no longer in the catalog: the dependency was removed or stopped shipping a plugin.\n\t// Nothing else removes these — the harness leaves them installed and failing to load.\n\tconst wanted = new Set(catalog.plugins.map((entry) => `${entry.name}@${catalog.name}`))\n\tfor (const entry of mine) {\n\t\tif (!wanted.has(entry.id)) {\n\t\t\tactions.push({\n\t\t\t\tkind: 'uninstall',\n\t\t\t\tsubject: entry.id,\n\t\t\t\tdetail: 'installed but no longer in the catalog; it will report a load failure until removed',\n\t\t\t})\n\t\t}\n\t}\n\n\treturn {\n\t\tactions,\n\t\t...(collision ? { collision } : {}),\n\t\tunregistered: !registration,\n\t}\n}\n","import { homedir } from 'node:os'\nimport { resolve } from 'node:path'\nimport type { cli } from 'clibuilder'\nimport { command, exitCodes, z } from 'clibuilder'\nimport { parseFormat, writeResult } from '../command-output/command-output.ts'\nimport { CATALOG_DIR, CATALOG_PATH, catalogStatus, deriveCatalog, type WriteOutcome, writeCatalog } from './generate.ts'\nimport { COPILOT_LIMITATION, type PluginRuntime, runtimes } from './harness-plugins.ts'\nimport { type Action, reconcile } from './reconcile.ts'\n\nexport type DepPluginsReport = {\n\tcatalog: string\n\tmarketplace: string\n\toutcome: WriteOutcome | 'would-change' | 'current'\n\t/** Always emitted, so a healthy run states its zero rather than leaving a reader to infer it. */\n\tplugins: { name: string; package: string; version: string }[] | string\n\t/** Every runtime detected on this machine, and whether it matches the catalog. */\n\tharness: { runtime: string; status: string }[] | string\n\t/**\n\t * What each runtime needs in order to match the catalog, and the command that does it. Reported\n\t * rather than run: this writes outside the repository, and a generation step should not reach into\n\t * a developer's harness state without being asked.\n\t *\n\t * One flat row per action rather than actions nested under each runtime: a uniform record renders\n\t * as a table in both TOON and text, where a nested array collapses into a JSON blob.\n\t */\n\tactions: { runtime: string; do: string; subject: string; why: string }[] | string\n\tnotes?: string[]\n\thelp?: string[]\n}\n\n/** The command that performs one action, so a reader is not left to look the verb up per runtime. */\nfunction commandFor(runtime: PluginRuntime, action: Action, marketplace: string): string {\n\tswitch (action.kind) {\n\t\tcase 'register':\n\t\t\treturn runtime.register(CATALOG_DIR)\n\t\tcase 'install':\n\t\t\treturn runtime.install(action.subject)\n\t\tcase 'update': {\n\t\t\t// Where a runtime caches the catalog it has to be refreshed first, or the update reinstalls\n\t\t\t// the version already cached rather than the one the catalog now pins. Codex re-reads a local\n\t\t\t// catalog on every add and needs no such step, so it declares no refresh command.\n\t\t\tconst refresh = runtime.refreshMarketplace(marketplace)\n\t\t\tconst update = runtime.update(action.subject)\n\t\t\treturn refresh ? `${refresh} && ${update}` : update\n\t\t}\n\t\tcase 'uninstall':\n\t\t\treturn runtime.uninstall(action.subject)\n\t}\n}\n\n/**\n * Notes name the conditions a caller cannot see from the entry list and would otherwise discover as a\n * failure later — a dependency that is declared but absent, a marketplace name two repositories are\n * likely to share, a resolver under which no dependency has a directory at all.\n */\nfunction notesFor(derived: ReturnType<typeof deriveCatalog>): string[] {\n\tconst notes: string[] = []\n\tif (derived.pnp) {\n\t\tnotes.push(\n\t\t\t'Yarn Plug’n’Play is active: dependencies live inside zip archives, so no harness can install from them. Set nodeLinker: node-modules, or unplug the plugin dependencies.',\n\t\t)\n\t}\n\tif (derived.genericName) {\n\t\tnotes.push(\n\t\t\t`Marketplace name \"${derived.catalog.name}\" is generic. Registering a second marketplace under a name already in use replaces the first silently, and the install that follows reports \"already installed\" — the repository then runs the other one's versions.`,\n\t\t)\n\t}\n\tfor (const { package: pkg } of derived.unresolved) {\n\t\tnotes.push(`Declared dependency \"${pkg}\" is not installed; install dependencies before generating.`)\n\t}\n\treturn notes\n}\n\nexport const depPluginsCommand: cli.Command = command({\n\tname: 'dep-plugins',\n\tdescription:\n\t\t'Derive the marketplace catalog for plugins shipped by this repository’s dependencies, so a harness can install them. Writes the catalog; installing is the harness’s own job.',\n\toptions: {\n\t\troot: {\n\t\t\tdescription: 'Repository directory. Defaults to the current directory.',\n\t\t\ttype: z.optional(z.string()),\n\t\t},\n\t\tcheck: {\n\t\t\tdescription: 'Report whether the catalog is current without writing it. For CI and postinstall.',\n\t\t\ttype: z.optional(z.boolean()),\n\t\t},\n\t\tformat: {\n\t\t\tdescription: 'Output format: toon (default), json, or text for a human-readable report.',\n\t\t\ttype: z.optional(z.string()),\n\t\t\tdefault: 'toon',\n\t\t},\n\t},\n\trun(args) {\n\t\ttry {\n\t\t\tconst format = parseFormat(args.format)\n\t\t\tconst root = args.root ?? process.cwd()\n\t\t\tconst derived = deriveCatalog(root)\n\n\t\t\t// `--check` compares without touching the disk, so it is safe from a postinstall hook and from\n\t\t\t// CI, where the question is only whether the committed catalog still matches the declared\n\t\t\t// dependencies.\n\t\t\tconst outcome = args.check\n\t\t\t\t? catalogStatus(root, derived.catalog) === 'unchanged'\n\t\t\t\t\t? ('current' as const)\n\t\t\t\t\t: ('would-change' as const)\n\t\t\t\t: writeCatalog(root, derived.catalog)\n\n\t\t\t// Read-only, and against each harness's own state files rather than its CLI: asking a runtime\n\t\t\t// what it has should not depend on that runtime being launchable from here. Only runtimes\n\t\t\t// actually set up on this machine are reported, so a report names work a reader can do.\n\t\t\tconst home = homedir()\n\t\t\tconst notes = notesFor(derived)\n\t\t\tconst detected = runtimes.filter((candidate) => candidate.present(home))\n\t\t\tconst harness: { runtime: string; status: string }[] = []\n\t\t\tconst actions: { runtime: string; do: string; subject: string; why: string }[] = []\n\n\t\t\tfor (const runtime of detected) {\n\t\t\t\tconst state = runtime.state(home)\n\t\t\t\tconst delta = reconcile({\n\t\t\t\t\tcatalog: derived.catalog,\n\t\t\t\t\tcatalogDir: CATALOG_DIR,\n\t\t\t\t\troot: resolve(root),\n\t\t\t\t\tinstalled: state.installed,\n\t\t\t\t\tmarketplaces: state.marketplaces,\n\t\t\t\t\tscoped: runtime.scoped,\n\t\t\t\t})\n\t\t\t\tif (delta.collision) {\n\t\t\t\t\tnotes.push(\n\t\t\t\t\t\t`${runtime.name}: marketplace \"${derived.catalog.name}\" is already registered against ${delta.collision}. Registering over it replaces the other repository's silently, and the install that follows reports \"already installed\" — rename this repository's marketplace instead.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tharness.push({\n\t\t\t\t\truntime: runtime.name,\n\t\t\t\t\tstatus: delta.actions.length ? `${delta.actions.length} to apply` : 'in sync',\n\t\t\t\t})\n\t\t\t\tfor (const action of delta.actions) {\n\t\t\t\t\tactions.push({\n\t\t\t\t\t\truntime: runtime.name,\n\t\t\t\t\t\tdo: commandFor(runtime, action, derived.catalog.name),\n\t\t\t\t\t\tsubject: action.subject,\n\t\t\t\t\t\twhy: action.detail,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (derived.plugins.length) notes.push(COPILOT_LIMITATION)\n\n\t\t\tconst report: DepPluginsReport = {\n\t\t\t\tcatalog: CATALOG_PATH,\n\t\t\t\tmarketplace: derived.catalog.name,\n\t\t\t\toutcome,\n\t\t\t\tplugins: derived.plugins.length\n\t\t\t\t\t? derived.plugins.map(({ plugin, package: pkg, version }) => ({ name: plugin, package: pkg, version }))\n\t\t\t\t\t: '0 dependencies ship a plugin — nothing to install',\n\t\t\t\tharness: harness.length ? harness : 'no supported harness detected on this machine',\n\t\t\t\tactions: actions.length ? actions : '0 actions — every detected harness matches the catalog',\n\t\t\t\t...(notes.length ? { notes } : {}),\n\t\t\t}\n\t\t\twriteResult(report, format)\n\n\t\t\t// A stale catalog under `--check` is the one failure this command reports through its exit\n\t\t\t// code, because that is the whole question the flag asks. Everything else the report carries.\n\t\t\treturn outcome === 'would-change' ? exitCodes.error : exitCodes.success\n\t\t} catch (error) {\n\t\t\tprocess.stderr.write(`error: ${error instanceof Error ? error.message : 'Catalog generation failed.'}\\n`)\n\t\t\treturn exitCodes.error\n\t\t}\n\t},\n})\n","import { readdirSync } from 'node:fs'\nimport { join, posix, relative, sep } from 'node:path'\n\n/**\n * Every file below `directory`, as paths relative to it, POSIX-separated and sorted. A directory\n * that does not exist reads as empty: `doctor` never creates one to look at it.\n */\nexport function filesUnder(directory: string): string[] {\n\ttry {\n\t\treturn readdirSync(directory, { recursive: true, withFileTypes: true })\n\t\t\t.filter((entry) => entry.isFile())\n\t\t\t.map((entry) => relative(directory, join(entry.parentPath, entry.name)).split(sep).join(posix.sep))\n\t\t\t.sort()\n\t} catch {\n\t\treturn []\n\t}\n}\n","import { lstatSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { InstructionBridge } from './instruction-bridge.ts'\nimport type { McpConfig } from './mcp-config.ts'\nimport type { NonstandardArtifact } from './nonstandard-artifact.ts'\n\nexport type HarnessName =\n\t| 'claude-code'\n\t| 'cursor'\n\t| 'codex'\n\t| 'copilot-cli'\n\t| 'gemini-cli'\n\t| 'devin-desktop'\n\t| 'windsurf'\n\n/** The two scopes a harness reads configuration at. Their roots differ; their shape does not. */\nexport type HarnessScopeName = 'project' | 'user'\n\n/**\n * What one harness does at one scope. Paths are relative to that scope's root: the repository root\n * at project scope, the user's home directory at user scope.\n *\n * `detect` is the directory whose presence means this harness is configured at this scope — a\n * different question per scope, which is why it is recorded per scope rather than once.\n *\n * `skillsDirectory` is the projection target the harness needs to see `.agents/skills` at this\n * scope. A harness that reads `.agents/skills` natively there has none, and is never projected into.\n *\n * `mcpConfig` is where this harness keeps its MCP servers at this scope. Unlike the other two it\n * is not a bridge into a canonical file the harness cannot read — it is the harness's own\n * configuration, which `doctor` compares against the golden set and never writes.\n *\n * `nonstandard` is what this harness reads that no other harness can. Unlike the three above it is\n * not something the tool maintains — it is what a repository accumulated before it was canonical,\n * and `doctor` reports it so it can be converted. Declared here because the paths are per harness\n * and belong beside the harness's other paths, not in a second list that would drift from this one.\n *\n * `instructionBridge` is the same question for `AGENTS.md`: what the harness needs at this scope in\n * order to read it. It belongs per scope for the same reason `skillsDirectory` does — the file\n * differs. Unlike `skillsDirectory`, the `init` command does not write it; the `init` skill does. It\n * is recorded here to be diagnosed and gated per harness, not to be projected.\n */\nexport type HarnessScope = {\n\tdetect: string\n\tskillsDirectory?: string\n\tinstructionBridge?: InstructionBridge\n\tmcpConfig?: McpConfig\n\tnonstandard?: readonly NonstandardArtifact[]\n}\n\nexport type Harness = {\n\tname: HarnessName\n\t/** What this harness does inside a repository. `init` and `doctor` act only here. */\n\tproject: HarnessScope\n\t/**\n\t * What this harness does for the user, outside any repository. Absent when no user-scope path is\n\t * primary-sourced — Devin documents none. Described and diagnosable; never written to.\n\t */\n\tuser?: HarnessScope\n\t/** Set when this name has been superseded; the value is the name that replaces it. */\n\tdeprecated?: HarnessName\n}\n\n/**\n * The two scopes disagree in general, and a single answer per harness could only record one of them:\n * Copilot CLI is `.github/skills` in a repository and `~/.copilot` for the user. Claude Code is the\n * only harness that needs a skills projection, and it needs one at both scopes. Codex, Cursor,\n * Copilot CLI, Gemini CLI, and Devin Desktop read `.agents/skills` themselves and are never\n * projected into.\n *\n * Gemini CLI carried a `.gemini/skills` projection until E-GEM-02: it reads the `.agents/skills`\n * alias at project scope too, where that alias takes precedence over `.gemini/skills`. It still\n * needs an instruction bridge, which is a separate axis and unaffected.\n *\n * Instruction bridges are recorded at project scope only. The user-scope equivalents exist, but\n * nothing writes or reads them yet: `init` works inside a repository, and so does `doctor`.\n *\n * `windsurf` is the former name of Devin Desktop, rebranded 2026-06-02. It is retained as a\n * deprecated alias: Devin still scans the legacy `.windsurf/skills` path, so its projection keeps\n * working, but new repositories should enable `devin-desktop` and have nothing written for them.\n *\n * See `.research/agentic-configuration-standards/` for the per-harness sources.\n */\nexport const harnessRegistry: readonly Harness[] = [\n\t{\n\t\tname: 'claude-code',\n\t\tproject: {\n\t\t\tdetect: '.claude',\n\t\t\tskillsDirectory: '.claude/skills',\n\t\t\tinstructionBridge: { kind: 'import', path: 'CLAUDE.md' },\n\t\t\tmcpConfig: { path: '.mcp.json', key: 'mcpServers', format: 'json' },\n\t\t\tnonstandard: [\n\t\t\t\t{ path: '.claude/commands', shape: 'directory', kind: 'command' },\n\t\t\t\t{ path: '.claude/rules', shape: 'directory', kind: 'rule' },\n\t\t\t\t{ path: '.claude/agents', shape: 'directory', kind: 'subagent' },\n\t\t\t],\n\t\t},\n\t\tuser: { detect: '.claude', skillsDirectory: '.claude/skills' },\n\t},\n\t{\n\t\tname: 'cursor',\n\t\tproject: {\n\t\t\tdetect: '.cursor',\n\t\t\tmcpConfig: { path: '.cursor/mcp.json', key: 'mcpServers', format: 'json' },\n\t\t\tnonstandard: [\n\t\t\t\t{ path: '.cursorrules', shape: 'file', kind: 'instructions' },\n\t\t\t\t{ path: '.cursor/rules', shape: 'directory', kind: 'rule' },\n\t\t\t\t{ path: '.cursor/commands', shape: 'directory', kind: 'command' },\n\t\t\t\t{ path: '.cursor/skills', shape: 'directory', kind: 'skill' },\n\t\t\t],\n\t\t},\n\t\tuser: { detect: '.cursor' },\n\t},\n\t{\n\t\tname: 'codex',\n\t\tproject: {\n\t\t\tdetect: '.codex',\n\t\t\tmcpConfig: { path: '.codex/config.toml', key: 'mcp_servers', format: 'toml' },\n\t\t\tnonstandard: [{ path: '.codex/skills', shape: 'directory', kind: 'skill' }],\n\t\t},\n\t\tuser: { detect: '.codex' },\n\t},\n\t{\n\t\tname: 'copilot-cli',\n\t\tproject: {\n\t\t\tdetect: '.github/skills',\n\t\t\tnonstandard: [\n\t\t\t\t{ path: '.github/copilot-instructions.md', shape: 'file', kind: 'instructions' },\n\t\t\t\t{ path: '.github/instructions', shape: 'directory', kind: 'instructions' },\n\t\t\t\t{ path: '.github/skills', shape: 'directory', kind: 'skill' },\n\t\t\t],\n\t\t},\n\t\tuser: { detect: '.copilot' },\n\t},\n\t{\n\t\tname: 'gemini-cli',\n\t\tproject: {\n\t\t\tdetect: '.gemini',\n\t\t\tinstructionBridge: { kind: 'settings-entry', path: '.gemini/settings.json', key: 'context.fileName' },\n\t\t\tmcpConfig: { path: '.gemini/settings.json', key: 'mcpServers', format: 'json', shared: true },\n\t\t\tnonstandard: [\n\t\t\t\t{ path: 'GEMINI.md', shape: 'file', kind: 'instructions' },\n\t\t\t\t{ path: '.gemini/skills', shape: 'directory', kind: 'skill' },\n\t\t\t],\n\t\t},\n\t\tuser: { detect: '.gemini' },\n\t},\n\t{ name: 'devin-desktop', project: { detect: '.devin' } },\n\t{\n\t\tname: 'windsurf',\n\t\tproject: {\n\t\t\tdetect: '.windsurf',\n\t\t\tskillsDirectory: '.windsurf/skills',\n\t\t\tnonstandard: [\n\t\t\t\t{ path: '.windsurfrules', shape: 'file', kind: 'instructions' },\n\t\t\t\t{ path: '.windsurf/rules', shape: 'directory', kind: 'rule' },\n\t\t\t],\n\t\t},\n\t\tdeprecated: 'devin-desktop',\n\t},\n]\n\nconst harnessNames = new Set<string>(harnessRegistry.map((harness) => harness.name))\n\nfunction isHarnessName(value: string): value is HarnessName {\n\treturn harnessNames.has(value)\n}\n\n/**\n * The `--harness` option, as names this registry knows. Both commands take the option and neither\n * owns what a name means, so the parse lives beside the list it validates against: a harness added\n * to the registry is accepted by both commands with nothing else to change, and the rejection reads\n * the same either way.\n *\n * An unknown name throws rather than being dropped. A caller that asked for a harness by name and\n * got a report about the two default ones has been told the wrong thing about their repository.\n */\nexport function parseHarnesses(value: string | undefined): HarnessName[] {\n\tconst requested = (value ?? '')\n\t\t.split(',')\n\t\t.map((name) => name.trim())\n\t\t.filter(Boolean)\n\tconst unsupported = requested.filter((name) => !isHarnessName(name))\n\tif (unsupported.length)\n\t\tthrow new Error(\n\t\t\t`Unsupported harness: ${unsupported.join(', ')}. Supported: ${harnessRegistry\n\t\t\t\t.map((harness) => harness.name)\n\t\t\t\t.join(', ')}.`,\n\t\t)\n\treturn requested.filter(isHarnessName)\n}\n\n/** Enabled whether or not the repository already contains their directories. */\nconst defaultHarnesses: readonly HarnessName[] = ['claude-code', 'cursor']\n\nfunction directoryExists(path: string): boolean {\n\ttry {\n\t\treturn lstatSync(path).isDirectory()\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/** The default harnesses, plus the preferred ones, plus every harness whose directory is present. */\nexport function selectHarnesses(root: string, preferred: readonly HarnessName[]): Harness[] {\n\tconst enabled = new Set<HarnessName>([...defaultHarnesses, ...preferred])\n\treturn harnessRegistry.filter(\n\t\t(harness) => enabled.has(harness.name) || directoryExists(join(root, harness.project.detect)),\n\t)\n}\n","/**\n * Where a finding is, in parts.\n *\n * A locator addresses a place inside a file — `.cursor/mcp.json#servers.linear.command` — because a\n * file holding twenty servers is not an address. The parts are known wherever a finding is raised,\n * and only the report and the repairs need the string, so the parts are what travel and this module\n * is the one place they become one. Nothing ever recovers a part by splitting a locator back apart:\n * a server may be named `io.github.foo`, and no split of the rendered string tells where the server\n * name ends and the field begins.\n */\n\n/**\n * Where a malformed file failed. **Position only.** A parser's own error message quotes the\n * offending line back, and in a file of MCP configuration that line is exactly the one holding a\n * credential — so neither the message nor the code block it carries is ever read. Line and column\n * are enough to fix the file and carry nothing out of it.\n */\nexport type Position = { line: number; column: number }\n\nexport type Locator = {\n\t/** Repository-relative path of the file the finding is about. */\n\tfile: string\n\t/** The MCP server inside that file, when the finding is about one. */\n\tserver?: string\n\t/** The field inside that server, dotted for the per-name fields — `command`, `env.LINEAR_TOKEN`. */\n\tfield?: string\n\t/** Where a file that does not parse failed, when the parser gave a position. */\n\tposition?: Position | undefined\n}\n\n/** The locator as one string: the finding's `path`, and what a repair names. */\nexport function locatorText({ file, server, field, position }: Locator): string {\n\tif (position) return `${file}#L${position.line}:${position.column}`\n\tif (server === undefined) return file\n\treturn `${file}#servers.${server}${field === undefined ? '' : `.${field}`}`\n}\n","import type { Harness, HarnessScope } from '../harness-registry/harness-registry.ts'\nimport { harnessRegistry } from '../harness-registry/harness-registry.ts'\nimport type { NonstandardKind } from '../harness-registry/nonstandard-artifact.ts'\nimport { type Locator, locatorText } from './locator.ts'\n\n/**\n * The one place `doctor`'s guidance is written. The command reads `detail` and `repair` to fill its\n * `findings` and `help` sections; `skills/doctor/SKILL.md` is generated from the same table by\n * `scripts/generate-skills.ts`, so the shipped skill cannot drift from what the command says.\n */\n\n/** Every way a skills bridge can fail, in the order `doctor` reports them. */\nexport type BridgeProblem =\n\t| 'no-canonical'\n\t| 'missing'\n\t| 'degraded'\n\t| 'stale'\n\t| 'diverged-bridge'\n\t| 'diverged-canonical'\n\t| 'diverged-both'\n\t| 'diverged-unknown'\n\t| 'unpinned-copy'\n\n/**\n * Every way an instruction bridge can fail. Separate from `BridgeProblem` because the two share no\n * repair: a skills bridge is rebuilt with `init` flags, and an instruction bridge is a file whose\n * content is the user's, so every repair here goes back to the `init` skill.\n */\n/**\n * Configuration that is present and **wrong**, as against a bridge that does not resolve. These are\n * the faults the `repair` skill owns: none is expressible as an `init` flag, because `init`\n * consolidates and creates but never corrects a file the user already wrote.\n */\nexport type ConfigurationFault = 'deprecated-harness' | 'ignored-bridge' | 'unread-local-override' | 'unloadable-skill'\n\n/**\n * Every way the golden MCP server set and a harness's copy of it can disagree, plus the two\n * credential findings.\n *\n * A family of its own because nothing it says is shared with the others. A bridge either resolves\n * or does not; two MCP files never share a byte, so every finding here is the result of a semantic\n * comparison, and each one names a **server** and usually a **field** rather than a file.\n */\nexport type McpProblem =\n\t| 'mcp-golden-unreadable'\n\t| 'mcp-target-unreadable'\n\t| 'mcp-unprojected'\n\t| 'mcp-undeclared'\n\t| 'mcp-diverged-target'\n\t| 'mcp-diverged-golden'\n\t| 'mcp-diverged-both'\n\t| 'mcp-diverged-unknown'\n\t| 'mcp-literal-secret'\n\t| 'mcp-committed-secret'\n\n/**\n * Configuration that works, for exactly one harness. Not wrong and not missing — the two states the\n * other families cover — which is why it is a family of its own: every other finding says something\n * is broken, and these say something reaches less far than the repository meant.\n *\n * Each one names the canonical form it converts to, because surfacing without a destination is just\n * a list of files somebody already knows about.\n */\nexport type NonstandardProblem =\n\t| 'nonstandard-instructions'\n\t| 'nonstandard-rule'\n\t| 'nonstandard-command'\n\t| 'nonstandard-skill'\n\t| 'nonstandard-subagent'\n\nexport type InstructionProblem =\n\t| 'no-instructions'\n\t| 'instructions-missing'\n\t| 'instructions-unbridged'\n\t| 'instructions-unreadable'\n\n/** Everything `doctor` can report against, across every section. */\nexport type DoctorProblem = BridgeProblem | InstructionProblem | ConfigurationFault | McpProblem | NonstandardProblem\n\n/** Alias kept for callers that name the whole set rather than one section. */\nexport type ConfigurationProblem = DoctorProblem\n\n/**\n * What resolves one finding, for `doctor`'s own output. Two fields rather than one string, because\n * the caller `doctor` is written for is an agent parsing TOON, and the question it has to answer is\n * \"can I execute this, or is this judgment I hand to a skill?\" A single string leaves that\n * answerable only by parsing English.\n *\n * The contract is exact, and it is what makes the field safe to act on blindly:\n *\n * - `command` non-empty — a shell invocation that, run verbatim, **completes** the repair.\n * - `command` empty — no single invocation does; act on `instruction` and do not synthesize one.\n *\n * So a diagnostic worth running is not a `command`. `diverged-both` carries none even though\n * `git diff --no-index …` is perfectly runnable: the diff shows what differs, it does not reconcile\n * anything. That is what stops an agent executing every `command` it is handed from rebuilding a\n * diverged bridge over whichever side holds the newer edit. A skill invocation is not a `command`\n * either — nothing in a shell runs `/buddy-agent-harness:init`.\n */\nexport type RepairAction = {\n\t/** A shell invocation that completes the repair, or empty when none does. */\n\tcommand: string\n\t/** The imperative, in prose, always present and complete on its own. */\n\tinstruction: string\n}\n\nexport type Repair = {\n\tproblem: DoctorProblem\n\t/** What `doctor` prints in the `findings` row for this problem. */\n\tdetail: string\n\t/**\n\t * What fixes it, for `doctor`'s own output. `at` is where the finding is, in parts — the file\n\t * alone for a bridge, the file, server, and field for an MCP finding; `cli` is how to invoke this\n\t * tool. A repair that names only part of a locator reads it off `at` rather than splitting the\n\t * rendered string, which no separator survives: a server may be named `io.github.foo`.\n\t */\n\trepair(at: Locator, cli: string): RepairAction\n\t/**\n\t * What the shipped skill tells an agent to do instead. A repair that rebuilds a bridge delegates\n\t * to the `init` skill rather than calling the `init` command, because rebuilding can move\n\t * user-authored skills and that judgment is the `init` skill's, not `doctor`'s.\n\t */\n\tskillRepair(at: Locator): string\n}\n\n/**\n * One row of a repair table. The problem is the key it is filed under rather than a field on it, so\n * a table typed `Record<…Problem, RepairRow>` cannot be written with a row missing: adding a\n * variant to one of the unions above fails to compile until its row exists. That is the only place\n * the invariant can be held — a union is not enumerable at runtime, so no test can walk it, and a\n * lookup that asserts its own completeness reads `.detail` off `undefined` the first time it is\n * wrong.\n */\nexport type RepairRow = Omit<Repair, 'problem'>\n\n/**\n * A table as the list `doctor` reports, in the order it was written. The keys of a\n * `Record<P, RepairRow>` are `P` by construction, which is what the assertion says; nothing here\n * claims a row exists.\n */\nfunction repairsOf<Problem extends DoctorProblem>(table: Record<Problem, RepairRow>): readonly Repair[] {\n\treturn (Object.entries(table) as [Problem, RepairRow][]).map(([problem, row]) => ({ problem, ...row }))\n}\n\n/** How the command names itself. */\nexport const commandInvocation = 'buddy-agent-harness'\n/**\n * How a skill invokes the command. A skill may run without the binary on PATH, so it goes through\n * `npx`, pinned to the caret range of the version that generated the skill.\n *\n * The pin is what makes the shipped guidance honest. A skill states the findings and flags of the\n * version it was generated from, so an unpinned `npx` — which resolves whatever is latest — can\n * hand an agent a table that does not describe the CLI it just ran. The caret keeps patches\n * flowing and stops at the next breaking line.\n */\nexport const skillInvocation = (version: string) => `npx -y ${commandInvocation}@^${version}`\n\n/**\n * The launcher a skill ships, named for the subcommand it runs so a stack trace or a process list\n * says which one it was. One script per command, beside the `SKILL.md` that documents it.\n */\nexport const launcherFor = (subcommand: string) => `scripts/${subcommand}.mjs`\n\n/**\n * How a skill names its launcher: the path as the skill sees it, resolved by the agent against the\n * directory it read the `SKILL.md` from.\n *\n * `node` stays in front. The launcher ships without an executable bit, and its shebang does nothing\n * on Windows, so naming the file alone would not run it.\n */\nexport const launcherInvocation = (subcommand: string) => `node ${launcherFor(subcommand)}`\n\n/**\n * The launcher written into a skill's `scripts/` directory. It resolves the CLI from its own\n * location rather than the working directory, so the skill runs the copy it shipped with and\n * fetches nothing. The repository it inspects is still the working directory, unchanged.\n */\nexport function renderSkillLauncher(subcommand: string): string {\n\treturn `#!/usr/bin/env node\n// Generated from src/diagnose-bridges/doctor-guidance.ts by scripts/generate-skills.ts. Do not edit by hand.\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\n\n// <package>/skills/<skill>/scripts/${subcommand}.mjs: four levels up is the package root.\nconst packageRoot = dirname(dirname(dirname(dirname(fileURLToPath(import.meta.url)))))\nconst { run } = await import(pathToFileURL(join(packageRoot, 'dist', 'cli.mjs')).href)\n\n// The subcommand is composed into a fresh argv rather than spliced into the global one, so nothing\n// outside this file observes the rewrite. Applied only when non-zero, so a usage code clibuilder\n// recorded itself is not overwritten by the zero \\`run\\` returns on that path.\nconst code = await run([...process.argv.slice(0, 2), '${subcommand}', ...process.argv.slice(2)])\nif (code !== 0) process.exitCode = code\n`\n}\n\n/**\n * How the skill hands a repair to `repair`. Bridge and instruction repairs go to `init`, which\n * writes both kinds of bridge in the first place; configuration that is present and wrong goes\n * here, because no `init` flag corrects a file the user already wrote.\n */\nexport const repairSkillInvocation = '/buddy-agent-harness:repair'\n\n/** How the skill hands a repair back to `init`. */\nexport const initSkillInvocation = '/buddy-agent-harness:init'\n\n/** Where the user authors the golden MCP server set, named in every MCP repair that points at it. */\nconst goldenSet = '.agents/buddy-agent-harness/mcp.toml'\n\n/** The skills bridges, reported in `bridges`. */\nconst bridgeTable: Record<BridgeProblem, RepairRow> = {\n\t'no-canonical': {\n\t\tdetail: 'the canonical skill directory does not exist, so no bridge can resolve',\n\t\trepair: (_at, cli) => ({\n\t\t\tcommand: `${cli} init`,\n\t\t\tinstruction: `run \\`${cli} init\\` to create .agents/skills and the bridges into it`,\n\t\t}),\n\t\tskillRepair: () => `run \\`${initSkillInvocation}\\`, which creates \\`.agents/skills\\` and the bridges`,\n\t},\n\tmissing: {\n\t\tdetail: 'no bridge at this path — the harness sees zero project skills',\n\t\trepair: ({ file }, cli) => ({\n\t\t\tcommand: `${cli} init`,\n\t\t\tinstruction: `run \\`${cli} init\\` to create the bridge at ${file}`,\n\t\t}),\n\t\tskillRepair: () => `run \\`${initSkillInvocation}\\``,\n\t},\n\tdegraded: {\n\t\tdetail: 'expected a directory but found a regular file — checkout without core.symlinks',\n\t\trepair: ({ file }, cli) => ({\n\t\t\tcommand: `${cli} init --copy --force ${file}`,\n\t\t\tinstruction: `run \\`${cli} init --copy --force ${file}\\` to rebuild ${file} as a real directory`,\n\t\t}),\n\t\tskillRepair: ({ file }) => `run \\`${initSkillInvocation} --copy --force ${file}\\``,\n\t},\n\tstale: {\n\t\tdetail: 'symlink does not resolve to .agents/skills',\n\t\trepair: ({ file }, cli) => ({\n\t\t\tcommand: `${cli} init --force ${file}`,\n\t\t\tinstruction: `run \\`${cli} init --force ${file}\\` to repoint ${file} at .agents/skills`,\n\t\t}),\n\t\tskillRepair: ({ file }) => `run \\`${initSkillInvocation} --force ${file}\\``,\n\t},\n\t'diverged-bridge': {\n\t\tdetail: 'only the bridge changed since the two last agreed — an agent wrote through the copy',\n\t\t// No command, unlike `diverged-canonical`, and the asymmetry is real: `init` only ever builds a\n\t\t// bridge *from* the canonical directory, so no flag promotes the bridge's newer content back\n\t\t// into it. Deciding to keep that side is the caller's, and the rebuild is what follows.\n\t\trepair: ({ file }, cli) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `replace .agents/skills with ${file} to keep the newer edit, then run \\`${cli} init --force ${file}\\``,\n\t\t}),\n\t\tskillRepair: ({ file }) =>\n\t\t\t`replace .agents/skills with ${file} to keep the newer edit, then run \\`${initSkillInvocation} --force ${file}\\``,\n\t},\n\t'diverged-canonical': {\n\t\tdetail: 'only .agents/skills changed since the two last agreed — the copy is stale',\n\t\trepair: ({ file }, cli) => ({\n\t\t\tcommand: `${cli} init --copy --force ${file}`,\n\t\t\tinstruction: `run \\`${cli} init --copy --force ${file}\\` to rebuild ${file} from the newer .agents/skills`,\n\t\t}),\n\t\tskillRepair: ({ file }) => `run \\`${initSkillInvocation} --copy --force ${file}\\``,\n\t},\n\t'diverged-both': {\n\t\tdetail: 'both sides changed since they last agreed — rebuilding would discard one of them',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `reconcile .agents/skills with ${file} by hand — rebuilding would discard one of them; \\`git diff --no-index .agents/skills ${file}\\` shows what differs`,\n\t\t}),\n\t\tskillRepair: ({ file }) => `run \\`git diff --no-index .agents/skills ${file}\\` and reconcile by hand`,\n\t},\n\t'diverged-unknown': {\n\t\tdetail: 'contents differ and no commit where they agreed was found — which side moved is unknown',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `reconcile .agents/skills with ${file} by hand — which side moved is unknown; \\`git diff --no-index .agents/skills ${file}\\` shows what differs`,\n\t\t}),\n\t\tskillRepair: ({ file }) => `run \\`git diff --no-index .agents/skills ${file}\\` and reconcile by hand`,\n\t},\n\t'unpinned-copy': {\n\t\tdetail: 'tracked copy without the skip-worktree bit — the tree is dirty with content that must not be committed',\n\t\t// The index entry is the tracked symlink on a Windows checkout but the individual files in a\n\t\t// committed copy, so the paths are read back from git rather than assumed.\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: `git ls-files -z ${file} | xargs -0 git update-index --skip-worktree`,\n\t\t\tinstruction: `run \\`git ls-files -z ${file} | xargs -0 git update-index --skip-worktree\\` to restore the skip-worktree bit`,\n\t\t}),\n\t\tskillRepair: ({ file }) => `run \\`git ls-files -z ${file} | xargs -0 git update-index --skip-worktree\\``,\n\t},\n}\n\nexport const bridgeRepairs: readonly Repair[] = repairsOf(bridgeTable)\n\n/**\n * The instruction bridges, reported in `instructions`. Every repair is the `init` skill: these are\n * files a person wrote, or files carrying content beside the bridge, and deciding what to preserve\n * while restoring the bridge is judgment no flag carries. `repair` therefore names the skill in\n * both places rather than pretending a shell command exists.\n */\nconst instructionTable: Record<InstructionProblem, RepairRow> = {\n\t'no-instructions': {\n\t\tdetail: 'no AGENTS.md at the repository root, so every instruction bridge points at nothing',\n\t\trepair: () => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `hand this to \\`${initSkillInvocation}\\`, which derives AGENTS.md and the bridges to it`,\n\t\t}),\n\t\tskillRepair: () => `run \\`${initSkillInvocation}\\`, which derives AGENTS.md and the bridges to it`,\n\t},\n\t'instructions-missing': {\n\t\tdetail: 'no instruction bridge at this path — the harness reads none of AGENTS.md',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `hand ${file} to \\`${initSkillInvocation}\\`, which writes the bridge into it`,\n\t\t}),\n\t\tskillRepair: () => `run \\`${initSkillInvocation}\\``,\n\t},\n\t'instructions-unbridged': {\n\t\tdetail: 'the file is present but names AGENTS.md nowhere — the harness reads none of it',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `hand ${file} to \\`${initSkillInvocation}\\`, which adds the bridge without discarding what the file already says`,\n\t\t}),\n\t\tskillRepair: () =>\n\t\t\t`run \\`${initSkillInvocation}\\`, which adds the bridge without discarding what the file already says`,\n\t},\n\t'instructions-unreadable': {\n\t\tdetail: 'the settings file does not parse, so the harness reads none of it',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `fix the JSON in ${file} by hand, then hand it to \\`${initSkillInvocation}\\``,\n\t\t}),\n\t\tskillRepair: () => `fix the JSON by hand, then run \\`${initSkillInvocation}\\``,\n\t},\n}\n\nexport const instructionRepairs: readonly Repair[] = repairsOf(instructionTable)\n\n/**\n * Configuration that is present and wrong. Detected here like everything else, but repaired by the\n * `repair` skill rather than by `init` — which is why it is a section of its own.\n */\nconst configurationTable: Record<ConfigurationFault, RepairRow> = {\n\t'deprecated-harness': {\n\t\tdetail:\n\t\t\t'a projection under a harness name that has been superseded — the replacement reads .agents/skills natively and needs no projection at all',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `remove ${file} and enable the harness that replaced it — \\`${repairSkillInvocation}\\` offers the correction`,\n\t\t}),\n\t\tskillRepair: () => `run \\`${repairSkillInvocation}\\``,\n\t},\n\t'ignored-bridge': {\n\t\tdetail: 'a .gitignore rule matches this bridge — an untracked bridge swallows a real edit silently',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `narrow or remove the .gitignore rule matching ${file} — \\`${repairSkillInvocation}\\` offers the correction`,\n\t\t}),\n\t\tskillRepair: () => `run \\`${repairSkillInvocation}\\``,\n\t},\n\t'unread-local-override': {\n\t\tdetail: 'no harness reads this filename, so everything in it is invisible to every agent',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `move ${file} to CLAUDE.local.md, or consolidate it into AGENTS.md — \\`${repairSkillInvocation}\\` offers the correction`,\n\t\t}),\n\t\tskillRepair: () => `run \\`${repairSkillInvocation}\\``,\n\t},\n\t'unloadable-skill': {\n\t\tdetail: 'frontmatter that does not parse, or no description — either one makes a harness skip the skill outright',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `quote the description in ${file}, or add one — \\`${repairSkillInvocation}\\` offers the correction`,\n\t\t}),\n\t\tskillRepair: () => `run \\`${repairSkillInvocation}\\``,\n\t},\n}\n\nconst configurationRepairs: readonly Repair[] = repairsOf(configurationTable)\n\n/**\n * The golden MCP server set against the harness copies of it.\n *\n * Every repair names a **locator** rather than a file — `.cursor/mcp.json#servers.linear.command` —\n * because a file holding twenty servers is not an address. The locator never carries a value: not\n * a whole one, and not a prefix. `doctor` is safe to run from a session-start hook, so a value it\n * echoes lands in agent context on every session and from there into transcripts, and `sk-ab…`\n * leaks into exactly the same place the whole string would.\n */\nconst mcpTable: Record<McpProblem, RepairRow> = {\n\t'mcp-golden-unreadable': {\n\t\tdetail:\n\t\t\t'the golden MCP set does not parse — the locator gives the line and column, and nothing else can be said about it',\n\t\trepair: (at) => ({ command: '', instruction: `fix the TOML at ${locatorText(at)}` }),\n\t\tskillRepair: (at) =>\n\t\t\t`fix the TOML at ${locatorText(at)} by hand — the reported line and column are all that can be quoted, because the parser's own message repeats the offending line and that line is the one holding the credential`,\n\t},\n\t'mcp-target-unreadable': {\n\t\tdetail:\n\t\t\t'this harness config does not parse, so the harness starts none of its servers and nothing in it can be compared',\n\t\trepair: (at) => ({ command: '', instruction: `fix the syntax of ${locatorText(at)}` }),\n\t\tskillRepair: (at) => `fix the syntax of ${locatorText(at)} by hand`,\n\t},\n\t'mcp-unprojected': {\n\t\tdetail: 'the golden set declares this server and the harness config does not carry it',\n\t\trepair: ({ file, server }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `add the server ${server} to ${file}, or drop it from the golden set`,\n\t\t}),\n\t\tskillRepair: (at) =>\n\t\t\t`add the server at ${locatorText(at)} to that file from its golden entry, or drop it from the golden set`,\n\t},\n\t'mcp-undeclared': {\n\t\tdetail: 'the harness config carries this server and the golden set does not declare it',\n\t\trepair: ({ file, server }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `add the server ${server} to ${goldenSet}, or drop it from ${file}`,\n\t\t}),\n\t\tskillRepair: (at) =>\n\t\t\t`copy the server at ${locatorText(at)} into ${goldenSet}, refusing any literal credential it carries, or drop it from ${at.file}`,\n\t},\n\t'mcp-diverged-target': {\n\t\tdetail: 'only the harness config changed since the two last agreed — the edit was made through the copy',\n\t\trepair: (at) => ({ command: '', instruction: `reconcile the value at ${locatorText(at)} back into ${goldenSet}` }),\n\t\tskillRepair: (at) => `reconcile the value at ${locatorText(at)} back into ${goldenSet}, field by field`,\n\t},\n\t'mcp-diverged-golden': {\n\t\tdetail: 'only the golden set changed since the two last agreed — the harness copy is stale',\n\t\trepair: (at) => ({ command: '', instruction: `update ${locatorText(at)} from the golden entry` }),\n\t\tskillRepair: (at) => `update ${locatorText(at)} from the golden entry`,\n\t},\n\t'mcp-diverged-both': {\n\t\tdetail: 'both sides changed since they last agreed — merging either way would discard the other',\n\t\trepair: (at) => ({ command: '', instruction: `reconcile ${locatorText(at)} against ${goldenSet} by hand` }),\n\t\tskillRepair: (at) =>\n\t\t\t`reconcile ${locatorText(at)} against ${goldenSet} by hand — never merge a three-way conflict automatically`,\n\t},\n\t'mcp-diverged-unknown': {\n\t\tdetail:\n\t\t\t'the two disagree and no baseline says which side moved — neither history nor a last-projected record covers this server',\n\t\trepair: (at) => ({ command: '', instruction: `compare ${locatorText(at)} against ${goldenSet} by hand` }),\n\t\tskillRepair: (at) => `compare ${locatorText(at)} against ${goldenSet} by hand`,\n\t},\n\t'mcp-literal-secret': {\n\t\tdetail: 'a credential-bearing field holds a literal rather than a reference to an environment variable',\n\t\trepair: (at) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `move the value at ${locatorText(at)} into an environment variable and reference it`,\n\t\t}),\n\t\tskillRepair: (at) =>\n\t\t\t`move the value at ${locatorText(at)} into an environment variable and reference it — read the value from the file, never from this report, and never repeat it back`,\n\t},\n\t'mcp-committed-secret': {\n\t\tdetail:\n\t\t\t'a credential-bearing field holds a literal in a git-tracked file — the credential is committed, and moving it does not un-commit it',\n\t\trepair: (at) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `rotate the credential behind ${locatorText(at)}, then reference it from an environment variable`,\n\t\t}),\n\t\tskillRepair: (at) =>\n\t\t\t`rotate the credential behind ${locatorText(at)} at its issuer, then reference it from an environment variable — it is in the repository's history, so moving it is not enough`,\n\t},\n}\n\nconst mcpRepairs: readonly Repair[] = repairsOf(mcpTable)\n\n/**\n * Configuration only one harness can read. Every repair here is a **conversion**, and every one of\n * them is the `init` skill's: consolidating what a repository already has is exactly its remit, and\n * the content is the user's, so it is offered and never written on sight. `repair` owns none of\n * them — it corrects configuration that is wrong, and none of this is wrong.\n *\n * `nonstandard-subagent` is the exception that names no owner. There is no cross-harness subagent\n * format to convert to, so the honest report is that the gap exists, not a repair that cannot run.\n */\nconst nonstandardTable: Record<NonstandardProblem, RepairRow> = {\n\t'nonstandard-instructions': {\n\t\tdetail: 'instruction content only one harness reads — AGENTS.md carries the same prose to all of them',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `consolidate ${file} into AGENTS.md and leave a generated bridge in its place — \\`${initSkillInvocation}\\` offers the consolidation`,\n\t\t}),\n\t\tskillRepair: ({ file }) => `hand ${file} to \\`${initSkillInvocation}\\`, which consolidates it into AGENTS.md`,\n\t},\n\t'nonstandard-rule': {\n\t\tdetail: 'a path-scoped rule only one harness reads — a skill reaches every harness where the scoping is incidental',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `convert ${file} into a skill under .agents/skills where its guidance is not tied to the paths it names — \\`${initSkillInvocation}\\` offers the conversion; keep it where the path scoping is the point`,\n\t\t}),\n\t\tskillRepair: ({ file }) =>\n\t\t\t`hand ${file} to \\`${initSkillInvocation}\\` to convert into a skill, unless the path scoping is load-bearing`,\n\t},\n\t'nonstandard-command': {\n\t\tdetail: 'a harness command file — a skill is the portable form, and this harness reads skills too',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `move ${file} to .agents/skills/<name>/SKILL.md — \\`${initSkillInvocation}\\` offers the move`,\n\t\t}),\n\t\tskillRepair: ({ file }) => `hand ${file} to \\`${initSkillInvocation}\\`, which moves it to .agents/skills`,\n\t},\n\t'nonstandard-skill': {\n\t\tdetail: 'a skill under a harness directory rather than the canonical one, where only that harness finds it',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `move ${file} to .agents/skills and project it back where the harness needs one — \\`${initSkillInvocation}\\` offers the move`,\n\t\t}),\n\t\tskillRepair: ({ file }) => `hand ${file} to \\`${initSkillInvocation}\\`, which moves it to .agents/skills`,\n\t},\n\t'nonstandard-subagent': {\n\t\tdetail:\n\t\t\t'a subagent definition with no cross-harness form — nothing outside this harness can read it, and no canonical form exists yet',\n\t\trepair: ({ file }) => ({\n\t\t\tcommand: '',\n\t\t\tinstruction: `leave ${file} where it is and record that it reaches one harness only — no portable form exists to convert it to`,\n\t\t}),\n\t\tskillRepair: ({ file }) =>\n\t\t\t`leave ${file} in place — no portable form exists yet, so this is work for a person rather than a skill`,\n\t},\n}\n\nexport const nonstandardRepairs: readonly Repair[] = repairsOf(nonstandardTable)\n\n/**\n * All five sections, in the order `doctor` reports them. Annotated over the whole union rather than\n * inferred, so a problem added to any of the four fails here too if its section was left alone.\n */\nconst doctorTable: Record<DoctorProblem, RepairRow> = {\n\t...bridgeTable,\n\t...instructionTable,\n\t...configurationTable,\n\t...mcpTable,\n\t...nonstandardTable,\n}\n\nexport const doctorRepairs: readonly Repair[] = repairsOf(doctorTable)\n\n/** The repair for one problem. Total by construction: the table is keyed by the union. */\nexport function repairFor(problem: DoctorProblem): Repair {\n\treturn { problem, ...doctorTable[problem] }\n}\n\nexport const doctorSkill = {\n\tname: 'doctor',\n\tdescription:\n\t\t'Use this skill when a repository loads no project skills, when skills are missing after a clone, when a harness appears to be ignoring AGENTS.md, or when checking whether the agent configuration bridges into .claude/skills, CLAUDE.md, and the other harness files still resolve.',\n} as const\n\n/** Written into the generated skill so a reader knows not to edit it in place. */\nexport const generatedSkillWarning =\n\t'<!-- Generated from src/diagnose-bridges/doctor-guidance.ts by scripts/generate-skills.ts. Do not edit by hand. -->'\n\n/**\n * A file the generator writes under `skills/doctor/`, path relative to that directory. The skill is\n * split the way `init`'s is: a lean `SKILL.md` an agent always reads, and reference pages it loads\n * only for the family it is acting on. One flat file made every reader of one finding pay for the\n * prose behind all five.\n */\nexport type GeneratedDoc = { path: string; content: string }\n\n/** A repair table, rendered for the skill's reader. */\nfunction repairTable(repairs: readonly Repair[]): string {\n\t// A repair may itself contain a pipe, which would otherwise end the table cell early.\n\tconst cell = (value: string) => value.replaceAll('|', '\\\\|')\n\treturn `| Finding | What it means | Repair |\n| --- | --- | --- |\n${repairs\n\t.map((entry) => `| \\`${entry.problem}\\` | ${entry.detail} | ${cell(entry.skillRepair({ file: '<path>' }))} |`)\n\t.join('\\n')}`\n}\n\n/**\n * The shipped `doctor` skill, rendered from the same table the command prints. `version` is the\n * package version the skill ships with; it pins the `npx` invocation so the table and the CLI that\n * produced it stay on the same breaking line.\n *\n * The finding tables are NOT here. They live in the reference pages below, and this file carries\n * only what every reader needs: how to run it, how to read the report, and where to go next.\n */\nexport function renderDoctorSkill(version: string): string {\n\treturn `---\nname: ${doctorSkill.name}\ndescription: ${doctorSkill.description}\n---\n\n${generatedSkillWarning}\n\n# Harness Doctor\n\nA repository keeps one canonical configuration: \\`.agents/skills\\` for its skills and \\`AGENTS.md\\` for its instructions. Harnesses that cannot read those get bridges pointing at them: Claude Code needs both, and Gemini CLI needs the instruction bridge only — it reads \\`.agents/skills\\` itself. A bridge that stops resolving is silent: the harness finds nothing and loads zero project skills, with no warning anywhere. An instruction bridge fails the same way and costs more, because the harness then reads none of the repository's instructions at all.\n\nDiagnose it:\n\n\\`\\`\\`sh\n${launcherInvocation('doctor')}\n\\`\\`\\`\n\nThat path is relative to this skill's own directory. The launcher runs the CLI that shipped beside it against the current working directory, so nothing is downloaded. Fall back to \\`${skillInvocation(version)} doctor\\` when the launcher cannot be resolved or run, which is the case when the plugin was installed from git rather than npm and its dependencies were never installed.\n\nThe command is read-only. It never repairs anything, so it is safe to run at any point, including from a session-start hook.\n\n## Reading the report\n\n\\`bridges\\` lists every skills bridge \\`init\\` would create for this repository, each with a \\`status\\` of \\`ok\\`, \\`missing\\`, \\`degraded\\`, \\`stale\\`, or \\`diverged\\`.\n\n\\`instructions\\` lists every instruction bridge into \\`AGENTS.md\\`, with a \\`status\\` of \\`ok\\`, \\`missing\\`, \\`unbridged\\`, or \\`unreadable\\`. They are a separate section because nothing about them is shared: a different \\`kind\\`, a different status vocabulary, and a repair that is never a command.\n\n\\`findings\\` explains each problem and carries more than the two sections above: the configuration, MCP, and non-standard findings have no section of their own, because they are about files rather than about bridges. \\`help\\` carries each repair, one row per distinct repair, with two columns:\n\n- \\`command\\` — a shell invocation that, run exactly as given, **completes** the repair.\n- \\`instruction\\` — the same repair in the imperative, always present and complete on its own.\n\n\\`command\\` is empty whenever no single invocation does the job, and that emptiness is the signal: act on \\`instruction\\` and do not assemble a command out of it. A runnable invocation quoted *inside* an \\`instruction\\` is not the repair either — \\`diverged-both\\` names \\`git diff --no-index\\` because the diff shows you what differs, not because running it reconciles anything. Apply the repair, then re-run \\`doctor\\`.\n\nNothing in \\`help\\` is wrapped. An earlier version prefixed every repair with \\`Run\\`, which read as an instruction to paste prose into a shell.\n\nDo not run an \\`init\\` command yourself. Rebuilding a skills bridge can move skills a user wrote, and rewriting an instruction file touches prose a person authored — both are the \\`init\\` skill's judgment, so hand the repair to \\`${initSkillInvocation}\\` instead. Every such repair carries an empty \\`command\\`: a skill invocation has no shell equivalent at all.\n\nWhen every bridge resolves, \\`findings\\` says so outright rather than being empty.\n\nThe default output is TOON, which is what you parse. Add \\`--format text\\` when you need to show the same report to a person, or \\`--format json\\`.\n\n## Where the detail is\n\nEvery \\`problem\\` name routes to exactly one page. Load the page for the finding in front of you and leave the rest unread — the tables are long, and reading four families to act on one is what this split exists to stop.\n\n| Page | Load it for |\n| --- | --- |\n| \\`references/bridges.md\\` | any \\`bridges\\` row that is not \\`ok\\` |\n| \\`references/instructions.md\\` | any \\`instructions\\` row that is not \\`ok\\` |\n| \\`references/configuration.md\\` | a finding about the configuration around the bridges rather than a bridge |\n| \\`references/mcp.md\\` | any finding whose path is an MCP locator — **always** before acting on a credential finding |\n| \\`references/nonstandard.md\\` | a finding about configuration only one harness can read |\n| \\`references/harnesses/<name>.md\\` | the paths and files one named harness uses |\n\n## Rules\n\n- Never repair a \\`diverged-both\\` or \\`diverged-unknown\\` bridge by re-running \\`init\\`. Rebuilding overwrites whichever side holds the newer edit. Reconcile the two directories first.\n- Edit skills at \\`.agents/skills/<name>/SKILL.md\\`. Editing through a bridge is only safe when that bridge is a symlink.\n- Do not add bridges to \\`.gitignore\\`. An untracked bridge swallows a real edit silently.\n- Never repeat a value from a file an \\`mcp-literal-secret\\` or \\`mcp-committed-secret\\` finding points at. The report withheld it on purpose, and quoting it back puts it in the transcript anyway.\n- Write instructions in \\`AGENTS.md\\`, never in \\`CLAUDE.md\\`. A bridge file holds the import and any harness-specific notes; content written there reaches one harness and drifts from the canonical file.\n`\n}\n\n/** Where each kind of harness-exclusive artifact is meant to end up, for the per-harness tables. */\nfunction conversionOf(kind: NonstandardKind): string {\n\tswitch (kind) {\n\t\tcase 'instructions':\n\t\t\treturn '`AGENTS.md`, with a generated bridge left behind'\n\t\tcase 'rule':\n\t\t\treturn 'a skill, where the path scoping is incidental'\n\t\tcase 'command':\n\t\t\treturn '`.agents/skills/<name>/SKILL.md`'\n\t\tcase 'skill':\n\t\t\treturn '`.agents/skills`, projected back if needed'\n\t\tcase 'subagent':\n\t\t\treturn 'nothing yet — no cross-harness format exists'\n\t}\n}\n\n/** How one harness's scope reads, for its generated reference page. */\nfunction scopeRows(scope: HarnessScope): string {\n\tconst bridge = scope.instructionBridge\n\tconst mcp = scope.mcpConfig\n\treturn [\n\t\t`| detection directory | \\`${scope.detect}\\` |`,\n\t\t`| skills projection | ${scope.skillsDirectory ? `\\`${scope.skillsDirectory}\\` — written by \\`init\\`` : 'none — reads `.agents/skills` natively'} |`,\n\t\t`| instruction bridge | ${\n\t\t\tbridge === undefined\n\t\t\t\t? 'none'\n\t\t\t\t: bridge.kind === 'import'\n\t\t\t\t\t? `\\`${bridge.path}\\` — an import of \\`AGENTS.md\\``\n\t\t\t\t\t: `\\`${bridge.path}\\` — \\`AGENTS.md\\` in the \\`${bridge.key}\\` entry`\n\t\t} |`,\n\t\t`| MCP configuration | ${mcp === undefined ? 'none' : `\\`${mcp.path}\\` — the \\`${mcp.key}\\` key, ${mcp.format}${mcp.shared ? ', shared with other settings' : ''}`} |`,\n\t].join('\\n')\n}\n\n/**\n * One page per harness, generated from the registry rather than written by hand. These are the\n * paths the detectors actually use, so a page written beside them would drift the first time a\n * harness moved a file. Editorial judgment about a harness — what is contested, what not to\n * generate — stays in the `init` skill's own reference pages, which these link to where one exists.\n */\nfunction harnessPage(harness: Harness, hasInitReference: boolean): GeneratedDoc {\n\tconst deprecated =\n\t\tharness.deprecated === undefined\n\t\t\t? ''\n\t\t\t: `\\n> **Superseded by \\`${harness.deprecated}\\`.** The legacy paths still work, so a projection here keeps resolving and \\`doctor\\` reports the name as deprecated rather than broken. New repositories should enable \\`${harness.deprecated}\\`.\\n`\n\n\tconst user =\n\t\tharness.user === undefined\n\t\t\t? '\\nNo user-scope paths are primary-sourced for this harness, so `doctor` describes none.\\n'\n\t\t\t: `\\n## User scope\n\nDescribed, never written: \\`init\\` and \\`doctor\\` both work inside a repository.\n\n| What | Path |\n| --- | --- |\n${scopeRows(harness.user)}\n`\n\n\tconst artifacts = harness.project.nonstandard ?? []\n\tconst nonstandard =\n\t\tartifacts.length === 0\n\t\t\t? ''\n\t\t\t: `\\n## Configuration only this harness reads\n\nReported by \\`doctor\\` so it can be converted; see \\`../nonstandard.md\\` for what each conversion is.\n\n| Path | Kind | Converts to |\n| --- | --- | --- |\n${artifacts\n\t.map(\n\t\t(artifact) =>\n\t\t\t`| \\`${artifact.path}${artifact.shape === 'directory' ? '/' : ''}\\` | ${artifact.kind} | ${conversionOf(artifact.kind)} |`,\n\t)\n\t.join('\\n')}\n`\n\n\tconst editorial = hasInitReference\n\t\t? `\\n## Judgment about this harness\\n\\nWhat to generate for it, what to leave alone, and which claims are contested: \\`../../../init/references/harnesses/${harness.name}.md\\`. That page is hand-written and is the one to read before writing anything for this harness.\\n`\n\t\t: ''\n\n\treturn {\n\t\tpath: `references/harnesses/${harness.name}.md`,\n\t\tcontent: `${generatedSkillWarning}\n\n# ${harness.name}\n${deprecated}\n## Project scope\n\nWhere \\`doctor\\` looks inside a repository.\n\n| What | Path |\n| --- | --- |\n${scopeRows(harness.project)}\n${user}${nonstandard}${editorial}`,\n\t}\n}\n\n/**\n * The reference pages, split by finding family plus one per harness. `initReferences` names the\n * harnesses the `init` skill has a hand-written page for; it is read off the filesystem by the\n * generator rather than written down here, so a page added there is linked without a second edit.\n */\nexport function renderDoctorReferences(initReferences: ReadonlySet<string>): GeneratedDoc[] {\n\treturn [\n\t\t{\n\t\t\tpath: 'references/bridges.md',\n\t\t\tcontent: `${generatedSkillWarning}\n\n# Skills bridge findings\n\n${repairTable(bridgeRepairs)}\n\nSubstitute the reported bridge path for \\`<path>\\`.\n\n## The Windows case\n\nThe common failure is \\`degraded\\`. Creating a symlink on Windows needs a privilege most accounts do not have, and Git for Windows gates it separately with \\`core.symlinks\\`, which its installer leaves off. With \\`core.symlinks=false\\` git does not error — it writes the symlink out as a regular file whose contents are the target path. \\`${initSkillInvocation} --copy --force\\` rebuilds every bridge as a real directory on that machine; name one bridge to rebuild only that one.\n\nA copy is a snapshot rather than a live projection, and an agent that edits a skill through it writes into the copy instead of into \\`.agents/skills\\`. That is what the \\`diverged\\` findings catch.\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'references/instructions.md',\n\t\t\tcontent: `${generatedSkillWarning}\n\n# Instruction bridge findings\n\n${repairTable(instructionRepairs)}\n\n\\`unbridged\\` is the one to read carefully. The file is there and looks fine, and it names \\`AGENTS.md\\` nowhere — a \\`CLAUDE.md\\` someone overwrote with real content, or a \\`.gemini/settings.json\\` another tool rewrote without \\`AGENTS.md\\` in \\`context.fileName\\`. Never fix it by replacing the file: the content that displaced the bridge may be the only copy of something.\n\nAn instruction bridge is reported per file, so a monorepo gets one row per \\`AGENTS.md\\` in the tree. Each nested \\`AGENTS.md\\` needs its own stub — an import bridges the file beside it and nothing deeper.\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'references/configuration.md',\n\t\t\tcontent: `${generatedSkillWarning}\n\n# Configuration findings\n\nThe bridges resolve, and the configuration around them is still wrong: a superseded harness name, a git-ignored bridge, a local-override file nothing reads, a skill whose frontmatter makes every harness skip it. None of these is an \\`init\\` flag — \\`init\\` consolidates and creates, and will not correct a file the user already wrote. They go to the \\`repair\\` skill, which offers each correction with its before and after and writes only what is approved.\n\n${repairTable(configurationRepairs)}\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'references/mcp.md',\n\t\t\tcontent: `${generatedSkillWarning}\n\n# MCP findings\n\nA repository may keep a **golden MCP server set** at \\`${goldenSet}\\` — one canonical entry per server, in the superset of fields the supported hosts accept, written by the user. Where it exists, \\`doctor\\` compares it against each harness's own MCP configuration and reports how the two have drifted. **No golden set means no MCP drift findings at all**, and a harness with no MCP file yet has nothing that could have drifted.\n\nComparison is semantic. Six config keys across three file formats means no two of these files are ever byte-equal, so each side is parsed into one model and the models are compared. A field the golden set leaves unset is never a difference, however a harness fills it in: a host restating its own default and a user's deliberate edit are indistinguishable there, and treating both as changes is what makes a golden set accumulate noise.\n\nEach finding names a **locator**, not a file: \\`.cursor/mcp.json#servers.linear.command\\` is the server and field, and that is what you route on.\n\n${repairTable(mcpRepairs)}\n\n## Credentials\n\nThe two secret findings are the ones to handle carefully.\n\n- **The report never contains the value.** It gives you the locator and stops. Read the value out of the file named in the locator, and do not repeat it into your reply, into a commit message, or into any other file. There is no truncated preview to work from because a truncated credential is a leaked credential in the same transcript.\n- **\\`mcp-committed-secret\\` is not \\`mcp-literal-secret\\` with worse wording.** The file is tracked, so the value is in the repository's history and every clone already has it. Moving it into an environment variable fixes the working tree and changes nothing about that. Rotate it at its issuer first.\n- **A reference passes.** \\`\\${LINEAR_TOKEN}\\` and \\`Bearer \\${LINEAR_TOKEN}\\` are the documented ways to write these fields and are never reported. The test is that shape, not how random the value looks.\n- **An unreadable golden set is reported by position only.** The parser's own message quotes the line it failed on, and in this file that line is the one holding the credential — so neither the message nor the offending line is ever carried into the report. Open the file at the reported line and column.\n`,\n\t\t},\n\t\t{\n\t\t\tpath: 'references/nonstandard.md',\n\t\t\tcontent: `${generatedSkillWarning}\n\n# Non-standard configuration\n\nConfiguration only one harness can read. Nothing here is broken — a Cursor rule does exactly what it says — which is why none of it is in the other four families. What these findings report is **reach**: guidance that lands in one tool and nowhere else, where nobody finds out except by noticing an agent behave differently somewhere else.\n\nEach finding names the canonical form it converts to. The direction is always the same: move the content to a canonical source, and let the harness file be **generated** from it rather than authored beside it. A repository with none of these is the target; it is a direction to walk, not a gate to pass.\n\n${repairTable(nonstandardRepairs)}\n\n## Two of them need judgment, not a move\n\n**A path-scoped rule may have nowhere to go.** \\`AGENTS.md\\` scopes by directory nesting and a skill is loaded on relevance, so neither reproduces \"these globs and no others\". Where the scoping is incidental — a rule that happens to name paths but says something generally true — a skill carries it everywhere. Where the scoping is the point, leave it, and the finding stays as the record of why.\n\n**A subagent has no portable form at all.** No cross-harness format exists, so the finding is the gap rather than a repair. It names no skill, and it is work for a person if it is work at all.\n\n## What is not reported\n\nA harness directory that is a **symlink** is a projection someone already made, not configuration authored here. An artifact is reported whether or not its harness is enabled: a \\`.cursorrules\\` in a repository nobody opens in Cursor is still instruction content \\`AGENTS.md\\` does not carry, and filtering by the enabled set would hide exactly the drift worth converting.\n\nHooks, LSP settings, and output styles are not covered yet. Their event names and shapes differ by harness with no safe projection, and half-reporting them would be worse than the silence.\n`,\n\t\t},\n\t\t...harnessRegistry.map((harness) => harnessPage(harness, initReferences.has(harness.name))),\n\t]\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { filesUnder } from '../diagnose-bridges/directory-files.ts'\nimport { type ConfigurationProblem, type RepairAction, repairFor } from '../diagnose-bridges/doctor-guidance.ts'\nimport type { GitBridgeState } from '../diagnose-bridges/git-bridge-state.ts'\nimport { selectHarnesses } from '../harness-registry/harness-registry.ts'\n\n/**\n * The configuration half of `doctor`. Where `diagnose-bridges` asks whether a bridge still\n * resolves, this asks whether the configuration around it is still *right* — a harness name that\n * has been superseded, an instruction file that never reaches `AGENTS.md`, a bridge git-ignores.\n *\n * Everything here is present-and-wrong rather than missing. Configuration that is absent is\n * `init`'s to create; configuration that is wrong is what the `repair` skill acts on, and this\n * module is where it reads its work from. Detection has one home so the two cannot drift.\n *\n * Instruction bridges are deliberately not here: `diagnose-instructions` owns them, and every one\n * of its repairs goes back to the `init` skill rather than to `repair`.\n *\n * Read-only, like every other part of `doctor`.\n */\nexport type ConfigurationFinding = {\n\t/** Repository-relative path the finding is about. */\n\tpath: string\n\tproblem: ConfigurationProblem\n\tdetail: string\n\t/** What repairs it, already carrying the path. Every fault here is judgment, so `command` is empty. */\n\trepair: RepairAction\n}\n\nexport type DiagnoseConfigurationOptions = {\n\troot: string\n\tgit: GitBridgeState\n\t/** How to name this tool in the repair commands. */\n\tcli: string\n}\n\nconst canonicalSkills = '.agents/skills'\nconst localOverride = 'AGENTS.local.md'\n\n/**\n * The two frontmatter faults that make a harness skip a skill outright, per\n * `skills/init/references/frontmatter.md`: YAML that does not parse, and a missing `description`.\n * A `name` that mismatches the directory is a warning and still loads, so it is not reported here.\n *\n * The unquoted colon is the documented cause of the first, and is checked directly rather than by\n * parsing YAML: the package ships no parser, and a targeted check names the actual fault.\n */\nfunction frontmatterFault(body: string): 'unparseable' | 'no-description' | undefined {\n\tconst block = /^---\\n([\\s\\S]*?)\\n---/.exec(body)\n\tif (!block) return 'unparseable'\n\tconst description = /^description:[ \\t]*(.*)$/m.exec(block[1] as string)\n\tconst value = description?.[1]?.trim()\n\tif (!value) return 'no-description'\n\tconst quoted = /^(['\"]).*\\1$/.test(value)\n\tif (!quoted && /:\\s/.test(value)) return 'unparseable'\n\treturn undefined\n}\n\n/**\n * Unlike `diagnoseBridges`, this takes no harness preference. Every check here requires the\n * projection to exist on disk, and a projection cannot exist without its harness's detection\n * directory — which selects that harness anyway. A preference could therefore never add a finding,\n * so accepting one would be surface that does nothing.\n */\nexport function diagnoseConfiguration({ root, git, cli }: DiagnoseConfigurationOptions): ConfigurationFinding[] {\n\tconst findings: ConfigurationFinding[] = []\n\tconst add = (path: string, problem: ConfigurationProblem) => {\n\t\tconst { detail, repair } = repairFor(problem)\n\t\tfindings.push({ path, problem, detail, repair: repair({ file: path }, cli) })\n\t}\n\n\tconst harnesses = selectHarnesses(root, [])\n\n\t// A superseded name whose projection still exists. `init` reports the deprecation and keeps\n\t// projecting, because the old path still works — so nothing else ever says it is stale.\n\tfor (const harness of harnesses) {\n\t\tconst projection = harness.project.skillsDirectory\n\t\tif (harness.deprecated && projection && existsSync(join(root, projection))) add(projection, 'deprecated-harness')\n\t}\n\n\t// An ignored bridge is untracked, and an untracked bridge swallows a real edit silently.\n\tfor (const harness of harnesses) {\n\t\tconst path = harness.project.skillsDirectory\n\t\tif (path && existsSync(join(root, path)) && git.isIgnored(path)) add(path, 'ignored-bridge')\n\t}\n\n\t// No harness reads this filename, so whatever it holds is invisible to every agent.\n\tif (existsSync(join(root, localOverride))) add(localOverride, 'unread-local-override')\n\n\t// `filesUnder` has already confirmed each of these is a readable file, so the read needs no guard\n\t// of its own; a failure here is a genuine IO error and belongs in the command's error path.\n\tfor (const file of filesUnder(join(root, canonicalSkills)).filter((file) => file.endsWith('SKILL.md'))) {\n\t\tif (frontmatterFault(readFileSync(join(root, canonicalSkills, file), 'utf8')))\n\t\t\tadd(`${canonicalSkills}/${file}`, 'unloadable-skill')\n\t}\n\n\treturn findings\n}\n","/**\n * `.gemini/settings.json` may legally carry comments — the Gemini CLI loader strips them before\n * parsing. A plain `JSON.parse` of a valid settings file therefore throws, and `doctor` would report\n * a working bridge as unreadable. Comments are removed before parsing for that reason.\n *\n * Only comments. A trailing comma stays a parse error, because nothing documents it as accepted.\n */\nexport function stripJsonComments(source: string): string {\n\tlet output = ''\n\tlet index = 0\n\tlet inString = false\n\tlet escaped = false\n\n\twhile (index < source.length) {\n\t\tconst character = source[index] as string\n\n\t\tif (inString) {\n\t\t\toutput += character\n\t\t\tif (escaped) escaped = false\n\t\t\telse if (character === '\\\\') escaped = true\n\t\t\telse if (character === '\"') inString = false\n\t\t\tindex += 1\n\t\t\tcontinue\n\t\t}\n\n\t\tif (character === '\"') {\n\t\t\tinString = true\n\t\t\toutput += character\n\t\t\tindex += 1\n\t\t\tcontinue\n\t\t}\n\n\t\t// A `//` inside a string is an ordinary URL; outside one it can only start a comment.\n\t\tif (character === '/' && source[index + 1] === '/') {\n\t\t\twhile (index < source.length && source[index] !== '\\n') index += 1\n\t\t\tcontinue\n\t\t}\n\n\t\tif (character === '/' && source[index + 1] === '*') {\n\t\t\tindex += 2\n\t\t\twhile (index < source.length && !(source[index] === '*' && source[index + 1] === '/')) index += 1\n\t\t\tindex += 2\n\t\t\tcontinue\n\t\t}\n\n\t\toutput += character\n\t\tindex += 1\n\t}\n\n\treturn output\n}\n\n/** The parsed value, or `undefined` when the file is not JSON even once its comments are gone. */\nexport function parseJsonWithComments(source: string): unknown {\n\ttry {\n\t\treturn JSON.parse(stripJsonComments(source))\n\t} catch {\n\t\treturn undefined\n\t}\n}\n","import { isRecord } from '../is-record/is-record.ts'\n/**\n * The shape both sides of an MCP comparison are normalized into.\n *\n * No two MCP configuration files are ever byte-equal — the supported harnesses spread the same\n * servers across six config keys and three serialization formats — so comparison cannot be a\n * diff. Each side is parsed into this model and the models are compared.\n *\n * The field list is a **superset** of what the hosts accept, which is the whole premise of a\n * golden set: `description`, `enabled`, and `timeout` are Goose's, `source` is Zed's, and a\n * converter writing into either has to supply them. Supplying a value the user wrote is\n * transcription; supplying one they did not is invention, and `init` invents nothing. See\n * `.research/agentic-configuration-standards/` (E-MCP-05) for what each host makes up today.\n */\nexport type McpTransport = 'stdio' | 'http' | 'sse'\n\nexport type McpServer = {\n\ttransport?: McpTransport\n\tcommand?: string\n\targs?: readonly string[]\n\tenv?: Readonly<Record<string, string>>\n\turl?: string\n\theaders?: Readonly<Record<string, string>>\n\tdescription?: string\n\tenabled?: boolean\n\ttimeout?: number\n\tsource?: string\n}\n\n/** Every field of the model, in the order a finding reports them. */\nexport const mcpFields = [\n\t'transport',\n\t'command',\n\t'args',\n\t'env',\n\t'url',\n\t'headers',\n\t'description',\n\t'enabled',\n\t'timeout',\n\t'source',\n] as const\n\nexport type McpField = (typeof mcpFields)[number]\n\n/** The two fields holding one value per name rather than a single value. */\nconst mapFields = new Set<McpField>(['env', 'headers'])\n\n/**\n * Whether two values of the same field agree. Arrays are ordered — `args` is a command line, and\n * reordering it changes what runs. Maps are not, so they are compared by their sorted entries.\n */\nfunction sameValue(field: McpField, left: unknown, right: unknown): boolean {\n\tif (mapFields.has(field) && isRecord(left) && isRecord(right)) {\n\t\tconst keys = Object.keys(left)\n\t\treturn keys.length === Object.keys(right).length && keys.every((key) => left[key] === right[key])\n\t}\n\treturn JSON.stringify(left) === JSON.stringify(right)\n}\n\n/**\n * The fields on which a target disagrees with the golden set.\n *\n * Asymmetric by design, and this is the rule that keeps a golden set from accumulating noise. A\n * field the golden set leaves unset is never a difference, however the target fills it: a host\n * restating its own default and a user's deliberate edit are indistinguishable in that position,\n * and pulling both back would grow the golden set on every round-trip. The golden set speaks only\n * about what it says.\n *\n * `env` and `headers` are compared per name for the same reason — a variable only the target sets\n * is that target's business, and one the golden set names must match.\n */\nexport function divergingFields(golden: McpServer, target: McpServer): McpField[] {\n\treturn mcpFields.filter((field) => {\n\t\tconst declared = golden[field]\n\t\tif (declared === undefined) return false\n\t\tif (mapFields.has(field) && isRecord(declared) && isRecord(target[field])) {\n\t\t\tconst observed = target[field] as Record<string, unknown>\n\t\t\treturn Object.keys(declared).some((key) => declared[key] !== observed[key])\n\t\t}\n\t\treturn !sameValue(field, declared, target[field])\n\t})\n}\n\n/** Whether one field of two models agrees, for naming the side that moved. */\nexport function sameField(field: McpField, left: McpServer | undefined, right: McpServer | undefined): boolean {\n\treturn sameValue(field, left?.[field], right?.[field])\n}\n","import { parse as parseToml } from 'smol-toml'\nimport { parseJsonWithComments } from '../diagnose-bridges/json-with-comments.ts'\nimport type { Position } from '../diagnose-bridges/locator.ts'\nimport type { McpConfig } from '../harness-registry/mcp-config.ts'\nimport { isRecord } from '../is-record/is-record.ts'\nimport type { McpServer, McpTransport } from './mcp-model.ts'\n\n/** Where the golden set lives, and why it is namespaced rather than sitting at `.agents/mcp.json`. */\nexport const goldenSetPath = '.agents/buddy-agent-harness/mcp.toml'\n\n/** The table the golden set keeps its servers under. */\nconst goldenKey = 'servers'\n\nexport type ParsedServers =\n\t/** The file is absent. Nothing to compare, and not a fault. */\n\t| { kind: 'absent' }\n\t| { kind: 'servers'; servers: Map<string, McpServer> }\n\t| { kind: 'unreadable'; position?: Position | undefined }\n\nfunction stringMap(value: unknown): Record<string, string> | undefined {\n\tif (!isRecord(value)) return undefined\n\tconst entries = Object.entries(value).filter(([, item]) => typeof item === 'string') as [string, string][]\n\treturn entries.length ? Object.fromEntries(entries) : undefined\n}\n\nconst transports = new Set<string>(['stdio', 'http', 'sse'])\n\n/**\n * The transport a host entry describes. Hosts spell it `type` (Claude Code, VS Code) or\n * `transport`, and several state it nowhere and leave it implied by which of `url` and `command`\n * is present. Inferring it is what lets a `url` entry in one file compare equal to a `type: http`\n * entry in another.\n */\nfunction transportOf(entry: Record<string, unknown>): McpTransport | undefined {\n\tconst declared = entry['type'] ?? entry['transport']\n\tif (typeof declared === 'string' && transports.has(declared)) return declared as McpTransport\n\tif (typeof entry['url'] === 'string') return 'http'\n\tif (typeof entry['command'] === 'string') return 'stdio'\n\treturn undefined\n}\n\n/**\n * One host entry, or one golden entry, in the canonical model. The two formats carry the same\n * field names — the divergence between hosts is the file, the top-level key, and the serialization,\n * not the entry — so one reader serves both directions of the comparison.\n *\n * A field whose value is the wrong type is dropped rather than carried through. A `timeout` that\n * is a string is not a timeout, and letting it into the model would report a divergence whose real\n * cause is a typo the finding does not name.\n */\nfunction serverFrom(entry: Record<string, unknown>): McpServer {\n\tconst args = Array.isArray(entry['args']) && entry['args'].every((item) => typeof item === 'string')\n\tconst transport = transportOf(entry)\n\tconst env = stringMap(entry['env'])\n\tconst headers = stringMap(entry['headers'])\n\treturn {\n\t\t...(transport ? { transport } : {}),\n\t\t...(typeof entry['command'] === 'string' ? { command: entry['command'] } : {}),\n\t\t...(args ? { args: entry['args'] as string[] } : {}),\n\t\t...(env ? { env } : {}),\n\t\t...(typeof entry['url'] === 'string' ? { url: entry['url'] } : {}),\n\t\t...(headers ? { headers } : {}),\n\t\t...(typeof entry['description'] === 'string' ? { description: entry['description'] } : {}),\n\t\t...(typeof entry['enabled'] === 'boolean' ? { enabled: entry['enabled'] } : {}),\n\t\t...(typeof entry['timeout'] === 'number' ? { timeout: entry['timeout'] } : {}),\n\t\t...(typeof entry['source'] === 'string' ? { source: entry['source'] } : {}),\n\t}\n}\n\nfunction serversUnder(document: unknown, key: string): Map<string, McpServer> {\n\tconst table = isRecord(document) ? document[key] : undefined\n\tif (!isRecord(table)) return new Map()\n\treturn new Map(\n\t\tObject.entries(table)\n\t\t\t.filter(([, entry]) => isRecord(entry))\n\t\t\t.map(([name, entry]) => [name, serverFrom(entry as Record<string, unknown>)]),\n\t)\n}\n\n/**\n * The position out of a thrown parse error, and nothing else out of it.\n *\n * `smol-toml` reports `line` and `column` on the error alongside a `codeblock` quoting the source,\n * and its `message` embeds that same code block. Both are unusable: in a file of MCP configuration\n * the line a parser failed on is exactly the line holding the credential. Only the two numbers are\n * read, and anything thrown that does not carry them is reported without a position rather than by\n * reaching for its message.\n *\n * Exported because that restriction is the point of the function rather than an implementation\n * detail of it, and it is verified directly.\n */\nexport function positionOf(error: unknown): Position | undefined {\n\tif (!isRecord(error)) return undefined\n\tconst { line, column } = error\n\treturn typeof line === 'number' && typeof column === 'number' ? { line, column } : undefined\n}\n\n/** The golden set, parsed. Absent is the common case and is not a fault. */\nexport function parseGoldenSet(source: string | undefined): ParsedServers {\n\tif (source === undefined) return { kind: 'absent' }\n\ttry {\n\t\treturn { kind: 'servers', servers: serversUnder(parseToml(source), goldenKey) }\n\t} catch (error) {\n\t\treturn { kind: 'unreadable', position: positionOf(error) }\n\t}\n}\n\n/**\n * One harness's MCP configuration, parsed. The file is read for its own key and nothing else:\n * `.gemini/settings.json` also carries the instruction bridge, and a reader that treated the whole\n * file as MCP would report the rest of a user's settings as servers it does not recognize.\n *\n * JSON is parsed with comments stripped, because Gemini CLI's loader strips them too and a\n * settings file may legally carry them.\n */\nexport function parseTarget(config: McpConfig, source: string | undefined): ParsedServers {\n\tif (source === undefined) return { kind: 'absent' }\n\tif (config.format === 'toml') {\n\t\ttry {\n\t\t\treturn { kind: 'servers', servers: serversUnder(parseToml(source), config.key) }\n\t\t} catch {\n\t\t\treturn { kind: 'unreadable' }\n\t\t}\n\t}\n\tconst document = parseJsonWithComments(source)\n\t// `parseJsonWithComments` answers `undefined` for a file that does not parse. A file whose\n\t// literal content is `null` parses and holds no servers, which reads the same here and is\n\t// reported the same way it would be for `{}`.\n\tif (document === undefined) return { kind: 'unreadable' }\n\treturn { kind: 'servers', servers: serversUnder(document, config.key) }\n}\n","import type { GitBridgeState } from '../diagnose-bridges/git-bridge-state.ts'\nimport { parseJsonWithComments } from '../diagnose-bridges/json-with-comments.ts'\nimport type { McpConfig } from '../harness-registry/mcp-config.ts'\nimport { isRecord } from '../is-record/is-record.ts'\nimport { type McpField, type McpServer, sameField } from './mcp-model.ts'\nimport { goldenSetPath, type ParsedServers, parseGoldenSet, parseTarget } from './mcp-sources.ts'\n\n/** Which side of a diverged server moved since the two last agreed. */\nexport type McpDirection = 'target' | 'golden' | 'both' | 'unknown'\n\n/** Where the record of what was last written to each target lives, beside the golden set it came from. */\nexport const projectionRecordPath = '.agents/buddy-agent-harness/mcp.projected.json'\n\n/**\n * The last-projected record: per target path, per server name, the model that was written there.\n *\n * It exists because git can only answer for a file it can see. Harnesses write their MCP\n * configuration themselves, at user scope, into files that are often untracked or ignored outright,\n * and for those \"which side moved since they last agreed\" has no answer in history. A record\n * written at projection time has one, exactly, because it *is* what was projected.\n *\n * A malformed record answers nothing rather than throwing. It is a cache of an answer, not the\n * answer, and a diagnosis that dies because its cache is corrupt is worse than one that says\n * `unknown`.\n */\nexport function parseProjectionRecord(source: string | undefined): Map<string, Map<string, McpServer>> {\n\tconst document = source === undefined ? undefined : parseJsonWithComments(source)\n\tconst targets = isRecord(document) ? document['targets'] : undefined\n\tif (!isRecord(targets)) return new Map()\n\treturn new Map(\n\t\tObject.entries(targets)\n\t\t\t.filter(([, servers]) => isRecord(servers))\n\t\t\t.map(([path, servers]) => [\n\t\t\t\tpath,\n\t\t\t\tnew Map(\n\t\t\t\t\tObject.entries(servers as Record<string, unknown>)\n\t\t\t\t\t\t.filter(([, server]) => isRecord(server))\n\t\t\t\t\t\t.map(([name, server]) => [name, server as McpServer]),\n\t\t\t\t),\n\t\t\t]),\n\t)\n}\n\nexport type BaselineOptions = {\n\tgit: GitBridgeState\n\trecord: Map<string, Map<string, McpServer>>\n}\n\n/**\n * Which side moved, for one server and one field.\n *\n * The record is asked first, because it records exactly what was written and therefore answers\n * exactly. Where it holds nothing, a **tracked** target falls back to git: the newest commit where\n * the golden set and the target agreed on that field, then which working tree still matches it.\n * Where neither can answer, the direction is `unknown` and the finding says so — naming a side on a\n * guess would send a reconcile at the wrong file.\n */\nexport class McpBaseline {\n\tconstructor(private readonly options: BaselineOptions) {}\n\n\t/**\n\t * One parse per commit per file, and one commit walk per target.\n\t *\n\t * `lastAgreed` runs per (config, server, field) that diverges without a projection record, and\n\t * every one of those walks reads the same two files at the same commits. Unmemoized, three\n\t * targets against five servers with two diverged fields each over fifty commits of history is\n\t * three thousand `git show` calls and as many parses — on the command the `doctor` skill says is\n\t * cheap enough for a session-start hook. One instance serves one diagnosis, so the memo lives and\n\t * dies with it and can never answer for a working tree that has moved on.\n\t */\n\tprivate readonly parsed = new Map<string, ParsedServers>()\n\tprivate readonly walked = new Map<string, string[]>()\n\n\tdirectionOf(config: McpConfig, name: string, field: McpField, golden: McpServer, target: McpServer): McpDirection {\n\t\tconst base = this.baseFor(config, name, field)\n\t\tif (base === undefined) return 'unknown'\n\t\tconst goldenMoved = !sameField(field, golden, base)\n\t\tconst targetMoved = !sameField(field, target, base)\n\t\tif (goldenMoved && targetMoved) return 'both'\n\t\treturn targetMoved ? 'target' : 'golden'\n\t}\n\n\tprivate baseFor(config: McpConfig, name: string, field: McpField): McpServer | undefined {\n\t\tconst recorded = this.options.record.get(config.path)?.get(name)\n\t\tif (recorded) return recorded\n\t\treturn this.lastAgreed(config, name, field)\n\t}\n\n\t/**\n\t * The newest committed state where both files described this field the same way. Read at model\n\t * granularity rather than by comparing blobs: the two files never share bytes, and a commit that\n\t * reformatted one of them did not change what it says.\n\t */\n\tprivate lastAgreed(config: McpConfig, name: string, field: McpField): McpServer | undefined {\n\t\tfor (const commit of this.commitsTouching(config)) {\n\t\t\tconst golden = this.parseAt(commit, goldenSetPath, parseGoldenSet)\n\t\t\tconst target = this.parseAt(commit, config.path, (source) => parseTarget(config, source))\n\t\t\tif (golden.kind !== 'servers' || target.kind !== 'servers') continue\n\t\t\tconst left = golden.servers.get(name)\n\t\t\tconst right = target.servers.get(name)\n\t\t\tif (left && right && sameField(field, left, right)) return left\n\t\t}\n\t\treturn undefined\n\t}\n\n\tprivate commitsTouching(config: McpConfig): string[] {\n\t\tconst walked = this.walked.get(config.path)\n\t\tif (walked) return walked\n\t\tconst commits = this.options.git.commitsTouching([goldenSetPath, config.path])\n\t\tthis.walked.set(config.path, commits)\n\t\treturn commits\n\t}\n\n\tprivate parseAt(commit: string, path: string, parse: (source: string | undefined) => ParsedServers): ParsedServers {\n\t\tconst key = `${commit}\\u0000${path}`\n\t\tconst parsed = this.parsed.get(key)\n\t\tif (parsed) return parsed\n\t\tconst result = parse(this.options.git.contentAt(commit, path))\n\t\tthis.parsed.set(key, result)\n\t\treturn result\n\t}\n}\n","import type { McpServer } from './mcp-model.ts'\n\n/**\n * Finding a literal credential in MCP configuration, without ever handling one.\n *\n * `doctor` is safe to run from a session-start hook, so its output lands in agent context on every\n * session and from there into transcripts. A value it echoes is amplified far past the file it came\n * from. Every function here answers **where**, never **what**: the return is a field path, and the\n * value never leaves this module.\n *\n * That is also why the redaction lives here rather than in a formatter. `doctor` renders three ways\n * — TOON, `--format text`, and `--format json` — and trusting each of them to mask is three chances\n * to drift. A value that never enters the finding cannot be printed by any of them.\n */\n\n/**\n * Words that mark a field as credential-bearing wherever they appear in its name. Each is one that\n * effectively never turns up inside an innocent word, so a bare substring test is safe and catches\n * the run-together spellings a segment test would miss — `MYTOKEN`, `githubsecret`.\n */\nconst credentialWord = /token|secret|password|passwd|credential/i\n\n/**\n * Words that mark a field only when they are a **segment** of its name. `key` and `auth` are the\n * two that matter, and both are common inside words that carry nothing — `MONKEY`, `AUTHOR`,\n * `KEYSTONE_URL`. Matching them as substrings reports those, and a scanner that cries wolf on a\n * hook-run command is one people stop reading.\n */\nconst credentialSegment = new Set(['key', 'apikey', 'auth', 'authorization', 'bearer'])\n\n/** A field name split the two ways these are written: `API_KEY` and `apiKey` alike. */\nfunction segmentsOf(key: string): string[] {\n\treturn key\n\t\t.replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n\t\t.split(/[^A-Za-z0-9]+/)\n\t\t.filter(Boolean)\n\t\t.map((segment) => segment.toLowerCase())\n}\n\n/**\n * A value that **names** a secret instead of holding one, in the forms the supported hosts\n * document: `${VAR}`, `$VAR`, and the `${env:VAR}` / `${input:id}` variants.\n *\n * Classification is by shape, not by content. An entropy test on the value would be a guess about\n * a string this module has already decided not to look at, and it would fail in both directions —\n * a short password is low-entropy and a base64 configuration blob is high.\n */\nconst reference = /\\$\\{[^}]+\\}|\\$[A-Za-z_][A-Za-z0-9_]*/\n\n/**\n * A credential-bearing field passes when it carries a reference anywhere in it, because the common\n * legitimate form is a template rather than a bare variable — `Bearer ${LINEAR_TOKEN}` is the\n * documented way to write an authorization header, and requiring the whole value to be a reference\n * would report every correct one.\n */\nfunction holdsLiteral(value: string): boolean {\n\treturn !reference.test(value)\n}\n\nfunction credentialKey(key: string): boolean {\n\treturn credentialWord.test(key) || segmentsOf(key).some((segment) => credentialSegment.has(segment))\n}\n\n/**\n * Whether a URL carries a credential in itself. Two shapes: a password in the userinfo component,\n * and a credential-named query parameter holding a literal. Neither value is read past the test.\n *\n * A URL that does not parse is not reported. It is malformed rather than leaky, and guessing at its\n * structure with a regular expression is how a scanner starts matching the thing it must not touch.\n */\nfunction urlHoldsCredential(value: string): boolean {\n\tlet url: URL\n\ttry {\n\t\turl = new URL(value)\n\t} catch {\n\t\treturn false\n\t}\n\tif (url.password) return true\n\tfor (const [key, parameter] of url.searchParams) if (credentialKey(key) && holdsLiteral(parameter)) return true\n\treturn false\n}\n\nfunction literalsIn(field: 'env' | 'headers', values: Readonly<Record<string, string>> | undefined): string[] {\n\treturn Object.entries(values ?? {})\n\t\t.filter(([key, value]) => credentialKey(key) && holdsLiteral(value))\n\t\t.map(([key]) => `${field}.${key}`)\n}\n\n/**\n * Every field of one server holding a literal credential, as a path within that server.\n *\n * The path is the whole answer. It is never a truncated value: `sk-ab…` is a leak into the same\n * transcript and buys a reader nothing the field name does not already give them.\n */\nexport function credentialFields(server: McpServer): string[] {\n\treturn [\n\t\t...literalsIn('env', server.env),\n\t\t...literalsIn('headers', server.headers),\n\t\t...(server.url && urlHoldsCredential(server.url) ? ['url'] : []),\n\t]\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { type McpProblem, repairFor } from '../diagnose-bridges/doctor-guidance.ts'\nimport type { GitBridgeState } from '../diagnose-bridges/git-bridge-state.ts'\nimport { type Locator, locatorText } from '../diagnose-bridges/locator.ts'\nimport type { ConfigurationFinding } from '../diagnose-configuration/diagnose-configuration.ts'\nimport { selectHarnesses } from '../harness-registry/harness-registry.ts'\nimport type { McpConfig } from '../harness-registry/mcp-config.ts'\nimport { McpBaseline, type McpDirection, parseProjectionRecord, projectionRecordPath } from './mcp-baseline.ts'\nimport { divergingFields, type McpServer } from './mcp-model.ts'\nimport { credentialFields } from './mcp-secrets.ts'\nimport { goldenSetPath, parseGoldenSet, parseTarget } from './mcp-sources.ts'\n\n/**\n * The MCP half of `doctor`: how a repository's **golden MCP server set** and the harness copies of\n * it have drifted apart, and where a literal credential is sitting in either.\n *\n * The golden set is a file the user authors. That is what makes this capability possible at all:\n * `init` reports MCP configuration rather than converting it because converting a config someone\n * wrote for one harness into another's format has to invent values they never wrote, and `init`\n * invents nothing. A set the user authored inverts the premise — a field they filled in is\n * transcription. **No golden set means no drift diagnosis**, and nothing here invents anything\n * either.\n *\n * Read-only, like the rest of `doctor`. Projecting the golden set forward and reconciling a\n * target-side change back into it are writes, and they need an approval-gated home; this module is\n * where that home reads its work from.\n */\nexport type DiagnoseMcpOptions = {\n\troot: string\n\tgit: GitBridgeState\n\t/** How to name this tool in the repair commands. */\n\tcli: string\n}\n\nfunction read(root: string, path: string): string | undefined {\n\tconst absolute = join(root, path)\n\treturn existsSync(absolute) ? readFileSync(absolute, 'utf8') : undefined\n}\n\n/**\n * The distinct MCP files the enabled harnesses read. Two harnesses may name the same file.\n *\n * No harness preference is accepted, for the same reason the configuration half accepts none: every\n * harness documenting an MCP file is already selected without one. Claude Code and Cursor are\n * selected unconditionally; Codex and Gemini CLI keep their file inside their own detection\n * directory, so it cannot exist without that directory selecting them. A preference could never add\n * a finding.\n */\nfunction targetsOf(root: string): McpConfig[] {\n\tconst seen = new Set<string>()\n\treturn selectHarnesses(root, [])\n\t\t.map((harness) => harness.project.mcpConfig)\n\t\t.filter((config): config is McpConfig => config !== undefined)\n\t\t.filter((config) => !seen.has(config.path) && seen.add(config.path))\n}\n\nconst divergence: Record<McpDirection, McpProblem> = {\n\ttarget: 'mcp-diverged-target',\n\tgolden: 'mcp-diverged-golden',\n\tboth: 'mcp-diverged-both',\n\tunknown: 'mcp-diverged-unknown',\n}\n\nexport function diagnoseMcp({ root, git, cli }: DiagnoseMcpOptions): ConfigurationFinding[] {\n\tconst findings: ConfigurationFinding[] = []\n\t// The finding carries the locator in parts as far as the repair, which is where a repair naming\n\t// only the server or only the file reads one off. The string is built here and nowhere else, and\n\t// nothing recovers a part from it: a server named `io.github.foo` survives no split of it.\n\tconst add = (at: Locator, problem: McpProblem) => {\n\t\tconst { detail, repair } = repairFor(problem)\n\t\tfindings.push({ path: locatorText(at), problem, detail, repair: repair(at, cli) })\n\t}\n\n\t/**\n\t * Credentials are reported for every file that holds servers, the golden set included — a user\n\t * pastes a token into whichever file is open, and the golden set is a file. Severity splits on\n\t * tracking, because a literal in a tracked file is already a committed credential and moving it\n\t * to an environment variable does not un-commit it.\n\t */\n\tconst reportSecrets = (path: string, servers: Map<string, McpServer>) => {\n\t\tconst committed = git.trackingOf(path) !== 'untracked'\n\t\tfor (const [name, server] of servers)\n\t\t\tfor (const field of credentialFields(server))\n\t\t\t\tadd({ file: path, server: name, field }, committed ? 'mcp-committed-secret' : 'mcp-literal-secret')\n\t}\n\n\t// Targets are read first so an unreadable one is reported whether or not a golden set exists: a\n\t// harness cannot read that file either, and its servers are not running.\n\tconst targets = new Map<string, Map<string, McpServer>>()\n\tconst configs: McpConfig[] = []\n\tfor (const config of targetsOf(root)) {\n\t\tconst parsed = parseTarget(config, read(root, config.path))\n\t\tif (parsed.kind === 'unreadable') {\n\t\t\tadd({ file: config.path }, 'mcp-target-unreadable')\n\t\t\tcontinue\n\t\t}\n\t\t// A harness with no MCP file has nothing that could have drifted. Writing one for the first\n\t\t// time is projection, which is a write and not this command's.\n\t\tif (parsed.kind === 'absent') continue\n\t\treportSecrets(config.path, parsed.servers)\n\t\ttargets.set(config.path, parsed.servers)\n\t\tconfigs.push(config)\n\t}\n\n\tconst golden = parseGoldenSet(read(root, goldenSetPath))\n\tif (golden.kind === 'unreadable') {\n\t\tadd({ file: goldenSetPath, position: golden.position }, 'mcp-golden-unreadable')\n\t\treturn findings\n\t}\n\tif (golden.kind === 'absent') return findings\n\treportSecrets(goldenSetPath, golden.servers)\n\n\tconst baseline = new McpBaseline({ git, record: parseProjectionRecord(read(root, projectionRecordPath)) })\n\n\tfor (const config of configs) {\n\t\tconst servers = targets.get(config.path) as Map<string, McpServer>\n\t\tfor (const [name, declared] of golden.servers) {\n\t\t\tconst carried = servers.get(name)\n\t\t\tif (!carried) {\n\t\t\t\tadd({ file: config.path, server: name }, 'mcp-unprojected')\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfor (const field of divergingFields(declared, carried))\n\t\t\t\tadd(\n\t\t\t\t\t{ file: config.path, server: name, field },\n\t\t\t\t\tdivergence[baseline.directionOf(config, name, field, declared, carried)],\n\t\t\t\t)\n\t\t}\n\t\tfor (const name of servers.keys())\n\t\t\tif (!golden.servers.has(name)) add({ file: config.path, server: name }, 'mcp-undeclared')\n\t}\n\n\treturn findings\n}\n","import { lstatSync, readFileSync } from 'node:fs'\nimport { join, posix } from 'node:path'\nimport { filesUnder } from '../diagnose-bridges/directory-files.ts'\nimport { type NonstandardProblem, type RepairAction, repairFor } from '../diagnose-bridges/doctor-guidance.ts'\nimport { harnessRegistry } from '../harness-registry/harness-registry.ts'\nimport type { NonstandardArtifact, NonstandardKind } from '../harness-registry/nonstandard-artifact.ts'\n\n/**\n * The fifth section of `doctor`: configuration that works for exactly one harness.\n *\n * The other four ask whether something is broken. This one asks how far what is there reaches, and\n * reports the answer when it is \"one harness\". Nothing here is a fault — a Cursor rule does what it\n * says — so nothing here is repaired by correcting it. Each finding names the canonical form it\n * would convert to, so a repository can be walked toward one where every harness file is generated\n * from a canonical source rather than authored beside it.\n *\n * Read-only, like every other part of `doctor`.\n */\nexport type NonstandardFinding = {\n\t/** Repository-relative path of the artifact, POSIX-separated. */\n\tpath: string\n\tproblem: NonstandardProblem\n\tdetail: string\n\t/** The conversion. `command` is always empty: every one of these is judgment about content. */\n\trepair: RepairAction\n}\n\nexport type DiagnoseNonstandardOptions = {\n\troot: string\n\t/** How to name this tool in the repair commands. */\n\tcli: string\n}\n\nconst problemOf: Record<NonstandardKind, NonstandardProblem> = {\n\tinstructions: 'nonstandard-instructions',\n\trule: 'nonstandard-rule',\n\tcommand: 'nonstandard-command',\n\tskill: 'nonstandard-skill',\n\tsubagent: 'nonstandard-subagent',\n}\n\n/**\n * A `.mdc` rule splits on whether its scoping is load-bearing. `globs:` with a value binds the rule\n * to paths, and `AGENTS.md` has no equivalent — it scopes by directory nesting only — so that one\n * converts to a skill, or stays. Without it the rule is always-on prose, which `AGENTS.md` holds\n * verbatim. Read from frontmatter rather than assumed, because the two need different conversions\n * and guessing wrong sends prose to the wrong destination.\n */\nfunction scopedByGlobs(body: string): boolean {\n\tconst block = /^---\\n([\\s\\S]*?)\\n---/.exec(body)\n\tif (!block) return false\n\tconst globs = /^globs:[ \\t]*(.*)$/m.exec(block[1] as string)\n\tconst value = globs?.[1]?.trim()\n\treturn value !== undefined && value.length > 0 && value !== '[]'\n}\n\n/** Every skill directory below `directory`, named by its `SKILL.md`. */\nfunction skillFiles(directory: string): string[] {\n\treturn filesUnder(directory).filter((file) => file.endsWith('SKILL.md'))\n}\n\n/**\n * A harness directory that is a symlink is a projection someone already made, not configuration\n * authored here — reporting it would tell a repository to convert what it already converted.\n */\nfunction authoredHere(path: string): boolean {\n\ttry {\n\t\treturn !lstatSync(path).isSymbolicLink()\n\t} catch {\n\t\treturn false\n\t}\n}\n\nfunction artifactFiles(root: string, artifact: NonstandardArtifact): string[] {\n\tconst absolute = join(root, artifact.path)\n\t// A path that is not there fails this too — `lstat` throws and the catch reads as \"not ours\".\n\tif (!authoredHere(absolute)) return []\n\tif (artifact.shape === 'file') return [artifact.path]\n\tconst below = artifact.kind === 'skill' ? skillFiles(absolute) : filesUnder(absolute)\n\treturn below.map((file) => posix.join(artifact.path, file))\n}\n\n/**\n * Unlike the other detectors this takes no harness preference and no enabled set. An artifact is\n * reachable by one harness whether or not that harness is enabled here — a `.cursorrules` in a\n * repository nobody opens in Cursor is still instruction content `AGENTS.md` does not carry — so\n * filtering by the enabled set would hide exactly the drift worth converting.\n */\nexport function diagnoseNonstandard({ root, cli }: DiagnoseNonstandardOptions): NonstandardFinding[] {\n\tconst findings: NonstandardFinding[] = []\n\n\tfor (const harness of harnessRegistry) {\n\t\tfor (const artifact of harness.project.nonstandard ?? []) {\n\t\t\tfor (const path of artifactFiles(root, artifact)) {\n\t\t\t\tconst kind =\n\t\t\t\t\tartifact.kind === 'rule' && path.endsWith('.mdc') && !scopedByGlobs(readFileSync(join(root, path), 'utf8'))\n\t\t\t\t\t\t? 'instructions'\n\t\t\t\t\t\t: artifact.kind\n\t\t\t\tconst problem = problemOf[kind]\n\t\t\t\tconst { detail, repair } = repairFor(problem)\n\t\t\t\tfindings.push({ path, problem, detail, repair: repair({ file: path }, cli) })\n\t\t\t}\n\t\t}\n\t}\n\n\t// No dedupe: one artifact is declared by one harness, and `harness-registry.test.ts` holds that\n\t// invariant. Collapsing a duplicate here would hide the registry mistake rather than surface it.\n\treturn findings.sort((left, right) => left.path.localeCompare(right.path))\n}\n","import { cpSync, lstatSync, mkdirSync, readdirSync, realpathSync, rmSync, symlinkSync } from 'node:fs'\nimport { platform } from 'node:os'\nimport { dirname, join, relative, resolve } from 'node:path'\nimport type { Harness, HarnessName } from '../harness-registry/harness-registry.ts'\n\nfunction skillNames(skillsDirectory: string): string[] {\n\treturn readdirSync(skillsDirectory, { withFileTypes: true })\n\t\t.filter((entry) => entry.isDirectory())\n\t\t.map((entry) => entry.name)\n\t\t.sort()\n}\n\nfunction pathOccupied(path: string): boolean {\n\ttry {\n\t\tlstatSync(path)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Whether `target` is a symbolic link that resolves to `canonical`. Resolved paths are compared\n * rather than link text, so a link a user wrote as an absolute path, one this tool wrote as a\n * Windows junction, and one reached through a symbolic link in a parent directory all read as the\n * correct bridge. A link that no longer resolves is not.\n */\nexport function linksTo(target: string, canonical: string): boolean {\n\ttry {\n\t\tif (!lstatSync(target).isSymbolicLink()) return false\n\t\treturn realpathSync(target) === realpathSync(canonical)\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/** Creates the canonical directory when absent, so a fresh repository reports zero skills. */\nexport function countSkills(canonicalSkills: string): number {\n\tmkdirSync(canonicalSkills, { recursive: true })\n\treturn skillNames(canonicalSkills).length\n}\n\n/**\n * A junction is the only link Windows grants an unprivileged process, and Node resolves a relative\n * junction target against the process directory rather than the link's own, so Windows is given the\n * absolute path. Every other platform keeps the link relative, so it survives the repository moving.\n */\nfunction linkTarget(target: string, canonicalSkills: string): string {\n\treturn platform() === 'win32' ? resolve(canonicalSkills) : relative(dirname(target), canonicalSkills)\n}\n\n/**\n * Which conflicting targets a run may replace. `true` is every one of them; a list names them\n * individually, so a run invoked to fix one bridge cannot reach past it to another. `false` and an\n * empty list both mean none, which is what makes a conflict stop the run.\n */\nexport type ForceSelection = boolean | readonly string[]\n\n/**\n * Translates what the option parser hands back into a selection. A valueless `--force` arrives as\n * the literal string `true` — that is how the parser encodes a flag given no value — and means\n * every conflicting target, which is what keeps an existing `init --force` doing what it always did.\n * Anything else is a comma-separated list of targets.\n */\nexport function parseForce(value: string): ForceSelection {\n\tif (value === 'true') return true\n\tif (value === 'false') return false\n\treturn value\n\t\t.split(',')\n\t\t.map((target) => target.trim())\n\t\t.filter((target) => target.length > 0)\n}\n\nexport type ProjectSkillsOptions = {\n\troot: string\n\tcanonicalSkills: string\n\tharnesses: readonly Harness[]\n\tcopy: boolean\n\tforce: ForceSelection\n}\n\nexport type ProjectSkillsResult = {\n\t/** Harnesses whose target now points at the canonical directory. */\n\tlinked: HarnessName[]\n\t/** Harnesses left untouched because their target conflicts and `--force` did not name it. */\n\tskipped: HarnessName[]\n}\n\n/**\n * Targets are matched against what the caller typed, resolved against the repository root, so the\n * repo-relative form the conflict message prints is the form that selects it back — and an absolute\n * path naming the same directory selects it too.\n */\nfunction namesTarget(root: string, target: string, given: string): boolean {\n\treturn resolve(root, given) === resolve(target)\n}\n\n/**\n * Points every harness that cannot read `.agents/skills` at the canonical directory. Symlinking is\n * preferred; a platform that refuses the link falls back to a copy.\n */\nexport function projectSkills({\n\troot,\n\tcanonicalSkills,\n\tharnesses,\n\tcopy,\n\tforce,\n}: ProjectSkillsOptions): ProjectSkillsResult {\n\tconst projections = harnesses\n\t\t.filter((harness) => harness.project.skillsDirectory)\n\t\t.map((harness) => ({\n\t\t\tharness,\n\t\t\ttarget: join(root, harness.project.skillsDirectory as string),\n\t\t}))\n\n\tconst named = Array.isArray(force) ? force : []\n\t// A name matching no projection target at all is a typo, not a narrower run: silently forcing\n\t// nothing would surface as the conflict error the flag was reached for, blaming the wrong thing.\n\tconst unknown = named.filter((given) => !projections.some(({ target }) => namesTarget(root, target, given)))\n\tif (unknown.length) {\n\t\tthrow new Error(\n\t\t\t`No skill target matches:\\n${unknown.map((given) => `- ${given}`).join('\\n')}\\n` +\n\t\t\t\t`Targets for the enabled harnesses:\\n${projections.map(({ target }) => `- ${relative(root, target)}`).join('\\n')}`,\n\t\t)\n\t}\n\n\tconst forces = (target: string): boolean => force === true || named.some((given) => namesTarget(root, target, given))\n\n\tconst conflicts = projections.filter(({ target }) => pathOccupied(target) && !linksTo(target, canonicalSkills))\n\tconst blocked = conflicts.filter(({ target }) => !forces(target))\n\n\t// Nothing was named, so nothing distinguishes one conflict from another: stop before writing.\n\t// Once a target IS named, the unnamed conflicts are the ones this run was told to leave alone,\n\t// so they are skipped and reported rather than turned into a refusal that writes nothing at all.\n\tif (blocked.length && named.length === 0) {\n\t\tthrow new Error(\n\t\t\t`Refusing to replace existing skill targets:\\n${blocked.map(({ target }) => `- ${relative(root, target)}`).join('\\n')}`,\n\t\t)\n\t}\n\n\tconst skipped = new Set(blocked.map(({ harness }) => harness.name))\n\n\tfor (const { harness, target } of projections) {\n\t\tif (skipped.has(harness.name)) continue\n\t\tmkdirSync(dirname(target), { recursive: true })\n\t\tif (linksTo(target, canonicalSkills)) continue\n\t\tif (pathOccupied(target)) rmSync(target, { recursive: true, force: true })\n\t\tif (copy) {\n\t\t\tcpSync(canonicalSkills, target, { recursive: true })\n\t\t\tcontinue\n\t\t}\n\t\ttry {\n\t\t\tsymlinkSync(linkTarget(target, canonicalSkills), target, platform() === 'win32' ? 'junction' : undefined)\n\t\t} catch (error) {\n\t\t\tif (pathOccupied(target)) throw error\n\t\t\tcpSync(canonicalSkills, target, { recursive: true })\n\t\t}\n\t}\n\n\treturn {\n\t\tlinked: projections.filter(({ harness }) => !skipped.has(harness.name)).map(({ harness }) => harness.name),\n\t\tskipped: projections.filter(({ harness }) => skipped.has(harness.name)).map(({ harness }) => harness.name),\n\t}\n}\n","import { type Dirent, existsSync, readdirSync } from 'node:fs'\nimport { join, posix } from 'node:path'\n\n/** Directories that never hold instructions this repository is responsible for bridging. */\nconst pruned = new Set(['node_modules'])\n\n/**\n * Every directory below `root` holding an `AGENTS.md`, repository-relative and POSIX-separated,\n * with the root itself as `''`. Sorted, root first.\n *\n * Dot-directories are pruned along with `node_modules`. That is not only a speed measure:\n * `.agents/AGENTS.md` is canonical shared instructions rather than a subtree-scoped file, and\n * bridging it would claim a scope it does not have.\n */\nexport function agentsFileDirectories(root: string): string[] {\n\tconst found: string[] = []\n\n\tconst walk = (relative: string): void => {\n\t\tif (existsSync(join(root, relative, 'AGENTS.md'))) found.push(relative)\n\t\tlet entries: Dirent[]\n\t\ttry {\n\t\t\tentries = readdirSync(join(root, relative), { withFileTypes: true })\n\t\t} catch {\n\t\t\treturn\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tif (!entry.isDirectory() || entry.name.startsWith('.') || pruned.has(entry.name)) continue\n\t\t\twalk(relative ? posix.join(relative, entry.name) : entry.name)\n\t\t}\n\t}\n\n\twalk('')\n\treturn found.sort()\n}\n","import { lstatSync, readFileSync, readlinkSync } from 'node:fs'\nimport { join, posix } from 'node:path'\nimport type { Harness, HarnessName } from '../harness-registry/harness-registry.ts'\nimport type { InstructionBridge } from '../harness-registry/instruction-bridge.ts'\nimport { agentsFileDirectories } from './agents-files.ts'\nimport type { BridgeFinding } from './diagnose-bridges.ts'\nimport { type InstructionProblem, repairFor } from './doctor-guidance.ts'\nimport { parseJsonWithComments } from './json-with-comments.ts'\n\n/** What is on disk where an instruction bridge belongs. */\nexport type InstructionKind = 'import' | 'symlink' | 'settings-entry' | 'file' | 'none'\n/**\n * `unbridged` is the case a skills bridge has no equivalent of: the file is there, so nothing looks\n * wrong, and it names `AGENTS.md` nowhere — a `CLAUDE.md` overwritten with real content, or a\n * settings file another tool rewrote.\n */\nexport type InstructionStatus = 'ok' | 'missing' | 'unbridged' | 'unreadable'\n\nexport type InstructionReport = {\n\tharness: HarnessName\n\t/** Repository-relative path of the file that carries the bridge. */\n\tpath: string\n\tkind: InstructionKind\n\tstatus: InstructionStatus\n}\n\nexport const canonicalInstructions = 'AGENTS.md'\n\ntype Inspection = {\n\tkind: InstructionKind\n\tstatus: InstructionStatus\n\tproblem?: InstructionProblem\n}\n\n/**\n * An import bridges the `AGENTS.md` beside it and nothing else, so the check is per directory\n * holding one rather than per harness. A line of its own is enough: the harness reference allows\n * Claude-specific notes below the import, and a file that carries the import plus notes still\n * bridges.\n */\nfunction inspectImport(target: string): Inspection {\n\tlet stats: ReturnType<typeof lstatSync>\n\ttry {\n\t\tstats = lstatSync(target)\n\t} catch {\n\t\treturn { kind: 'none', status: 'missing', problem: 'instructions-missing' }\n\t}\n\n\tif (stats.isSymbolicLink()) {\n\t\treturn readlinkSync(target) === canonicalInstructions\n\t\t\t? { kind: 'symlink', status: 'ok' }\n\t\t\t: { kind: 'symlink', status: 'unbridged', problem: 'instructions-unbridged' }\n\t}\n\n\tconst imported = readFileSync(target, 'utf8')\n\t\t.split('\\n')\n\t\t.some((line) => line.trim() === `@${canonicalInstructions}`)\n\treturn imported\n\t\t? { kind: 'import', status: 'ok' }\n\t\t: { kind: 'file', status: 'unbridged', problem: 'instructions-unbridged' }\n}\n\n/** Reads a dotted key out of parsed JSON without asserting anything about the rest of the file. */\nfunction valueAt(settings: unknown, key: string): unknown {\n\treturn key.split('.').reduce<unknown>((value, segment) => {\n\t\tif (typeof value !== 'object' || value === null) return undefined\n\t\treturn (value as Record<string, unknown>)[segment]\n\t}, settings)\n}\n\nfunction inspectSettingsEntry(target: string, key: string): Inspection {\n\tlet source: string\n\ttry {\n\t\tsource = readFileSync(target, 'utf8')\n\t} catch {\n\t\treturn { kind: 'none', status: 'missing', problem: 'instructions-missing' }\n\t}\n\n\tconst settings = parseJsonWithComments(source)\n\tif (settings === undefined) return { kind: 'file', status: 'unreadable', problem: 'instructions-unreadable' }\n\n\tconst entry = valueAt(settings, key)\n\treturn Array.isArray(entry) && entry.includes(canonicalInstructions)\n\t\t? { kind: 'settings-entry', status: 'ok' }\n\t\t: { kind: 'file', status: 'unbridged', problem: 'instructions-unbridged' }\n}\n\n/** Every path an instruction bridge of this kind belongs at, in report order. */\nfunction pathsFor(root: string, bridge: InstructionBridge): string[] {\n\tif (bridge.kind !== 'import') return [bridge.path]\n\treturn agentsFileDirectories(root).map((directory) => (directory ? posix.join(directory, bridge.path) : bridge.path))\n}\n\nfunction inspect(target: string, bridge: InstructionBridge): Inspection {\n\treturn bridge.kind === 'import' ? inspectImport(target) : inspectSettingsEntry(target, bridge.key)\n}\n\n/**\n * Reports whether every enabled harness can still read `AGENTS.md`.\n *\n * Losing one of these is quieter than losing a skills bridge and costs more: a harness with no\n * instruction bridge reads none of the repository's instructions, and says nothing about it. The\n * checks are read-only, and no repair is a command — rewriting an instruction file is the `init`\n * skill's judgment.\n */\nexport function diagnoseInstructions(\n\troot: string,\n\tharnesses: readonly Harness[],\n\tcli: string,\n): { instructions: InstructionReport[]; findings: BridgeFinding[] } {\n\t// Project scope only: `doctor` diagnoses a repository, and the user-scope bridges are neither\n\t// written nor read by this tool.\n\tconst bridged = harnesses\n\t\t.map((harness) => ({ name: harness.name, bridge: harness.project.instructionBridge }))\n\t\t.filter((harness): harness is { name: HarnessName; bridge: InstructionBridge } => Boolean(harness.bridge))\n\n\tconst instructions: InstructionReport[] = []\n\tconst findings: BridgeFinding[] = []\n\tif (!bridged.length) return { instructions, findings }\n\n\t// Only reported when something bridges into it. Every other harness reads `AGENTS.md` where it\n\t// lies, so its absence is a repository that has not run `init` rather than a broken bridge.\n\tif (!agentsFileDirectories(root).includes('')) {\n\t\tconst { detail, repair } = repairFor('no-instructions')\n\t\tfindings.push({\n\t\t\tpath: canonicalInstructions,\n\t\t\tproblem: 'no-instructions',\n\t\t\tdetail,\n\t\t\trepair: repair({ file: canonicalInstructions }, cli),\n\t\t})\n\t}\n\n\tfor (const { name, bridge } of bridged) {\n\t\tfor (const path of pathsFor(root, bridge)) {\n\t\t\tconst inspection = inspect(join(root, path), bridge)\n\t\t\tinstructions.push({ harness: name, path, kind: inspection.kind, status: inspection.status })\n\t\t\tif (inspection.problem) {\n\t\t\t\tconst { detail, repair } = repairFor(inspection.problem)\n\t\t\t\tfindings.push({ path, problem: inspection.problem, detail, repair: repair({ file: path }, cli) })\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { instructions, findings }\n}\n","import { execFileSync } from 'node:child_process'\nimport { join } from 'node:path'\nimport { filesUnder } from './directory-files.ts'\n\n/** Which side of a diverged bridge moved since the last commit where the two agreed. */\nexport type DivergenceDirection = 'bridge' | 'canonical' | 'both' | 'unknown'\n\n/** Whether a path is tracked, and if so whether its skip-worktree bit is still set. */\nexport type TrackingState = 'untracked' | 'tracked' | 'skip-worktree'\n\nfunction git(root: string, args: string[], input?: string): string | undefined {\n\ttry {\n\t\treturn execFileSync('git', args, {\n\t\t\tcwd: root,\n\t\t\tencoding: 'utf8',\n\t\t\t...(input === undefined ? {} : { input }),\n\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t})\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nfunction lines(output: string | undefined): string[] {\n\treturn (output ?? '').split('\\n').filter(Boolean)\n}\n\n/**\n * Reads git state for the two sides of a bridge. Every method degrades to \"cannot tell\" rather than\n * throwing, so `doctor` still reports on a tarball, a worktree without git, or a repository with no\n * commits.\n */\nexport class GitBridgeState {\n\t/** Path of `root` relative to the repository root, `''` at the top, `undefined` outside a repository. */\n\tprivate readonly prefix: string | undefined\n\n\tconstructor(private readonly root: string) {\n\t\tthis.prefix = git(root, ['rev-parse', '--show-prefix'])?.trim()\n\t}\n\n\t/**\n\t * `init --copy` marks a bridge skip-worktree so a machine-local copy over a tracked symlink stops\n\t * showing as a pending change. The bit is a hint that some checkouts clear, so it is verified\n\t * rather than assumed.\n\t */\n\ttrackingOf(path: string): TrackingState {\n\t\tif (this.prefix === undefined) return 'untracked'\n\t\tconst entries = lines(git(this.root, ['ls-files', '-v', '--', path]))\n\t\tif (!entries.length) return 'untracked'\n\t\treturn entries.every((entry) => entry.startsWith('S')) ? 'skip-worktree' : 'tracked'\n\t}\n\n\t/**\n\t * Whether a `.gitignore` rule matches this path. Asked of git rather than matched against the\n\t * file, so a rule on a parent directory — the common way a bridge gets ignored — is caught.\n\t * Outside a repository nothing is ignored, which is the same \"cannot tell\" degradation the rest\n\t * of this class uses.\n\t */\n\tisIgnored(path: string): boolean {\n\t\tif (this.prefix === undefined) return false\n\t\treturn git(this.root, ['check-ignore', '-q', '--', path]) !== undefined\n\t}\n\n\t/**\n\t * The newest commits that touched any of these paths, newest first. Empty outside a repository,\n\t * which is the same \"cannot tell\" degradation as everything else here.\n\t *\n\t * Bounded like `lastAgreedTree` is: a baseline that is not in the recent history of the two files\n\t * is not a baseline anyone can act on, and walking a whole repository to prove its absence costs\n\t * more than the answer is worth.\n\t */\n\tcommitsTouching(paths: readonly string[]): string[] {\n\t\tif (this.prefix === undefined) return []\n\t\treturn lines(git(this.root, ['rev-list', '-n', '200', 'HEAD', '--', ...paths]))\n\t}\n\n\t/** One file's content at one commit, or `undefined` when it did not exist there. */\n\tcontentAt(commit: string, path: string): string | undefined {\n\t\tif (this.prefix === undefined) return undefined\n\t\treturn git(this.root, ['show', `${commit}:${this.prefix}${path}`])\n\t}\n\n\t/**\n\t * Names the side that moved by finding the newest commit whose two trees agreed, then asking which\n\t * of the working directories still matches it.\n\t */\n\tdirectionOf(bridge: string, canonical: string): DivergenceDirection {\n\t\tif (this.prefix === undefined) return 'unknown'\n\t\tconst base = this.lastAgreedTree(bridge, canonical)\n\t\tif (base === undefined) return 'unknown'\n\t\tconst bridgeMoved = !this.matchesTree(bridge, base)\n\t\tconst canonicalMoved = !this.matchesTree(canonical, base)\n\t\tif (bridgeMoved && canonicalMoved) return 'both'\n\t\tif (bridgeMoved) return 'bridge'\n\t\treturn 'canonical'\n\t}\n\n\t/** The tree of the newest commit where both paths held identical content. */\n\tprivate lastAgreedTree(bridge: string, canonical: string): string | undefined {\n\t\tfor (const commit of lines(git(this.root, ['rev-list', '-n', '200', 'HEAD', '--', bridge, canonical]))) {\n\t\t\tconst bridgeObject = git(this.root, ['rev-parse', '-q', '--verify', `${commit}:${this.prefix}${bridge}`])?.trim()\n\t\t\tif (!bridgeObject) continue\n\t\t\tconst canonicalObject = git(this.root, [\n\t\t\t\t'rev-parse',\n\t\t\t\t'-q',\n\t\t\t\t'--verify',\n\t\t\t\t`${commit}:${this.prefix}${canonical}`,\n\t\t\t])?.trim()\n\t\t\tif (bridgeObject === canonicalObject) return bridgeObject\n\t\t}\n\t\treturn undefined\n\t}\n\n\t/**\n\t * Compares a working directory against a git tree by object id. `hash-object` is used rather than\n\t * hashing in-process so the comparison follows whatever object format the repository uses.\n\t */\n\tprivate matchesTree(directory: string, tree: string): boolean {\n\t\tconst expected = new Map(\n\t\t\tlines(git(this.root, ['ls-tree', '-r', tree])).map((entry) => {\n\t\t\t\tconst [meta, path] = entry.split('\\t')\n\t\t\t\treturn [path, (meta as string).split(' ')[2] as string] as const\n\t\t\t}),\n\t\t)\n\t\tconst files = filesUnder(join(this.root, directory))\n\t\tif (files.length !== expected.size || files.some((file) => !expected.has(file))) return false\n\t\tconst hashed = lines(\n\t\t\tgit(this.root, ['hash-object', '--stdin-paths'], files.map((file) => join(directory, file)).join('\\n')),\n\t\t)\n\t\treturn hashed.length === files.length && files.every((file, index) => expected.get(file) === hashed[index])\n\t}\n}\n","import { lstatSync, readFileSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { type HarnessName, selectHarnesses } from '../harness-registry/harness-registry.ts'\nimport { linksTo } from '../skill-projection/skill-projection.ts'\nimport { diagnoseInstructions, type InstructionReport } from './diagnose-instructions.ts'\nimport { filesUnder } from './directory-files.ts'\nimport { type BridgeProblem, type DoctorProblem, type RepairAction, repairFor } from './doctor-guidance.ts'\nimport { type DivergenceDirection, GitBridgeState } from './git-bridge-state.ts'\n\nexport type BridgeKind = 'symlink' | 'copy' | 'file' | 'none'\nexport type BridgeStatus = 'ok' | 'missing' | 'degraded' | 'stale' | 'diverged'\n\nexport type BridgeReport = {\n\tharness: HarnessName\n\t/** Repository-relative, as the harness registry declares it. */\n\tpath: string\n\tkind: BridgeKind\n\tstatus: BridgeStatus\n}\n\nexport type BridgeFinding = {\n\tpath: string\n\t/**\n\t * Which problem this is. Emitted so a caller routes on a name rather than by matching `detail`\n\t * prose, which would break the moment the wording is improved.\n\t */\n\tproblem: DoctorProblem\n\tdetail: string\n\t/** What repairs this finding, already carrying the bridge path. Empty `command` means judgment. */\n\trepair: RepairAction\n}\n\nexport type DivergenceReport = {\n\tpath: string\n\tdirection: DivergenceDirection\n}\n\nexport type DiagnoseOptions = {\n\troot: string\n\tharnesses?: HarnessName[]\n\t/** How to name this tool in the repair commands. */\n\tcli: string\n}\n\nexport type DiagnoseResult = {\n\tbridges: BridgeReport[]\n\t/**\n\t * The instruction bridges into `AGENTS.md`, kept out of `bridges` on purpose: a different `kind`\n\t * and `status` vocabulary, and a repair that is the `init` skill rather than a command.\n\t */\n\tinstructions: InstructionReport[]\n\tdivergence: DivergenceReport[]\n\tfindings: BridgeFinding[]\n}\n\nconst canonicalPath = '.agents/skills'\n\nfunction pathState(target: string): 'missing' | 'symlink' | 'directory' | 'file' {\n\ttry {\n\t\tconst stats = lstatSync(target)\n\t\tif (stats.isSymbolicLink()) return 'symlink'\n\t\treturn stats.isDirectory() ? 'directory' : 'file'\n\t} catch {\n\t\treturn 'missing'\n\t}\n}\n\nfunction isDirectory(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isDirectory()\n\t} catch {\n\t\treturn false\n\t}\n}\n\nfunction sameContent(left: string, right: string): boolean {\n\tconst leftFiles = filesUnder(left)\n\tconst rightFiles = filesUnder(right)\n\treturn (\n\t\tleftFiles.length === rightFiles.length &&\n\t\tleftFiles.every(\n\t\t\t(file, index) =>\n\t\t\t\trightFiles[index] === file && readFileSync(join(left, file)).equals(readFileSync(join(right, file))),\n\t\t)\n\t)\n}\n\ntype Inspection = {\n\tkind: BridgeKind\n\tstatus: BridgeStatus\n\tproblem?: BridgeProblem\n\tdirection?: DivergenceDirection\n}\n\nfunction inspect(target: string, path: string, canonical: string, git: GitBridgeState): Inspection {\n\tswitch (pathState(target)) {\n\t\tcase 'missing':\n\t\t\treturn { kind: 'none', status: 'missing', problem: 'missing' }\n\t\t// The flagship Windows failure: git with `core.symlinks=false` writes the link out as a\n\t\t// regular file holding the target path, and the harness silently loads nothing.\n\t\tcase 'file':\n\t\t\treturn { kind: 'file', status: 'degraded', problem: 'degraded' }\n\t\t// The very test `init` uses to decide a projection is already correct, so a bridge `init` would\n\t\t// leave alone never reports as one `doctor` wants rebuilt.\n\t\tcase 'symlink':\n\t\t\treturn linksTo(target, canonical)\n\t\t\t\t? { kind: 'symlink', status: 'ok' }\n\t\t\t\t: { kind: 'symlink', status: 'stale', problem: 'stale' }\n\t\tdefault: {\n\t\t\tif (!sameContent(target, canonical)) {\n\t\t\t\tconst direction = git.directionOf(path, canonicalPath)\n\t\t\t\treturn { kind: 'copy', status: 'diverged', problem: `diverged-${direction}`, direction }\n\t\t\t}\n\t\t\t// A copy over a tracked symlink only stays out of `git status` while its skip-worktree bit\n\t\t\t// survives, and some checkout and merge operations clear it.\n\t\t\treturn git.trackingOf(path) === 'tracked'\n\t\t\t\t? { kind: 'copy', status: 'ok', problem: 'unpinned-copy' }\n\t\t\t\t: { kind: 'copy', status: 'ok' }\n\t\t}\n\t}\n}\n\n/**\n * Reports whether every bridge this repository needs still resolves: the skills bridges into\n * `.agents/skills`, and the instruction bridges into `AGENTS.md`. Read-only: nothing is created,\n * moved, or repaired, so the caller decides what to run from the repair each finding carries.\n */\nexport function diagnoseBridges({ root, harnesses: preferred = [], cli }: DiagnoseOptions): DiagnoseResult {\n\tconst canonical = join(root, '.agents', 'skills')\n\tconst git = new GitBridgeState(root)\n\tconst selected = selectHarnesses(root, preferred)\n\tconst bridged = selected\n\t\t.map((harness) => ({ name: harness.name, skillsDirectory: harness.project.skillsDirectory }))\n\t\t.filter((harness): harness is { name: HarnessName; skillsDirectory: string } => Boolean(harness.skillsDirectory))\n\n\tconst bridges: BridgeReport[] = []\n\tconst divergence: DivergenceReport[] = []\n\tconst findings: BridgeFinding[] = []\n\n\tif (!isDirectory(canonical)) {\n\t\tconst { detail, repair } = repairFor('no-canonical')\n\t\tfindings.push({\n\t\t\tpath: canonicalPath,\n\t\t\tproblem: 'no-canonical',\n\t\t\tdetail,\n\t\t\trepair: repair({ file: canonicalPath }, cli),\n\t\t})\n\t}\n\n\tfor (const harness of bridged) {\n\t\tconst path = harness.skillsDirectory\n\t\tconst inspection = inspect(join(root, path), path, canonical, git)\n\t\tbridges.push({ harness: harness.name, path, kind: inspection.kind, status: inspection.status })\n\t\tif (inspection.direction) divergence.push({ path, direction: inspection.direction })\n\t\tif (inspection.problem) {\n\t\t\tconst { detail, repair } = repairFor(inspection.problem)\n\t\t\tfindings.push({ path, problem: inspection.problem, detail, repair: repair({ file: path }, cli) })\n\t\t}\n\t}\n\n\tconst instructions = diagnoseInstructions(root, selected, cli)\n\tfindings.push(...instructions.findings)\n\n\treturn { bridges, instructions: instructions.instructions, divergence, findings }\n}\n","import { homedir } from 'node:os'\nimport type { cli } from 'clibuilder'\nimport { command, exitCodes, z } from 'clibuilder'\nimport { binPath, parseFormat, writeResult } from '../command-output/command-output.ts'\nimport { type ConfigurationFinding, diagnoseConfiguration } from '../diagnose-configuration/diagnose-configuration.ts'\nimport { diagnoseMcp } from '../diagnose-mcp/diagnose-mcp.ts'\nimport { diagnoseNonstandard } from '../diagnose-nonstandard/diagnose-nonstandard.ts'\nimport { parseHarnesses } from '../harness-registry/harness-registry.ts'\nimport { type DiagnoseResult, diagnoseBridges } from './diagnose-bridges.ts'\nimport { commandInvocation, type DoctorProblem, type RepairAction } from './doctor-guidance.ts'\nimport { GitBridgeState } from './git-bridge-state.ts'\n\nexport type DoctorReport = {\n\tbin: string\n\tbridges: DiagnoseResult['bridges']\n\tinstructions: DiagnoseResult['instructions']\n\tdivergence?: DiagnoseResult['divergence']\n\t/**\n\t * The repair is lifted out into `help`, so a finding row stays to the diagnosis itself. `problem`\n\t * stays on the row: it is how a caller routes without parsing `detail` prose.\n\t */\n\tfindings: { path: string; problem: DoctorProblem; detail: string }[] | string\n\t/**\n\t * One entry per distinct repair. Two fields rather than a sentence, because the caller has to\n\t * tell an executable repair from an instruction, and `RepairAction` is where that lives.\n\t *\n\t * Both keys are always emitted, `command` as an empty string when there is none. An optional key\n\t * would drop the whole array out of TOON's tabular form into the nested list form — worse for\n\t * exactly the consumer the default format exists for.\n\t */\n\thelp?: RepairAction[]\n}\n\n/**\n * AXI §5: the healthy answer states the zero with context, so an agent does not re-run with other\n * flags to confirm that an empty section really meant \"nothing wrong\".\n */\nexport function buildDoctorReport(\n\tbin: string,\n\tresult: DiagnoseResult,\n\tconfiguration: ConfigurationFinding[] = [],\n): DoctorReport {\n\tconst findings = [...result.findings, ...configuration]\n\t// Both sections are bridges, so the healthy line counts them together rather than making a reader\n\t// add up two numbers to learn that nothing is wrong.\n\tif (!findings.length) {\n\t\tconst count = result.bridges.length + result.instructions.length\n\t\tconst bridges = count === 1 ? 'the 1 bridge resolves' : `all ${count} bridges resolve`\n\t\treturn {\n\t\t\tbin,\n\t\t\tbridges: result.bridges,\n\t\t\tinstructions: result.instructions,\n\t\t\tfindings: `0 problems found — ${bridges} and the configuration around them is current`,\n\t\t}\n\t}\n\n\treturn {\n\t\tbin,\n\t\tbridges: result.bridges,\n\t\tinstructions: result.instructions,\n\t\t...(result.divergence.length ? { divergence: result.divergence } : {}),\n\t\tfindings: findings.map(({ path, problem, detail }) => ({ path, problem, detail })),\n\t\t// Deduped on the whole pair: several findings often share one repair, and repeating it reads as\n\t\t// more work than there is. Nothing wraps a repair — a `Run …` around every one of them is what\n\t\t// this report used to do, and most repairs are not commands.\n\t\thelp: [\n\t\t\t...new Map(\n\t\t\t\tfindings.map((finding) => [`${finding.repair.command}\\u0000${finding.repair.instruction}`, finding.repair]),\n\t\t\t).values(),\n\t\t],\n\t}\n}\n\nexport const doctorCommand: cli.Command = command({\n\tname: 'doctor',\n\tdescription:\n\t\t\"Report whether this repository's harness bridges still resolve — skills into .agents/skills, instructions into AGENTS.md. Read-only.\",\n\toptions: {\n\t\troot: {\n\t\t\tdescription: 'Repository or package directory. Defaults to the current directory.',\n\t\t\ttype: z.optional(z.string()),\n\t\t},\n\t\tharness: {\n\t\t\tdescription: 'Comma-separated harnesses to check in addition to Claude Code and Cursor, e.g. codex,gemini-cli.',\n\t\t\ttype: z.optional(z.string()),\n\t\t},\n\t\tformat: {\n\t\t\tdescription: 'Output format: toon (default), json, or text for a human-readable report.',\n\t\t\ttype: z.optional(z.string()),\n\t\t\tdefault: 'toon',\n\t\t},\n\t},\n\trun(args) {\n\t\ttry {\n\t\t\tconst format = parseFormat(args.format)\n\t\t\tconst harnesses = parseHarnesses(args.harness)\n\t\t\tconst root = args.root ?? process.cwd()\n\t\t\tconst result = diagnoseBridges({\n\t\t\t\troot,\n\t\t\t\t...(harnesses.length ? { harnesses } : {}),\n\t\t\t\tcli: commandInvocation,\n\t\t\t})\n\t\t\tconst git = new GitBridgeState(root)\n\t\t\tconst configuration = [\n\t\t\t\t...diagnoseConfiguration({ root, git, cli: commandInvocation }),\n\t\t\t\t...diagnoseMcp({ root, git, cli: commandInvocation }),\n\t\t\t\t...diagnoseNonstandard({ root, cli: commandInvocation }),\n\t\t\t]\n\t\t\t// Exit stays 0 even with findings: the diagnosis succeeded, and a non-zero code reads to an\n\t\t\t// agent as \"this command is broken, try something else\".\n\t\t\twriteResult(buildDoctorReport(binPath(homedir(), process.argv[1]), result, configuration), format)\n\t\t\treturn exitCodes.success\n\t\t} catch (error) {\n\t\t\tprocess.stderr.write(`error: ${error instanceof Error ? error.message : 'Harness diagnosis failed.'}\\n`)\n\t\t\t// Returned, not written: a command that writes the code reports its failure past `run`\n\t\t\t// rather than to it, leaving a caller that is not the process no way to learn of it.\n\t\t\treturn exitCodes.error\n\t\t}\n\t},\n})\n","import { join } from 'node:path'\nimport { type HarnessName, selectHarnesses } from '../harness-registry/harness-registry.ts'\nimport { countSkills, type ForceSelection, projectSkills } from '../skill-projection/skill-projection.ts'\n\nexport type InitializeOptions = {\n\troot: string\n\tharnesses?: HarnessName[]\n\tcopy?: boolean\n\tforce?: ForceSelection\n}\nexport type InitializeResult = {\n\troot: string\n\tharnesses: HarnessName[]\n\tnative: HarnessName[]\n\tlinked: HarnessName[]\n\t/** Enabled harnesses left untouched because their target conflicts and `--force` did not name it. */\n\tskipped: HarnessName[]\n\t/** Enabled harnesses whose name has been superseded, as `{ name, replacedBy }`. */\n\tdeprecated: { name: HarnessName; replacedBy: HarnessName }[]\n\tskills: number\n\tcopied: boolean\n}\n\nexport function initializeHarnesses({\n\troot,\n\tharnesses: preferred = [],\n\tcopy = false,\n\tforce = false,\n}: InitializeOptions): InitializeResult {\n\tconst canonicalSkills = join(root, '.agents', 'skills')\n\tconst skills = countSkills(canonicalSkills)\n\tconst harnesses = selectHarnesses(root, preferred)\n\tconst { linked, skipped } = projectSkills({ root, canonicalSkills, harnesses, copy, force })\n\n\treturn {\n\t\troot,\n\t\tharnesses: harnesses.map((harness) => harness.name),\n\t\tnative: harnesses.filter((harness) => !harness.project.skillsDirectory).map((harness) => harness.name),\n\t\tlinked,\n\t\tskipped,\n\t\tdeprecated: harnesses\n\t\t\t.filter((harness) => harness.deprecated)\n\t\t\t.map((harness) => ({ name: harness.name, replacedBy: harness.deprecated as HarnessName })),\n\t\tskills,\n\t\tcopied: copy,\n\t}\n}\n","import type { cli } from 'clibuilder'\nimport { command, exitCodes, z } from 'clibuilder'\nimport { parseFormat, writeResult } from '../command-output/command-output.ts'\nimport { doctorCommand } from '../diagnose-bridges/doctor.command.ts'\nimport { parseHarnesses } from '../harness-registry/harness-registry.ts'\nimport { parseForce } from '../skill-projection/skill-projection.ts'\nimport { initializeHarnesses } from './initialize-harnesses.ts'\n\nexport const initCommand: cli.Command = command({\n\tname: 'init',\n\tdescription: 'Set up canonical agent skills for the harnesses already enabled in a repository.',\n\toptions: {\n\t\troot: {\n\t\t\tdescription: 'Repository or package directory. Defaults to the current directory.',\n\t\t\ttype: z.optional(z.string()),\n\t\t},\n\t\tharness: {\n\t\t\tdescription: 'Comma-separated harnesses to enable in addition to Claude Code and Cursor, e.g. codex,gemini-cli.',\n\t\t\ttype: z.optional(z.string()),\n\t\t},\n\t\tcopy: {\n\t\t\tdescription: 'Copy skills instead of linking them.',\n\t\t\ttype: z.optional(z.boolean()),\n\t\t},\n\t\tforce: {\n\t\t\tdescription:\n\t\t\t\t'Replace conflicting target skill directories. Names the targets to replace, comma-separated (e.g. .claude/skills); given alone it replaces every conflicting target.',\n\t\t\ttype: z.optional(z.string()),\n\t\t},\n\t\tformat: {\n\t\t\tdescription: 'Output format: toon (default), json, or text for a human-readable report.',\n\t\t\ttype: z.optional(z.string()),\n\t\t\tdefault: 'toon',\n\t\t},\n\t},\n\trun(args) {\n\t\ttry {\n\t\t\tconst format = parseFormat(args.format)\n\t\t\tconst harnesses = parseHarnesses(args.harness)\n\t\t\twriteResult(\n\t\t\t\tinitializeHarnesses({\n\t\t\t\t\troot: args.root ?? process.cwd(),\n\t\t\t\t\t...(harnesses.length ? { harnesses } : {}),\n\t\t\t\t\t...(args.copy === undefined ? {} : { copy: args.copy }),\n\t\t\t\t\t...(args.force === undefined ? {} : { force: parseForce(args.force) }),\n\t\t\t\t}),\n\t\t\t\tformat,\n\t\t\t)\n\t\t\treturn exitCodes.success\n\t\t} catch (error) {\n\t\t\tprocess.stderr.write(`error: ${error instanceof Error ? error.message : 'Harness initialization failed.'}\\n`)\n\t\t\t// Returned, not written: a command that writes the code reports its failure past `run`\n\t\t\t// rather than to it, leaving a caller that is not the process no way to learn of it.\n\t\t\treturn exitCodes.error\n\t\t}\n\t},\n})\n\n// Named for the package rather than shortened to `harness`: `repobuddy` mounts every plugin into one\n// command namespace, where `harness` is generic enough to collide with another plugin's.\nexport const harnessCommand: cli.Command = command({\n\tname: 'agent-harness',\n\tdescription: 'Commands for configuring agent harness compatibility.',\n\tcommands: [initCommand, doctorCommand],\n})\n\nexport function activate({ addCommand }: { addCommand(command: typeof harnessCommand): void }): void {\n\taddCommand(harnessCommand)\n}\n","import { readFileSync } from 'node:fs'\nimport { cli, exitCodes } from 'clibuilder'\nimport { depPluginsCommand } from './dep-plugins/dep-plugins.command.ts'\nimport { doctorCommand } from './diagnose-bridges/doctor.command.ts'\nimport { initCommand } from './initialize-harnesses/init.command.ts'\n\n/**\n * Read from the manifest rather than restated here: a version written into this call is a second\n * home for a number that already has one, and it stayed at `0.1.0` for five minor releases.\n *\n * `../package.json` resolves from `src/cli.ts` and from the bundled `dist/cli.mjs` alike — both sit\n * one directory under the package root.\n */\nconst version = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version as string\n\n/**\n * A factory rather than a module-level constant: `cli()` builds state, and state built at import\n * time is shared by every later call in the process.\n */\nfunction app() {\n\treturn cli({\n\t\tname: 'buddy-agent-harness',\n\t\tversion,\n\t\tdescription: 'Initialize agent harness skill compatibility in consumer repositories.',\n\t})\n\t\t.command(initCommand)\n\t\t.command(doctorCommand)\n\t\t.command(depPluginsCommand)\n}\n\n/**\n * The application boundary: argv in, exit code out, nothing read from or written to the process.\n *\n * The exit code is returned rather than written so that a caller which is not the process — a skill\n * launcher, a test — can run a command and learn how it went. `bin/buddy-agent-harness.mjs` is the\n * only place that turns the returned code back into a process outcome.\n *\n * One exit path this return cannot carry: `clibuilder` answers an unknown option or an unknown\n * command by printing help and writing `process.exitCode` itself, returning nothing. `run` reports\n * `0` there, which is why `bin` and the launchers apply the returned code only when it is non-zero.\n */\nexport async function run(argv: string[]): Promise<number> {\n\ttry {\n\t\tconst code = await app().parse<number | undefined>(argv)\n\t\treturn typeof code === 'number' ? code : exitCodes.success\n\t} catch (error) {\n\t\t// stderr, not stdout: `doctor`'s default output is TOON that an agent parses, and an error\n\t\t// line on that stream lands in the middle of what it is parsing.\n\t\tprocess.stderr.write(`error: ${error instanceof Error ? error.message : 'Invalid command.'}\\n`)\n\t\treturn exitCodes.usage\n\t}\n}\n"],"mappings":"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"}
|
package/dist/cli.d.mts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
//#region src/cli.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* The application boundary: argv in, exit code out, nothing read from or written to the process.
|
|
4
|
-
*
|
|
5
|
-
* The exit code is returned rather than written so that a caller which is not the process — a skill
|
|
6
|
-
* launcher, a test — can run a command and learn how it went. `bin/buddy-agent-harness.mjs` is the
|
|
7
|
-
* only place that turns the returned code back into a process outcome.
|
|
8
|
-
*
|
|
9
|
-
* One exit path this return cannot carry: `clibuilder` answers an unknown option or an unknown
|
|
10
|
-
* command by printing help and writing `process.exitCode` itself, returning nothing. `run` reports
|
|
11
|
-
* `0` there, which is why `bin` and the launchers apply the returned code only when it is non-zero.
|
|
12
|
-
*/
|
|
13
|
-
export declare function run(argv: string[]): Promise<number>;
|
|
14
|
-
//#endregion
|
|
15
|
-
//# sourceMappingURL=cli.d.mts.map
|
package/dist/cli.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.mts","names":[],"sources":["../src/cli.ts"],"mappings":";;;;;;;;;;;;wBAyCsB,IAAI,iBAAiB"}
|