etymd 0.16.0 → 0.19.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 (30) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +28 -5
  3. package/dist/{approve-K5FFTPA4.js → approve-Y36ZO6IL.js} +3 -3
  4. package/dist/audit-RONSEYFS.js +11 -0
  5. package/dist/{brief-DOQ2DQEG.js → brief-ILB6RMSQ.js} +3 -3
  6. package/dist/{chunk-FKDT4RBP.js → chunk-4IESOWHQ.js} +2 -2
  7. package/dist/{chunk-EJM47PIF.js → chunk-DLHC7QON.js} +83 -10
  8. package/dist/{chunk-Y4E6ERCM.js → chunk-EYUW3UJU.js} +59 -11
  9. package/dist/chunk-EZRK4MV3.js +109 -0
  10. package/dist/{chunk-OKJ3RGJB.js → chunk-HSRHOVII.js} +113 -117
  11. package/dist/{chunk-UV3YIBDW.js → chunk-KIPD3N77.js} +11 -6
  12. package/dist/{chunk-UYCGP5BS.js → chunk-L4KIIKXI.js} +1 -1
  13. package/dist/{chunk-YQZDYDAK.js → chunk-U7BCBJIH.js} +1 -1
  14. package/dist/{chunk-ANXGT7V3.js → chunk-ZCOIZVSM.js} +37 -5
  15. package/dist/cli.js +17 -17
  16. package/dist/{doctor-X6XAGKV7.js → doctor-EHRADIUZ.js} +6 -6
  17. package/dist/{fleet-UEFLKIRI.js → fleet-KFF3N2O7.js} +22 -6
  18. package/dist/{gates-NVWWKMT2.js → gates-UJ23THID.js} +9 -5
  19. package/dist/{generate-YEARCSHW.js → generate-6IPTCLYG.js} +3 -2
  20. package/dist/index.d.ts +29 -3
  21. package/dist/index.js +313 -52
  22. package/dist/{init-Z5O2WVRJ.js → init-7HWVHZMF.js} +5 -4
  23. package/dist/{premise-4QRU7LRI.js → premise-U6JIB2QG.js} +9 -5
  24. package/dist/{propose-IKE4CUIM.js → propose-NZMEPZIL.js} +6 -6
  25. package/dist/{scan-H3NF5IV5.js → scan-DLKYG5BB.js} +3 -3
  26. package/dist/scan-V53PGAQ6.js +5 -0
  27. package/dist/{screen-E4FC7W5N.js → screen-S736DXQ5.js} +56 -24
  28. package/package.json +2 -2
  29. package/dist/audit-HBNXNGD6.js +0 -11
  30. package/dist/scan-64Z6SNYF.js +0 -5
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { applyFiles } from './chunk-GWGKEPRX.js';
3
- import { planWorkflow } from './chunk-OKJ3RGJB.js';
3
+ import { planWorkflow } from './chunk-EZRK4MV3.js';
4
+ import './chunk-HSRHOVII.js';
4
5
  import { theme, renderFacts, print, renderPlan, section, glyph } from './chunk-HI7NWPRA.js';
5
- import { scanProject } from './chunk-EJM47PIF.js';
6
- import { VERSION } from './chunk-UYCGP5BS.js';
6
+ import { scanProject } from './chunk-DLHC7QON.js';
7
+ import { VERSION } from './chunk-L4KIIKXI.js';
7
8
  import { writeCachedFacts, deriveProfile, writeBaseline, CACHE_DIR } from './chunk-P6ATKV2R.js';
8
- import { PACK_VERSION } from './chunk-YQZDYDAK.js';
9
+ import { PACK_VERSION } from './chunk-U7BCBJIH.js';
9
10
  import { git, readText } from './chunk-4VPBP6K6.js';
10
11
  import { promises } from 'fs';
11
12
  import path from 'path';
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { parseFailOnTier, meetsFailOn, buildTruthEnv, emptyCounters, checkTextClaims, checkDocRefs, loadDecisionLedger, checkDecisionRefs, rankFindings, PATH_TOKEN_RE, KNOWN_EXTENSIONS, NAMESPACE_IDENT, NAMESPACE_STOP } from './chunk-Y4E6ERCM.js';
2
+ import { parseFailOnTier, meetsFailOn, buildTruthEnv, emptyCounters, checkTextClaims, checkDocRefs, loadDecisionLedger, checkDecisionRefs, rankFindings, PATH_TOKEN_RE, HOSTNAME_RE, KNOWN_EXTENSIONS, NAMESPACE_IDENT, NAMESPACE_STOP } from './chunk-EYUW3UJU.js';
3
3
  import { print, section, theme, renderFindings } from './chunk-HI7NWPRA.js';
4
- import { scanProject } from './chunk-EJM47PIF.js';
5
- import './chunk-UYCGP5BS.js';
4
+ import { scanProject } from './chunk-DLHC7QON.js';
5
+ import './chunk-L4KIIKXI.js';
6
6
  import { ETYMD_DIR } from './chunk-P6ATKV2R.js';
7
- import './chunk-YQZDYDAK.js';
7
+ import './chunk-U7BCBJIH.js';
8
8
  import { readText, pathExists } from './chunk-4VPBP6K6.js';
9
9
  import path from 'path';
10
10
  import { promises } from 'fs';
@@ -21,7 +21,6 @@ var STOP_WORDS = new Set(
21
21
  );
22
22
  var WRAP_RE = /^([([{"']*)(.*?)([.,;:!?)\]}"']*)$/;
23
23
  var TRAIL_RE = /[.,;:!?)\]}"']*$/;
24
- var HOSTNAME_RE = /^[a-z0-9-]+(?:\.[a-z0-9-]+)*\.([a-z]{2,})$/i;
25
24
  function promoteBareTokens(text, ctx) {
26
25
  const skips = {
27
26
  bareInvocations: 0,
@@ -209,6 +208,11 @@ async function runPremise(opts) {
209
208
  `${counters.prospectiveSkipped} path(s) sit in create-this prose (the task says to create them) \u2014 forward-looking, not missing; skipped.`
210
209
  );
211
210
  }
211
+ if (counters.fetchedSkipped) {
212
+ disclosures.push(
213
+ `${counters.fetchedSkipped} path(s) are named beside the URL they are fetched from \u2014 another tree, not this repo; skipped, not flagged.`
214
+ );
215
+ }
212
216
  if (counters.placeholderSkipped) {
213
217
  disclosures.push(
214
218
  `${counters.placeholderSkipped} path(s) are naming stand-ins (e.g. \`my-feature\`) rather than real references; skipped.`
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { sweepFleet, loadFleetManifest, FLEET_JSON_SCHEMA } from './chunk-ANXGT7V3.js';
3
- import './chunk-UV3YIBDW.js';
4
- import './chunk-Y4E6ERCM.js';
2
+ import { sweepFleet, loadFleetManifest, FLEET_JSON_SCHEMA } from './chunk-ZCOIZVSM.js';
3
+ import './chunk-KIPD3N77.js';
4
+ import './chunk-EYUW3UJU.js';
5
5
  import './chunk-3E2IPCRY.js';
6
6
  import { print, section, theme, renderFleetNotes } from './chunk-HI7NWPRA.js';
7
- import './chunk-EJM47PIF.js';
8
- import './chunk-UYCGP5BS.js';
7
+ import './chunk-DLHC7QON.js';
8
+ import './chunk-L4KIIKXI.js';
9
9
  import './chunk-P6ATKV2R.js';
10
- import './chunk-YQZDYDAK.js';
10
+ import './chunk-U7BCBJIH.js';
11
11
  import { readText } from './chunk-4VPBP6K6.js';
12
12
  import path from 'path';
13
13
 
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { print, renderFacts, theme } from './chunk-HI7NWPRA.js';
3
- import { scanProject } from './chunk-EJM47PIF.js';
4
- import './chunk-UYCGP5BS.js';
3
+ import { scanProject } from './chunk-DLHC7QON.js';
4
+ import './chunk-L4KIIKXI.js';
5
5
  import { writeCachedFacts } from './chunk-P6ATKV2R.js';
6
- import './chunk-YQZDYDAK.js';
6
+ import './chunk-U7BCBJIH.js';
7
7
  import './chunk-4VPBP6K6.js';
8
8
 
9
9
  // src/commands/scan.ts
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ export { scanProject } from './chunk-DLHC7QON.js';
3
+ import './chunk-L4KIIKXI.js';
4
+ import './chunk-U7BCBJIH.js';
5
+ import './chunk-4VPBP6K6.js';
@@ -6,17 +6,28 @@ import { promises } from 'fs';
6
6
  import path from 'path';
7
7
 
8
8
  var CLASS_DIRECTIVE = /^#[ \t]*class:[ \t]*(secret|vocabulary)\b/i;
9
- var MACHINE_PATH_RE = /\/(?:Users|home)\/[A-Za-z0-9._-]+\//;
9
+ var MACHINE_PATH_RE = /(?:^|[\s"'=(\[:])\/(?:Users|home)\/[A-Za-z0-9._-]+\//;
10
10
  var ALLOW_MARKER = "allow-published-string";
11
11
  var ALLOW_FILE = ".etymd-screen-allow";
12
12
  var LEGACY_ALLOW_FILE = ".artifact-check-allow";
13
- var LABELED_LINE = /^(pattern|reason|date|author)[ \t]+(.*)$/;
13
+ var LABELED_LINE = /^(pattern|generated|reason|date|author)[ \t]+(.*)$/;
14
+ function compileLooseRegex(source) {
15
+ try {
16
+ return new RegExp(source, "i");
17
+ } catch {
18
+ return new RegExp(source.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i");
19
+ }
20
+ }
14
21
  function compileAllow(raw) {
15
- const entries = [];
22
+ const lines = [];
23
+ const generated = [];
16
24
  let current = null;
25
+ let currentGen = null;
17
26
  const close = () => {
18
- if (current) entries.push(current);
27
+ if (current) lines.push(current);
28
+ if (currentGen) generated.push(currentGen);
19
29
  current = null;
30
+ currentGen = null;
20
31
  };
21
32
  for (const rawLine of raw.split("\n")) {
22
33
  const l = rawLine.trim();
@@ -26,29 +37,26 @@ function compileAllow(raw) {
26
37
  const [, label = "", value = ""] = labeled;
27
38
  if (label === "pattern") {
28
39
  close();
29
- current = makeAllowEntry(value);
40
+ current = { pattern: compileLooseRegex(value), isSelfName: false };
41
+ } else if (label === "generated") {
42
+ close();
43
+ currentGen = { path: compileLooseRegex(value) };
30
44
  } else if (current) {
31
45
  if (label === "reason") current.reason = value.trim();
32
46
  else if (label === "date") current.date = value.trim();
33
47
  else current.author = value.trim();
48
+ } else if (currentGen) {
49
+ if (label === "reason") currentGen.reason = value.trim();
50
+ else if (label === "date") currentGen.date = value.trim();
51
+ else currentGen.author = value.trim();
34
52
  }
35
53
  } else {
36
54
  close();
37
- current = makeAllowEntry(l);
55
+ current = { pattern: compileLooseRegex(l), isSelfName: false };
38
56
  }
39
57
  }
40
58
  close();
41
- return entries;
42
- }
43
- function makeAllowEntry(patternStr) {
44
- try {
45
- return { pattern: new RegExp(patternStr, "i"), isSelfName: false };
46
- } catch {
47
- return {
48
- pattern: new RegExp(patternStr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i"),
49
- isSelfName: false
50
- };
51
- }
59
+ return { lines, generated };
52
60
  }
53
61
  function compileOnePattern(l) {
54
62
  try {
@@ -117,11 +125,11 @@ async function selfIdentities(cwd) {
117
125
  names.delete("");
118
126
  return [...names];
119
127
  }
120
- function screenText(text, file, patterns, allow = [], upstreamOwned = false) {
128
+ function screenText(text, file, patterns, allow = [], upstreamOwned = false, generatedData = false) {
121
129
  const classed = patterns.map(
122
130
  (p) => p instanceof RegExp ? { re: p, cls: "secret" } : p
123
131
  );
124
- const active = upstreamOwned ? classed.filter((p) => p.cls === "secret") : classed;
132
+ const active = upstreamOwned || generatedData ? classed.filter((p) => p.cls === "secret") : classed;
125
133
  const hits = [];
126
134
  const lines = text.split("\n");
127
135
  for (const [i, line] of lines.entries()) {
@@ -213,9 +221,9 @@ async function run(opts) {
213
221
  }
214
222
  const usingLegacy = !await pathExists(path.join(opts.cwd, ALLOW_FILE)) && await pathExists(path.join(opts.cwd, LEGACY_ALLOW_FILE));
215
223
  const allowRaw = usingLegacy ? await readText(path.join(opts.cwd, LEGACY_ALLOW_FILE)) : await readText(path.join(opts.cwd, ALLOW_FILE));
216
- const allowEntries = allowRaw ? compileAllow(allowRaw) : [];
224
+ const allowFile = allowRaw ? compileAllow(allowRaw) : { lines: [], generated: [] };
217
225
  const selves = await selfIdentities(opts.cwd);
218
- const validatedEntries = allowEntries.filter((entry) => {
226
+ const validatedEntries = allowFile.lines.filter((entry) => {
219
227
  if (entry.isSelfName) return true;
220
228
  if (isSelfName(entry.pattern, selves)) {
221
229
  entry.isSelfName = true;
@@ -233,6 +241,17 @@ async function run(opts) {
233
241
  return true;
234
242
  });
235
243
  const allow = validatedEntries;
244
+ const generatedPaths = allowFile.generated.filter((entry) => {
245
+ if (entry.reason && entry.date && entry.author) return true;
246
+ print(
247
+ ` ${glyph.partial} ${theme.warn(`Missing provenance for generated-path exemption`)} ${theme.dim(String(entry.path))}`
248
+ );
249
+ print(
250
+ ` ${theme.dim("Format: a `generated` line, then `reason`, `date`, `author` lines \u2014 all four required")}`
251
+ );
252
+ return false;
253
+ });
254
+ const isGenerated = (rel) => generatedPaths.some((e) => e.path.test(rel));
236
255
  const active = selves.length ? patterns.filter((p) => !isSelfName(p.re, selves)) : patterns;
237
256
  const hasVocab = active.some((p) => p.cls === "vocabulary");
238
257
  const upstreamRemote = hasVocab && (opts.scope === "staged" || opts.scope === "tree") ? opts.upstream ?? await git(opts.cwd, ["config", "--get", "etymd.upstream"]) ?? void 0 : void 0;
@@ -263,6 +282,7 @@ async function run(opts) {
263
282
  const hits = [];
264
283
  let scanned = 0;
265
284
  let skippedBinary = 0;
285
+ const generatedExempted = [];
266
286
  if (opts.scope === "message") {
267
287
  const file = opts.target;
268
288
  if (!file) throw new Error("--message needs the message file path (the commit-msg hook's $1)");
@@ -284,7 +304,10 @@ async function run(opts) {
284
304
  continue;
285
305
  }
286
306
  scanned++;
287
- hits.push(...screenText(raw, path.relative(dir, f), active, allow));
307
+ const rel = path.relative(dir, f);
308
+ const gen = isGenerated(rel);
309
+ if (gen) generatedExempted.push(rel);
310
+ hits.push(...screenText(raw, rel, active, allow, false, gen));
288
311
  }
289
312
  } else {
290
313
  const listing = opts.scope === "tree" ? await git(opts.cwd, ["ls-files"]) : await git(opts.cwd, ["diff", "--cached", "--name-only", "--diff-filter=ACM"]);
@@ -300,7 +323,9 @@ async function run(opts) {
300
323
  continue;
301
324
  }
302
325
  scanned++;
303
- hits.push(...screenText(raw, rel, active, allow, isUpstreamOwned(rel)));
326
+ const gen = isGenerated(rel);
327
+ if (gen) generatedExempted.push(rel);
328
+ hits.push(...screenText(raw, rel, active, allow, isUpstreamOwned(rel), gen));
304
329
  }
305
330
  }
306
331
  if (upstreamNote) {
@@ -308,9 +333,16 @@ async function run(opts) {
308
333
  }
309
334
  section(
310
335
  `Content screen ${theme.dim(
311
- `\xB7 ${opts.scope} \xB7 ${scanned} file(s)${skippedBinary ? ` \xB7 ${skippedBinary} binary skipped` : ""}`
336
+ `\xB7 ${opts.scope} \xB7 ${scanned} file(s)${skippedBinary ? ` \xB7 ${skippedBinary} binary skipped` : ""}${generatedExempted.length ? ` \xB7 ${generatedExempted.length} generated exempt` : ""}`
312
337
  )}`
313
338
  );
339
+ if (generatedExempted.length) {
340
+ print(
341
+ ` ${glyph.partial} ${theme.dim(
342
+ `generated data, vocabulary-class dropped (secret-class and machine paths still screened): ${generatedExempted.join(", ")}`
343
+ )}`
344
+ );
345
+ }
314
346
  if (!hits.length) {
315
347
  print(` ${glyph.ok} ${theme.dim("clean")}`);
316
348
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "etymd",
3
- "version": "0.16.0",
3
+ "version": "0.19.0",
4
4
  "description": "Keep your agent instructions true — verify AGENTS.md, CLAUDE.md, rules, skills, and the task you hand an agent against the actual repo, with drift caught over time and a regression ledger.",
5
5
  "keywords": [
6
6
  "cli",
@@ -74,7 +74,7 @@
74
74
  "prettier": "3.4.2",
75
75
  "tsup": "8.5.1",
76
76
  "typescript": "5.7.2",
77
- "vitest": "4.1.10"
77
+ "vitest": "4.1.11"
78
78
  },
79
79
  "publishConfig": {
80
80
  "access": "public"
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- export { run } from './chunk-FKDT4RBP.js';
3
- import './chunk-UV3YIBDW.js';
4
- import './chunk-Y4E6ERCM.js';
5
- import './chunk-3E2IPCRY.js';
6
- import './chunk-HI7NWPRA.js';
7
- import './chunk-EJM47PIF.js';
8
- import './chunk-UYCGP5BS.js';
9
- import './chunk-P6ATKV2R.js';
10
- import './chunk-YQZDYDAK.js';
11
- import './chunk-4VPBP6K6.js';
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- export { scanProject } from './chunk-EJM47PIF.js';
3
- import './chunk-UYCGP5BS.js';
4
- import './chunk-YQZDYDAK.js';
5
- import './chunk-4VPBP6K6.js';