vigiles 33.3.0 → 34.0.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/action.yml CHANGED
@@ -19,9 +19,15 @@ inputs:
19
19
  required: false
20
20
  default: ""
21
21
  version:
22
- description: "Version of the vigiles npm package to run (e.g. '1', '1.2.3', 'latest'). Use 'local' to run the action's own checkout — for dogfooding via `uses: ./` in this repo's CI."
22
+ description: >
23
+ Version of the vigiles npm package to run (e.g. '34', '34.1.2', 'latest').
24
+ Empty (the default) runs the vigiles THIS repo declares in package.json:
25
+ the installed copy when node_modules has it, else the version in
26
+ package-lock.json, else the declared range — and 'latest' only when the
27
+ repo does not declare vigiles. Use 'local' to run the action's own
28
+ checkout — for dogfooding via `uses: ./` in this repo's CI.
23
29
  required: false
24
- default: "latest"
30
+ default: ""
25
31
  max-rules:
26
32
  description: "Maximum number of rules per spec file (maps to the CLI's --max-rules)."
27
33
  required: false
@@ -109,17 +115,48 @@ runs:
109
115
  fi
110
116
 
111
117
  # Resolve the runner. 'local' runs the action's own build (CI
112
- # dogfood via `uses: ./`); any other value runs the published npm
113
- # package at that version, so the Action reuses the same tested
114
- # artifact users get from `npx vigiles`.
115
- version="${VIGILES_VERSION:-latest}"
118
+ # dogfood via `uses: ./`); an explicit version runs the published npm
119
+ # package at that version. EMPTY (the default) runs the vigiles this
120
+ # repo declares, so CI and a local `npx vigiles` are the same version
121
+ # rather than two pins that drift: the installed copy, else the
122
+ # lockfile's version, else the declared range; `latest` only when the
123
+ # repo does not declare vigiles at all.
124
+ version="${VIGILES_VERSION:-}"
116
125
  if [[ "$version" == "local" ]]; then
117
126
  runner=(node "${VIGILES_ACTION_PATH}/dist/cli.js")
118
- else
127
+ from="the action's own checkout"
128
+ elif [[ -n "$version" ]]; then
119
129
  runner=(npx --yes "vigiles@${version}")
130
+ from="the version input"
131
+ else
132
+ declared="$(node -e '
133
+ const fs = require("fs");
134
+ const read = (f) => { try { return JSON.parse(fs.readFileSync(f, "utf8")); } catch { return null; } };
135
+ const pkg = read("package.json") ?? {};
136
+ const spec = pkg.devDependencies?.vigiles ?? pkg.dependencies?.vigiles;
137
+ if (typeof spec !== "string") { console.log("none"); process.exit(0); }
138
+ if (fs.existsSync("node_modules/.bin/vigiles")) { console.log("installed"); process.exit(0); }
139
+ const lock = read("package-lock.json") ?? {};
140
+ const locked = lock.packages?.["node_modules/vigiles"]?.version ?? lock.dependencies?.vigiles?.version;
141
+ console.log(typeof locked === "string" ? "lock " + locked : "range " + spec);
142
+ ' 2>/dev/null || echo none)"
143
+ case "$declared" in
144
+ installed)
145
+ runner=(npx --no-install vigiles)
146
+ from="this repo's installed vigiles" ;;
147
+ "lock "*)
148
+ runner=(npx --yes "vigiles@${declared#lock }")
149
+ from="package-lock.json" ;;
150
+ "range "*)
151
+ runner=(npx --yes "vigiles@${declared#range }")
152
+ from="the range package.json declares" ;;
153
+ *)
154
+ runner=(npx --yes "vigiles@latest")
155
+ from="latest (this repo does not declare vigiles)" ;;
156
+ esac
120
157
  fi
121
158
 
122
- echo "vigiles: ${runner[*]} ${args[*]}"
159
+ echo "vigiles: ${runner[*]} ${args[*]} (version from ${from})"
123
160
 
124
161
  # Run, streaming to the log AND capturing for the summary/comment.
125
162
  out="$(mktemp)"
@@ -75,6 +75,7 @@ exports.COMMAND_FLAGS = {
75
75
  "--no-gha",
76
76
  "--no-plugin",
77
77
  "--ci-only",
78
+ "--full",
78
79
  ],
79
80
  compile: [],
80
81
  eject: ["--keep-spec"],
package/dist/cli-main.js CHANGED
@@ -27,6 +27,7 @@ const generate_harness_js_1 = require("./core/generate-harness.js");
27
27
  const capability_diff_js_1 = require("./core/capability-diff.js");
28
28
  const validate_js_1 = require("./core/validate.js");
29
29
  const eval_lock_js_1 = require("./eval-lock.js");
30
+ const zod_1 = require("zod");
30
31
  const cli_flags_js_1 = require("./cli-flags.js");
31
32
  const cli_commands_js_1 = require("./cli-commands.js");
32
33
  const cli_flag_check_js_1 = require("./cli-flag-check.js");
@@ -2636,6 +2637,9 @@ function vigilesWorkflow(plan, harnesses, hasPackageJson) {
2636
2637
  - uses: actions/setup-node@v4
2637
2638
  with:
2638
2639
  node-version: "20"
2640
+ # No \`version:\` on purpose: the Action runs the vigiles your
2641
+ # package.json declares, so CI and a local \`npx vigiles\` cannot
2642
+ # drift onto two different pins.
2639
2643
  - uses: zernie/vigiles@v1
2640
2644
  `
2641
2645
  : "";
@@ -3036,6 +3040,9 @@ function redirectSyncToolTargets(cwd, targets) {
3036
3040
  `staling the integrity hash.`);
3037
3041
  return out;
3038
3042
  }
3043
+ /** The two files `init`'s lint layer generates under `.vigiles/`. */
3044
+ const GENERATED_TYPES = ".vigiles/generated.d.ts";
3045
+ const GENERATED_SCHEMA = ".vigiles/schema.json";
3039
3046
  /** Pillar 1 — specs + types + schema + compile. Scaffolds a spec for every
3040
3047
  * instruction file (so `--lint` always delivers a spec), but never compiles
3041
3048
  * OVER a hand-written file — that is left to the adopt-spec skill. */
@@ -3081,7 +3088,7 @@ async function setupPillar1(detected, targetValue, harnesses) {
3081
3088
  const outDir = (0, node_path_1.resolve)(cwd, ".vigiles");
3082
3089
  if (!(0, node_fs_1.existsSync)(outDir))
3083
3090
  (0, node_fs_1.mkdirSync)(outDir, { recursive: true });
3084
- (0, node_fs_1.writeFileSync)((0, node_path_1.resolve)(cwd, ".vigiles/generated.d.ts"), typesResult.dts);
3091
+ (0, node_fs_1.writeFileSync)((0, node_path_1.resolve)(cwd, GENERATED_TYPES), typesResult.dts);
3085
3092
  for (const l of typesResult.linters) {
3086
3093
  console.log(` ${l.linter}: ${String(l.rules.length)} rules`);
3087
3094
  }
@@ -3089,14 +3096,14 @@ async function setupPillar1(detected, targetValue, harnesses) {
3089
3096
  console.log(` npm scripts: ${String(typesResult.scripts.length)}`);
3090
3097
  }
3091
3098
  console.log("✓ Generated .vigiles/generated.d.ts");
3092
- written.push(".vigiles/generated.d.ts");
3099
+ written.push(GENERATED_TYPES);
3093
3100
  const schemaResult = (0, generate_schema_js_1.generateSchema)({
3094
3101
  basePath: cwd,
3095
3102
  linters: (0, validate_js_1.loadConfig)().linters,
3096
3103
  });
3097
- (0, node_fs_1.writeFileSync)((0, node_path_1.resolve)(cwd, ".vigiles/schema.json"), schemaResult.json);
3104
+ (0, node_fs_1.writeFileSync)((0, node_path_1.resolve)(cwd, GENERATED_SCHEMA), schemaResult.json);
3098
3105
  console.log("✓ Generated .vigiles/schema.json (YAML-LSP frontmatter schema)");
3099
- written.push(".vigiles/schema.json");
3106
+ written.push(GENERATED_SCHEMA);
3100
3107
  // Compile — but NEVER overwrite an existing hand-written file during `init`:
3101
3108
  // we compile only GREENFIELD targets (the file doesn't exist yet) and targets
3102
3109
  // we already manage (carry our hash). An ADOPTED file is left untouched — the
@@ -3609,8 +3616,24 @@ function printSetupSummary(opts) {
3609
3616
  console.log(`\n Commit:\n git add ${files.join(" ")} && git commit -m "Add vigiles"`);
3610
3617
  }
3611
3618
  }
3619
+ /** The project config file `init` reads and writes. */
3620
+ const VIGILESRC = ".vigilesrc.json";
3612
3621
  async function setup(args) {
3613
3622
  const parsed = (0, setup_plan_js_1.parseSetupArgs)(args);
3623
+ // An already-adopted repo gets the MINIMAL re-run unless a flag asks for more
3624
+ // (#338): re-running `init` to pick up one new piece must not also wire CI,
3625
+ // widen the rules, scaffold files or rewrite a pin. Decided from disk, before
3626
+ // any prompt — the minimal run asks nothing.
3627
+ const state = (0, setup_plan_js_1.adoptionState)({
3628
+ hasConfig: (0, node_fs_1.existsSync)((0, node_path_1.resolve)(process.cwd(), VIGILESRC)),
3629
+ specs: detectProject()
3630
+ .instructionFiles.filter((f) => f.hasSpec)
3631
+ .map((f) => `${f.path}.spec.ts`),
3632
+ });
3633
+ if ((0, setup_plan_js_1.rerunScope)(parsed, state) === "minimal" && state.kind === "adopted") {
3634
+ minimalRerun(parsed, state);
3635
+ return;
3636
+ }
3614
3637
  // Plan: defaults → flags → interactive prompts (only a human at a TTY).
3615
3638
  let plan = (0, setup_plan_js_1.resolvePlan)(parsed);
3616
3639
  const prompted = (0, setup_plan_js_1.shouldPrompt)(parsed, process.stdin.isTTY ?? false);
@@ -3707,6 +3730,152 @@ async function setup(args) {
3707
3730
  console.log("\nℹ Ran the standard setup. Already have a harness, or not a JS/Python repo, and want only the CI integrity gate (nothing installed)? Re-run `npx vigiles init --ci-only`.");
3708
3731
  }
3709
3732
  }
3733
+ /**
3734
+ * The minimal re-run on an adopted repo (#338): add what is missing AND safe and
3735
+ * local — the skill links and a missing devDependency — then say what a full
3736
+ * setup would add and the flag that does it. It writes NOTHING else: no CI
3737
+ * workflow, no `.vigilesrc.json` edit, no harness/schema scaffold, no spec for a
3738
+ * hand-written file, no edit to an existing version pin, no global install.
3739
+ * The decisions are pure (`setup-plan.ts`); this is the IO.
3740
+ */
3741
+ function minimalRerun(parsed, state) {
3742
+ const plan = (0, setup_plan_js_1.resolvePlan)(parsed);
3743
+ const detected = detectProject();
3744
+ const harnesses = resolveHarnesses(parsed, detected, (0, adapter_registry_js_1.declaredHarnessNames)((0, validate_js_1.loadConfig)().harnesses));
3745
+ console.log(`vigiles is already set up here (found ${state.evidence}) — adding only what is missing.\n`);
3746
+ printDetection(detected, harnesses);
3747
+ const links = plan.plugin ? linkSkillsOnly(harnesses) : [];
3748
+ const devDep = plan.lint || plan.test ? addMissingVigilesDevDep() : [];
3749
+ const written = [...links, ...devDep];
3750
+ if (written.length === 0)
3751
+ console.log("\nNothing was missing.");
3752
+ const followUps = (0, setup_plan_js_1.fullSetupFollowUps)(fullSetupGap(plan, detected, harnesses));
3753
+ if (followUps.length > 0)
3754
+ console.log(`\n${followUps.join("\n")}`);
3755
+ if (written.length > 0)
3756
+ console.log(`\n Commit:\n git add ${written.join(" ")} && git commit -m "Update vigiles"`);
3757
+ }
3758
+ /**
3759
+ * Link the shipped skills for each harness that links them — the safe, local
3760
+ * half of `installPlugins`, without the global install or config wiring.
3761
+ * Returns the links created or rewritten.
3762
+ */
3763
+ function linkSkillsOnly(harnesses) {
3764
+ return (0, setup_plan_js_1.planPluginInstall)(harnesses, { hasClaude: false }).flatMap((plan) => {
3765
+ const home = plan.linkSkills ? harnessSkillsHome(plan.harness) : null;
3766
+ if (home === null)
3767
+ return [];
3768
+ const outcome = linkVigilesSkills(home, process.cwd());
3769
+ console.log("");
3770
+ (0, skill_links_js_1.formatSkillLinks)(outcome).forEach((l) => {
3771
+ console.log(l);
3772
+ });
3773
+ return changedLinks(outcome);
3774
+ });
3775
+ }
3776
+ /** Add vigiles to `devDependencies` only when nothing declares it. */
3777
+ function addMissingVigilesDevDep() {
3778
+ const pkgPath = (0, node_path_1.resolve)(process.cwd(), PACKAGE_JSON);
3779
+ if (!(0, node_fs_1.existsSync)(pkgPath))
3780
+ return [];
3781
+ const pkg = readPackageJson(pkgPath);
3782
+ if (pkg === null || pkg.name === "vigiles")
3783
+ return [];
3784
+ const spec = vigilesDepSpec();
3785
+ const devDependencies = (0, setup_plan_js_1.missingVigilesDevDep)(pkg, spec);
3786
+ if (devDependencies === null)
3787
+ return [];
3788
+ (0, node_fs_1.writeFileSync)(pkgPath, JSON.stringify({ ...pkg, devDependencies }, null, 2) + "\n");
3789
+ console.log(`\n✓ Added vigiles@${spec} to devDependencies — run \`npm install\` to fetch it`);
3790
+ return [PACKAGE_JSON];
3791
+ }
3792
+ /**
3793
+ * The fields of package.json the dependency decisions read. LOOSE on purpose:
3794
+ * every other key is carried through untouched, because the result is written
3795
+ * back and a re-run must not drop a field it never looked at.
3796
+ */
3797
+ const packageDepsSchema = zod_1.z.looseObject({
3798
+ name: zod_1.z.string().optional(),
3799
+ dependencies: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).optional(),
3800
+ devDependencies: zod_1.z.record(zod_1.z.string(), zod_1.z.string()).optional(),
3801
+ });
3802
+ /** package.json parsed for its dependency fields; null when unreadable. */
3803
+ function readPackageJson(path) {
3804
+ try {
3805
+ const parsed = packageDepsSchema.safeParse(JSON.parse((0, node_fs_1.readFileSync)(path, "utf-8")));
3806
+ return parsed.success ? parsed.data : null;
3807
+ }
3808
+ catch {
3809
+ return null;
3810
+ }
3811
+ }
3812
+ /**
3813
+ * What a full setup would add here, read from disk — the input to the
3814
+ * follow-up lines. It mirrors the full path's own conditions (the plan's
3815
+ * layers, the existence checks those steps make), so the list names what
3816
+ * `--full` would actually do rather than everything it could.
3817
+ */
3818
+ function fullSetupGap(plan, detected, harnesses) {
3819
+ const cwd = process.cwd();
3820
+ const has = (rel) => (0, node_fs_1.existsSync)((0, node_path_1.resolve)(cwd, rel));
3821
+ const config = readProjectConfig((0, node_path_1.resolve)(cwd, VIGILESRC));
3822
+ const pillar1 = plan.lint && plan.scaffoldSpecs;
3823
+ const unspecced = pillar1
3824
+ ? [
3825
+ ...determineTargets(detected, undefined, [...harnesses]),
3826
+ ...discoverAdoptableSurfaces(cwd),
3827
+ ].filter((t, i, all) => all.indexOf(t) === i && !has(`${t}.spec.ts`))
3828
+ : [];
3829
+ const pkg = has(PACKAGE_JSON)
3830
+ ? readPackageJson((0, node_path_1.resolve)(cwd, PACKAGE_JSON))
3831
+ : null;
3832
+ const declared = pkg?.devDependencies?.vigiles ?? pkg?.dependencies?.vigiles ?? null;
3833
+ const spec = vigilesDepSpec();
3834
+ return {
3835
+ workflow: plan.gha && !has(".github/workflows/vigiles.yml"),
3836
+ config: configGap(config, plan.lint),
3837
+ harnessTest: plan.test && !has("vigiles.harness.mjs"),
3838
+ unspecced,
3839
+ generated: pillar1 && !(has(GENERATED_TYPES) && has(GENERATED_SCHEMA)),
3840
+ installs: plan.plugin ? fullInstallLines(harnesses) : [],
3841
+ devDep: declared !== null && declared !== spec && pkg?.name !== "vigiles"
3842
+ ? { from: declared, to: spec }
3843
+ : null,
3844
+ };
3845
+ }
3846
+ /** Where `.vigilesrc.json` stands against the preset a full setup writes. */
3847
+ function configGap(config, lint) {
3848
+ if (config === null)
3849
+ return "missing";
3850
+ return config.extends === undefined && lint ? "no-preset" : "has-preset";
3851
+ }
3852
+ /** `.vigilesrc.json` as a plain object, or null when absent or unreadable. */
3853
+ function readProjectConfig(path) {
3854
+ if (!(0, node_fs_1.existsSync)(path))
3855
+ return null;
3856
+ try {
3857
+ const raw = JSON.parse((0, node_fs_1.readFileSync)(path, "utf-8"));
3858
+ return typeof raw === "object" && raw !== null && !Array.isArray(raw)
3859
+ ? { ...raw }
3860
+ : null;
3861
+ }
3862
+ catch {
3863
+ return null;
3864
+ }
3865
+ }
3866
+ /** The per-machine installs and config wiring the full plugin step would run. */
3867
+ function fullInstallLines(harnesses) {
3868
+ return (0, setup_plan_js_1.planPluginInstall)(harnesses, { hasClaude: true }).flatMap((plan) => [
3869
+ ...(plan.globalInstallCarriesHooks && plan.commands.length > 0
3870
+ ? [
3871
+ `install the ${plan.harness} plugin for this machine (${plan.commands.join(" && ")}) and declare it in the project settings`,
3872
+ ]
3873
+ : []),
3874
+ ...(plan.vendors
3875
+ ? [`wire vigiles's nudge hooks into the ${plan.harness} config`]
3876
+ : []),
3877
+ ]);
3878
+ }
3710
3879
  /** Canonical, de-duplicated harness names — the keys `harnesses` gets. */
3711
3880
  function harnessConfigKeys(harnesses) {
3712
3881
  return [...new Set(harnesses.map(adapter_registry_js_1.normalizeHarnessName))];
@@ -5446,6 +5615,10 @@ function capabilitiesOfReport(report, dialect) {
5446
5615
  const COMMAND_HELP = {
5447
5616
  init: {
5448
5617
  usage: " vigiles init [flags] Set up this repo — specs, plugin, and CI.",
5618
+ detail: [
5619
+ " Already set up? A re-run only adds what is missing (skill links, the devDependency);",
5620
+ " --full run the whole setup again",
5621
+ ],
5449
5622
  },
5450
5623
  compile: { usage: " vigiles compile [files...] Compile .spec.ts → .md" },
5451
5624
  eject: {
@@ -69,20 +69,20 @@ export declare const vigilesConfigSchema: z.ZodObject<{
69
69
  checkboxes: "checkboxes";
70
70
  }>>>;
71
71
  rules: z.ZodPrefault<z.ZodObject<{
72
- "spec-refs": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
73
- "orphan-docs": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
74
- "duplicate-rules": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
75
- "require-instructions-spec": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
76
- "require-skill-spec": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
77
- integrity: z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
78
- coverage: z.ZodDefault<z.ZodUnion<readonly [z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>, z.ZodTuple<[z.ZodLiteral<"warn">, z.ZodObject<{
72
+ "spec-refs": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
73
+ "orphan-docs": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
74
+ "duplicate-rules": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
75
+ "require-instructions-spec": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
76
+ "require-skill-spec": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
77
+ integrity: z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
78
+ coverage: z.ZodDefault<z.ZodUnion<readonly [z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>, z.ZodTuple<[z.ZodLiteral<"warn">, z.ZodObject<{
79
79
  linterRules: z.ZodOptional<z.ZodNumber>;
80
80
  scripts: z.ZodOptional<z.ZodNumber>;
81
81
  }, z.core.$strict>], null>, z.ZodTuple<[z.ZodLiteral<"error">, z.ZodObject<{
82
82
  linterRules: z.ZodOptional<z.ZodNumber>;
83
83
  scripts: z.ZodOptional<z.ZodNumber>;
84
84
  }, z.core.$strict>], null>]>>;
85
- "untested-skill": z.ZodDefault<z.ZodUnion<readonly [z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>, z.ZodTuple<[z.ZodLiteral<"warn">, z.ZodObject<{
85
+ "untested-skill": z.ZodDefault<z.ZodUnion<readonly [z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>, z.ZodTuple<[z.ZodLiteral<"warn">, z.ZodObject<{
86
86
  include: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>, z.ZodTransform<readonly string[], string | string[]>>>;
87
87
  exclude: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>, z.ZodTransform<readonly string[], string | string[]>>>;
88
88
  testExtension: z.ZodOptional<z.ZodString>;
@@ -91,7 +91,7 @@ export declare const vigilesConfigSchema: z.ZodObject<{
91
91
  exclude: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>, z.ZodTransform<readonly string[], string | string[]>>>;
92
92
  testExtension: z.ZodOptional<z.ZodString>;
93
93
  }, z.core.$strict>], null>]>>;
94
- "untested-subagent": z.ZodDefault<z.ZodUnion<readonly [z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>, z.ZodTuple<[z.ZodLiteral<"warn">, z.ZodObject<{
94
+ "untested-subagent": z.ZodDefault<z.ZodUnion<readonly [z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>, z.ZodTuple<[z.ZodLiteral<"warn">, z.ZodObject<{
95
95
  include: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>, z.ZodTransform<readonly string[], string | string[]>>>;
96
96
  exclude: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>, z.ZodTransform<readonly string[], string | string[]>>>;
97
97
  testExtension: z.ZodOptional<z.ZodString>;
@@ -100,7 +100,7 @@ export declare const vigilesConfigSchema: z.ZodObject<{
100
100
  exclude: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>, z.ZodTransform<readonly string[], string | string[]>>>;
101
101
  testExtension: z.ZodOptional<z.ZodString>;
102
102
  }, z.core.$strict>], null>]>>;
103
- "untested-hook": z.ZodDefault<z.ZodUnion<readonly [z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>, z.ZodTuple<[z.ZodLiteral<"warn">, z.ZodObject<{
103
+ "untested-hook": z.ZodDefault<z.ZodUnion<readonly [z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>, z.ZodTuple<[z.ZodLiteral<"warn">, z.ZodObject<{
104
104
  include: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>, z.ZodTransform<readonly string[], string | string[]>>>;
105
105
  exclude: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>, z.ZodTransform<readonly string[], string | string[]>>>;
106
106
  testExtension: z.ZodOptional<z.ZodString>;
@@ -109,7 +109,7 @@ export declare const vigilesConfigSchema: z.ZodObject<{
109
109
  exclude: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>, z.ZodTransform<readonly string[], string | string[]>>>;
110
110
  testExtension: z.ZodOptional<z.ZodString>;
111
111
  }, z.core.$strict>], null>]>>;
112
- "untested-output-style": z.ZodDefault<z.ZodUnion<readonly [z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>, z.ZodTuple<[z.ZodLiteral<"warn">, z.ZodObject<{
112
+ "untested-output-style": z.ZodDefault<z.ZodUnion<readonly [z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>, z.ZodTuple<[z.ZodLiteral<"warn">, z.ZodObject<{
113
113
  include: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>, z.ZodTransform<readonly string[], string | string[]>>>;
114
114
  exclude: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>, z.ZodTransform<readonly string[], string | string[]>>>;
115
115
  testExtension: z.ZodOptional<z.ZodString>;
@@ -118,29 +118,29 @@ export declare const vigilesConfigSchema: z.ZodObject<{
118
118
  exclude: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>, z.ZodTransform<readonly string[], string | string[]>>>;
119
119
  testExtension: z.ZodOptional<z.ZodString>;
120
120
  }, z.core.$strict>], null>]>>;
121
- "unmarked-refs": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
122
- "subagent-tool-contract": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
123
- "hook-events": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
124
- "subagent-frontmatter": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
125
- "mcp-config": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
126
- "skill-frontmatter": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
127
- "mcp-tool-resolves": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
128
- "hook-script-exists": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
129
- "prefer-compiled-hooks": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
130
- "disallowed-tools-contract": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
131
- "description-overlap": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
132
- "skill-description-budget": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
133
- "frontmatter-valid": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
134
- "mcp-hook-target-resolves": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
135
- "lethal-trifecta": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
136
- "skill-resource-resolves": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
137
- "skill-missing-fence": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
138
- "plugin-dir-layout": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
139
- "delegation-trifecta": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
140
- "hook-block-ineffective": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
141
- "hook-matcher": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
142
- "doc-refs": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
143
- "instruction-weight": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "warn" | "error", boolean | 0 | 1 | 2 | "warn" | "error" | "off">>>;
121
+ "unmarked-refs": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
122
+ "subagent-tool-contract": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
123
+ "hook-events": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
124
+ "subagent-frontmatter": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
125
+ "mcp-config": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
126
+ "skill-frontmatter": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
127
+ "mcp-tool-resolves": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
128
+ "hook-script-exists": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
129
+ "prefer-compiled-hooks": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
130
+ "disallowed-tools-contract": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
131
+ "description-overlap": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
132
+ "skill-description-budget": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
133
+ "frontmatter-valid": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
134
+ "mcp-hook-target-resolves": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
135
+ "lethal-trifecta": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
136
+ "skill-resource-resolves": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
137
+ "skill-missing-fence": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
138
+ "plugin-dir-layout": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
139
+ "delegation-trifecta": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
140
+ "hook-block-ineffective": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
141
+ "hook-matcher": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
142
+ "doc-refs": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
143
+ "instruction-weight": z.ZodDefault<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"warn">, z.ZodLiteral<"error">, z.ZodLiteral<false>, z.ZodLiteral<"off">, z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<true>]>, z.ZodTransform<false | "error" | "warn", boolean | 0 | 1 | 2 | "error" | "warn" | "off">>>;
144
144
  }, z.core.$strict>>;
145
145
  files: z.ZodDefault<z.ZodArray<z.ZodString>>;
146
146
  maxRules: z.ZodOptional<z.ZodNumber>;
@@ -170,8 +170,27 @@ export declare const vigilesConfigSchema: z.ZodObject<{
170
170
  apiVersion: z.ZodOptional<z.ZodNumber>;
171
171
  }, z.core.$strict>>;
172
172
  nudge: z.ZodOptional<z.ZodLiteral<"dismissed">>;
173
+ extends: z.ZodOptional<z.ZodEnum<{
174
+ "vigiles:recommended": "vigiles:recommended";
175
+ }>>;
173
176
  $schema: z.ZodOptional<z.ZodString>;
174
177
  }, z.core.$strict>;
178
+ /**
179
+ * Parse a raw `.vigilesrc.json` value — THE one entry point every reader uses.
180
+ *
181
+ * 🔴 THE PRESET IS MERGED INTO THE RAW INPUT, BEFORE THE SCHEMA, for one reason:
182
+ * after parsing, every rule carries a value (the schema fills each default), so
183
+ * "the user wrote `hook-events: warn`" and "nobody wrote it" are the same
184
+ * output, and an explicit downgrade could not win over the preset. Before
185
+ * parsing, the user's own `rules` keys are exactly what they wrote. So the
186
+ * preset's severities go UNDER them, and the schema then normalizes and
187
+ * validates the result as if the user had typed both.
188
+ *
189
+ * Only a recognized preset with an object (or absent) `rules` is expanded;
190
+ * anything else is passed through untouched for the schema to refuse with its
191
+ * own message, so a typo in `extends` is reported, never silently ignored.
192
+ */
193
+ export declare function parseVigilesConfig(raw: unknown): Readonly<ReturnType<typeof vigilesConfigSchema.safeParse>>;
175
194
  /**
176
195
  * A `.vigilesrc.json` that cannot be honoured as written.
177
196
  *
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.REPLACED_KEYS = exports.VigilesConfigError = exports.vigilesConfigSchema = exports.RULE_NAMES = void 0;
4
+ exports.parseVigilesConfig = parseVigilesConfig;
4
5
  exports.replacedKeyMessage = replacedKeyMessage;
5
6
  exports.formatConfigIssues = formatConfigIssues;
6
7
  /**
@@ -48,6 +49,7 @@ exports.formatConfigIssues = formatConfigIssues;
48
49
  */
49
50
  const zod_1 = require("zod");
50
51
  const edit_distance_js_1 = require("./edit-distance.js");
52
+ const presets_js_1 = require("./presets.js");
51
53
  // ---------------------------------------------------------------------------
52
54
  // Primitives
53
55
  // ---------------------------------------------------------------------------
@@ -232,6 +234,14 @@ exports.vigilesConfigSchema = zod_1.z
232
234
  audit: zod_1.z.object({ measure: zod_1.z.boolean().optional() }).strict().optional(),
233
235
  eval: zod_1.z.object({ apiVersion: zod_1.z.number().optional() }).strict().optional(),
234
236
  nudge: zod_1.z.literal("dismissed").optional(),
237
+ /**
238
+ * A named preset whose rule severities apply UNDER `rules` — ESLint's
239
+ * `extends`. `"vigiles:recommended"` gates the structural group (see
240
+ * `./presets.ts`); an explicit `rules` entry overrides it. Applied by
241
+ * {@link parseVigilesConfig}, which is why this key is only VALIDATED here.
242
+ * Placed after the keys people misspell most: the "Known:" list is capped.
243
+ */
244
+ extends: zod_1.z.enum(presets_js_1.PRESET_NAMES).optional(),
235
245
  /**
236
246
  * The editor's pointer at the published JSON Schema. Accepted, never read.
237
247
  *
@@ -262,6 +272,38 @@ exports.vigilesConfigSchema = zod_1.z
262
272
  $schema: zod_1.z.string().optional(),
263
273
  })
264
274
  .strict();
275
+ /**
276
+ * Parse a raw `.vigilesrc.json` value — THE one entry point every reader uses.
277
+ *
278
+ * 🔴 THE PRESET IS MERGED INTO THE RAW INPUT, BEFORE THE SCHEMA, for one reason:
279
+ * after parsing, every rule carries a value (the schema fills each default), so
280
+ * "the user wrote `hook-events: warn`" and "nobody wrote it" are the same
281
+ * output, and an explicit downgrade could not win over the preset. Before
282
+ * parsing, the user's own `rules` keys are exactly what they wrote. So the
283
+ * preset's severities go UNDER them, and the schema then normalizes and
284
+ * validates the result as if the user had typed both.
285
+ *
286
+ * Only a recognized preset with an object (or absent) `rules` is expanded;
287
+ * anything else is passed through untouched for the schema to refuse with its
288
+ * own message, so a typo in `extends` is reported, never silently ignored.
289
+ */
290
+ function parseVigilesConfig(raw) {
291
+ return exports.vigilesConfigSchema.safeParse(withPresetRules(raw));
292
+ }
293
+ /** `raw` with its `extends` preset's rules placed under its own `rules`. */
294
+ function withPresetRules(raw) {
295
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
296
+ return raw;
297
+ const preset = "extends" in raw ? raw.extends : undefined;
298
+ const rules = "rules" in raw ? raw.rules : undefined;
299
+ if (!(0, presets_js_1.isPresetName)(preset))
300
+ return raw;
301
+ if (rules === undefined)
302
+ return { ...raw, rules: { ...(0, presets_js_1.presetRules)(preset) } };
303
+ if (typeof rules !== "object" || rules === null || Array.isArray(rules))
304
+ return raw;
305
+ return { ...raw, rules: { ...(0, presets_js_1.presetRules)(preset), ...rules } };
306
+ }
265
307
  /**
266
308
  * A `.vigilesrc.json` that cannot be honoured as written.
267
309
  *
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Named rule PRESETS a `.vigilesrc.json` can `extends` — the ESLint
3
+ * `recommended` model, stored as data.
4
+ *
5
+ * 🔴 WHY A PRESET READ AT LOAD TIME, NOT RULE LINES WRITTEN AT SETUP TIME. Every
6
+ * rule's built-in default stays `warn` (`config-schema.ts`); the structural rules
7
+ * gate only because `init` turns them up. It used to do that by WRITING nine
8
+ * `"<rule>": "error"` lines into the new repo's config — a snapshot of the group
9
+ * on the day `init` ran. A repo adopted before a rule joined the group never got
10
+ * it from an upgrade, and re-running `init` to fetch it rewrote far more than
11
+ * the rule (#338). `"extends": "vigiles:recommended"` is resolved by the loader
12
+ * on every run, so the repo follows the group as the installed vigiles ships it,
13
+ * and an explicit `rules` entry still overrides it.
14
+ *
15
+ * ⚠️ ADDING A RULE HERE IS A BREAKING CHANGE (a `!` commit, a semver major): it
16
+ * can turn a consumer's CI red with no edit on their side. Removing one is not.
17
+ * ESLint treats `eslint:recommended` the same way.
18
+ *
19
+ * Harness-agnostic data: rule names only, no adapter fact.
20
+ */
21
+ import type { RulesConfig } from "./types.js";
22
+ /**
23
+ * The rules `vigiles:recommended` raises to `error` — the FP-safe structural
24
+ * group: each one fires only on a genuine defect (a never-available or typo'd
25
+ * tool, a subagent missing `name`/`description`, a typo'd hook event, a dead hook
26
+ * script, a broken MCP reference, two skills that collide in the selector), so a
27
+ * well-formed harness stays green.
28
+ *
29
+ * Deliberately EXCLUDES the workflow rules (`require-instructions-spec`,
30
+ * `untested-*`): a clean repo fails those until the work is done, so they stay
31
+ * an explicit opt-in (`init --strict`).
32
+ */
33
+ export declare const RECOMMENDED_RULES: readonly ["subagent-tool-contract", "subagent-frontmatter", "hook-events", "hook-script-exists", "mcp-config", "mcp-tool-resolves", "mcp-hook-target-resolves", "disallowed-tools-contract", "description-overlap"];
34
+ /** The preset names `extends` accepts. One today; the tuple is the enum. */
35
+ export declare const PRESET_NAMES: readonly ["vigiles:recommended"];
36
+ /** A preset name `extends` accepts. */
37
+ export type PresetName = (typeof PRESET_NAMES)[number];
38
+ /** The preset `init` writes. */
39
+ export declare const RECOMMENDED_PRESET: PresetName;
40
+ /** Whether a raw value names a preset. */
41
+ export declare function isPresetName(value: unknown): value is PresetName;
42
+ /** The rule severities a preset sets, in the config's raw spelling. */
43
+ export declare function presetRules(name: PresetName): Readonly<Partial<Record<keyof RulesConfig, "error">>>;
44
+ //# sourceMappingURL=presets.d.ts.map
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RECOMMENDED_PRESET = exports.PRESET_NAMES = exports.RECOMMENDED_RULES = void 0;
4
+ exports.isPresetName = isPresetName;
5
+ exports.presetRules = presetRules;
6
+ /**
7
+ * The rules `vigiles:recommended` raises to `error` — the FP-safe structural
8
+ * group: each one fires only on a genuine defect (a never-available or typo'd
9
+ * tool, a subagent missing `name`/`description`, a typo'd hook event, a dead hook
10
+ * script, a broken MCP reference, two skills that collide in the selector), so a
11
+ * well-formed harness stays green.
12
+ *
13
+ * Deliberately EXCLUDES the workflow rules (`require-instructions-spec`,
14
+ * `untested-*`): a clean repo fails those until the work is done, so they stay
15
+ * an explicit opt-in (`init --strict`).
16
+ */
17
+ exports.RECOMMENDED_RULES = [
18
+ "subagent-tool-contract",
19
+ "subagent-frontmatter",
20
+ "hook-events",
21
+ "hook-script-exists",
22
+ "mcp-config",
23
+ "mcp-tool-resolves",
24
+ "mcp-hook-target-resolves",
25
+ "disallowed-tools-contract",
26
+ "description-overlap",
27
+ ];
28
+ /** The preset names `extends` accepts. One today; the tuple is the enum. */
29
+ exports.PRESET_NAMES = ["vigiles:recommended"];
30
+ /** The preset `init` writes. */
31
+ exports.RECOMMENDED_PRESET = "vigiles:recommended";
32
+ /** What each preset sets, as the config would spell it. */
33
+ const PRESETS = {
34
+ "vigiles:recommended": Object.fromEntries(exports.RECOMMENDED_RULES.map((r) => [r, "error"])),
35
+ };
36
+ /** Whether a raw value names a preset. */
37
+ function isPresetName(value) {
38
+ return exports.PRESET_NAMES.some((n) => n === value);
39
+ }
40
+ /** The rule severities a preset sets, in the config's raw spelling. */
41
+ function presetRules(name) {
42
+ return PRESETS[name];
43
+ }
44
+ //# sourceMappingURL=presets.js.map
@@ -147,7 +147,7 @@ function loadConfig(searchFrom, { onInvalid = "throw" } = {}) {
147
147
  }
148
148
  }
149
149
  if (problems.length === 0) {
150
- const parsed = config_schema_js_1.vigilesConfigSchema.safeParse(raw);
150
+ const parsed = (0, config_schema_js_1.parseVigilesConfig)(raw);
151
151
  if (parsed.success)
152
152
  return parsed.data;
153
153
  problems.push(...(0, config_schema_js_1.formatConfigIssues)(parsed.error.issues));
@@ -77,6 +77,11 @@ export interface ParsedSetupArgs {
77
77
  * DISCOVERY row of `cohesive-feature-delivery`.
78
78
  */
79
79
  ciOnly: boolean;
80
+ /**
81
+ * `--full` — run the whole setup even on a repo that already uses vigiles.
82
+ * Without it, a re-run there is the minimal one (see {@link rerunScope}).
83
+ */
84
+ full: boolean;
80
85
  }
81
86
  /** Parse `init` args into the choices the user pinned. The two pillars are
82
87
  * selected with `--lint` / `--test`. */
@@ -87,29 +92,6 @@ export declare function parseSetupArgs(args: readonly string[]): ParsedSetupArgs
87
92
  * raw files WITHOUT scaffolding a spec (gate-first-adoption). Non-interactive
88
93
  * behaviour is unchanged: bare `init` (lint on) scaffolds as before. */
89
94
  export declare function defaultPlan(strict?: boolean): SetupPlan;
90
- /**
91
- * Pure config-merge for what `vigiles init` writes to `.vigilesrc.json`: record
92
- * the `harness` if absent, add strict rule severities if `--strict`, NEVER
93
- * clobber an existing key. Returns the merged config, or `null` when nothing
94
- * changed (so the IO layer skips the write). The IO (read/parse/write + the
95
- * malformed-file guard) stays in cli.ts.
96
- */
97
- /**
98
- * The structural rules `init` gates BY DEFAULT (severity `error`, so a broken
99
- * surface fails `vigiles lint`). Every one is HIGH-PRECISION / FP-safe — it fires
100
- * only on a genuine defect (a never-available/typo'd tool, a subagent missing
101
- * `name`/`description`, a typo'd hook event, a dead hook script, a broken MCP
102
- * ref, two skills that collide in the selector) — so a well-formed plugin stays
103
- * green and catching real breakage out of the box never cries wolf.
104
- *
105
- * Deliberately EXCLUDES `require-instructions-spec` and the workflow-forcing rules:
106
- * those make a CLEAN repo fail (you simply haven't written the spec/test yet), so
107
- * they stay opt-in under `--strict` (progressive adoption — see
108
- * `STRICT_EXTRA_RULES`).
109
- *
110
- * This is the **`structural`** rule group (see research/install-enforcement-dx.md).
111
- */
112
- export declare const STRUCTURAL_RULES: readonly ["subagent-tool-contract", "subagent-frontmatter", "hook-events", "hook-script-exists", "mcp-config", "mcp-tool-resolves", "mcp-hook-target-resolves", "disallowed-tools-contract", "description-overlap"];
113
95
  /**
114
96
  * The **`workflow`** group — the WORKFLOW-FORCING / opinionated tier `--strict`
115
97
  * gates, which a clean repo can still fail because you haven't done the work yet:
@@ -135,7 +117,24 @@ export declare const WORKFLOW_RULES: readonly ["require-instructions-spec", "unt
135
117
  * (research/install-enforcement-dx.md).
136
118
  */
137
119
  export declare const NUDGE_RULES: readonly ["skill-description-budget", "frontmatter-valid", "skill-frontmatter", "prefer-compiled-hooks", "unmarked-refs", "lethal-trifecta", "skill-resource-resolves", "skill-missing-fence", "plugin-dir-layout", "delegation-trifecta", "hook-block-ineffective", "hook-matcher", "doc-refs"];
138
- export declare function mergeProjectConfig(existing: Record<string, unknown>, opts: {
120
+ /**
121
+ * Pure config-merge for what `vigiles init` writes to `.vigilesrc.json`: record
122
+ * the harnesses if absent, gate the structural rules by EXTENDING the
123
+ * `vigiles:recommended` preset (never by writing each rule), add the workflow
124
+ * rules on `--strict`, and NEVER clobber an existing key or severity. Returns
125
+ * the merged config, or `null` when nothing changed (so the IO layer skips the
126
+ * write). The IO (read/parse/write + the malformed-file guard) stays in cli.ts.
127
+ *
128
+ * 🔴 THE PRESET, NOT NINE LINES (#338). Written lines are a snapshot of the
129
+ * structural group on the day `init` ran, so a repo adopted earlier never got a
130
+ * rule added to the group later; the preset is resolved by the loader on every
131
+ * run (`src/core/presets.ts`).
132
+ *
133
+ * `--report-only` is the exception and stays EXPLICIT: it promises that nothing
134
+ * fails CI, and extending a preset that later gains a rule at `error` would
135
+ * break that promise on an upgrade. So it writes the same rules at `warn`.
136
+ */
137
+ export declare function mergeProjectConfig(existing: Readonly<Record<string, unknown>>, opts: Readonly<{
139
138
  /** Canonical harness names this repo targets, in the order to declare them. */
140
139
  harnesses: readonly string[];
141
140
  strict: boolean;
@@ -144,7 +143,7 @@ export declare function mergeProjectConfig(existing: Record<string, unknown>, op
144
143
  * concern, so a test-only setup (`init --test` / `--no-lint`) records the
145
144
  * harness but writes NO lint rules. */
146
145
  lint?: boolean;
147
- }): Record<string, unknown> | null;
146
+ }>): Record<string, unknown> | null;
148
147
  /**
149
148
  * Whether to drop into interactive prompts: a human at a TTY who passed neither
150
149
  * `--yes` nor an explicit `--target`, and who hasn't already pinned every choice
@@ -292,6 +291,75 @@ export declare function codexPluginHooks(): CodexPluginHook[];
292
291
  * (and every other config key) untouched. Returns a new object.
293
292
  */
294
293
  export declare function applyCodexPluginHooks(existing: Record<string, unknown>): Record<string, unknown>;
294
+ /**
295
+ * Whether this repo already uses vigiles, decided from what is ON DISK.
296
+ *
297
+ * 🔴 THE SIGNAL IS `.vigilesrc.json` OR A SPEC NEXT TO AN INSTRUCTION FILE, and
298
+ * deliberately NOT a `vigiles` devDependency: the documented first step on a new
299
+ * repo can be `npm i -D vigiles` followed by `npx vigiles init`, and that run
300
+ * must still be the full setup. Every `init` writes `.vigilesrc.json` (it records
301
+ * the harnesses even on a test-only run), and a hand-written one means the owner
302
+ * has already configured vigiles. A spec beside `CLAUDE.md`/`AGENTS.md` covers a
303
+ * repo adopted before the config file existed.
304
+ */
305
+ export type AdoptionState = {
306
+ readonly kind: "fresh";
307
+ } | {
308
+ readonly kind: "adopted";
309
+ readonly evidence: string;
310
+ };
311
+ /** {@link AdoptionState} from the two disk facts that decide it. */
312
+ export declare function adoptionState(facts: Readonly<{
313
+ hasConfig: boolean;
314
+ specs: readonly string[];
315
+ }>): AdoptionState;
316
+ /**
317
+ * Which setup a run performs: the full one, or the minimal re-run (#338).
318
+ *
319
+ * MINIMAL only when the repo is adopted AND the run named nothing that asks for
320
+ * more. A flag that selects or widens the setup (`--full`, `--strict`,
321
+ * `--ci-only`, `--report-only`, `--force`, `--target=`, a positive `--lint` /
322
+ * `--test`) is an explicit request and keeps today's behaviour; the narrowing
323
+ * flags (`--no-*`, `--harness=`, `--yes`) do not, because they can only make a
324
+ * run do less.
325
+ */
326
+ export declare function rerunScope(parsed: Readonly<ParsedSetupArgs>, state: AdoptionState): "full" | "minimal";
327
+ /**
328
+ * The `devDependencies` a minimal re-run writes, or null to leave package.json
329
+ * alone. It ADDS vigiles when no dependency field declares it and never edits an
330
+ * existing declaration: rewriting `^33.3.0` to `^33` loosens a pin the owner
331
+ * chose, and moving it between fields is a decision, not a missing piece.
332
+ */
333
+ export declare function missingVigilesDevDep(pkg: Readonly<{
334
+ dependencies?: Readonly<Record<string, string>>;
335
+ devDependencies?: Readonly<Record<string, string>>;
336
+ }>, spec: string): Readonly<Record<string, string>> | null;
337
+ /** What a full setup would add on an adopted repo — the facts that decide it. */
338
+ export interface FullSetupGap {
339
+ /** `.github/workflows/vigiles.yml` is absent (and CI wiring is not opted out). */
340
+ readonly workflow: boolean;
341
+ /** `.vigilesrc.json`: absent, present without the preset, or already extending it. */
342
+ readonly config: "missing" | "no-preset" | "has-preset";
343
+ /** `vigiles.harness.mjs` is absent (and the test layer is not opted out). */
344
+ readonly harnessTest: boolean;
345
+ /** Instruction files and surfaces a full setup would adopt into a spec. */
346
+ readonly unspecced: readonly string[];
347
+ /** `.vigiles/generated.d.ts` / `.vigiles/schema.json` would be created. */
348
+ readonly generated: boolean;
349
+ /** The global plugin install / hook wiring a full setup would run, one line each. */
350
+ readonly installs: readonly string[];
351
+ /** The devDependency rewrite a full setup would make, if any. */
352
+ readonly devDep: Readonly<{
353
+ from: string;
354
+ to: string;
355
+ }> | null;
356
+ }
357
+ /**
358
+ * The lines a minimal re-run prints about what it did NOT do: each thing a full
359
+ * setup would add here, then the flag that does it. Empty when nothing is
360
+ * missing, so a fully set-up repo is not told to run anything.
361
+ */
362
+ export declare function fullSetupFollowUps(gap: FullSetupGap): readonly string[];
295
363
  /**
296
364
  * Resolve the final plan: defaults, then flags, then interactive answers (each
297
365
  * layer overrides the previous only where it has an opinion). `--target` pins a
@@ -10,7 +10,7 @@
10
10
  * on a prompt. See docs/agent-setup.md.
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.SHIPPED_SKILLS = exports.NUDGE_RULES = exports.WORKFLOW_RULES = exports.STRUCTURAL_RULES = void 0;
13
+ exports.SHIPPED_SKILLS = exports.NUDGE_RULES = exports.WORKFLOW_RULES = void 0;
14
14
  exports.parseSetupArgs = parseSetupArgs;
15
15
  exports.defaultPlan = defaultPlan;
16
16
  exports.mergeProjectConfig = mergeProjectConfig;
@@ -21,7 +21,12 @@ exports.planPluginInstall = planPluginInstall;
21
21
  exports.shouldRunGlobalInstall = shouldRunGlobalInstall;
22
22
  exports.codexPluginHooks = codexPluginHooks;
23
23
  exports.applyCodexPluginHooks = applyCodexPluginHooks;
24
+ exports.adoptionState = adoptionState;
25
+ exports.rerunScope = rerunScope;
26
+ exports.missingVigilesDevDep = missingVigilesDevDep;
27
+ exports.fullSetupFollowUps = fullSetupFollowUps;
24
28
  exports.resolvePlan = resolvePlan;
29
+ const presets_js_1 = require("./core/presets.js");
25
30
  function flagValue(args, prefix) {
26
31
  return args.find((a) => a.startsWith(prefix))?.slice(prefix.length);
27
32
  }
@@ -48,6 +53,7 @@ function parseSetupArgs(args) {
48
53
  gha: args.includes("--no-gha") ? false : undefined,
49
54
  plugin: args.includes("--no-plugin") ? false : undefined,
50
55
  ciOnly: args.includes("--ci-only"),
56
+ full: args.includes("--full"),
51
57
  };
52
58
  }
53
59
  /** The non-interactive defaults: both pillars, CI, and the plugin. `scaffoldSpecs`
@@ -66,39 +72,6 @@ function defaultPlan(strict = false) {
66
72
  force: false,
67
73
  };
68
74
  }
69
- /**
70
- * Pure config-merge for what `vigiles init` writes to `.vigilesrc.json`: record
71
- * the `harness` if absent, add strict rule severities if `--strict`, NEVER
72
- * clobber an existing key. Returns the merged config, or `null` when nothing
73
- * changed (so the IO layer skips the write). The IO (read/parse/write + the
74
- * malformed-file guard) stays in cli.ts.
75
- */
76
- /**
77
- * The structural rules `init` gates BY DEFAULT (severity `error`, so a broken
78
- * surface fails `vigiles lint`). Every one is HIGH-PRECISION / FP-safe — it fires
79
- * only on a genuine defect (a never-available/typo'd tool, a subagent missing
80
- * `name`/`description`, a typo'd hook event, a dead hook script, a broken MCP
81
- * ref, two skills that collide in the selector) — so a well-formed plugin stays
82
- * green and catching real breakage out of the box never cries wolf.
83
- *
84
- * Deliberately EXCLUDES `require-instructions-spec` and the workflow-forcing rules:
85
- * those make a CLEAN repo fail (you simply haven't written the spec/test yet), so
86
- * they stay opt-in under `--strict` (progressive adoption — see
87
- * `STRICT_EXTRA_RULES`).
88
- *
89
- * This is the **`structural`** rule group (see research/install-enforcement-dx.md).
90
- */
91
- exports.STRUCTURAL_RULES = [
92
- "subagent-tool-contract",
93
- "subagent-frontmatter",
94
- "hook-events",
95
- "hook-script-exists",
96
- "mcp-config",
97
- "mcp-tool-resolves",
98
- "mcp-hook-target-resolves",
99
- "disallowed-tools-contract",
100
- "description-overlap",
101
- ];
102
75
  /**
103
76
  * The **`workflow`** group — the WORKFLOW-FORCING / opinionated tier `--strict`
104
77
  * gates, which a clean repo can still fail because you haven't done the work yet:
@@ -147,41 +120,53 @@ exports.NUDGE_RULES = [
147
120
  // than config. See docs/rules/doc-refs.md.
148
121
  "doc-refs",
149
122
  ];
123
+ /**
124
+ * Pure config-merge for what `vigiles init` writes to `.vigilesrc.json`: record
125
+ * the harnesses if absent, gate the structural rules by EXTENDING the
126
+ * `vigiles:recommended` preset (never by writing each rule), add the workflow
127
+ * rules on `--strict`, and NEVER clobber an existing key or severity. Returns
128
+ * the merged config, or `null` when nothing changed (so the IO layer skips the
129
+ * write). The IO (read/parse/write + the malformed-file guard) stays in cli.ts.
130
+ *
131
+ * 🔴 THE PRESET, NOT NINE LINES (#338). Written lines are a snapshot of the
132
+ * structural group on the day `init` ran, so a repo adopted earlier never got a
133
+ * rule added to the group later; the preset is resolved by the loader on every
134
+ * run (`src/core/presets.ts`).
135
+ *
136
+ * `--report-only` is the exception and stays EXPLICIT: it promises that nothing
137
+ * fails CI, and extending a preset that later gains a rule at `error` would
138
+ * break that promise on an upgrade. So it writes the same rules at `warn`.
139
+ */
150
140
  function mergeProjectConfig(existing, opts) {
151
- const config = { ...existing };
152
- let changed = false;
153
141
  // The NESTED key (#240). `init` writes the declaration with no roots — a repo
154
142
  // whose surfaces sit where its harness reads them needs none, and a root is a
155
- // fact only the owner knows. Writing the flat `harness`/`surfaceRoots` pair
156
- // here would emit a config the loader now REFUSES, which is why this one line
157
- // moved with the shape even though the rest of `init` did not.
158
- if (config.harnesses === undefined) {
159
- config.harnesses = Object.fromEntries(opts.harnesses.map((h) => [h, {}]));
160
- changed = true;
161
- }
162
- // The rule gate belongs to the LINT layer — a test-only setup records the
163
- // harness but writes no rules (honoring the positive-flag contract that
164
- // `--test` selects only the test pillar).
165
- if (opts.lint !== false) {
166
- // Gate the FP-safe `structural` group by default; `--strict` adds the
167
- // `workflow` group on top. `--report-only` is the orthogonal severity dial —
168
- // it writes the SAME rule set at "warn" (nothing fails CI; the
169
- // migration/observe mode). Never clobber a severity the user already set —
170
- // only fill the undefined ones.
171
- const severity = opts.reportOnly ? "warn" : "error";
172
- const gate = opts.strict
173
- ? [...exports.STRUCTURAL_RULES, ...exports.WORKFLOW_RULES]
174
- : [...exports.STRUCTURAL_RULES];
175
- const rules = { ...config.rules };
176
- for (const r of gate) {
177
- if (rules[r] === undefined) {
178
- rules[r] = severity;
179
- changed = true;
180
- }
181
- }
182
- config.rules = rules;
183
- }
184
- return changed ? config : null;
143
+ // fact only the owner knows.
144
+ const harnesses = existing.harnesses === undefined
145
+ ? { harnesses: Object.fromEntries(opts.harnesses.map((h) => [h, {}])) }
146
+ : {};
147
+ const gate = opts.lint === false ? {} : lintGate(existing, opts);
148
+ const added = { ...harnesses, ...gate };
149
+ return Object.keys(added).length === 0 ? null : { ...existing, ...added };
150
+ }
151
+ /**
152
+ * The lint-layer keys `init` adds: `extends` and/or the `rules` it fills.
153
+ * Only the keys that CHANGE are returned, so an already-satisfied config
154
+ * yields `{}` and is not rewritten.
155
+ */
156
+ function lintGate(existing, opts) {
157
+ const workflow = opts.strict ? exports.WORKFLOW_RULES : [];
158
+ const explicit = opts.reportOnly
159
+ ? [...presets_js_1.RECOMMENDED_RULES, ...workflow]
160
+ : workflow;
161
+ const severity = opts.reportOnly ? "warn" : "error";
162
+ const before = (existing.rules ?? {});
163
+ // Never clobber a severity the user already set — only fill the undefined ones.
164
+ const filled = Object.fromEntries(explicit.filter((r) => before[r] === undefined).map((r) => [r, severity]));
165
+ const rules = Object.keys(filled).length === 0 ? {} : { rules: { ...before, ...filled } };
166
+ const preset = opts.reportOnly || existing.extends !== undefined
167
+ ? {}
168
+ : { extends: presets_js_1.RECOMMENDED_PRESET };
169
+ return { ...preset, ...rules };
185
170
  }
186
171
  /**
187
172
  * Whether to drop into interactive prompts: a human at a TTY who passed neither
@@ -288,7 +273,7 @@ function gateOnlyInvitation(plan) {
288
273
  const gateOnly = !plan.plugin && !plan.scaffoldSpecs;
289
274
  if (!gateOnly)
290
275
  return null;
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.";
276
+ return "→ Want your agent to maintain this + measure whether your skills fire? Run `npx vigiles init --full` and choose 'full' (installs the skills). Optional — the gate above already works.";
292
277
  }
293
278
  /**
294
279
  * The SHIPPED consumer skills (the `skills/` dir, published via `plugin.json`) —
@@ -470,6 +455,87 @@ function applyCodexPluginHooks(existing) {
470
455
  }
471
456
  return { ...existing, hooks };
472
457
  }
458
+ /** {@link AdoptionState} from the two disk facts that decide it. */
459
+ function adoptionState(facts) {
460
+ if (facts.hasConfig)
461
+ return { kind: "adopted", evidence: ".vigilesrc.json" };
462
+ const [spec] = facts.specs;
463
+ return spec === undefined
464
+ ? { kind: "fresh" }
465
+ : { kind: "adopted", evidence: spec };
466
+ }
467
+ /**
468
+ * Which setup a run performs: the full one, or the minimal re-run (#338).
469
+ *
470
+ * MINIMAL only when the repo is adopted AND the run named nothing that asks for
471
+ * more. A flag that selects or widens the setup (`--full`, `--strict`,
472
+ * `--ci-only`, `--report-only`, `--force`, `--target=`, a positive `--lint` /
473
+ * `--test`) is an explicit request and keeps today's behaviour; the narrowing
474
+ * flags (`--no-*`, `--harness=`, `--yes`) do not, because they can only make a
475
+ * run do less.
476
+ */
477
+ function rerunScope(parsed, state) {
478
+ if (state.kind === "fresh")
479
+ return "full";
480
+ const asksForMore = parsed.full ||
481
+ parsed.strict ||
482
+ parsed.ciOnly ||
483
+ parsed.reportOnly ||
484
+ parsed.force ||
485
+ parsed.target !== undefined ||
486
+ parsed.lint === true ||
487
+ parsed.test === true;
488
+ return asksForMore ? "full" : "minimal";
489
+ }
490
+ /**
491
+ * The `devDependencies` a minimal re-run writes, or null to leave package.json
492
+ * alone. It ADDS vigiles when no dependency field declares it and never edits an
493
+ * existing declaration: rewriting `^33.3.0` to `^33` loosens a pin the owner
494
+ * chose, and moving it between fields is a decision, not a missing piece.
495
+ */
496
+ function missingVigilesDevDep(pkg, spec) {
497
+ const declared = pkg.dependencies?.vigiles !== undefined ||
498
+ pkg.devDependencies?.vigiles !== undefined;
499
+ return declared ? null : { ...pkg.devDependencies, vigiles: spec };
500
+ }
501
+ /**
502
+ * The lines a minimal re-run prints about what it did NOT do: each thing a full
503
+ * setup would add here, then the flag that does it. Empty when nothing is
504
+ * missing, so a fully set-up repo is not told to run anything.
505
+ */
506
+ function fullSetupFollowUps(gap) {
507
+ const configLine = {
508
+ missing: `write .vigilesrc.json with your harnesses and "extends": "${presets_js_1.RECOMMENDED_PRESET}" (structural rules fail CI)`,
509
+ "no-preset": `add "extends": "${presets_js_1.RECOMMENDED_PRESET}" to .vigilesrc.json (structural rules fail CI)`,
510
+ "has-preset": null,
511
+ };
512
+ const items = [
513
+ gap.workflow
514
+ ? "create .github/workflows/vigiles.yml (a CI workflow — new CI minutes)"
515
+ : null,
516
+ configLine[gap.config],
517
+ gap.harnessTest
518
+ ? "scaffold vigiles.harness.mjs (a starter harness test)"
519
+ : null,
520
+ gap.unspecced.length > 0
521
+ ? `write specs for ${String(gap.unspecced.length)} hand-written file(s): ${gap.unspecced.join(", ")}`
522
+ : null,
523
+ gap.generated
524
+ ? "generate .vigiles/generated.d.ts and .vigiles/schema.json"
525
+ : null,
526
+ ...gap.installs,
527
+ gap.devDep === null
528
+ ? null
529
+ : `set the vigiles devDependency to ${gap.devDep.to} (yours: ${gap.devDep.from})`,
530
+ ].filter((l) => l !== null);
531
+ return items.length === 0
532
+ ? []
533
+ : [
534
+ "A full setup would also:",
535
+ ...items.map((l) => ` - ${l}`),
536
+ "Run `npx vigiles init --full` for that (flags such as --no-gha still apply).",
537
+ ];
538
+ }
473
539
  /**
474
540
  * Resolve the final plan: defaults, then flags, then interactive answers (each
475
541
  * layer overrides the previous only where it has an opinion). `--target` pins a
@@ -1851,6 +1851,12 @@
1851
1851
  "type": "string",
1852
1852
  "const": "dismissed"
1853
1853
  },
1854
+ "extends": {
1855
+ "type": "string",
1856
+ "enum": [
1857
+ "vigiles:recommended"
1858
+ ]
1859
+ },
1854
1860
  "$schema": {
1855
1861
  "type": "string"
1856
1862
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigiles",
3
- "version": "33.3.0",
3
+ "version": "34.0.0",
4
4
  "description": "Audit, test and measure the harness your AI agent runs on — grade your CLAUDE.md / AGENTS.md, skills, subagents and hooks, run them against a scripted model, and measure whether they actually fire.",
5
5
  "keywords": [
6
6
  "claude-code",