etymd 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/{approve-JEOEC2MV.js → approve-3B2UL7LG.js} +2 -2
- package/dist/{audit-GYZ5YZ7U.js → audit-4YB3K62W.js} +4 -4
- package/dist/{brief-ZK7TXAC4.js → brief-6EEXGQUW.js} +2 -2
- package/dist/{chunk-NFENQ3IK.js → chunk-DKC7MLWG.js} +1 -1
- package/dist/{chunk-MKYPLNLH.js → chunk-HOLFW5QY.js} +1 -1
- package/dist/{chunk-XTAX4YNT.js → chunk-MEJ4MBVL.js} +1 -1
- package/dist/{chunk-V3DPLOOM.js → chunk-V6NZEIHF.js} +1 -1
- package/dist/{chunk-3NYDVB4Z.js → chunk-VFUYQLW7.js} +22 -4
- package/dist/cli.js +13 -13
- package/dist/{doctor-C2RTL2QV.js → doctor-T76ZNL4S.js} +4 -4
- package/dist/{fleet-YZGR4UC2.js → fleet-WOPKM2Z3.js} +10 -5
- package/dist/{gates-ILE4PXDA.js → gates-WIGDS5VF.js} +5 -15
- package/dist/generate-I75IEXXF.js +6 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +28 -4
- package/dist/{init-OVLO4OBZ.js → init-NSYVVMF2.js} +4 -3
- package/dist/scan-BAD2UJAL.js +5 -0
- package/dist/{scan-RAJUFTQR.js → scan-COEPZJBO.js} +2 -2
- package/package.json +2 -2
- package/dist/generate-IYCQETQB.js +0 -4
- package/dist/scan-ZYZ7XDSJ.js +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# etymd
|
|
2
2
|
|
|
3
|
+
## 0.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Regeneration keeps what a repo already had, and a fleet can declare a repo has no gates.
|
|
8
|
+
|
|
9
|
+
**Preservation moved into the generator.** `etymd gates` read the existing hook and kept a test
|
|
10
|
+
step it already ran; every other caller of `planWorkflow` did not — including the fleet gate-drift
|
|
11
|
+
check, which therefore compared each repo against a hook missing checks the repo really runs.
|
|
12
|
+
That reported permanent false drift, and anyone acting on it would have silently lost a check. A
|
|
13
|
+
guarantee that holds only on the path someone remembered to wire it into is not a guarantee.
|
|
14
|
+
|
|
15
|
+
**`gates: "none"` in a fleet manifest** declares that generated gates are deliberately absent — a
|
|
16
|
+
prose repo with nothing mechanically checkable, or one that gates itself by hand. Gate drift sits
|
|
17
|
+
among the wall checks, which are deliberately not ledger-quietable because a leak or a partition
|
|
18
|
+
breach has no honest resolution except a fix; that reasoning does not transfer to a settled
|
|
19
|
+
choice, and an un-silenceable finding for a decision already made is how a report teaches people
|
|
20
|
+
to stop reading it. The absence is disclosed rather than hidden, and an _undeclared_ absence still
|
|
21
|
+
reports — silence has to be earned by declaring it.
|
|
22
|
+
|
|
23
|
+
**A hand-written `.etymd/config.json` no longer crashes the generator.** Setting only
|
|
24
|
+
`gates.failOn` threw: the type declares `commands` as required, describing the shape etymd writes
|
|
25
|
+
rather than the shape a user writes.
|
|
26
|
+
|
|
27
|
+
**The publish gate points at the committed script.** `prepublishOnly` resolving a bare
|
|
28
|
+
`artifact-check` from `PATH` meant the door guarding what actually ships was silently absent for
|
|
29
|
+
every clone but one.
|
|
30
|
+
|
|
3
31
|
## 0.3.0
|
|
4
32
|
|
|
5
33
|
### Minor Changes
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { section, theme, print, renderBaselineDrift, glyph } from './chunk-TADKOW6P.js';
|
|
3
|
+
import { scanProject } from './chunk-HOLFW5QY.js';
|
|
4
|
+
import { VERSION } from './chunk-DKC7MLWG.js';
|
|
3
5
|
import { readBaseline, summarizeBaselineDrift, isDriftEmpty, writeBaseline, deriveProfile } from './chunk-R74SJ7HS.js';
|
|
4
|
-
import { scanProject } from './chunk-MKYPLNLH.js';
|
|
5
|
-
import { VERSION } from './chunk-NFENQ3IK.js';
|
|
6
6
|
import { PACK_VERSION } from './chunk-CRJHIXJH.js';
|
|
7
7
|
import './chunk-YLNQQZ5F.js';
|
|
8
8
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export { run } from './chunk-
|
|
3
|
-
import './chunk-
|
|
2
|
+
export { run } from './chunk-MEJ4MBVL.js';
|
|
3
|
+
import './chunk-V6NZEIHF.js';
|
|
4
4
|
import './chunk-LT67FU2Y.js';
|
|
5
5
|
import './chunk-WWBF4Y7K.js';
|
|
6
6
|
import './chunk-NMZ3RHWW.js';
|
|
7
7
|
import './chunk-TADKOW6P.js';
|
|
8
|
+
import './chunk-HOLFW5QY.js';
|
|
9
|
+
import './chunk-DKC7MLWG.js';
|
|
8
10
|
import './chunk-R74SJ7HS.js';
|
|
9
|
-
import './chunk-MKYPLNLH.js';
|
|
10
|
-
import './chunk-NFENQ3IK.js';
|
|
11
11
|
import './chunk-CRJHIXJH.js';
|
|
12
12
|
import './chunk-YLNQQZ5F.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { print, theme } from './chunk-TADKOW6P.js';
|
|
3
|
+
import { scanProject } from './chunk-HOLFW5QY.js';
|
|
4
|
+
import './chunk-DKC7MLWG.js';
|
|
3
5
|
import { ETYMD_DIR } from './chunk-R74SJ7HS.js';
|
|
4
|
-
import { scanProject } from './chunk-MKYPLNLH.js';
|
|
5
|
-
import './chunk-NFENQ3IK.js';
|
|
6
6
|
import './chunk-CRJHIXJH.js';
|
|
7
7
|
import './chunk-YLNQQZ5F.js';
|
|
8
8
|
import { promises } from 'node:fs';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { VERSION } from './chunk-
|
|
2
|
+
import { VERSION } from './chunk-DKC7MLWG.js';
|
|
3
3
|
import { PACK_VERSION } from './chunk-CRJHIXJH.js';
|
|
4
4
|
import { normalizeRelPath, isDirectory, readJson, git, matchesAnyGlob, pathExists, readText } from './chunk-YLNQQZ5F.js';
|
|
5
5
|
import path from 'node:path';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { parseFailOnTier, runAudit, meetsFailOn } from './chunk-
|
|
2
|
+
import { parseFailOnTier, runAudit, meetsFailOn } from './chunk-V6NZEIHF.js';
|
|
3
3
|
import { print, section, theme, renderLensCoverage, renderFindings, renderLedgerDiff } from './chunk-TADKOW6P.js';
|
|
4
4
|
|
|
5
5
|
// src/commands/audit.ts
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { measureContext } from './chunk-LT67FU2Y.js';
|
|
3
3
|
import { readLedger, reconcileLedger, writeLedger, visibleFindings } from './chunk-WWBF4Y7K.js';
|
|
4
4
|
import { DEFAULT_CONFIG, readConfig, CONFIG_FILE } from './chunk-NMZ3RHWW.js';
|
|
5
|
+
import { scanProject, expandFileGlobs } from './chunk-HOLFW5QY.js';
|
|
5
6
|
import { ETYMD_DIR, writeCachedFacts, readBaseline, deriveProfile, baselineCarriesMachinePath, BASELINE_FILE } from './chunk-R74SJ7HS.js';
|
|
6
|
-
import { scanProject, expandFileGlobs } from './chunk-MKYPLNLH.js';
|
|
7
7
|
import { PACK_VERSION } from './chunk-CRJHIXJH.js';
|
|
8
8
|
import { pathExists, readJson, git, readText, isDirectory, matchesAnyGlob, isCiEnvironment, normalizeRelPath } from './chunk-YLNQQZ5F.js';
|
|
9
9
|
import path from 'node:path';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { DEFAULT_CONFIG } from './chunk-NMZ3RHWW.js';
|
|
2
3
|
import { PACK_VERSION } from './chunk-CRJHIXJH.js';
|
|
3
|
-
import {
|
|
4
|
+
import { readText, pathExists } from './chunk-YLNQQZ5F.js';
|
|
4
5
|
import path from 'node:path';
|
|
5
6
|
|
|
6
7
|
// src/pack/templates.ts
|
|
@@ -160,7 +161,7 @@ exit 0
|
|
|
160
161
|
function generatePrePushHook(facts, gates) {
|
|
161
162
|
const run = runPrefix(facts.packageManager);
|
|
162
163
|
const c = facts.commands;
|
|
163
|
-
const candidates = gates?.commands
|
|
164
|
+
const candidates = gates?.commands?.length ? gates.commands : [c.formatCheck, c.typecheck, c.lint];
|
|
164
165
|
const allowed = new Set(gates?.allowWriting ?? []);
|
|
165
166
|
const steps = candidates.filter(
|
|
166
167
|
(key) => Boolean(key) && (allowed.has(key) || isSafeGateCommand(c.raw[key]))
|
|
@@ -227,6 +228,16 @@ exit 0
|
|
|
227
228
|
}
|
|
228
229
|
|
|
229
230
|
// src/core/generate.ts
|
|
231
|
+
function derivedCommands(facts, existingHook) {
|
|
232
|
+
const c = facts.commands;
|
|
233
|
+
const base = [c.formatCheck, c.typecheck, c.lint].filter(
|
|
234
|
+
(k) => Boolean(k) && isSafeGateCommand(c.raw[k])
|
|
235
|
+
);
|
|
236
|
+
if (c.test && existingHook && new RegExp(`\\b${c.test}\\b`).test(existingHook)) {
|
|
237
|
+
base.push(c.test);
|
|
238
|
+
}
|
|
239
|
+
return base;
|
|
240
|
+
}
|
|
230
241
|
async function planWorkflow(root, facts, opts) {
|
|
231
242
|
const out = [];
|
|
232
243
|
const add = async (rel, contents, label, executable = false) => {
|
|
@@ -246,6 +257,7 @@ async function planWorkflow(root, facts, opts) {
|
|
|
246
257
|
await add("AGENTS.md", generateAgentsMd(facts), "Minimal operating contract (scaffold)");
|
|
247
258
|
}
|
|
248
259
|
if (opts.gates) {
|
|
260
|
+
const existingPrePush = await readText(path.join(root, ".githooks", "pre-push"));
|
|
249
261
|
await add(".githooks/pre-commit", generatePreCommitHook(), "Process gate (pre-commit)", true);
|
|
250
262
|
await add(
|
|
251
263
|
".githooks/commit-msg",
|
|
@@ -253,9 +265,15 @@ async function planWorkflow(root, facts, opts) {
|
|
|
253
265
|
"Content screen (commit message)",
|
|
254
266
|
true
|
|
255
267
|
);
|
|
268
|
+
const gateConfig = opts.gateConfig?.commands?.length ? opts.gateConfig : {
|
|
269
|
+
commands: derivedCommands(facts, existingPrePush ?? void 0),
|
|
270
|
+
failOn: opts.gateConfig?.failOn ?? DEFAULT_CONFIG.gates.failOn,
|
|
271
|
+
publishGate: opts.gateConfig?.publishGate,
|
|
272
|
+
allowWriting: opts.gateConfig?.allowWriting ?? []
|
|
273
|
+
};
|
|
256
274
|
await add(
|
|
257
275
|
".githooks/pre-push",
|
|
258
|
-
generatePrePushHook(facts,
|
|
276
|
+
generatePrePushHook(facts, gateConfig),
|
|
259
277
|
"Correctness gate (pre-push)",
|
|
260
278
|
true
|
|
261
279
|
);
|
|
@@ -271,4 +289,4 @@ async function planWorkflow(root, facts, opts) {
|
|
|
271
289
|
return out;
|
|
272
290
|
}
|
|
273
291
|
|
|
274
|
-
export { isSafeGateCommand, planWorkflow, runPrefix };
|
|
292
|
+
export { derivedCommands, isSafeGateCommand, planWorkflow, runPrefix };
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { VERSION } from './chunk-
|
|
2
|
+
import { VERSION } from './chunk-DKC7MLWG.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-
|
|
34
|
+
const { run } = await import('./audit-4YB3K62W.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-
|
|
47
|
+
const { run } = await import('./init-NSYVVMF2.js');
|
|
48
48
|
await run({ cwd: resolveCwd(cmd), yes: opts.yes });
|
|
49
49
|
})
|
|
50
50
|
);
|
|
@@ -52,19 +52,19 @@ 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-
|
|
55
|
+
const { run } = await import('./approve-3B2UL7LG.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-
|
|
61
|
+
const { run } = await import('./scan-COEPZJBO.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-
|
|
67
|
+
const { run } = await import('./doctor-T76ZNL4S.js');
|
|
68
68
|
await run({ cwd: resolveCwd(cmd), json: opts.json });
|
|
69
69
|
})
|
|
70
70
|
);
|
|
@@ -76,7 +76,7 @@ program.command("context").description("Measure the always-loaded context footpr
|
|
|
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-
|
|
79
|
+
const { run } = await import('./brief-6EEXGQUW.js');
|
|
80
80
|
await run({ cwd: resolveCwd(cmd), human: opts.human });
|
|
81
81
|
})
|
|
82
82
|
);
|
|
@@ -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-
|
|
114
|
+
const { sweep } = await import('./fleet-WOPKM2Z3.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-
|
|
129
|
+
const { check } = await import('./fleet-WOPKM2Z3.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-
|
|
139
|
+
const { add } = await import('./fleet-WOPKM2Z3.js');
|
|
140
140
|
const shared = cmd.optsWithGlobals();
|
|
141
141
|
await add({
|
|
142
142
|
cwd: resolveCwd(cmd),
|
|
@@ -154,7 +154,7 @@ fleet.command("dismiss").argument("<name>", "the registered project name").argum
|
|
|
154
154
|
"Dismiss a project's finding from any cwd \u2014 corp ledgers persist beside the manifest"
|
|
155
155
|
).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(
|
|
156
156
|
(name, id, _opts, cmd) => action(async () => {
|
|
157
|
-
const { dismiss } = await import('./fleet-
|
|
157
|
+
const { dismiss } = await import('./fleet-WOPKM2Z3.js');
|
|
158
158
|
const opts = cmd.optsWithGlobals();
|
|
159
159
|
await dismiss({
|
|
160
160
|
cwd: resolveCwd(cmd),
|
|
@@ -169,7 +169,7 @@ fleet.command("accept").argument("<name>", "the registered project name").argume
|
|
|
169
169
|
"Accept a project's finding as a known trade-off \u2014 corp ledgers persist beside the manifest"
|
|
170
170
|
).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(
|
|
171
171
|
(name, id, _opts, cmd) => action(async () => {
|
|
172
|
-
const { accept } = await import('./fleet-
|
|
172
|
+
const { accept } = await import('./fleet-WOPKM2Z3.js');
|
|
173
173
|
const opts = cmd.optsWithGlobals();
|
|
174
174
|
await accept({ cwd: resolveCwd(cmd), manifest: opts.manifest, name, id, reason: opts.reason });
|
|
175
175
|
})
|
|
@@ -191,7 +191,7 @@ program.command("screen").description(
|
|
|
191
191
|
);
|
|
192
192
|
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(
|
|
193
193
|
(opts, cmd) => action(async () => {
|
|
194
|
-
const { run } = await import('./gates-
|
|
194
|
+
const { run } = await import('./gates-WIGDS5VF.js');
|
|
195
195
|
await run({ cwd: resolveCwd(cmd), ci: opts.ci, yes: opts.yes });
|
|
196
196
|
})
|
|
197
197
|
);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { run } from './chunk-
|
|
3
|
-
import './chunk-
|
|
2
|
+
import { run } from './chunk-MEJ4MBVL.js';
|
|
3
|
+
import './chunk-V6NZEIHF.js';
|
|
4
4
|
import './chunk-LT67FU2Y.js';
|
|
5
5
|
import './chunk-WWBF4Y7K.js';
|
|
6
6
|
import './chunk-NMZ3RHWW.js';
|
|
7
7
|
import './chunk-TADKOW6P.js';
|
|
8
|
+
import './chunk-HOLFW5QY.js';
|
|
9
|
+
import './chunk-DKC7MLWG.js';
|
|
8
10
|
import './chunk-R74SJ7HS.js';
|
|
9
|
-
import './chunk-MKYPLNLH.js';
|
|
10
|
-
import './chunk-NFENQ3IK.js';
|
|
11
11
|
import './chunk-CRJHIXJH.js';
|
|
12
12
|
import './chunk-YLNQQZ5F.js';
|
|
13
13
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { dismiss, accept } from './chunk-MMPV67FW.js';
|
|
3
|
-
import { parseFailOnTier, meetsFailOn, runAudit } from './chunk-
|
|
3
|
+
import { parseFailOnTier, meetsFailOn, runAudit } from './chunk-V6NZEIHF.js';
|
|
4
4
|
import './chunk-LT67FU2Y.js';
|
|
5
5
|
import { readLedger } from './chunk-WWBF4Y7K.js';
|
|
6
6
|
import { DEFAULT_CONFIG } from './chunk-NMZ3RHWW.js';
|
|
7
7
|
import { print, theme, section, renderFleetRows, renderFleetNotes, renderFindings, TIER_BADGE, glyph } from './chunk-TADKOW6P.js';
|
|
8
|
+
import { scanProject } from './chunk-HOLFW5QY.js';
|
|
9
|
+
import './chunk-DKC7MLWG.js';
|
|
8
10
|
import { ETYMD_DIR } from './chunk-R74SJ7HS.js';
|
|
9
|
-
import { scanProject } from './chunk-MKYPLNLH.js';
|
|
10
|
-
import './chunk-NFENQ3IK.js';
|
|
11
11
|
import './chunk-CRJHIXJH.js';
|
|
12
12
|
import { readText, pathExists, git, isDirectory } from './chunk-YLNQQZ5F.js';
|
|
13
13
|
import { promises } from 'node:fs';
|
|
@@ -119,6 +119,7 @@ function loadRegistryEntries(projects, manifest, manifestFile) {
|
|
|
119
119
|
// An unknown value is NOT coerced to a default: trust gates content screening, so a typo
|
|
120
120
|
// must surface as "undeclared" (a finding) rather than quietly picking an answer.
|
|
121
121
|
trust: isFleetTrust(rec.trust) ? rec.trust : void 0,
|
|
122
|
+
gates: asString(rec.gates),
|
|
122
123
|
trustRaw: asString(rec.trust),
|
|
123
124
|
staleAfterDays: typeof rec.staleAfterDays === "number" ? rec.staleAfterDays : void 0,
|
|
124
125
|
stateBudget: typeof rec.stateBudget === "number" ? rec.stateBudget : void 0,
|
|
@@ -686,13 +687,17 @@ async function checkCorpEmails(manifest, findings, disclosures) {
|
|
|
686
687
|
}
|
|
687
688
|
}
|
|
688
689
|
async function checkGateDrift(manifest, findings, disclosures) {
|
|
689
|
-
const { planWorkflow } = await import('./generate-
|
|
690
|
-
const { scanProject: scanProject2 } = await import('./scan-
|
|
690
|
+
const { planWorkflow } = await import('./generate-I75IEXXF.js');
|
|
691
|
+
const { scanProject: scanProject2 } = await import('./scan-BAD2UJAL.js');
|
|
691
692
|
const { readConfig } = await import('./config-MX7OYI7F.js');
|
|
692
693
|
for (const entry of manifest.entries) {
|
|
693
694
|
const root = entry.resolvedRoot;
|
|
694
695
|
if (!root || !await isDirectory(root)) continue;
|
|
695
696
|
if (entry.profile === "corp" || entry.private) continue;
|
|
697
|
+
if (entry.gates === "none") {
|
|
698
|
+
disclosures.push(`${entry.name}: gates declared \`none\` \u2014 deliberately absent, not missing`);
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
696
701
|
const facts = await scanProject2(root);
|
|
697
702
|
if (!facts.git.isRepo) continue;
|
|
698
703
|
if (facts.hooks.source === "husky" || facts.hooks.source === "husky-legacy") {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readConfig, CONFIG_FILE, configPath } from './chunk-NMZ3RHWW.js';
|
|
3
2
|
import { applyFiles } from './chunk-EF6BPBG6.js';
|
|
4
|
-
import {
|
|
3
|
+
import { derivedCommands, planWorkflow, runPrefix, isSafeGateCommand } from './chunk-VFUYQLW7.js';
|
|
4
|
+
import { readConfig, CONFIG_FILE, configPath } from './chunk-NMZ3RHWW.js';
|
|
5
5
|
import { section, print, glyph, theme, renderPlan } from './chunk-TADKOW6P.js';
|
|
6
|
+
import { scanProject } from './chunk-HOLFW5QY.js';
|
|
7
|
+
import './chunk-DKC7MLWG.js';
|
|
6
8
|
import './chunk-R74SJ7HS.js';
|
|
7
|
-
import { scanProject } from './chunk-MKYPLNLH.js';
|
|
8
|
-
import './chunk-NFENQ3IK.js';
|
|
9
9
|
import './chunk-CRJHIXJH.js';
|
|
10
10
|
import { readText, git } from './chunk-YLNQQZ5F.js';
|
|
11
11
|
import { promises } from 'node:fs';
|
|
@@ -55,16 +55,6 @@ var PUBLISH_GATE_VALUE = `./${PUBLISH_GATE_SCRIPT}`;
|
|
|
55
55
|
function publishGateKey(route) {
|
|
56
56
|
return route === "vscode" ? "vscode:prepublish" : "prepublishOnly";
|
|
57
57
|
}
|
|
58
|
-
function derivedCommands(facts, existingHook) {
|
|
59
|
-
const c = facts.commands;
|
|
60
|
-
const base = [c.formatCheck, c.typecheck, c.lint].filter(
|
|
61
|
-
(k) => Boolean(k) && isSafeGateCommand(c.raw[k])
|
|
62
|
-
);
|
|
63
|
-
if (c.test && existingHook && new RegExp(`\\b${c.test}\\b`).test(existingHook)) {
|
|
64
|
-
base.push(c.test);
|
|
65
|
-
}
|
|
66
|
-
return base;
|
|
67
|
-
}
|
|
68
58
|
function printGateSummary(gates, facts, source) {
|
|
69
59
|
const run2 = runPrefix(facts.packageManager);
|
|
70
60
|
const cmds = gates.commands.length ? gates.commands.map((c) => `${run2} ${c}`).join(", ") : "none detected";
|
|
@@ -269,4 +259,4 @@ async function run(opts) {
|
|
|
269
259
|
}
|
|
270
260
|
}
|
|
271
261
|
|
|
272
|
-
export {
|
|
262
|
+
export { mergeGateSection, run };
|
package/dist/index.d.ts
CHANGED
|
@@ -492,6 +492,15 @@ interface FleetEntry {
|
|
|
492
492
|
/** A declared `trust` value outside the vocabulary — preserved verbatim so the check can name it. */
|
|
493
493
|
trustRaw?: string;
|
|
494
494
|
staleAfterDays?: number;
|
|
495
|
+
/**
|
|
496
|
+
* How this entry's git hooks are governed. `"none"` declares that generated gates are
|
|
497
|
+
* deliberately absent — a prose repo with nothing mechanically checkable, or one whose gate is
|
|
498
|
+
* hand-written on purpose. Wall findings cannot be quieted through the ledger (004: their only
|
|
499
|
+
* honest resolution is fixing them), which is right for leak and partition conditions but
|
|
500
|
+
* wrong for a state a fleet legitimately chooses; without this, a settled decision is
|
|
501
|
+
* re-reported on every sweep until the report is ignored.
|
|
502
|
+
*/
|
|
503
|
+
gates?: string;
|
|
495
504
|
/** Per-entry state char-budget override (ships unset — the schema slot exists). */
|
|
496
505
|
stateBudget?: number;
|
|
497
506
|
contract: FleetContract;
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ var init_package = __esm({
|
|
|
29
29
|
"package.json"() {
|
|
30
30
|
package_default = {
|
|
31
31
|
name: "etymd",
|
|
32
|
-
version: "0.3.
|
|
32
|
+
version: "0.3.1",
|
|
33
33
|
description: "Keep your agent instructions true \u2014 verify AGENTS.md, CLAUDE.md, rules & skills against the actual repo, with drift caught over time and a regression ledger.",
|
|
34
34
|
keywords: [
|
|
35
35
|
"cli",
|
|
@@ -87,7 +87,7 @@ var init_package = __esm({
|
|
|
87
87
|
"format:check": "prettier --check . && npm run guard:md",
|
|
88
88
|
"guard:md": "! grep -rnF --include=*.md --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.git '\\*\\*' .",
|
|
89
89
|
prepare: "npm run build",
|
|
90
|
-
prepublishOnly: "npm run build && artifact-check",
|
|
90
|
+
prepublishOnly: "npm run build && ./scripts/artifact-check.sh",
|
|
91
91
|
changeset: "changeset",
|
|
92
92
|
"version:packages": "changeset version",
|
|
93
93
|
release: "npm run build && changeset publish"
|
|
@@ -1118,7 +1118,7 @@ exit 0
|
|
|
1118
1118
|
function generatePrePushHook(facts, gates) {
|
|
1119
1119
|
const run = runPrefix(facts.packageManager);
|
|
1120
1120
|
const c = facts.commands;
|
|
1121
|
-
const candidates = gates?.commands
|
|
1121
|
+
const candidates = gates?.commands?.length ? gates.commands : [c.formatCheck, c.typecheck, c.lint];
|
|
1122
1122
|
const allowed = new Set(gates?.allowWriting ?? []);
|
|
1123
1123
|
const steps = candidates.filter(
|
|
1124
1124
|
(key) => Boolean(key) && (allowed.has(key) || isSafeGateCommand(c.raw[key]))
|
|
@@ -1194,8 +1194,19 @@ var init_templates = __esm({
|
|
|
1194
1194
|
// src/core/generate.ts
|
|
1195
1195
|
var generate_exports = {};
|
|
1196
1196
|
__export(generate_exports, {
|
|
1197
|
+
derivedCommands: () => derivedCommands,
|
|
1197
1198
|
planWorkflow: () => planWorkflow
|
|
1198
1199
|
});
|
|
1200
|
+
function derivedCommands(facts, existingHook) {
|
|
1201
|
+
const c = facts.commands;
|
|
1202
|
+
const base = [c.formatCheck, c.typecheck, c.lint].filter(
|
|
1203
|
+
(k) => Boolean(k) && isSafeGateCommand(c.raw[k])
|
|
1204
|
+
);
|
|
1205
|
+
if (c.test && existingHook && new RegExp(`\\b${c.test}\\b`).test(existingHook)) {
|
|
1206
|
+
base.push(c.test);
|
|
1207
|
+
}
|
|
1208
|
+
return base;
|
|
1209
|
+
}
|
|
1199
1210
|
async function planWorkflow(root, facts, opts) {
|
|
1200
1211
|
const out = [];
|
|
1201
1212
|
const add = async (rel, contents, label, executable = false) => {
|
|
@@ -1215,6 +1226,7 @@ async function planWorkflow(root, facts, opts) {
|
|
|
1215
1226
|
await add("AGENTS.md", generateAgentsMd(facts), "Minimal operating contract (scaffold)");
|
|
1216
1227
|
}
|
|
1217
1228
|
if (opts.gates) {
|
|
1229
|
+
const existingPrePush = await readText(path.join(root, ".githooks", "pre-push"));
|
|
1218
1230
|
await add(".githooks/pre-commit", generatePreCommitHook(), "Process gate (pre-commit)", true);
|
|
1219
1231
|
await add(
|
|
1220
1232
|
".githooks/commit-msg",
|
|
@@ -1222,9 +1234,15 @@ async function planWorkflow(root, facts, opts) {
|
|
|
1222
1234
|
"Content screen (commit message)",
|
|
1223
1235
|
true
|
|
1224
1236
|
);
|
|
1237
|
+
const gateConfig = opts.gateConfig?.commands?.length ? opts.gateConfig : {
|
|
1238
|
+
commands: derivedCommands(facts, existingPrePush ?? void 0),
|
|
1239
|
+
failOn: opts.gateConfig?.failOn ?? DEFAULT_CONFIG.gates.failOn,
|
|
1240
|
+
publishGate: opts.gateConfig?.publishGate,
|
|
1241
|
+
allowWriting: opts.gateConfig?.allowWriting ?? []
|
|
1242
|
+
};
|
|
1225
1243
|
await add(
|
|
1226
1244
|
".githooks/pre-push",
|
|
1227
|
-
generatePrePushHook(facts,
|
|
1245
|
+
generatePrePushHook(facts, gateConfig),
|
|
1228
1246
|
"Correctness gate (pre-push)",
|
|
1229
1247
|
true
|
|
1230
1248
|
);
|
|
@@ -1242,6 +1260,7 @@ async function planWorkflow(root, facts, opts) {
|
|
|
1242
1260
|
var init_generate = __esm({
|
|
1243
1261
|
"src/core/generate.ts"() {
|
|
1244
1262
|
init_templates();
|
|
1263
|
+
init_config();
|
|
1245
1264
|
init_util();
|
|
1246
1265
|
}
|
|
1247
1266
|
});
|
|
@@ -2895,6 +2914,7 @@ function loadRegistryEntries(projects, manifest, manifestFile) {
|
|
|
2895
2914
|
// An unknown value is NOT coerced to a default: trust gates content screening, so a typo
|
|
2896
2915
|
// must surface as "undeclared" (a finding) rather than quietly picking an answer.
|
|
2897
2916
|
trust: isFleetTrust(rec.trust) ? rec.trust : void 0,
|
|
2917
|
+
gates: asString(rec.gates),
|
|
2898
2918
|
trustRaw: asString(rec.trust),
|
|
2899
2919
|
staleAfterDays: typeof rec.staleAfterDays === "number" ? rec.staleAfterDays : void 0,
|
|
2900
2920
|
stateBudget: typeof rec.stateBudget === "number" ? rec.stateBudget : void 0,
|
|
@@ -3474,6 +3494,10 @@ async function checkGateDrift(manifest, findings, disclosures) {
|
|
|
3474
3494
|
const root = entry.resolvedRoot;
|
|
3475
3495
|
if (!root || !await isDirectory(root)) continue;
|
|
3476
3496
|
if (entry.profile === "corp" || entry.private) continue;
|
|
3497
|
+
if (entry.gates === "none") {
|
|
3498
|
+
disclosures.push(`${entry.name}: gates declared \`none\` \u2014 deliberately absent, not missing`);
|
|
3499
|
+
continue;
|
|
3500
|
+
}
|
|
3477
3501
|
const facts = await scanProject2(root);
|
|
3478
3502
|
if (!facts.git.isRepo) continue;
|
|
3479
3503
|
if (facts.hooks.source === "husky" || facts.hooks.source === "husky-legacy") {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { applyFiles } from './chunk-EF6BPBG6.js';
|
|
3
|
-
import { planWorkflow } from './chunk-
|
|
3
|
+
import { planWorkflow } from './chunk-VFUYQLW7.js';
|
|
4
|
+
import './chunk-NMZ3RHWW.js';
|
|
4
5
|
import { theme, renderFacts, print, renderPlan, section, glyph } from './chunk-TADKOW6P.js';
|
|
6
|
+
import { scanProject } from './chunk-HOLFW5QY.js';
|
|
7
|
+
import { VERSION } from './chunk-DKC7MLWG.js';
|
|
5
8
|
import { writeCachedFacts, deriveProfile, writeBaseline, CACHE_DIR } from './chunk-R74SJ7HS.js';
|
|
6
|
-
import { scanProject } from './chunk-MKYPLNLH.js';
|
|
7
|
-
import { VERSION } from './chunk-NFENQ3IK.js';
|
|
8
9
|
import { PACK_VERSION } from './chunk-CRJHIXJH.js';
|
|
9
10
|
import { git, readText } from './chunk-YLNQQZ5F.js';
|
|
10
11
|
import { promises } from 'node:fs';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { print, renderFacts, theme } from './chunk-TADKOW6P.js';
|
|
3
|
+
import { scanProject } from './chunk-HOLFW5QY.js';
|
|
4
|
+
import './chunk-DKC7MLWG.js';
|
|
3
5
|
import { writeCachedFacts } from './chunk-R74SJ7HS.js';
|
|
4
|
-
import { scanProject } from './chunk-MKYPLNLH.js';
|
|
5
|
-
import './chunk-NFENQ3IK.js';
|
|
6
6
|
import './chunk-CRJHIXJH.js';
|
|
7
7
|
import './chunk-YLNQQZ5F.js';
|
|
8
8
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "etymd",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Keep your agent instructions true — verify AGENTS.md, CLAUDE.md, rules & skills against the actual repo, with drift caught over time and a regression ledger.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"format:check": "prettier --check . && npm run guard:md",
|
|
59
59
|
"guard:md": "! grep -rnF --include=*.md --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.git '\\*\\*' .",
|
|
60
60
|
"prepare": "npm run build",
|
|
61
|
-
"prepublishOnly": "npm run build && artifact-check",
|
|
61
|
+
"prepublishOnly": "npm run build && ./scripts/artifact-check.sh",
|
|
62
62
|
"changeset": "changeset",
|
|
63
63
|
"version:packages": "changeset version",
|
|
64
64
|
"release": "npm run build && changeset publish"
|