/** * Are vigiles's SHIPPED SKILLS actually reachable by the agent in this repo? * * vigiles publishes six user-facing skills (`SHIPPED_SKILLS`) — the teaching * surface. `test-harness` alone answers "which testing tier do I want?", the * question this project's docs are otherwise organized around. They reach the * agent two ways (`docs/agent-setup.md`): * * - the LINKS `vigiles init` commits into `.claude/skills/` (one relative * symlink per skill into `node_modules/vigiles/skills/`, `src/skill-links.ts`) * — a fact about the REPOSITORY, so every clone and container has them once * `npm install` has run; * - the GLOBAL plugin install (`claude plugin install vigiles@vigiles`, into * `~/.claude/plugins/`) — a fact about ONE machine, namespaced * `vigiles:`. * * The failure this module makes loud: `npm install vigiles` ALSO puts those * skills on disk, at `node_modules/vigiles/skills/` (they are in package.json's * `files`, because the npm tarball doubles as the plugin payload). Claude Code * never scans `node_modules`. So a repo that took the dependency but never ran * the plugin install has all six skills present, unreachable, and **silent** — * observed in a real consumer repo, where a full day went into re-deriving what * `test-harness` teaches while it sat three directories away. * * ⚠️ The part that is easy to get wrong, and did get wrong TWICE, in opposite * directions: * * 1. The authoritative record of a `claude plugin install` is the GLOBAL * `~/.claude/plugins/installed_plugins.json`. A repo's `.claude/settings.json` * carries PROJECT-level `enabledPlugins`, which a correctly-installed * user-scope plugin does not appear in. Judging "is it wired?" from * `settings.json` alone reports a working install as broken — the misread * that made this look like an npm packaging bug. * 2. The converse is ALSO false: a project `enabledPlugins` entry does not make * the plugin load. Per the Claude Code docs (Discover plugins → "Configure * team marketplaces"), as of CC v2.1.195 — "A plugin that only the project's * `.claude/settings.json` enables, and that comes from an external source * such as a GitHub repository or npm package, doesn't load until the team * member installs it." vigiles ships from a GitHub marketplace, so that is * exactly our case. Committing `extraKnownMarketplaces` + `enabledPlugins` * makes Claude Code PROMPT each collaborator to install; it does not install. * Confirmed empirically: in a repo whose committed settings.json declares an * external plugin project-level, the global registry had no marketplace * entry, no cache dir, and no install record for it, while a plugin installed * the normal way on the same machine had all three. * * So a project declaration is a THIRD state — declared, not installed — that * still warrants a warning, with a different fix line: the collaborator runs the * install, the repo cannot run it for them. * * Shape follows `./dialect-drift.ts`: pure parsers + a best-effort local read * that NEVER throws + a formatter that returns null when there is nothing to * say, so `vigiles audit` can print it without a new verb, flag, or failure mode. * It is ADVISORY — it never touches the audit score, because reachability is * partly a property of the machine (is the plugin installed? has `npm install` * run?), and a score that moved between a laptop and CI for identical source * would be a lie. */ import type { InstallReader } from "../../core/adapter.js"; /** The plugin id `claude plugin install` records — `@`. */ export declare const VIGILES_PLUGIN_ID = "vigiles@vigiles"; /** * Where a reachable install was found. Deliberately does NOT include a project * `enabledPlugins` entry: that declares the plugin, it does not install it (see * the header). It is reported as {@link SkillReachability.declaredNotInstalled}. */ export type ReachabilitySource = /** `~/.claude/plugins/installed_plugins.json` — what `claude plugin install` writes. */ "global-plugin" /** The repo's own `.claude/skills//SKILL.md` resolves — a link `init` made, or a copy. */ | "repo-skills"; /** The advisory: can the agent see vigiles's skills from this repo? */ export interface SkillReachability { /** True when at least one {@link ReachabilitySource} was found. */ readonly reachable: boolean; /** Every source that resolved, in check order. Empty when un-wired. */ readonly sources: readonly ReachabilitySource[]; /** * The repo's `.claude/settings.json` enables the plugin, but no install was * found. Claude Code will prompt this collaborator to install it; until they * do, it does not load. A distinct state from "nothing configured at all", * because the fix belongs to the person, not the repo. */ readonly declaredNotInstalled: boolean; /** * Shipped skills found under `node_modules/vigiles/skills/` while UNREACHABLE * — present on disk, invisible to the agent. Empty when reachable (there is * nothing stranded if they are wired) or when the package isn't installed yet. */ readonly strandedSkills: readonly string[]; /** * Shipped skills whose `.claude/skills/` entry is a LINK that does not * resolve: the links came with `git clone`, the package they point into did * not. The fix is `npm install`, not re-linking. */ readonly danglingLinks: readonly string[]; /** * Skills the package ships that the repo has not linked while it HAS linked * others — the state after an upgrade adds a skill. Empty unless at least one * shipped skill resolves in the repo. */ readonly unlinkedSkills: readonly string[]; } /** * Does the global registry record a live install of the vigiles plugin? Pure over * the raw `installed_plugins.json` text. An entry with an EMPTY array is a * leftover record, not an install, so it does not count. */ export declare function hasGlobalPluginInstall(installedPluginsJson: string): boolean; /** * Does the repo's `.claude/settings.json` explicitly enable the vigiles plugin? * Pure. An explicit `false` is a deliberate disable and does NOT count. */ export declare function hasEnabledPlugin(settingsJson: string): boolean; /** * Best-effort, read-local reachability check for `vigiles audit`. Returns null * when the question does not apply — the repo does not depend on vigiles, or IS * vigiles — so a non-consumer is never nagged. NEVER throws: every read the * reader performs degrades to "not found". * * 🔴 IT READS THROUGH A REPO-BOUND {@link InstallReader}, NOT `node:fs`. Two of * its reads are of files NO adapter claims (`package.json`, vigiles's own * package under `node_modules`), so the domain performs those and passes the * ANSWER; the rest go through a reader that refuses any path this adapter does * not claim. That is why this module does not import `node:fs` at all. * * Each shipped skill is probed for its own `SKILL.md` rather than by listing * `.claude/skills/` — enumeration is what the reader exists to prevent, and the * names are known: the installed package's when it is installed, else the * shipped list. A directory named after a shipped skill but holding no * `SKILL.md` is not reachable; it was never loadable by the agent. */ export declare function checkSkillReachability(read: InstallReader): SkillReachability | null; /** * The advisory, or null when there is nothing to say. Most specific state * first, each ending on the command that fixes it: * * 1. links that dangle — `npm install`; * 2. nothing reaches the agent — link them (per repo), or install the plugin * (per machine); * 3. only this machine's plugin reaches it — a fresh clone or container will * not have them. Not a failure; said in one line; * 4. some linked, some not (an upgrade added one) — link the rest. */ export declare function formatSkillReachability(r: SkillReachability | null): string | null; //# sourceMappingURL=skill-reachability.d.ts.map