etymd 0.5.0 → 0.6.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +4 -3
  3. package/dist/{approve-ASWRO6OD.js → approve-ZLOW4A7R.js} +6 -6
  4. package/dist/audit-ICT2JK7A.js +12 -0
  5. package/dist/{brief-HWD7CILP.js → brief-PDUGGMLI.js} +6 -6
  6. package/dist/{chunk-TADKOW6P.js → chunk-5BVKFJWM.js} +7 -3
  7. package/dist/{chunk-IGHES6AK.js → chunk-6DUDIVIC.js} +1 -1
  8. package/dist/{chunk-CYSEGWIW.js → chunk-BQGCLPHS.js} +2 -2
  9. package/dist/{chunk-R74SJ7HS.js → chunk-C7LBUFNU.js} +1 -1
  10. package/dist/{chunk-OJVQXB5Z.js → chunk-HLWCODYX.js} +1 -1
  11. package/dist/{chunk-YLNQQZ5F.js → chunk-IV3FYVTS.js} +11 -1
  12. package/dist/{chunk-NMZ3RHWW.js → chunk-K7QBTANO.js} +2 -2
  13. package/dist/{chunk-OX3LUOZR.js → chunk-KTIEYKFK.js} +46 -22
  14. package/dist/{chunk-WWBF4Y7K.js → chunk-OAFYLBVG.js} +1 -1
  15. package/dist/{chunk-QJRPKZPA.js → chunk-OESQNO2J.js} +77 -9
  16. package/dist/{chunk-LT67FU2Y.js → chunk-OKU3HXIH.js} +2 -2
  17. package/dist/{chunk-4KOP36RD.js → chunk-ULEWJ3ZK.js} +3 -3
  18. package/dist/{chunk-MMPV67FW.js → chunk-V2SOHPXF.js} +2 -2
  19. package/dist/cli.js +18 -18
  20. package/dist/config-UJRSCDMD.js +4 -0
  21. package/dist/{context-4DGXO7JR.js → context-ZEUI5ESZ.js} +5 -5
  22. package/dist/doctor-PORXFSYT.js +19 -0
  23. package/dist/{fleet-SAFXVN44.js → fleet-JHNQOLSY.js} +24 -17
  24. package/dist/{gates-D5SBAVXB.js → gates-KHC7HPIF.js} +21 -13
  25. package/dist/generate-UTKQ4GE6.js +6 -0
  26. package/dist/index.d.ts +21 -2
  27. package/dist/index.js +138 -31
  28. package/dist/{init-AVE2GDJ6.js → init-IPUPP4GB.js} +8 -8
  29. package/dist/ledger-B2XGANTH.js +5 -0
  30. package/dist/{scan-5DSECI43.js → scan-6L4L2GXT.js} +6 -6
  31. package/dist/scan-D4M7JXE3.js +5 -0
  32. package/dist/{screen-U7JMWZPS.js → screen-6R6NXDSY.js} +2 -2
  33. package/package.json +3 -4
  34. package/dist/audit-LPZOGOHE.js +0 -12
  35. package/dist/config-MX7OYI7F.js +0 -4
  36. package/dist/doctor-MJ6RBZ2Q.js +0 -19
  37. package/dist/generate-CBMTNN7P.js +0 -6
  38. package/dist/ledger-TMIV45AA.js +0 -5
  39. package/dist/scan-ES7IOXOC.js +0 -5
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { VERSION } from './chunk-OJVQXB5Z.js';
2
+ import { VERSION } from './chunk-HLWCODYX.js';
3
3
  import path from 'node:path';
4
4
  import { Command } from 'commander';
5
5
  import pc from 'picocolors';
@@ -31,7 +31,7 @@ program.command("audit").description("Verify every instruction claim against the
31
31
  "exit non-zero when findings at/above this tier exist (risk|gap|polish)"
32
32
  ).action(
33
33
  (opts, cmd) => action(async () => {
34
- const { run } = await import('./audit-LPZOGOHE.js');
34
+ const { run } = await import('./audit-ICT2JK7A.js');
35
35
  await run({
36
36
  cwd: resolveCwd(cmd),
37
37
  json: opts.json,
@@ -44,7 +44,7 @@ program.command("audit").description("Verify every instruction claim against the
44
44
  );
45
45
  program.command("init").description("Onboard the truth guard: approve the baseline; scaffold AGENTS.md only if missing").option("-y, --yes", "accept defaults without prompting (never overwrites)").action(
46
46
  (opts, cmd) => action(async () => {
47
- const { run } = await import('./init-AVE2GDJ6.js');
47
+ const { run } = await import('./init-IPUPP4GB.js');
48
48
  await run({ cwd: resolveCwd(cmd), yes: opts.yes });
49
49
  })
50
50
  );
@@ -52,49 +52,49 @@ program.command("approve").description(
52
52
  "Re-approve the committed baseline after intentional structural changes (non-interactive)"
53
53
  ).action(
54
54
  (_opts, cmd) => action(async () => {
55
- const { run } = await import('./approve-ASWRO6OD.js');
55
+ const { run } = await import('./approve-ZLOW4A7R.js');
56
56
  await run({ cwd: resolveCwd(cmd) });
57
57
  })
58
58
  );
59
59
  program.command("scan").description("Deterministically reckon the project into a facts index").option("--json", "print the raw facts as JSON").option("--no-save", "do not write the .etymd cache").action(
60
60
  (opts, cmd) => action(async () => {
61
- const { run } = await import('./scan-5DSECI43.js');
61
+ const { run } = await import('./scan-6L4L2GXT.js');
62
62
  await run({ cwd: resolveCwd(cmd), json: opts.json, save: opts.save });
63
63
  })
64
64
  );
65
65
  program.command("doctor").description('Alias for `audit --truth` \u2014 "are the recorded instructions still true?"').option("--json", "print findings as JSON").action(
66
66
  (opts, cmd) => action(async () => {
67
- const { run } = await import('./doctor-MJ6RBZ2Q.js');
67
+ const { run } = await import('./doctor-PORXFSYT.js');
68
68
  await run({ cwd: resolveCwd(cmd), json: opts.json });
69
69
  })
70
70
  );
71
71
  program.command("context").description("Measure the always-loaded context footprint and flag extraction candidates").option("--json", "print the budget as JSON").action(
72
72
  (opts, cmd) => action(async () => {
73
- const { run } = await import('./context-4DGXO7JR.js');
73
+ const { run } = await import('./context-ZEUI5ESZ.js');
74
74
  await run({ cwd: resolveCwd(cmd), json: opts.json });
75
75
  })
76
76
  );
77
77
  program.command("brief").description("Emit a grounded briefing for the in-repo agent to complete the semantic layer").option("--human", "write a human onboarding brief instead of the agent briefing").action(
78
78
  (opts, cmd) => action(async () => {
79
- const { run } = await import('./brief-HWD7CILP.js');
79
+ const { run } = await import('./brief-PDUGGMLI.js');
80
80
  await run({ cwd: resolveCwd(cmd), human: opts.human });
81
81
  })
82
82
  );
83
83
  program.command("ledger").description("List every tracked finding grouped by status (open \xB7 accepted \xB7 dismissed \xB7 \u2026)").option("--json", "print the raw ledger as JSON").action(
84
84
  (opts, cmd) => action(async () => {
85
- const { list } = await import('./ledger-TMIV45AA.js');
85
+ const { list } = await import('./ledger-B2XGANTH.js');
86
86
  await list({ cwd: resolveCwd(cmd), json: opts.json });
87
87
  })
88
88
  );
89
89
  program.command("dismiss").argument("<finding-id>", "the finding id (from `etymd audit`) to dismiss").description("Dismiss a finding as a false positive / not applicable (hidden from future audits)").requiredOption("--reason <text>", "why it is dismissed \u2014 recorded so the decision survives").action(
90
90
  (id, opts, cmd) => action(async () => {
91
- const { dismiss } = await import('./ledger-TMIV45AA.js');
91
+ const { dismiss } = await import('./ledger-B2XGANTH.js');
92
92
  await dismiss({ cwd: resolveCwd(cmd), id, reason: opts.reason });
93
93
  })
94
94
  );
95
95
  program.command("accept").argument("<finding-id>", "the finding id (from `etymd audit`) to accept").description("Accept a real finding as a known trade-off (hidden from future audits)").option("--reason <text>", "optional note on why the trade-off is accepted").action(
96
96
  (id, opts, cmd) => action(async () => {
97
- const { accept } = await import('./ledger-TMIV45AA.js');
97
+ const { accept } = await import('./ledger-B2XGANTH.js');
98
98
  await accept({ cwd: resolveCwd(cmd), id, reason: opts.reason });
99
99
  })
100
100
  );
@@ -111,7 +111,7 @@ var fleet = program.command("fleet").description(
111
111
  "exit non-zero when findings at/above this tier exist (risk|gap|polish)"
112
112
  ).action(
113
113
  (opts, cmd) => action(async () => {
114
- const { sweep } = await import('./fleet-SAFXVN44.js');
114
+ const { sweep } = await import('./fleet-JHNQOLSY.js');
115
115
  await sweep({
116
116
  cwd: resolveCwd(cmd),
117
117
  manifest: opts.manifest,
@@ -126,7 +126,7 @@ var fleet = program.command("fleet").description(
126
126
  );
127
127
  fleet.command("check").description("Validate the manifest pair only \u2014 no lenses: dangling mappings, duplicates, leaks").option("--manifest <file>", "the fleet manifest \u2014 required unless the cwd holds registry.json").option("--json", "print the findings as JSON (EXPERIMENTAL through 0.2.x)").action(
128
128
  (_opts, cmd) => action(async () => {
129
- const { check } = await import('./fleet-SAFXVN44.js');
129
+ const { check } = await import('./fleet-JHNQOLSY.js');
130
130
  const opts = cmd.optsWithGlobals();
131
131
  await check({ cwd: resolveCwd(cmd), manifest: opts.manifest, json: opts.json });
132
132
  })
@@ -136,7 +136,7 @@ fleet.command("add").argument("<dir>", "directory of the project to register").d
136
136
  "public-repo | public-bound | private \u2014 mandatory for personal entries"
137
137
  ).option("-y, --yes", "skip prompts; every mandatory value must be passed as a flag").option("--manifest <file>", "the fleet manifest \u2014 required unless the cwd holds registry.json").action(
138
138
  (dir, opts, cmd) => action(async () => {
139
- const { add } = await import('./fleet-SAFXVN44.js');
139
+ const { add } = await import('./fleet-JHNQOLSY.js');
140
140
  const shared = cmd.optsWithGlobals();
141
141
  await add({
142
142
  cwd: resolveCwd(cmd),
@@ -159,7 +159,7 @@ fleet.command("dismiss").argument("<name>", "the registered project name").argum
159
159
  "Dismiss a project's finding from any cwd \u2014 corp ledgers persist beside the manifest"
160
160
  ).requiredOption("--reason <text>", "why it is dismissed \u2014 recorded so the decision survives").option("--manifest <file>", "the fleet manifest \u2014 required unless the cwd holds registry.json").action(
161
161
  (name, id, _opts, cmd) => action(async () => {
162
- const { dismiss } = await import('./fleet-SAFXVN44.js');
162
+ const { dismiss } = await import('./fleet-JHNQOLSY.js');
163
163
  const opts = cmd.optsWithGlobals();
164
164
  await dismiss({
165
165
  cwd: resolveCwd(cmd),
@@ -174,7 +174,7 @@ fleet.command("accept").argument("<name>", "the registered project name").argume
174
174
  "Accept a project's finding as a known trade-off \u2014 corp ledgers persist beside the manifest"
175
175
  ).option("--reason <text>", "optional note on why the trade-off is accepted").option("--manifest <file>", "the fleet manifest \u2014 required unless the cwd holds registry.json").action(
176
176
  (name, id, _opts, cmd) => action(async () => {
177
- const { accept } = await import('./fleet-SAFXVN44.js');
177
+ const { accept } = await import('./fleet-JHNQOLSY.js');
178
178
  const opts = cmd.optsWithGlobals();
179
179
  await accept({ cwd: resolveCwd(cmd), manifest: opts.manifest, name, id, reason: opts.reason });
180
180
  })
@@ -183,7 +183,7 @@ program.command("screen").description(
183
183
  "Content screen: check for text that must never be published. Bring your own patterns \u2014 etymd ships none."
184
184
  ).option("--staged", "screen what a commit is about to add (default)").option("--tree", "screen every tracked file \u2014 what is about to leave the machine").option("--message <file>", "screen a commit message (the commit-msg hook's $1)").option("--dir <dir>", "screen an unpacked build artifact \u2014 what actually ships").option("--patterns <file>", "pattern file (default: ~/.config/etymd/screen-patterns)").option("--advisory", "report without failing").action(
185
185
  (opts, cmd) => action(async () => {
186
- const { run } = await import('./screen-U7JMWZPS.js');
186
+ const { run } = await import('./screen-6R6NXDSY.js');
187
187
  const scope = opts.message ? "message" : opts.dir ? "dir" : opts.tree ? "tree" : "staged";
188
188
  await run({
189
189
  cwd: resolveCwd(cmd),
@@ -196,7 +196,7 @@ program.command("screen").description(
196
196
  );
197
197
  program.command("gates").description("Install the local git-hook gates (process \u2192 pre-commit, correctness \u2192 pre-push)").option("--ci", "note about the CI review gate (ships later; local gates install now)").option("-y, --yes", "skip prompts; never overwrites a hand-edited hook").action(
198
198
  (opts, cmd) => action(async () => {
199
- const { run } = await import('./gates-D5SBAVXB.js');
199
+ const { run } = await import('./gates-KHC7HPIF.js');
200
200
  await run({ cwd: resolveCwd(cmd), ci: opts.ci, yes: opts.yes });
201
201
  })
202
202
  );
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ export { CONFIG_FILE, DEFAULT_CONFIG, configPath, readConfig } from './chunk-K7QBTANO.js';
3
+ import './chunk-C7LBUFNU.js';
4
+ import './chunk-IV3FYVTS.js';
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { measureContext } from './chunk-LT67FU2Y.js';
3
- import { readConfig } from './chunk-NMZ3RHWW.js';
4
- import { print, renderContext } from './chunk-TADKOW6P.js';
5
- import './chunk-R74SJ7HS.js';
6
- import './chunk-YLNQQZ5F.js';
2
+ import { measureContext } from './chunk-OKU3HXIH.js';
3
+ import { readConfig } from './chunk-K7QBTANO.js';
4
+ import { print, renderContext } from './chunk-5BVKFJWM.js';
5
+ import './chunk-C7LBUFNU.js';
6
+ import './chunk-IV3FYVTS.js';
7
7
 
8
8
  // src/commands/context.ts
9
9
  async function run(opts) {
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import { run } from './chunk-BQGCLPHS.js';
3
+ import './chunk-OESQNO2J.js';
4
+ import './chunk-OKU3HXIH.js';
5
+ import './chunk-OAFYLBVG.js';
6
+ import './chunk-K7QBTANO.js';
7
+ import './chunk-5BVKFJWM.js';
8
+ import './chunk-ULEWJ3ZK.js';
9
+ import './chunk-HLWCODYX.js';
10
+ import './chunk-C7LBUFNU.js';
11
+ import './chunk-6DUDIVIC.js';
12
+ import './chunk-IV3FYVTS.js';
13
+
14
+ // src/commands/doctor.ts
15
+ async function run2(opts) {
16
+ await run({ cwd: opts.cwd, json: opts.json, truth: true });
17
+ }
18
+
19
+ export { run2 as run };
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { dismiss, accept } from './chunk-MMPV67FW.js';
3
- import { parseFailOnTier, meetsFailOn, runAudit } from './chunk-QJRPKZPA.js';
4
- import './chunk-LT67FU2Y.js';
5
- import { readLedger } from './chunk-WWBF4Y7K.js';
6
- import { DEFAULT_CONFIG } from './chunk-NMZ3RHWW.js';
7
- import { print, theme, section, renderFleetRows, renderFleetNotes, renderFindings, TIER_BADGE, glyph } from './chunk-TADKOW6P.js';
8
- import { scanProject } from './chunk-4KOP36RD.js';
9
- import './chunk-OJVQXB5Z.js';
10
- import { ETYMD_DIR } from './chunk-R74SJ7HS.js';
11
- import './chunk-IGHES6AK.js';
12
- import { readText, pathExists, git, isDirectory } from './chunk-YLNQQZ5F.js';
2
+ import { dismiss, accept } from './chunk-V2SOHPXF.js';
3
+ import { parseFailOnTier, meetsFailOn, runAudit } from './chunk-OESQNO2J.js';
4
+ import './chunk-OKU3HXIH.js';
5
+ import { readLedger } from './chunk-OAFYLBVG.js';
6
+ import { DEFAULT_CONFIG } from './chunk-K7QBTANO.js';
7
+ import { print, theme, section, renderFleetRows, renderFleetNotes, renderFindings, TIER_BADGE, glyph } from './chunk-5BVKFJWM.js';
8
+ import { scanProject } from './chunk-ULEWJ3ZK.js';
9
+ import './chunk-HLWCODYX.js';
10
+ import { ETYMD_DIR } from './chunk-C7LBUFNU.js';
11
+ import './chunk-6DUDIVIC.js';
12
+ import { readText, pathExists, git, isDirectory } from './chunk-IV3FYVTS.js';
13
13
  import { promises } from 'node:fs';
14
14
  import os from 'node:os';
15
15
  import path from 'node:path';
@@ -687,9 +687,9 @@ async function checkCorpEmails(manifest, findings, disclosures) {
687
687
  }
688
688
  }
689
689
  async function checkGateDrift(manifest, findings, disclosures) {
690
- const { planWorkflow } = await import('./generate-CBMTNN7P.js');
691
- const { scanProject: scanProject2 } = await import('./scan-ES7IOXOC.js');
692
- const { readConfig } = await import('./config-MX7OYI7F.js');
690
+ const { planWorkflow } = await import('./generate-UTKQ4GE6.js');
691
+ const { scanProject: scanProject2 } = await import('./scan-D4M7JXE3.js');
692
+ const { readConfig } = await import('./config-UJRSCDMD.js');
693
693
  for (const entry of manifest.entries) {
694
694
  const root = entry.resolvedRoot;
695
695
  if (!root || !await isDirectory(root)) continue;
@@ -709,7 +709,14 @@ async function checkGateDrift(manifest, findings, disclosures) {
709
709
  const { config } = await readConfig(root);
710
710
  const planned = (await planWorkflow(root, facts, { agents: false, gates: true, gateConfig: config.gates })).filter((f) => f.executable);
711
711
  const missing = planned.filter((f) => !f.exists).map((f) => f.path);
712
- const stale = planned.filter((f) => f.exists && f.differs).map((f) => f.path);
712
+ const differing = planned.filter((f) => f.exists && f.differs);
713
+ const stale = differing.filter((f) => f.drift !== "edited").map((f) => f.path);
714
+ const edited = differing.filter((f) => f.drift === "edited").map((f) => f.path);
715
+ if (edited.length) {
716
+ disclosures.push(
717
+ `${entry.name}: ${edited.join(", ")} edited since etymd generated ${edited.length === 1 ? "it" : "them"} \u2014 a customisation, not drift; etymd will not overwrite ${edited.length === 1 ? "it" : "them"}`
718
+ );
719
+ }
713
720
  if (missing.length) {
714
721
  findings.push(
715
722
  finding(
@@ -729,8 +736,8 @@ async function checkGateDrift(manifest, findings, disclosures) {
729
736
  "polish",
730
737
  `\`${entry.name}\` has ${stale.length} gate${stale.length === 1 ? "" : "s"} that differ from what its own inputs generate`,
731
738
  stale.map((s) => `${entry.name}: ${s}`),
732
- "A hand-edited or outdated hook drifts from the pack silently \u2014 the repo believes it runs checks the file no longer contains.",
733
- "Re-run `etymd gates` (it asks before overwriting), or record the intent in .etymd/config.json."
739
+ "An outdated hook drifts from the pack silently \u2014 the repo believes it runs checks the file no longer contains.",
740
+ "Re-run `etymd gates` in that repo \u2014 it regenerates a gate it provably wrote and asks before touching anything else."
734
741
  )
735
742
  );
736
743
  }
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { applyFiles } from './chunk-EF6BPBG6.js';
3
- import { derivedCommands, planWorkflow, runPrefix, isSafeGateCommand } from './chunk-OX3LUOZR.js';
4
- import { readConfig, CONFIG_FILE, configPath } from './chunk-NMZ3RHWW.js';
5
- import { section, print, glyph, theme, renderPlan } from './chunk-TADKOW6P.js';
6
- import { scanProject } from './chunk-4KOP36RD.js';
7
- import './chunk-OJVQXB5Z.js';
8
- import './chunk-R74SJ7HS.js';
9
- import './chunk-IGHES6AK.js';
10
- import { readText, git } from './chunk-YLNQQZ5F.js';
3
+ import { derivedCommands, planWorkflow, runPrefix, isSafeGateCommand } from './chunk-KTIEYKFK.js';
4
+ import { readConfig, CONFIG_FILE, configPath } from './chunk-K7QBTANO.js';
5
+ import { section, print, glyph, theme, renderPlan } from './chunk-5BVKFJWM.js';
6
+ import { scanProject } from './chunk-ULEWJ3ZK.js';
7
+ import './chunk-HLWCODYX.js';
8
+ import './chunk-C7LBUFNU.js';
9
+ import './chunk-6DUDIVIC.js';
10
+ import { readText, git } from './chunk-IV3FYVTS.js';
11
11
  import { promises } from 'node:fs';
12
12
  import path from 'node:path';
13
13
  import { confirm, isCancel, cancel, select, multiselect } from '@clack/prompts';
@@ -185,7 +185,7 @@ async function run(opts) {
185
185
  return files.filter((f) => f.executable);
186
186
  };
187
187
  let gateFiles = await plan();
188
- renderPlan(gateFiles);
188
+ renderPlan(gateFiles, { regeneratesStale: true });
189
189
  printGateSummary(gateConfig, facts, configSource);
190
190
  if (!opts.yes) {
191
191
  const choice = await select({
@@ -217,13 +217,13 @@ async function run(opts) {
217
217
  const overwrite = /* @__PURE__ */ new Set();
218
218
  for (const f of gateFiles) {
219
219
  if (!f.exists) continue;
220
- if (!f.differs) {
220
+ if (!f.differs || f.drift === "stale") {
221
221
  overwrite.add(f.path);
222
222
  continue;
223
223
  }
224
224
  if (opts.yes) continue;
225
225
  const ow = await confirm({
226
- message: `${f.path} exists with different content (hand-edited?). Overwrite with the pack version?`,
226
+ message: f.drift === "edited" ? `${f.path} has been edited since etymd generated it. Overwrite with the pack version?` : `${f.path} carries no etymd generation stamp, so it may be hand-written. Overwrite with the pack version?`,
227
227
  initialValue: false
228
228
  });
229
229
  if (isCancel(ow)) {
@@ -236,8 +236,16 @@ async function run(opts) {
236
236
  await git(opts.cwd, ["config", "core.hooksPath", ".githooks"]);
237
237
  section("Done");
238
238
  for (const w of result.written) print(` ${glyph.ok} ${theme.dim("wrote")} ${theme.info(w)}`);
239
- for (const sk of result.skipped)
240
- print(` ${glyph.bullet} ${theme.dim("kept (hand-edited)")} ${theme.dim(sk)}`);
239
+ for (const sk of result.skipped) {
240
+ const drift = gateFiles.find((f) => f.path === sk)?.drift;
241
+ const reason = drift === "edited" ? "kept (edited since generated)" : "kept (no etymd stamp)";
242
+ print(` ${glyph.bullet} ${theme.dim(reason)} ${theme.dim(sk)}`);
243
+ if (opts.yes) {
244
+ print(
245
+ ` ${theme.dim("its content differs from what this repo now generates \u2014 re-run without --yes to review and overwrite")}`
246
+ );
247
+ }
248
+ }
241
249
  print(` ${glyph.ok} ${theme.dim("set")} ${theme.code("core.hooksPath = .githooks")}`);
242
250
  const wrotePublishGate = result.written.includes(PUBLISH_GATE_SCRIPT);
243
251
  if (wrotePublishGate) {
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ export { derivedCommands, planWorkflow } from './chunk-KTIEYKFK.js';
3
+ import './chunk-K7QBTANO.js';
4
+ import './chunk-C7LBUFNU.js';
5
+ import './chunk-6DUDIVIC.js';
6
+ import './chunk-IV3FYVTS.js';
package/dist/index.d.ts CHANGED
@@ -256,8 +256,15 @@ interface GeneratedFile {
256
256
  contents: string;
257
257
  /** Present on disk already — apply will skip or ask before overwriting. */
258
258
  exists: boolean;
259
- /** The existing file's content differs from what the pack would generate (hand-edited or stale). */
259
+ /** The existing file's content differs from what the pack would generate. */
260
260
  differs?: boolean;
261
+ /**
262
+ * WHY it differs, when it does — the two cases pull in opposite directions and must not share
263
+ * a bucket. `stale` is provably etymd's own output against inputs that have since moved (an
264
+ * older pack, a renamed script, a changed package manager): regenerating it destroys nothing.
265
+ * `edited` and `unstamped` may hold someone's work, so they are kept.
266
+ */
267
+ drift?: "stale" | "edited" | "unstamped";
261
268
  /** Hooks need the executable bit. */
262
269
  executable?: boolean;
263
270
  label: string;
@@ -745,6 +752,18 @@ interface GateInventory {
745
752
  prePush: GateTool[];
746
753
  commitMsg: GateTool[];
747
754
  lintStaged: GateTool[];
755
+ /** Companion files the hooks call and whose checks ARE counted above. */
756
+ companions: string[];
757
+ /**
758
+ * Counted, but the execute bit could not be judged here — no POSIX mode bits, or a hook that
759
+ * is not itself executable, which means this filesystem's bits prove nothing either way.
760
+ */
761
+ unverifiedCompanions: string[];
762
+ /**
763
+ * Companions a hook calls that exist and provably lack the execute bit while the hook beside
764
+ * them has it. NOT counted: the call skips them on every run.
765
+ */
766
+ inertCompanions: string[];
748
767
  };
749
768
  ci: {
750
769
  system: ProjectFacts["ci"]["system"];
@@ -769,7 +788,7 @@ declare const gateIntegrityLens: Lens;
769
788
  * change meaning. Stamped into facts, baselines, and generated artifacts so drift against the
770
789
  * pack is computable and `harvest` has something to diff.
771
790
  */
772
- declare const PACK_VERSION = "5";
791
+ declare const PACK_VERSION = "6";
773
792
 
774
793
  declare const VERSION: string;
775
794
  declare const NAME: string;