vigiles 33.1.0 → 33.3.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/README.md +3 -2
- package/dist/adapter-conformance.js +14 -10
- package/dist/adapter.d.ts +3 -1
- package/dist/adapters/claude-code/layout.js +3 -0
- package/dist/adapters/claude-code/output-style.d.ts +3 -0
- package/dist/adapters/claude-code/output-style.js +81 -0
- package/dist/adapters/claude-code/replies.d.ts +14 -0
- package/dist/adapters/claude-code/replies.js +78 -0
- package/dist/adapters/claude-code/runtime.js +70 -0
- package/dist/adapters/claude-code/skill-reachability.d.ts +42 -21
- package/dist/adapters/claude-code/skill-reachability.js +91 -28
- package/dist/audit-report.d.ts +5 -0
- package/dist/audit-report.js +29 -20
- package/dist/audit-report.template.html +25 -25
- package/dist/audit-score.js +2 -2
- package/dist/check.d.ts +20 -0
- package/dist/check.js +87 -2
- package/dist/cli-main.js +249 -30
- package/dist/core/adapter.d.ts +7 -0
- package/dist/core/config-schema.d.ts +9 -0
- package/dist/core/config-schema.js +1 -0
- package/dist/core/eval-driver.d.ts +10 -0
- package/dist/core/frontmatter-read.d.ts +31 -0
- package/dist/core/frontmatter-read.js +4 -2
- package/dist/core/harness-driver.d.ts +2 -0
- package/dist/core/install-reader.js +15 -0
- package/dist/core/layout.d.ts +17 -0
- package/dist/core/layout.js +17 -0
- package/dist/core/output-style.d.ts +84 -0
- package/dist/core/output-style.js +90 -0
- package/dist/core/rule-meta.d.ts +1 -1
- package/dist/core/rule-meta.js +8 -0
- package/dist/core/run-env.d.ts +50 -0
- package/dist/core/run-env.js +51 -0
- package/dist/core/runtime.d.ts +44 -0
- package/dist/core/skill-link-target.d.ts +20 -0
- package/dist/core/skill-link-target.js +33 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/coverage-evidence.d.ts +42 -0
- package/dist/coverage-evidence.js +39 -0
- package/dist/eval.d.ts +65 -42
- package/dist/eval.js +116 -120
- package/dist/harness-test.d.ts +65 -1
- package/dist/harness-test.js +114 -22
- package/dist/optimize.js +1 -5
- package/dist/output-style-arms.d.ts +27 -0
- package/dist/output-style-arms.js +51 -0
- package/dist/plugin-declaration.d.ts +6 -6
- package/dist/plugin-declaration.js +6 -6
- package/dist/scan-files.js +1 -1
- package/dist/scan.d.ts +6 -0
- package/dist/scan.js +14 -4
- package/dist/score-core.d.ts +3 -0
- package/dist/score-core.js +5 -1
- package/dist/setup-plan.d.ts +50 -10
- package/dist/setup-plan.js +103 -68
- package/dist/skill-links.d.ts +183 -0
- package/dist/skill-links.js +255 -0
- package/dist/test-coverage-files.d.ts +3 -1
- package/dist/test-coverage-files.js +20 -6
- package/dist/test-coverage.d.ts +18 -1
- package/dist/test-coverage.js +101 -15
- package/dist/test.d.ts +3 -1
- package/dist/test.js +8 -2
- package/dist/vigilesrc.schema.json +134 -0
- package/package.json +1 -1
package/dist/setup-plan.d.ts
CHANGED
|
@@ -16,7 +16,10 @@ export interface SetupPlan {
|
|
|
16
16
|
test: boolean;
|
|
17
17
|
/** Wire CI (the `zernie/vigiles@v1` Action; creates a workflow if none). */
|
|
18
18
|
gha: boolean;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Install vigiles's skills + hooks for the agent: the skills LINKED into the
|
|
21
|
+
* repo (every clone), the Claude Code hooks via the per-machine plugin.
|
|
22
|
+
*/
|
|
20
23
|
plugin: boolean;
|
|
21
24
|
/**
|
|
22
25
|
* Adopt/scaffold the instruction file(s) into typed `.spec.ts` (the compiled
|
|
@@ -118,7 +121,7 @@ export declare const STRUCTURAL_RULES: readonly ["subagent-tool-contract", "suba
|
|
|
118
121
|
* here: they're acknowledged-noisy recommendations we never gate on (see
|
|
119
122
|
* research/install-enforcement-dx.md).
|
|
120
123
|
*/
|
|
121
|
-
export declare const WORKFLOW_RULES: readonly ["require-instructions-spec", "untested-skill", "untested-subagent", "untested-hook"];
|
|
124
|
+
export declare const WORKFLOW_RULES: readonly ["require-instructions-spec", "untested-skill", "untested-subagent", "untested-hook", "untested-output-style"];
|
|
122
125
|
/**
|
|
123
126
|
* The **`nudge`** group — recommendations / acknowledged-noisy checks that NEVER
|
|
124
127
|
* gate (not even under `--strict`): `frontmatter-valid` (js-yaml is stricter than
|
|
@@ -193,15 +196,18 @@ export interface InstallPlan {
|
|
|
193
196
|
notes: string[];
|
|
194
197
|
/** Whether this method writes files into the consumer's repo (vendoring). */
|
|
195
198
|
vendors: boolean;
|
|
199
|
+
/**
|
|
200
|
+
* Link the package's shipped skills into the repo's skills home
|
|
201
|
+
* (`src/skill-links.ts`). True only where the vendor documents following a
|
|
202
|
+
* symlinked skill folder — a link the harness skips is a silent absence.
|
|
203
|
+
*/
|
|
204
|
+
linkSkills: boolean;
|
|
205
|
+
/**
|
|
206
|
+
* The global install carries HOOKS as well as skills, so linked skills do not
|
|
207
|
+
* replace it (see {@link shouldRunGlobalInstall}).
|
|
208
|
+
*/
|
|
209
|
+
globalInstallCarriesHooks: boolean;
|
|
196
210
|
}
|
|
197
|
-
/** Per-harness install plan. `hasClaude` gates the auto-run `claude plugin` CLI
|
|
198
|
-
* (else the same two steps are printed as `/plugin` slash commands).
|
|
199
|
-
*
|
|
200
|
-
* Both methods install GLOBALLY, never into the repo: Claude through its plugin
|
|
201
|
-
* marketplace (~/.claude/plugins/), Codex through the cross-agent `skills` CLI
|
|
202
|
-
* with `-g -y` (the global store ~/.agents/skills/, which Codex reads). Codex
|
|
203
|
-
* gets the skills but NOT hooks — Codex hook wiring (.codex/config.toml [hooks])
|
|
204
|
-
* is not automated yet. */
|
|
205
211
|
/**
|
|
206
212
|
* The SHIPPED consumer skills (the `skills/` dir, published via `plugin.json`) —
|
|
207
213
|
* the ONLY ones a user's install should get. The cross-agent `skills` CLI would
|
|
@@ -212,9 +218,43 @@ export interface InstallPlan {
|
|
|
212
218
|
* dogfood test in `src/adapters/claude-code/skills-dogfood.test.ts`.
|
|
213
219
|
*/
|
|
214
220
|
export declare const SHIPPED_SKILLS: readonly ["adopt-spec", "debug-my-harness", "edit-spec", "linter-docs", "strengthen", "test-harness"];
|
|
221
|
+
/**
|
|
222
|
+
* Per-harness install plan. `hasClaude` gates the auto-run `claude plugin` CLI
|
|
223
|
+
* (else the same two steps are printed as `/plugin` slash commands).
|
|
224
|
+
*
|
|
225
|
+
* TWO CARRIERS, by what each one reaches. The SKILLS are linked into the repo
|
|
226
|
+
* (`linkSkills`), so every clone and container has them after `npm install`. The
|
|
227
|
+
* GLOBAL installs reach one machine: Claude Code's plugin (~/.claude/plugins/)
|
|
228
|
+
* is still the only carrier of vigiles's Claude Code HOOKS, so it always runs;
|
|
229
|
+
* Codex's `skills` CLI install (~/.agents/skills/) carries skills only, so it
|
|
230
|
+
* runs only when the links could not be made (no `package.json` to install
|
|
231
|
+
* into). Codex's nudge hooks are written into `.codex/config.toml` directly.
|
|
232
|
+
*
|
|
233
|
+
* THE CODEX GLOBAL COMMAND. The cross-agent `skills` CLI with `-g -y` installs
|
|
234
|
+
* to the global store ~/.agents/skills/ (NOT the repo, and NOT ~/.codex/ —
|
|
235
|
+
* verified against the real CLI). It is scoped to the SHIPPED skills with `-s`
|
|
236
|
+
* — without it the `skills` CLI installs EVERY SKILL.md in the repo, leaking
|
|
237
|
+
* the contributor-only .claude/skills/ into the user's global store
|
|
238
|
+
* (#dogfood-I2; confirmed — the CLI's `-l` list shows 17 skills for this repo,
|
|
239
|
+
* incl. .claude/skills). The `-s` parser is SPACE-separated (it consumes
|
|
240
|
+
* consecutive non-dash args), NOT comma-separated — a comma list is read as one
|
|
241
|
+
* literal skill name, matches nothing, and the install exits 1 (verified against
|
|
242
|
+
* the real `skills@1.5.20` arg parser).
|
|
243
|
+
*/
|
|
215
244
|
export declare function planPluginInstall(harnesses: readonly string[], opts: {
|
|
216
245
|
hasClaude: boolean;
|
|
217
246
|
}): InstallPlan[];
|
|
247
|
+
/**
|
|
248
|
+
* Run a plan's GLOBAL install, given whether the skills were linked into the
|
|
249
|
+
* repo? Skip it only when linked skills make it redundant — when it carries
|
|
250
|
+
* nothing but skills. Running it anyway would list every skill twice (Codex:
|
|
251
|
+
* "If two skills share the same `name`, Codex doesn't merge them; both can
|
|
252
|
+
* appear in skill selectors"). Claude Code's plugin carries the hooks, so it
|
|
253
|
+
* always runs; its skills are namespaced `vigiles:<name>` and load beside the
|
|
254
|
+
* linked `<name>` (a known, documented duplicate until the hooks move into the
|
|
255
|
+
* repo too).
|
|
256
|
+
*/
|
|
257
|
+
export declare function shouldRunGlobalInstall(plan: Readonly<Pick<InstallPlan, "globalInstallCarriesHooks">>, skillsLinked: boolean): boolean;
|
|
218
258
|
/** One vigiles-managed Codex hook: a `[[hooks.<event>]]` entry. */
|
|
219
259
|
export interface CodexPluginHook {
|
|
220
260
|
readonly event: string;
|
package/dist/setup-plan.js
CHANGED
|
@@ -18,6 +18,7 @@ exports.shouldPrompt = shouldPrompt;
|
|
|
18
18
|
exports.collectSetupAnswers = collectSetupAnswers;
|
|
19
19
|
exports.gateOnlyInvitation = gateOnlyInvitation;
|
|
20
20
|
exports.planPluginInstall = planPluginInstall;
|
|
21
|
+
exports.shouldRunGlobalInstall = shouldRunGlobalInstall;
|
|
21
22
|
exports.codexPluginHooks = codexPluginHooks;
|
|
22
23
|
exports.applyCodexPluginHooks = applyCodexPluginHooks;
|
|
23
24
|
exports.resolvePlan = resolvePlan;
|
|
@@ -114,6 +115,7 @@ exports.WORKFLOW_RULES = [
|
|
|
114
115
|
"untested-skill",
|
|
115
116
|
"untested-subagent",
|
|
116
117
|
"untested-hook",
|
|
118
|
+
"untested-output-style",
|
|
117
119
|
];
|
|
118
120
|
/**
|
|
119
121
|
* The **`nudge`** group — recommendations / acknowledged-noisy checks that NEVER
|
|
@@ -224,7 +226,7 @@ async function collectSetupAnswers(ask) {
|
|
|
224
226
|
}
|
|
225
227
|
const pillars = (await ask("Set up which pillars? [both/lint/test] (both): ", "both")).toLowerCase();
|
|
226
228
|
const gha = isYesAnswer(await ask("Wire CI (GitHub Action)? [Y/n]: ", "y"));
|
|
227
|
-
const plugin = isYesAnswer(await ask("Install
|
|
229
|
+
const plugin = isYesAnswer(await ask("Install vigiles's skills + hooks for your agent (skills linked into the repo, hooks via the Claude Code plugin)? [Y/n]: ", "y"));
|
|
228
230
|
// Structural gating (broken tools/hooks/MCP/collisions) is always on. This asks
|
|
229
231
|
// about the WORKFLOW tier — a spec per file + a test per surface — which a clean
|
|
230
232
|
// repo can fail just for not having done the work yet, so it's the recommended
|
|
@@ -288,14 +290,6 @@ function gateOnlyInvitation(plan) {
|
|
|
288
290
|
return null;
|
|
289
291
|
return "→ Want your agent to maintain this + measure whether your skills fire? Run `npx vigiles init` and choose 'full' (installs the skills). Optional — the gate above already works.";
|
|
290
292
|
}
|
|
291
|
-
/** Per-harness install plan. `hasClaude` gates the auto-run `claude plugin` CLI
|
|
292
|
-
* (else the same two steps are printed as `/plugin` slash commands).
|
|
293
|
-
*
|
|
294
|
-
* Both methods install GLOBALLY, never into the repo: Claude through its plugin
|
|
295
|
-
* marketplace (~/.claude/plugins/), Codex through the cross-agent `skills` CLI
|
|
296
|
-
* with `-g -y` (the global store ~/.agents/skills/, which Codex reads). Codex
|
|
297
|
-
* gets the skills but NOT hooks — Codex hook wiring (.codex/config.toml [hooks])
|
|
298
|
-
* is not automated yet. */
|
|
299
293
|
/**
|
|
300
294
|
* The SHIPPED consumer skills (the `skills/` dir, published via `plugin.json`) —
|
|
301
295
|
* the ONLY ones a user's install should get. The cross-agent `skills` CLI would
|
|
@@ -313,70 +307,111 @@ exports.SHIPPED_SKILLS = [
|
|
|
313
307
|
"strengthen",
|
|
314
308
|
"test-harness",
|
|
315
309
|
];
|
|
310
|
+
/**
|
|
311
|
+
* Per-harness install plan. `hasClaude` gates the auto-run `claude plugin` CLI
|
|
312
|
+
* (else the same two steps are printed as `/plugin` slash commands).
|
|
313
|
+
*
|
|
314
|
+
* TWO CARRIERS, by what each one reaches. The SKILLS are linked into the repo
|
|
315
|
+
* (`linkSkills`), so every clone and container has them after `npm install`. The
|
|
316
|
+
* GLOBAL installs reach one machine: Claude Code's plugin (~/.claude/plugins/)
|
|
317
|
+
* is still the only carrier of vigiles's Claude Code HOOKS, so it always runs;
|
|
318
|
+
* Codex's `skills` CLI install (~/.agents/skills/) carries skills only, so it
|
|
319
|
+
* runs only when the links could not be made (no `package.json` to install
|
|
320
|
+
* into). Codex's nudge hooks are written into `.codex/config.toml` directly.
|
|
321
|
+
*
|
|
322
|
+
* THE CODEX GLOBAL COMMAND. The cross-agent `skills` CLI with `-g -y` installs
|
|
323
|
+
* to the global store ~/.agents/skills/ (NOT the repo, and NOT ~/.codex/ —
|
|
324
|
+
* verified against the real CLI). It is scoped to the SHIPPED skills with `-s`
|
|
325
|
+
* — without it the `skills` CLI installs EVERY SKILL.md in the repo, leaking
|
|
326
|
+
* the contributor-only .claude/skills/ into the user's global store
|
|
327
|
+
* (#dogfood-I2; confirmed — the CLI's `-l` list shows 17 skills for this repo,
|
|
328
|
+
* incl. .claude/skills). The `-s` parser is SPACE-separated (it consumes
|
|
329
|
+
* consecutive non-dash args), NOT comma-separated — a comma list is read as one
|
|
330
|
+
* literal skill name, matches nothing, and the install exits 1 (verified against
|
|
331
|
+
* the real `skills@1.5.20` arg parser).
|
|
332
|
+
*/
|
|
316
333
|
function planPluginInstall(harnesses, opts) {
|
|
317
|
-
return harnesses.map((harness) => {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
// The cross-agent `skills` CLI with `-g -y` installs to the global store
|
|
340
|
-
// ~/.agents/skills/ (NOT the repo, and NOT ~/.codex/ — verified against
|
|
341
|
-
// the real CLI). Skills install globally; the proactive NUDGE hooks
|
|
342
|
-
// (eval-lock + refs) are wired into the repo's .codex/config.toml by
|
|
343
|
-
// `init` (see codexPluginHooks / wireCodexHooks) — Codex config is
|
|
344
|
-
// repo-committed, so that's the idiomatic place.
|
|
345
|
-
// Scope to the SHIPPED skills with `-s` — without it the `skills` CLI
|
|
346
|
-
// installs EVERY SKILL.md in the repo, leaking the contributor-only
|
|
347
|
-
// .claude/skills/ into the user's global store (#dogfood-I2; confirmed —
|
|
348
|
-
// the CLI's `-l` list shows 17 skills for this repo, incl. .claude/skills).
|
|
349
|
-
// The `-s` parser is SPACE-separated (it consumes consecutive non-dash
|
|
350
|
-
// args), NOT comma-separated — a comma list is read as one literal skill
|
|
351
|
-
// name, matches nothing, and the install exits 1 (verified against the
|
|
352
|
-
// real `skills@1.5.20` arg parser).
|
|
353
|
-
const skillScope = `-s ${exports.SHIPPED_SKILLS.join(" ")}`;
|
|
354
|
-
return {
|
|
355
|
-
harness,
|
|
356
|
-
commands: [
|
|
357
|
-
`npx --yes skills add zernie/vigiles -a codex ${skillScope} -g -y`,
|
|
358
|
-
],
|
|
359
|
-
successMessage: "✓ Installed the vigiles skills into ~/.agents/skills/ (global, not vendored)",
|
|
360
|
-
manualSteps: [
|
|
361
|
-
`npx skills add zernie/vigiles -a codex ${skillScope} -g -y`,
|
|
362
|
-
],
|
|
363
|
-
notes: [
|
|
364
|
-
"Codex reads AGENTS.md directly; the skills install globally to ~/.agents/skills/ (not the repo).",
|
|
365
|
-
"The eval-lock + refs NUDGE hooks are wired into .codex/config.toml (repo-committed, the Codex norm).",
|
|
366
|
-
"Still manual on Codex: the SessionStart lint summary + compile-on-edit/pre-edit guards (no harness-neutral entrypoint yet).",
|
|
367
|
-
],
|
|
368
|
-
vendors: true,
|
|
369
|
-
};
|
|
370
|
-
}
|
|
334
|
+
return harnesses.map((harness) => ({
|
|
335
|
+
...globalInstallFor(harness, opts),
|
|
336
|
+
...skillCarriers(harness),
|
|
337
|
+
}));
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* How the skills and the hooks travel for one harness. Linking is on only where
|
|
341
|
+
* the vendor documents following a symlinked skill folder (quoted in
|
|
342
|
+
* `./skill-links.ts`); the global install "carries hooks" only for Claude Code,
|
|
343
|
+
* whose plugin is where its hooks live.
|
|
344
|
+
*/
|
|
345
|
+
function skillCarriers(harness) {
|
|
346
|
+
if (harness === "claude")
|
|
347
|
+
return { linkSkills: true, globalInstallCarriesHooks: true };
|
|
348
|
+
// eslint-disable-next-line local/no-harness-names -- init's per-harness install plan is keyed by harness name, like the global install beside it
|
|
349
|
+
if (harness === "codex")
|
|
350
|
+
return { linkSkills: true, globalInstallCarriesHooks: false };
|
|
351
|
+
return { linkSkills: false, globalInstallCarriesHooks: false };
|
|
352
|
+
}
|
|
353
|
+
/** The global install half of {@link planPluginInstall}, per harness. */
|
|
354
|
+
function globalInstallFor(harness, opts) {
|
|
355
|
+
if (harness === "claude") {
|
|
371
356
|
return {
|
|
372
357
|
harness,
|
|
373
|
-
commands:
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
358
|
+
commands: opts.hasClaude
|
|
359
|
+
? [
|
|
360
|
+
"claude plugin marketplace add zernie/vigiles",
|
|
361
|
+
"claude plugin install vigiles@vigiles",
|
|
362
|
+
]
|
|
363
|
+
: [],
|
|
364
|
+
successMessage: "✓ Installed the vigiles plugin (hooks + skills) into ~/.claude/plugins/",
|
|
365
|
+
manualSteps: [
|
|
366
|
+
"/plugin marketplace add zernie/vigiles",
|
|
367
|
+
"/plugin install vigiles@vigiles",
|
|
368
|
+
],
|
|
369
|
+
notes: [
|
|
370
|
+
"The plugin installs globally to ~/.claude/plugins/ and carries the hooks (and a namespaced copy of the skills, listed as vigiles:<name>) on this machine only.",
|
|
371
|
+
],
|
|
377
372
|
vendors: false,
|
|
378
373
|
};
|
|
379
|
-
}
|
|
374
|
+
}
|
|
375
|
+
if (harness === "codex") {
|
|
376
|
+
const skillScope = `-s ${exports.SHIPPED_SKILLS.join(" ")}`;
|
|
377
|
+
return {
|
|
378
|
+
harness,
|
|
379
|
+
commands: [
|
|
380
|
+
`npx --yes skills add zernie/vigiles -a codex ${skillScope} -g -y`,
|
|
381
|
+
],
|
|
382
|
+
successMessage: "✓ Installed the vigiles skills into ~/.agents/skills/ (global, not vendored)",
|
|
383
|
+
manualSteps: [
|
|
384
|
+
`npx skills add zernie/vigiles -a codex ${skillScope} -g -y`,
|
|
385
|
+
],
|
|
386
|
+
notes: [
|
|
387
|
+
"Codex reads AGENTS.md directly; the skills install globally to ~/.agents/skills/ (not the repo).",
|
|
388
|
+
"The eval-lock + refs NUDGE hooks are wired into .codex/config.toml (repo-committed, the Codex norm).",
|
|
389
|
+
"Still manual on Codex: the SessionStart lint summary + compile-on-edit/pre-edit guards (no harness-neutral entrypoint yet).",
|
|
390
|
+
],
|
|
391
|
+
vendors: true,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
return {
|
|
395
|
+
harness,
|
|
396
|
+
commands: [],
|
|
397
|
+
successMessage: "",
|
|
398
|
+
manualSteps: [],
|
|
399
|
+
notes: [`No plugin install path for harness '${harness}'.`],
|
|
400
|
+
vendors: false,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Run a plan's GLOBAL install, given whether the skills were linked into the
|
|
405
|
+
* repo? Skip it only when linked skills make it redundant — when it carries
|
|
406
|
+
* nothing but skills. Running it anyway would list every skill twice (Codex:
|
|
407
|
+
* "If two skills share the same `name`, Codex doesn't merge them; both can
|
|
408
|
+
* appear in skill selectors"). Claude Code's plugin carries the hooks, so it
|
|
409
|
+
* always runs; its skills are namespaced `vigiles:<name>` and load beside the
|
|
410
|
+
* linked `<name>` (a known, documented duplicate until the hooks move into the
|
|
411
|
+
* repo too).
|
|
412
|
+
*/
|
|
413
|
+
function shouldRunGlobalInstall(plan, skillsLinked) {
|
|
414
|
+
return plan.globalInstallCarriesHooks || !skillsLinked;
|
|
380
415
|
}
|
|
381
416
|
/**
|
|
382
417
|
* vigiles's proactive nudges, wired into a Codex repo's `.codex/config.toml`.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/** The package whose skills are linked. */
|
|
2
|
+
export declare const VIGILES_PACKAGE = "vigiles";
|
|
3
|
+
/** What occupies `<skills home>/<name>` today, as the IO edge observed it. */
|
|
4
|
+
export type SkillEntry = {
|
|
5
|
+
readonly kind: "missing";
|
|
6
|
+
} | {
|
|
7
|
+
readonly kind: "link";
|
|
8
|
+
/** The link's stored target, exactly as `readlink` returns it. */
|
|
9
|
+
readonly target: string;
|
|
10
|
+
/** Where it resolves, or null when it dangles. */
|
|
11
|
+
readonly resolvesTo: string | null;
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: "directory";
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: "file";
|
|
16
|
+
};
|
|
17
|
+
/** What `init` should do about one skill. */
|
|
18
|
+
export type SkillLinkDecision = {
|
|
19
|
+
readonly name: string;
|
|
20
|
+
/** `replace`: an earlier `init`'s link that now points at the wrong place. */
|
|
21
|
+
readonly action: "create" | "replace";
|
|
22
|
+
readonly target: string;
|
|
23
|
+
} | {
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly action: "keep";
|
|
26
|
+
} | {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly action: "refuse";
|
|
29
|
+
readonly reason: string;
|
|
30
|
+
};
|
|
31
|
+
/** The link `init` wants for one skill. */
|
|
32
|
+
export interface WantedLink {
|
|
33
|
+
/** The relative target to write (and to recognise on a re-run). */
|
|
34
|
+
readonly target: string;
|
|
35
|
+
/** The real path of the shipped skill, or null while it is not installed. */
|
|
36
|
+
readonly real: string | null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Decide one entry. A link with OUR target is ours even while it dangles — that
|
|
40
|
+
* is exactly how a committed link looks in a clone that has not run
|
|
41
|
+
* `npm install` yet, and calling it foreign would make re-running `init` noisy
|
|
42
|
+
* in precisely the place it is meant to help.
|
|
43
|
+
*/
|
|
44
|
+
export declare function decideSkillLink(name: string, entry: SkillEntry, want: WantedLink): SkillLinkDecision;
|
|
45
|
+
/** Where the package is, as the project spells it. */
|
|
46
|
+
export type PackageSite =
|
|
47
|
+
/** Installed: `<ancestor>/node_modules/vigiles`, inside the repository. */
|
|
48
|
+
{
|
|
49
|
+
readonly kind: "installed";
|
|
50
|
+
readonly dir: string;
|
|
51
|
+
}
|
|
52
|
+
/** Not installed yet: where npm puts the devDependency `init` declares. */
|
|
53
|
+
| {
|
|
54
|
+
readonly kind: "expected";
|
|
55
|
+
readonly dir: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Not installed, and the project is a member of a workspace: npm and yarn
|
|
59
|
+
* hoist its dependencies to the workspace ROOT's `node_modules`, pnpm keeps
|
|
60
|
+
* them in the member's own, and a hoist can be refused by a version
|
|
61
|
+
* conflict. Which one happens is decided by the install, so no link is made
|
|
62
|
+
* before it — a guessed link can dangle for good.
|
|
63
|
+
*/
|
|
64
|
+
| {
|
|
65
|
+
readonly kind: "unsure";
|
|
66
|
+
readonly reason: string;
|
|
67
|
+
};
|
|
68
|
+
/** What the walk may ask of the filesystem. */
|
|
69
|
+
export interface PackageProbe {
|
|
70
|
+
/** Is `dir` an installed vigiles package (its `package.json` names it)? */
|
|
71
|
+
readonly isPackage: (dir: string) => boolean;
|
|
72
|
+
/** Is `dir` the repository root (it holds `.git`)? The walk stops there. */
|
|
73
|
+
readonly isRepoRoot: (dir: string) => boolean;
|
|
74
|
+
/** Is `dir` a workspace root (`workspaces` in package.json, or pnpm-workspace.yaml)? */
|
|
75
|
+
readonly isWorkspaceRoot: (dir: string) => boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Node's own lookup — the first `node_modules/vigiles` up the tree — bounded by
|
|
79
|
+
* the repository root, because a committed link that leaves the repository is a
|
|
80
|
+
* link only this machine can follow.
|
|
81
|
+
*/
|
|
82
|
+
export declare function locatePackage(project: string, probe: PackageProbe): PackageSite;
|
|
83
|
+
/**
|
|
84
|
+
* Is the real path `path` the project's own real path or somewhere under it?
|
|
85
|
+
* Both must be physical (symlink-free), or a link could pass for a child.
|
|
86
|
+
*/
|
|
87
|
+
export declare function isWithinProject(project: string, path: string): boolean;
|
|
88
|
+
/** The facts that decide whether linking makes sense at all here. */
|
|
89
|
+
export interface LinkFacts {
|
|
90
|
+
/** The project IS vigiles — its skills are the plugin's own `skills/`. */
|
|
91
|
+
readonly isVigilesItself: boolean;
|
|
92
|
+
/** `package.json` declares vigiles (so `npm install` will put it in node_modules). */
|
|
93
|
+
readonly dependsOnVigiles: boolean;
|
|
94
|
+
/** The package already resolves from the project. */
|
|
95
|
+
readonly installed: boolean;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Why NOT to link, or null to go ahead. A link into a `node_modules/vigiles`
|
|
99
|
+
* that no install will ever create is a dangling link committed to the repo — a
|
|
100
|
+
* worse state than no link, because it looks like the job was done.
|
|
101
|
+
*/
|
|
102
|
+
export declare function linkPrecondition(facts: LinkFacts): string | null;
|
|
103
|
+
/** Everything {@link planSkillLinks} needs, observed by the IO edge. */
|
|
104
|
+
export interface SkillLinkInput {
|
|
105
|
+
/** The skills the package ships. */
|
|
106
|
+
readonly names: readonly string[];
|
|
107
|
+
readonly site: Exclude<PackageSite, {
|
|
108
|
+
kind: "unsure";
|
|
109
|
+
}>;
|
|
110
|
+
/**
|
|
111
|
+
* The skills home as it PHYSICALLY exists (`.claude` may itself be a link).
|
|
112
|
+
* A relative link target resolves against the directory that holds the link.
|
|
113
|
+
*/
|
|
114
|
+
readonly physicalHome: string;
|
|
115
|
+
readonly entries: (name: string) => SkillEntry;
|
|
116
|
+
/** The real path of the installed skill, or null. */
|
|
117
|
+
readonly realOf: (name: string) => string | null;
|
|
118
|
+
}
|
|
119
|
+
/** The decisions, and whether the links will dangle until `npm install`. */
|
|
120
|
+
export interface SkillLinkPlan {
|
|
121
|
+
readonly pending: boolean;
|
|
122
|
+
readonly decisions: readonly SkillLinkDecision[];
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A link target as it is committed: `/` separators on every OS. `relative()` on
|
|
126
|
+
* Windows yields `..\\..\\node_modules\\…`, and a clone on Linux or macOS
|
|
127
|
+
* reads those backslashes as ordinary filename characters, so the link dangles.
|
|
128
|
+
*/
|
|
129
|
+
export declare function portableTarget(target: string, separator?: string): string;
|
|
130
|
+
export declare function planSkillLinks(input: SkillLinkInput): SkillLinkPlan;
|
|
131
|
+
/** The three filesystem calls that write a link; the IO edge passes the real ones. */
|
|
132
|
+
export interface LinkIo {
|
|
133
|
+
readonly symlink: (target: string, path: string) => void;
|
|
134
|
+
readonly rename: (from: string, to: string) => void;
|
|
135
|
+
readonly unlink: (path: string) => void;
|
|
136
|
+
}
|
|
137
|
+
/** Where a replacement link is built before it takes the old one's place. */
|
|
138
|
+
export declare function relinkTempPath(entry: string): string;
|
|
139
|
+
/**
|
|
140
|
+
* Write the link `decision` asks for. A `replace` never leaves a gap: the new
|
|
141
|
+
* link is built under a temporary name beside the old one and renamed over it,
|
|
142
|
+
* which is atomic. Removing the old link first would lose it for good when the
|
|
143
|
+
* new one cannot be created (a Windows account without the symlink privilege,
|
|
144
|
+
* a full disk) — a skill that worked yesterday gone, and only a warning to show
|
|
145
|
+
* for it. Any failure leaves the old link in place and throws.
|
|
146
|
+
*/
|
|
147
|
+
export declare function writeSkillLink(io: LinkIo, entry: string, target: string, action: "create" | "replace"): void;
|
|
148
|
+
/** Why a `symlink` call failed, with the fix when there is a known one. */
|
|
149
|
+
export declare function linkFailureReason(code: string, message: string): string;
|
|
150
|
+
/** One skill's result after the IO ran. */
|
|
151
|
+
export type SkillLinkResult = {
|
|
152
|
+
readonly name: string;
|
|
153
|
+
readonly status: "created" | "relinked" | "present";
|
|
154
|
+
} | {
|
|
155
|
+
readonly name: string;
|
|
156
|
+
readonly status: "skipped";
|
|
157
|
+
readonly reason: string;
|
|
158
|
+
};
|
|
159
|
+
/** What `init` did about the skills for one harness. */
|
|
160
|
+
export type SkillLinkOutcome = {
|
|
161
|
+
readonly kind: "linked";
|
|
162
|
+
/** Repo-relative skills home, e.g. `.claude/skills`. */
|
|
163
|
+
readonly home: string;
|
|
164
|
+
/**
|
|
165
|
+
* The same home with every symlink resolved, repo-relative with `/`.
|
|
166
|
+
* Git refuses a pathspec that runs through a symlink, so the commit hint
|
|
167
|
+
* names this one.
|
|
168
|
+
*/
|
|
169
|
+
readonly physicalHome: string;
|
|
170
|
+
readonly pending: boolean;
|
|
171
|
+
readonly results: readonly SkillLinkResult[];
|
|
172
|
+
} | {
|
|
173
|
+
readonly kind: "not-linked";
|
|
174
|
+
readonly reason: string;
|
|
175
|
+
};
|
|
176
|
+
/** At least one shipped skill now reaches the agent through a link. */
|
|
177
|
+
export declare function skillLinksUsable(outcome: SkillLinkOutcome): boolean;
|
|
178
|
+
/**
|
|
179
|
+
* The lines `init` prints. Skipped skills are named one by one: the user loses
|
|
180
|
+
* that skill, and the reason is theirs to act on.
|
|
181
|
+
*/
|
|
182
|
+
export declare function formatSkillLinks(outcome: SkillLinkOutcome): readonly string[];
|
|
183
|
+
//# sourceMappingURL=skill-links.d.ts.map
|