vigiles 29.1.0 → 30.0.1

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.
Files changed (122) hide show
  1. package/dist/adapter-conformance.d.ts +1 -1
  2. package/dist/adapter-conformance.js +106 -25
  3. package/dist/adapter-registry.d.ts +61 -14
  4. package/dist/adapter-registry.js +78 -10
  5. package/dist/adapter.d.ts +23 -2
  6. package/dist/adapter.js +13 -1
  7. package/dist/adapters/claude-code/adapter.d.ts +32 -2
  8. package/dist/adapters/claude-code/adapter.js +44 -23
  9. package/dist/adapters/claude-code/agent-runtime.js +3 -1
  10. package/dist/adapters/claude-code/dialect.js +87 -21
  11. package/dist/adapters/claude-code/effect-region.js +3 -1
  12. package/dist/adapters/claude-code/hook-protocol.js +16 -0
  13. package/dist/adapters/claude-code/instruction-chain.d.ts +25 -0
  14. package/dist/adapters/claude-code/instruction-chain.js +626 -0
  15. package/dist/adapters/claude-code/layout.d.ts +2 -2
  16. package/dist/adapters/claude-code/layout.js +42 -8
  17. package/dist/adapters/claude-code/model-access.d.ts +41 -0
  18. package/dist/adapters/claude-code/model-access.js +46 -0
  19. package/dist/adapters/claude-code/skill-reachability.d.ts +125 -0
  20. package/dist/adapters/claude-code/skill-reachability.js +111 -0
  21. package/dist/adapters/claude-code/skill-runtime.js +3 -1
  22. package/dist/adapters/codex/adapter.d.ts +39 -2
  23. package/dist/adapters/codex/adapter.js +29 -29
  24. package/dist/adapters/codex/dialect.js +11 -6
  25. package/dist/adapters/codex/eval.d.ts +10 -0
  26. package/dist/adapters/codex/eval.js +48 -1
  27. package/dist/adapters/codex/hook-protocol.d.ts +2 -1
  28. package/dist/adapters/codex/hook-protocol.js +10 -0
  29. package/dist/adapters/codex/instruction-chain.d.ts +40 -0
  30. package/dist/adapters/codex/instruction-chain.js +105 -0
  31. package/dist/adapters/codex/layout.d.ts +1 -1
  32. package/dist/adapters/codex/layout.js +41 -14
  33. package/dist/adapters/opencode/adapter.d.ts +33 -2
  34. package/dist/adapters/opencode/adapter.js +36 -36
  35. package/dist/adapters/opencode/dialect.js +2 -2
  36. package/dist/adapters/opencode/instruction-chain.d.ts +37 -0
  37. package/dist/adapters/opencode/instruction-chain.js +70 -0
  38. package/dist/adapters/opencode/layout.d.ts +19 -0
  39. package/dist/adapters/opencode/layout.js +34 -15
  40. package/dist/adoptability.d.ts +31 -1
  41. package/dist/adoptability.js +57 -0
  42. package/dist/cli-main.js +185 -102
  43. package/dist/core/adapter.d.ts +213 -61
  44. package/dist/core/compile.d.ts +2 -2
  45. package/dist/core/compile.js +57 -46
  46. package/dist/core/compose.d.ts +5 -3
  47. package/dist/core/compose.js +5 -3
  48. package/dist/core/config-schema.d.ts +14 -2
  49. package/dist/core/config-schema.js +20 -7
  50. package/dist/core/dialect.d.ts +54 -12
  51. package/dist/core/dialect.js +56 -0
  52. package/dist/core/eval-driver.d.ts +194 -0
  53. package/dist/core/eval-driver.js +3 -0
  54. package/dist/core/frontmatter-read.d.ts +10 -0
  55. package/dist/core/frontmatter-read.js +30 -3
  56. package/dist/core/guards.js +3 -1
  57. package/dist/core/hook-program.d.ts +27 -2
  58. package/dist/core/hook-program.js +29 -24
  59. package/dist/core/hook-protocol.d.ts +54 -0
  60. package/dist/core/install-reader.d.ts +18 -0
  61. package/dist/core/install-reader.js +88 -0
  62. package/dist/core/instruction-chain.d.ts +444 -0
  63. package/dist/core/instruction-chain.js +292 -0
  64. package/dist/core/instruction-weight.d.ts +96 -14
  65. package/dist/core/instruction-weight.js +65 -30
  66. package/dist/core/layout.d.ts +220 -33
  67. package/dist/core/layout.js +115 -1
  68. package/dist/core/lethal-trifecta.d.ts +12 -7
  69. package/dist/core/lethal-trifecta.js +13 -13
  70. package/dist/core/live-driver.d.ts +137 -0
  71. package/dist/core/live-driver.js +14 -0
  72. package/dist/core/markdown.d.ts +23 -0
  73. package/dist/core/markdown.js +77 -28
  74. package/dist/core/orphans.js +9 -7
  75. package/dist/core/settings-codec.d.ts +17 -0
  76. package/dist/core/settings-codec.js +56 -0
  77. package/dist/core/surface-discovery.d.ts +2 -2
  78. package/dist/core/surface-discovery.js +24 -12
  79. package/dist/core/surface-scopes.d.ts +26 -6
  80. package/dist/core/surface-scopes.js +52 -11
  81. package/dist/core/validate.js +16 -3
  82. package/dist/coverage-artifact.d.ts +3 -2
  83. package/dist/coverage-artifact.js +6 -5
  84. package/dist/eval-cache.d.ts +6 -1
  85. package/dist/eval-cache.js +11 -1
  86. package/dist/eval.d.ts +16 -108
  87. package/dist/eval.js +36 -2
  88. package/dist/harness-test.d.ts +3 -63
  89. package/dist/hook-install.d.ts +12 -1
  90. package/dist/hook-install.js +12 -1
  91. package/dist/hook-runtime.js +4 -2
  92. package/dist/hook-state-store.js +3 -1
  93. package/dist/local-files-tracked.d.ts +17 -0
  94. package/dist/local-files-tracked.js +70 -0
  95. package/dist/local-files.d.ts +62 -0
  96. package/dist/local-files.js +183 -0
  97. package/dist/observe.d.ts +3 -2
  98. package/dist/observe.js +7 -6
  99. package/dist/plugin-loader.d.ts +1 -1
  100. package/dist/plugin-loader.js +43 -36
  101. package/dist/scan-behavioral.d.ts +34 -25
  102. package/dist/scan-behavioral.js +122 -58
  103. package/dist/scan-core.js +37 -18
  104. package/dist/scan-files.d.ts +1 -1
  105. package/dist/scan-files.js +53 -33
  106. package/dist/scan-trigger-suggest.d.ts +0 -21
  107. package/dist/scan-trigger-suggest.js +0 -23
  108. package/dist/scan.d.ts +4 -4
  109. package/dist/scan.js +120 -84
  110. package/dist/skill-harness.d.ts +21 -5
  111. package/dist/skill-harness.js +29 -11
  112. package/dist/surface-discovery-fs.d.ts +2 -0
  113. package/dist/surface-discovery-fs.js +108 -6
  114. package/dist/test-coverage-files.js +24 -17
  115. package/dist/test-coverage.d.ts +9 -3
  116. package/dist/test-coverage.js +32 -22
  117. package/dist/verify-plugin-guards.js +1 -1
  118. package/package.json +1 -1
  119. package/dist/skill-reachability.d.ts +0 -68
  120. package/dist/skill-reachability.js +0 -205
  121. /package/dist/{dialect-drift.d.ts → adapters/claude-code/dialect-drift.d.ts} +0 -0
  122. /package/dist/{dialect-drift.js → adapters/claude-code/dialect-drift.js} +0 -0
@@ -1,30 +1,64 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.claudeCodeLayout = void 0;
4
+ /**
5
+ * claudeCodeLayout — the Claude Code plugin/repo layout (the `PluginLayout`
6
+ * port's reference implementation). `loadPlugin` defaults to it; a Codex adapter
7
+ * defines a sibling `codexLayout` and passes it to the same loader.
8
+ * 🔴 THE PATHS BELOW ARE DOCUMENTED IN `docs/configuration.md`. Change any of
9
+ * them — `instructionFile`, `surfaces`, `userSurfaceRoot`, `rulesDir` — and
10
+ * that page is wrong until you edit it too. The page marks this symbol with
11
+ * `vigiles:symbol`, so RENAMING it turns `vigiles lint` red and forces the
12
+ * edit; changing a VALUE in place does not, and nothing today catches that.
13
+ */
14
+ const layout_js_1 = require("../../core/layout.js");
15
+ const instruction_chain_js_1 = require("../../core/instruction-chain.js");
16
+ const settings_codec_js_1 = require("../../core/settings-codec.js");
17
+ const instruction_chain_js_2 = require("./instruction-chain.js");
4
18
  exports.claudeCodeLayout = {
5
19
  name: "claude-code",
6
20
  manifestPath: ".claude-plugin/plugin.json",
7
21
  hooksConventionPath: "hooks/hooks.json",
8
22
  settingsPath: ".claude/settings.json",
9
- settingsFormat: "json",
23
+ settings: settings_codec_js_1.jsonSettingsCodec,
10
24
  instructionFile: "CLAUDE.md",
11
- surfaceDirs: ["skills", "agents", "commands"],
25
+ surfaces: { skill: "skills", agent: "agents", command: "commands" },
12
26
  // A plain Claude Code USER keeps skills/agents/commands under `.claude/`, not at
13
27
  // the repo root (that's the published-plugin shape). Read both so a normal repo
14
- // isn't seen as an empty machine.
28
+ // isn't seen as an empty machine. This is also the materialize prefix — it used
29
+ // to be a second field, `materializeRoot: ".claude"`, saying the same thing.
15
30
  userSurfaceRoot: ".claude",
16
- skillDir: "skills",
17
- agentDir: "agents",
18
- commandDir: "commands",
19
31
  // `.claude/rules/*.md` — path-scoped project instructions (see PluginLayout).
20
32
  rulesDir: "rules",
21
- materializeRoot: ".claude",
33
+ // `.claude/settings.local.json` — the per-machine layer, gitignored by
34
+ // `init`. Declared because only this adapter knows the word; Codex and
35
+ // OpenCode name none, so none is synthesized for them.
36
+ settingsLocalInfix: "local",
37
+ // Plugin hook SCRIPTS (`hooks/*.sh`), distinct from where hooks are
38
+ // REGISTERED (`hooks/hooks.json`, `.claude/settings.json`).
39
+ hookScriptsDir: "hooks",
22
40
  pluginRootToken: "${CLAUDE_PLUGIN_ROOT}",
23
41
  // Both names Claude Code uses for the project root (mirrors the
24
42
  // `NON_PLUGIN_VARS` set in plugin-loader.ts / scan-files.ts).
25
43
  projectRootTokens: ["${CLAUDE_PROJECT_DIR}", "${CLAUDE_PROJECT}"],
26
44
  mcpConfigFile: ".mcp.json",
27
45
  mcpManifestKey: "mcpServers",
28
- intraRefDirs: ["hooks", "skills", "agents", "commands"],
46
+ // What a repo-root session LOADS, and why each remaining candidate does not —
47
+ // see ./instruction-chain.ts for the vendor quotes behind every branch. The
48
+ // regexes and paths handed over are DERIVED from the fields above, so moving
49
+ // `rulesDir` or `userSurfaceRoot` moves the chain with it.
50
+ instructionChain(files) {
51
+ return (0, instruction_chain_js_2.claudeCodeInstructionChain)(files, {
52
+ instructionFile: exports.claudeCodeLayout.instructionFile,
53
+ userSurfaceRoot: exports.claudeCodeLayout.userSurfaceRoot ?? "",
54
+ settingsSources: (0, instruction_chain_js_1.settingsSources)(exports.claudeCodeLayout),
55
+ parseSettings: (text) => exports.claudeCodeLayout.settings.parse(text),
56
+ // ANCHORED to `.claude/rules`, not to any path segment spelled `rules`
57
+ // — see `ruleFileRe`. `??` is safe here and not a silent default: this
58
+ // layout declares `rulesDir`, and a layout that did not would want no
59
+ // rule matcher at all rather than one matching nothing.
60
+ ruleRe: (0, layout_js_1.ruleFileRe)(exports.claudeCodeLayout) ?? /(?!)/,
61
+ });
62
+ },
29
63
  };
30
64
  //# sourceMappingURL=layout.js.map
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Claude Code's answer to "is a real model reachable, and on whose bill" — the
3
+ * env-only half of `HarnessLiveDriver.access`.
4
+ *
5
+ * 🔴 IT USED TO LIVE IN `src/scan-trigger-suggest.ts`, the module named for the
6
+ * harness-AGNOSTIC read-vs-run decision, and its whole body was one harness's
7
+ * environment variables. The CLI then paired it with a name check for the other
8
+ * harness (`adapter.name === "codex" || hasModelAccess(process.env)`), which is
9
+ * the same fact said twice in two vocabularies. Here it is one adapter's
10
+ * knowledge of its own credentials, and the CLI asks the port.
11
+ *
12
+ * A tiny env-only predicate, never a live probe: deciding whether to offer a
13
+ * measurement must not itself spend a token.
14
+ */
15
+ /** Only the env vars that signal a reachable model (parse, don't validate). */
16
+ export interface ModelEnv {
17
+ readonly ANTHROPIC_API_KEY?: string;
18
+ readonly CLAUDECODE?: string;
19
+ readonly CLAUDE_CODE_ENTRYPOINT?: string;
20
+ }
21
+ /**
22
+ * Is a real model reachable for the executing tiers? Either a metered API key
23
+ * (`ANTHROPIC_API_KEY`), OR an authenticated Claude Code session (`CLAUDECODE=1`
24
+ * / `CLAUDE_CODE_ENTRYPOINT`, web/desktop/CLI) — the latter drives the `claude`
25
+ * CLI on the user's subscription, no key needed and $0 metered.
26
+ */
27
+ export declare function hasModelAccess(env: ModelEnv): boolean;
28
+ /**
29
+ * Is the reachable model METERED (a paid API key) rather than a subscription?
30
+ * Only affects the consent DISCLOSURE wording (a metered key bills per token; a
31
+ * subscription is $0 metered) — the run/skip decision itself is consent-driven,
32
+ * not metered-driven.
33
+ */
34
+ export declare function isMeteredAccess(env: ModelEnv): boolean;
35
+ /**
36
+ * The one line printed instead of running, when nothing is reachable. It names
37
+ * THIS harness's CLI and credential — the string it replaces was printed for
38
+ * every harness, so a Codex repo was told to authenticate `claude`.
39
+ */
40
+ export declare const CLAUDE_CODE_ACCESS_FIX = "authenticate the `claude` CLI or set ANTHROPIC_API_KEY";
41
+ //# sourceMappingURL=model-access.d.ts.map
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ /**
3
+ * Claude Code's answer to "is a real model reachable, and on whose bill" — the
4
+ * env-only half of `HarnessLiveDriver.access`.
5
+ *
6
+ * 🔴 IT USED TO LIVE IN `src/scan-trigger-suggest.ts`, the module named for the
7
+ * harness-AGNOSTIC read-vs-run decision, and its whole body was one harness's
8
+ * environment variables. The CLI then paired it with a name check for the other
9
+ * harness (`adapter.name === "codex" || hasModelAccess(process.env)`), which is
10
+ * the same fact said twice in two vocabularies. Here it is one adapter's
11
+ * knowledge of its own credentials, and the CLI asks the port.
12
+ *
13
+ * A tiny env-only predicate, never a live probe: deciding whether to offer a
14
+ * measurement must not itself spend a token.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.CLAUDE_CODE_ACCESS_FIX = void 0;
18
+ exports.hasModelAccess = hasModelAccess;
19
+ exports.isMeteredAccess = isMeteredAccess;
20
+ /**
21
+ * Is a real model reachable for the executing tiers? Either a metered API key
22
+ * (`ANTHROPIC_API_KEY`), OR an authenticated Claude Code session (`CLAUDECODE=1`
23
+ * / `CLAUDE_CODE_ENTRYPOINT`, web/desktop/CLI) — the latter drives the `claude`
24
+ * CLI on the user's subscription, no key needed and $0 metered.
25
+ */
26
+ function hasModelAccess(env) {
27
+ return Boolean(env.ANTHROPIC_API_KEY ||
28
+ env.CLAUDECODE === "1" ||
29
+ env.CLAUDE_CODE_ENTRYPOINT);
30
+ }
31
+ /**
32
+ * Is the reachable model METERED (a paid API key) rather than a subscription?
33
+ * Only affects the consent DISCLOSURE wording (a metered key bills per token; a
34
+ * subscription is $0 metered) — the run/skip decision itself is consent-driven,
35
+ * not metered-driven.
36
+ */
37
+ function isMeteredAccess(env) {
38
+ return Boolean(env.ANTHROPIC_API_KEY);
39
+ }
40
+ /**
41
+ * The one line printed instead of running, when nothing is reachable. It names
42
+ * THIS harness's CLI and credential — the string it replaces was printed for
43
+ * every harness, so a Codex repo was told to authenticate `claude`.
44
+ */
45
+ exports.CLAUDE_CODE_ACCESS_FIX = "authenticate the `claude` CLI or set ANTHROPIC_API_KEY";
46
+ //# sourceMappingURL=model-access.js.map
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Are vigiles's SHIPPED SKILLS actually reachable by the agent in this repo?
3
+ *
4
+ * vigiles publishes six user-facing skills (`SHIPPED_SKILLS`) — the teaching
5
+ * surface. `test-harness` alone answers "which testing tier do I want?", the
6
+ * question this project's docs are otherwise organized around. They reach the
7
+ * agent through the GLOBAL plugin install (`vigiles init`, or
8
+ * `claude plugin marketplace add zernie/vigiles` + `claude plugin install
9
+ * vigiles@vigiles`), which lands in `~/.claude/plugins/` — deliberately never
10
+ * vendored into the repo (`docs/agent-setup.md`).
11
+ *
12
+ * The failure this module makes loud: `npm install vigiles` ALSO puts those
13
+ * skills on disk, at `node_modules/vigiles/skills/` (they are in package.json's
14
+ * `files`, because the npm tarball doubles as the plugin payload). Claude Code
15
+ * never scans `node_modules`. So a repo that took the dependency but never ran
16
+ * the plugin install has all six skills present, unreachable, and **silent** —
17
+ * observed in a real consumer repo, where a full day went into re-deriving what
18
+ * `test-harness` teaches while it sat three directories away.
19
+ *
20
+ * ⚠️ The part that is easy to get wrong, and did get wrong TWICE, in opposite
21
+ * directions:
22
+ *
23
+ * 1. The authoritative record of a `claude plugin install` is the GLOBAL
24
+ * `~/.claude/plugins/installed_plugins.json`. A repo's `.claude/settings.json`
25
+ * carries PROJECT-level `enabledPlugins`, which a correctly-installed
26
+ * user-scope plugin does not appear in. Judging "is it wired?" from
27
+ * `settings.json` alone reports a working install as broken — the misread
28
+ * that made this look like an npm packaging bug.
29
+ * 2. The converse is ALSO false: a project `enabledPlugins` entry does not make
30
+ * the plugin load. Per the Claude Code docs (Discover plugins → "Configure
31
+ * team marketplaces"), as of CC v2.1.195 — "A plugin that only the project's
32
+ * `.claude/settings.json` enables, and that comes from an external source
33
+ * such as a GitHub repository or npm package, doesn't load until the team
34
+ * member installs it." vigiles ships from a GitHub marketplace, so that is
35
+ * exactly our case. Committing `extraKnownMarketplaces` + `enabledPlugins`
36
+ * makes Claude Code PROMPT each collaborator to install; it does not install.
37
+ * Confirmed empirically: in a repo whose committed settings.json declares an
38
+ * external plugin project-level, the global registry had no marketplace
39
+ * entry, no cache dir, and no install record for it, while a plugin installed
40
+ * the normal way on the same machine had all three.
41
+ *
42
+ * So a project declaration is a THIRD state — declared, not installed — that
43
+ * still warrants a warning, with a different fix line: the collaborator runs the
44
+ * install, the repo cannot run it for them.
45
+ *
46
+ * Shape follows `./dialect-drift.ts`: pure parsers + a best-effort local read
47
+ * that NEVER throws + a formatter that returns null when there is nothing to
48
+ * say, so `vigiles audit` can print it without a new verb, flag, or failure mode.
49
+ * It is ADVISORY — it never touches the audit score, because reachability is a
50
+ * property of the machine (is the plugin installed?), not of the repo, and a
51
+ * score that moved between a laptop and CI for identical source would be a lie.
52
+ */
53
+ import type { InstallReader } from "../../core/adapter.js";
54
+ /** The plugin id `claude plugin install` records — `<plugin>@<marketplace>`. */
55
+ export declare const VIGILES_PLUGIN_ID = "vigiles@vigiles";
56
+ /**
57
+ * Where a reachable install was found. Deliberately does NOT include a project
58
+ * `enabledPlugins` entry: that declares the plugin, it does not install it (see
59
+ * the header). It is reported as {@link SkillReachability.declaredNotInstalled}.
60
+ */
61
+ export type ReachabilitySource =
62
+ /** `~/.claude/plugins/installed_plugins.json` — what `claude plugin install` writes. */
63
+ "global-plugin"
64
+ /** The skills vendored into the repo's own `.claude/skills/` (standalone config, always loaded). */
65
+ | "repo-skills";
66
+ /** The advisory: can the agent see vigiles's skills from this repo? */
67
+ export interface SkillReachability {
68
+ /** True when at least one {@link ReachabilitySource} was found. */
69
+ readonly reachable: boolean;
70
+ /** Every source that resolved, in check order. Empty when un-wired. */
71
+ readonly sources: readonly ReachabilitySource[];
72
+ /**
73
+ * The repo's `.claude/settings.json` enables the plugin, but no install was
74
+ * found. Claude Code will prompt this collaborator to install it; until they
75
+ * do, it does not load. A distinct state from "nothing configured at all",
76
+ * because the fix belongs to the person, not the repo.
77
+ */
78
+ readonly declaredNotInstalled: boolean;
79
+ /**
80
+ * Shipped skills found under `node_modules/vigiles/skills/` while UNREACHABLE
81
+ * — present on disk, invisible to the agent. Empty when reachable (there is
82
+ * nothing stranded if they are wired) or when the package isn't installed yet.
83
+ */
84
+ readonly strandedSkills: readonly string[];
85
+ }
86
+ /**
87
+ * Does the global registry record a live install of the vigiles plugin? Pure over
88
+ * the raw `installed_plugins.json` text. An entry with an EMPTY array is a
89
+ * leftover record, not an install, so it does not count.
90
+ */
91
+ export declare function hasGlobalPluginInstall(installedPluginsJson: string): boolean;
92
+ /**
93
+ * Does the repo's `.claude/settings.json` explicitly enable the vigiles plugin?
94
+ * Pure. An explicit `false` is a deliberate disable and does NOT count.
95
+ */
96
+ export declare function hasEnabledPlugin(settingsJson: string): boolean;
97
+ /**
98
+ * Best-effort, read-local reachability check for `vigiles audit`. Returns null
99
+ * when the question does not apply — the repo does not depend on vigiles, or IS
100
+ * vigiles — so a non-consumer is never nagged. NEVER throws: every read the
101
+ * reader performs degrades to "not found".
102
+ *
103
+ * 🔴 IT READS THROUGH A REPO-BOUND {@link InstallReader}, NOT `node:fs`. Two of
104
+ * its five reads are of files NO adapter claims (`package.json`, vigiles's own
105
+ * package under `node_modules`), so the domain performs those and passes the
106
+ * ANSWER; the rest go through a reader that refuses any path this adapter does
107
+ * not claim. That is why this module no longer imports `node:fs` at all.
108
+ *
109
+ * ⚠️ ONE MEASURED CHANGE, NAMED BECAUSE IT IS A CHANGE: the repo-vendored check
110
+ * used to ENUMERATE `.claude/skills/` and compare directory names. Enumeration
111
+ * is precisely what the reader exists to prevent, and it is not needed here —
112
+ * the names being looked for are a fixed list — so each shipped skill is probed
113
+ * for its own `SKILL.md` instead. The narrow difference: a directory named after
114
+ * a shipped skill but holding no `SKILL.md` used to count as reachable and no
115
+ * longer does. It was never loadable by the agent, which is the question asked.
116
+ */
117
+ export declare function checkSkillReachability(read: InstallReader): SkillReachability | null;
118
+ /**
119
+ * The warning for an un-wired repo, or null when there is nothing to say
120
+ * (reachable, or the check did not apply). Names the skill the user most likely
121
+ * went looking for, says where the copies are stranded when they exist, and ends
122
+ * on a command that fixes it.
123
+ */
124
+ export declare function formatSkillReachability(r: SkillReachability | null): string | null;
125
+ //# sourceMappingURL=skill-reachability.d.ts.map
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VIGILES_PLUGIN_ID = void 0;
4
+ exports.hasGlobalPluginInstall = hasGlobalPluginInstall;
5
+ exports.hasEnabledPlugin = hasEnabledPlugin;
6
+ exports.checkSkillReachability = checkSkillReachability;
7
+ exports.formatSkillReachability = formatSkillReachability;
8
+ const setup_plan_js_1 = require("../../setup-plan.js");
9
+ /** The plugin id `claude plugin install` records — `<plugin>@<marketplace>`. */
10
+ exports.VIGILES_PLUGIN_ID = "vigiles@vigiles";
11
+ /**
12
+ * Does the global registry record a live install of the vigiles plugin? Pure over
13
+ * the raw `installed_plugins.json` text. An entry with an EMPTY array is a
14
+ * leftover record, not an install, so it does not count.
15
+ */
16
+ function hasGlobalPluginInstall(installedPluginsJson) {
17
+ try {
18
+ const parsed = JSON.parse(installedPluginsJson);
19
+ const entry = parsed.plugins?.[exports.VIGILES_PLUGIN_ID];
20
+ return Array.isArray(entry) && entry.length > 0;
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ /**
27
+ * Does the repo's `.claude/settings.json` explicitly enable the vigiles plugin?
28
+ * Pure. An explicit `false` is a deliberate disable and does NOT count.
29
+ */
30
+ function hasEnabledPlugin(settingsJson) {
31
+ try {
32
+ const parsed = JSON.parse(settingsJson);
33
+ return parsed.enabledPlugins?.[exports.VIGILES_PLUGIN_ID] === true;
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ /**
40
+ * Best-effort, read-local reachability check for `vigiles audit`. Returns null
41
+ * when the question does not apply — the repo does not depend on vigiles, or IS
42
+ * vigiles — so a non-consumer is never nagged. NEVER throws: every read the
43
+ * reader performs degrades to "not found".
44
+ *
45
+ * 🔴 IT READS THROUGH A REPO-BOUND {@link InstallReader}, NOT `node:fs`. Two of
46
+ * its five reads are of files NO adapter claims (`package.json`, vigiles's own
47
+ * package under `node_modules`), so the domain performs those and passes the
48
+ * ANSWER; the rest go through a reader that refuses any path this adapter does
49
+ * not claim. That is why this module no longer imports `node:fs` at all.
50
+ *
51
+ * ⚠️ ONE MEASURED CHANGE, NAMED BECAUSE IT IS A CHANGE: the repo-vendored check
52
+ * used to ENUMERATE `.claude/skills/` and compare directory names. Enumeration
53
+ * is precisely what the reader exists to prevent, and it is not needed here —
54
+ * the names being looked for are a fixed list — so each shipped skill is probed
55
+ * for its own `SKILL.md` instead. The narrow difference: a directory named after
56
+ * a shipped skill but holding no `SKILL.md` used to count as reachable and no
57
+ * longer does. It was never loadable by the agent, which is the question asked.
58
+ */
59
+ function checkSkillReachability(read) {
60
+ if (!read.repoDependsOnVigiles)
61
+ return null;
62
+ const sources = [];
63
+ const installed = read.home(".claude/plugins/installed_plugins.json");
64
+ if (installed !== null && hasGlobalPluginInstall(installed))
65
+ sources.push("global-plugin");
66
+ // Vendored copies: only vigiles's OWN skill names count. A repo with 38
67
+ // unrelated skills in `.claude/skills/` is still un-wired.
68
+ if (setup_plan_js_1.SHIPPED_SKILLS.some((s) => read.repo(`.claude/skills/${s}/SKILL.md`)))
69
+ sources.push("repo-skills");
70
+ const reachable = sources.length > 0;
71
+ // A project declaration is NOT a source — it makes Claude Code prompt for an
72
+ // install, it does not perform one. Only meaningful while unreachable.
73
+ const settings = read.repo(".claude/settings.json");
74
+ const declaredNotInstalled = !reachable && settings !== null && hasEnabledPlugin(settings);
75
+ const vendored = new Set(read.vendoredSkillNames);
76
+ return {
77
+ reachable,
78
+ sources,
79
+ declaredNotInstalled,
80
+ strandedSkills: reachable
81
+ ? []
82
+ : setup_plan_js_1.SHIPPED_SKILLS.filter((s) => vendored.has(s)),
83
+ };
84
+ }
85
+ /**
86
+ * The warning for an un-wired repo, or null when there is nothing to say
87
+ * (reachable, or the check did not apply). Names the skill the user most likely
88
+ * went looking for, says where the copies are stranded when they exist, and ends
89
+ * on a command that fixes it.
90
+ */
91
+ function formatSkillReachability(r) {
92
+ if (!r || r.reachable)
93
+ return null;
94
+ const stranded = r.strandedSkills.length > 0
95
+ ? ` ${String(r.strandedSkills.length)} of them are sitting in ` +
96
+ `node_modules/vigiles/skills/, which the agent never scans.`
97
+ : "";
98
+ const why = r.declaredNotInstalled
99
+ ? `This repo DECLARES the vigiles plugin in .claude/settings.json, but a ` +
100
+ `project declaration doesn't install it — Claude Code loads an ` +
101
+ `external-source plugin only once each collaborator installs it on their ` +
102
+ `own machine.`
103
+ : `This repo depends on vigiles, but its plugin isn't installed.`;
104
+ return (`⚠ vigiles's skills are NOT reachable by your agent here. ${why} So the ` +
105
+ `shipped skills (${setup_plan_js_1.SHIPPED_SKILLS.join(", ")}) can't be selected — ` +
106
+ `including test-harness, which picks the testing tier for you.${stranded}\n` +
107
+ ` Fix (per machine): claude plugin marketplace add zernie/vigiles && ` +
108
+ `claude plugin install ${exports.VIGILES_PLUGIN_ID}\n` +
109
+ ` (or run \`vigiles init\`, which does both)`);
110
+ }
111
+ //# sourceMappingURL=skill-reachability.js.map
@@ -38,6 +38,7 @@ const node_fs_1 = require("node:fs");
38
38
  const node_path_1 = require("node:path");
39
39
  const effects_js_1 = require("../../core/effects.js");
40
40
  const dialect_js_1 = require("./dialect.js");
41
+ const local_files_js_1 = require("../../local-files.js");
41
42
  const STEP_RE = /^###\s+Step\s+(\d+)/;
42
43
  const GATE_CMD_RE = /<!--\s*vigiles:gate\s+"([^"]*)"(?:\s+retry:(\d+))?\s*-->/;
43
44
  const GATE_FILE_RE = /<!--\s*vigiles:gate\s+file:(\S+)\s*-->/;
@@ -235,11 +236,12 @@ function runSkillGates(gates, cwd) {
235
236
  // (Claude Code hooks don't surface the active skill, so we record it). Wiring
236
237
  // `skill-start` to fire automatically is the integration step; the decision
237
238
  // logic below is harness-agnostic and fully testable.
238
- const ACTIVE_PATH = ".vigiles/active-skill.json";
239
+ const ACTIVE_PATH = `${local_files_js_1.VIGILES_DIR}/${local_files_js_1.ACTIVE_SKILL_FILE}`;
239
240
  /** Record the skill the agent is currently executing. */
240
241
  function setActiveSkill(cwd, skillPath) {
241
242
  const p = (0, node_path_1.resolve)(cwd, ACTIVE_PATH);
242
243
  (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(p), { recursive: true });
244
+ (0, local_files_js_1.ensureLocalFilesIgnored)((0, node_path_1.dirname)(p));
243
245
  (0, node_fs_1.writeFileSync)(p, JSON.stringify({ skill: skillPath }) + "\n");
244
246
  }
245
247
  /** Clear the active-skill marker (the skill finished). */
@@ -1,3 +1,40 @@
1
- import type { HarnessAdapter } from "../../core/adapter.js";
2
- export declare const codexAdapter: HarnessAdapter;
1
+ /**
2
+ * codexAdapter — the OpenAI Codex `HarnessAdapter`. Bundles the five Codex ports
3
+ * + a `detect`. SHIPPED: registered in `src/adapter-registry.ts` (the CLI
4
+ * auto-detects a `.codex/config.toml` or `AGENTS.md` repo) and exported as
5
+ * `vigiles/codex`. It passes `assertAdapterConformance`/`assertAdapterLoadsHooks`,
6
+ * drives the compiler + loader against real Codex fixtures (codex.test.ts), and
7
+ * its transport (`mock-model.ts`) is proven against the real `codex` binary.
8
+ *
9
+ * Caveat — pillar 1 (compile) is partial: the instruction/skill *renderers* still
10
+ * emit the Claude-Code shape until the format-axis renderers land
11
+ * (`research/code-adapter-architecture.md`). Pillar 2 (harness testing) is full.
12
+ */
13
+ import type { DetectSignal } from "../../core/adapter.js";
14
+ export declare const codexAdapter: {
15
+ readonly name: "codex";
16
+ readonly harnessTesting: true;
17
+ readonly shellHooks: true;
18
+ readonly subagents: false;
19
+ readonly dialect: import("../claude-code/dialect.js").HarnessDialect;
20
+ readonly layout: import("../../adapter.js").PluginLayout;
21
+ readonly runtime: import("../../adapter.js").HarnessRuntime;
22
+ readonly hookProtocol: import("../../adapter.js").HookProtocol;
23
+ readonly modelMock: import("../../adapter.js").ModelMock;
24
+ readonly harnessTestDriver: () => Promise<import("../../harness-test.js").HarnessTestDriver>;
25
+ readonly liveDriver: () => Promise<import("../../core/live-driver.js").HarnessLiveDriver>;
26
+ readonly claims: (path: string) => boolean;
27
+ readonly detect: (exists: (repoRelative: string) => boolean) => DetectSignal;
28
+ /**
29
+ * Nothing to say about a Codex install today, and `[]` IS the answer rather
30
+ * than a missing capability: the CLI prints whatever it is given, so an empty
31
+ * list produces exactly the silence a `localAdvisories: false` flag would
32
+ * have bought, without a second fact to keep in step with this method.
33
+ *
34
+ * What would go here: anything about THIS machine's `codex` install that bears
35
+ * on how far the report can be trusted — a vendor version whose tool catalog
36
+ * has drifted from ours, or a config the agent cannot read from this repo.
37
+ */
38
+ readonly advisories: () => readonly string[];
39
+ };
3
40
  //# sourceMappingURL=adapter.d.ts.map
@@ -1,20 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.codexAdapter = void 0;
4
- /**
5
- * codexAdapter — the OpenAI Codex `HarnessAdapter`. Bundles the five Codex ports
6
- * + a `detect`. SHIPPED: registered in `src/adapter-registry.ts` (the CLI
7
- * auto-detects a `.codex/config.toml` or `AGENTS.md` repo) and exported as
8
- * `vigiles/codex`. It passes `assertAdapterConformance`/`assertAdapterLoadsHooks`,
9
- * drives the compiler + loader against real Codex fixtures (codex.test.ts), and
10
- * its transport (`mock-model.ts`) is proven against the real `codex` binary.
11
- *
12
- * Caveat — pillar 1 (compile) is partial: the instruction/skill *renderers* still
13
- * emit the Claude-Code shape until the format-axis renderers land
14
- * (`research/code-adapter-architecture.md`). Pillar 2 (harness testing) is full.
15
- */
16
- const node_fs_1 = require("node:fs");
17
- const node_path_1 = require("node:path");
18
4
  const dialect_js_1 = require("./dialect.js");
19
5
  const layout_js_1 = require("./layout.js");
20
6
  const surface_discovery_js_1 = require("../../core/surface-discovery.js");
@@ -25,33 +11,47 @@ exports.codexAdapter = {
25
11
  name: "codex",
26
12
  // Full convergence with Claude Code: mockable (Responses SSE) + shell hooks
27
13
  // with veto (permissionDecision/exit 2). Both pillars, all tiers.
28
- capabilities: {
29
- referenceVerification: true,
30
- harnessTesting: true,
31
- shellHooks: true,
32
- // Codex `[agents]` is a concurrency table, not a subagent tool-contract file
33
- // — the subagent-surface rules report n/a here (a deliberate non-goal).
34
- subagents: false,
35
- },
14
+ harnessTesting: true,
15
+ shellHooks: true,
16
+ // Codex `[agents]` is a concurrency table, not a subagent tool-contract file
17
+ // — the subagent-surface rules report n/a here (a deliberate non-goal).
18
+ subagents: false,
36
19
  dialect: dialect_js_1.codexDialect,
37
20
  layout: layout_js_1.codexLayout,
38
21
  runtime: runtime_js_1.codexRuntime,
39
22
  hookProtocol: hook_protocol_js_1.codexHookProtocol,
40
23
  modelMock: model_mock_js_1.codexModelMock,
41
24
  harnessTestDriver: async () => (await import("./driver.js")).codexDriver,
25
+ liveDriver: async () => (await import("./eval.js")).codexLiveDriver,
42
26
  // Derived from the layout, never listed again here — see `claims` on
43
27
  // `HarnessAdapter` for why this method takes a PATH and not a root.
44
28
  claims(path) {
45
29
  return (0, surface_discovery_js_1.layoutClaims)(layout_js_1.codexLayout, path);
46
30
  },
47
- detect(root) {
31
+ detect(exists) {
48
32
  // A `.codex/config.toml` is a strong signal; a bare AGENTS.md is weak (many
49
- // harnesses read it). (Unused while unregistered — kept for symmetry.)
50
- if ((0, node_fs_1.existsSync)((0, node_path_1.join)(root, ".codex", "config.toml")))
51
- return 3;
52
- if ((0, node_fs_1.existsSync)((0, node_path_1.join)(root, layout_js_1.codexLayout.instructionFile)))
53
- return 1;
54
- return 0;
33
+ // harnesses read it). The manifest path IS `.codex/config.toml` — asked for
34
+ // by the layout field rather than respelled, so a layout that moves takes
35
+ // its detection with it (the path used to be typed out here as
36
+ // `join(root, ".codex", "config.toml")`).
37
+ if (exists(layout_js_1.codexLayout.manifestPath))
38
+ return { specificity: 3, via: "manifest" };
39
+ if (exists(layout_js_1.codexLayout.instructionFile))
40
+ return { specificity: 1, via: "instruction-file" };
41
+ return { specificity: 0, via: "instruction-file" };
42
+ },
43
+ /**
44
+ * Nothing to say about a Codex install today, and `[]` IS the answer rather
45
+ * than a missing capability: the CLI prints whatever it is given, so an empty
46
+ * list produces exactly the silence a `localAdvisories: false` flag would
47
+ * have bought, without a second fact to keep in step with this method.
48
+ *
49
+ * What would go here: anything about THIS machine's `codex` install that bears
50
+ * on how far the report can be trusted — a vendor version whose tool catalog
51
+ * has drifted from ours, or a config the agent cannot read from this repo.
52
+ */
53
+ advisories() {
54
+ return [];
55
55
  },
56
56
  };
57
57
  //# sourceMappingURL=adapter.js.map
@@ -36,14 +36,19 @@ exports.codexDialect = {
36
36
  limit: 32768,
37
37
  onExceed: "truncates",
38
38
  capturedFrom: "codex config project_doc_max_bytes default 32 * 1024; truncation quoted in openai/codex#7138",
39
- // Read root-to-leaf and concatenated, so a nested AGENTS.md pays into the
40
- // same budget — the sum is what gets cut, not the individual file.
41
- alwaysLoaded: ["AGENTS.md", "**/AGENTS.md"],
42
39
  },
40
+ // 🔴 `alwaysLoaded: ["AGENTS.md", "**/AGENTS.md"]` USED TO STAND HERE, and it
41
+ // was wrong twice over: the core expanded that second glob by walking the
42
+ // whole repository (an adapter choosing what the domain reads), and the set it
43
+ // produced is not what a root session loads — Codex takes AT MOST ONE FILE PER
44
+ // DIRECTORY along root→cwd, so at the root it is one file. A monorepo with
45
+ // twelve package-level AGENTS.md files was told it was 12x over a budget no
46
+ // session approaches. Which files load is now `codexLayout.instructionChain`.
43
47
  instructionTargets: ["AGENTS.md"],
44
48
  pluginRootToken: "${PLUGIN_ROOT}",
45
- // Codex SKILL.md frontmatter is name + description ONLY — the CC-only keys
46
- // (disable-model-invocation, argument-hint, …) are not part of its format.
47
- skillFrontmatter: "minimal",
49
+ // Codex SKILL.md frontmatter is name + description ONLY — the richer keys
50
+ // (disable-model-invocation, argument-hint, disallowed-tools, …) are not part
51
+ // of its format, so the compiler omits them instead of writing inert noise.
52
+ skillFrontmatterKeys: ["name", "description"],
48
53
  };
49
54
  //# sourceMappingURL=dialect.js.map
@@ -25,6 +25,7 @@
25
25
  * SKILL.md read (`codexSkillFired`). Best-effort by nature (a cached skill might
26
26
  * not be re-read); pair with a behavioral/judged check for certainty.
27
27
  */
28
+ import type { HarnessLiveDriver } from "../../core/live-driver.js";
28
29
  import type { ParsedModelRun, AgentRunArgs, RunOut, EvalDriver } from "../../eval.js";
29
30
  import type { ToolCall } from "../../core/harness-driver.js";
30
31
  /** Parse `codex exec --json` stdout into the common trace fields (confirmed schema). */
@@ -103,6 +104,15 @@ export declare const CODEX_TRIGGER_RATE_EXPERIMENTAL: string;
103
104
  * the spec's `fired` with `codexSkillFired` (Codex has no Skill-tool event).
104
105
  */
105
106
  export declare const codexEvalDriver: EvalDriver;
107
+ /**
108
+ * The Codex {@link HarnessLiveDriver} — the EXECUTING tiers' side of the
109
+ * adapter, reached through `codexAdapter.liveDriver()`.
110
+ *
111
+ * It is the object `scan-behavioral.ts:buildProbe` used to build from
112
+ * `harness === "codex"`: the same four answers, now carried by the adapter that
113
+ * knows them instead of switched on by a name in the application layer.
114
+ */
115
+ export declare const codexLiveDriver: HarnessLiveDriver;
106
116
  /**
107
117
  * Spawn real `codex exec --json` for the eval tier (real model, the user's codex
108
118
  * auth — NOT the mock). CONFIRMED flags (codex 0.139.0): `--json` for the event