@alecsibilia/luca 13.0.0-alpha.9 → 13.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.
Files changed (98) hide show
  1. package/dist/chunks/brain.mjs +77 -0
  2. package/dist/chunks/branch.mjs +11 -7
  3. package/dist/chunks/checks.mjs +11 -7
  4. package/dist/chunks/claim-verify.mjs +15 -410
  5. package/dist/chunks/classify.mjs +2 -2
  6. package/dist/chunks/confidence.mjs +26 -18
  7. package/dist/chunks/config-version-skew.mjs +1 -4
  8. package/dist/chunks/doctor.mjs +7 -1
  9. package/dist/chunks/hook.mjs +72 -15
  10. package/dist/chunks/init.mjs +395 -140
  11. package/dist/chunks/legacy-claude-artifacts.mjs +105 -0
  12. package/dist/chunks/luca-gitignore.mjs +93 -0
  13. package/dist/chunks/muninn-mcp.mjs +13 -10
  14. package/dist/chunks/phase.mjs +15 -9
  15. package/dist/chunks/plan.mjs +51 -0
  16. package/dist/chunks/pr-review.mjs +15 -9
  17. package/dist/chunks/preferences.mjs +13 -8
  18. package/dist/chunks/repair.mjs +1 -1
  19. package/dist/chunks/repo.mjs +15 -15
  20. package/dist/chunks/retro.mjs +7 -6
  21. package/dist/chunks/roadmap.mjs +13 -8
  22. package/dist/chunks/rules.mjs +14 -17
  23. package/dist/chunks/shared-tmp-payloads.mjs +72 -0
  24. package/dist/chunks/state.mjs +17 -10
  25. package/dist/chunks/stray-local-install.mjs +4 -4
  26. package/dist/chunks/telemetry.mjs +260 -55
  27. package/dist/chunks/todo.mjs +99 -20
  28. package/dist/chunks/vault-config-location.mjs +6 -2
  29. package/dist/chunks/vault-init.mjs +10 -229
  30. package/dist/chunks/verification.mjs +12 -8
  31. package/dist/chunks/workflow.mjs +11 -7
  32. package/dist/claude/.claude/agents/architect.md +76 -21
  33. package/dist/claude/.claude/agents/build.md +8 -2
  34. package/dist/claude/.claude/agents/debater.md +7 -0
  35. package/dist/claude/.claude/agents/discuss.md +6 -0
  36. package/dist/claude/.claude/agents/discussion.md +7 -0
  37. package/dist/claude/.claude/agents/execute.md +47 -15
  38. package/dist/claude/.claude/agents/executor.md +44 -6
  39. package/dist/claude/.claude/agents/fast.md +6 -0
  40. package/dist/claude/.claude/agents/finalize.md +121 -42
  41. package/dist/claude/.claude/agents/learner.md +56 -9
  42. package/dist/claude/.claude/agents/plan-reviewer.md +12 -0
  43. package/dist/claude/.claude/agents/plan.md +6 -0
  44. package/dist/claude/.claude/agents/research.md +8 -1
  45. package/dist/claude/.claude/agents/researcher.md +7 -0
  46. package/dist/claude/.claude/agents/review.md +47 -10
  47. package/dist/claude/.claude/agents/reviewer.md +21 -10
  48. package/dist/claude/.claude/agents/shadow-scanner.md +7 -0
  49. package/dist/claude/.claude/agents/test-writer.md +7 -0
  50. package/dist/claude/.claude/agents/triage.md +20 -19
  51. package/dist/claude/.claude/agents/verifier.md +73 -12
  52. package/dist/claude/.claude/commands/lu.md +3 -3
  53. package/dist/claude/.claude/commands/milestone-new.md +1 -1
  54. package/dist/claude/.claude/commands/phase-plan.md +5 -1
  55. package/dist/claude/.claude/commands/repo-cleanup.md +1 -1
  56. package/dist/claude/.claude/commands/todo-check.md +3 -3
  57. package/dist/claude/.claude/hooks/context-refresher.ts +3 -8
  58. package/dist/claude/.claude/hooks/continuation-messages.ts +2 -1
  59. package/dist/claude/.claude/hooks/pipeline-guard.ts +2 -1
  60. package/dist/claude/.claude/luca-statusline.ts +146 -0
  61. package/dist/claude/skills/gh-issue-triage/SKILL.md +7 -7
  62. package/dist/claude/skills/gh-prepare/SKILL.md +1 -1
  63. package/dist/claude/skills/lu/SKILL.md +94 -3
  64. package/dist/claude/skills/luca-telemetry-report/SKILL.md +70 -5
  65. package/dist/claude/skills/luca-write-surface/SKILL.md +11 -0
  66. package/dist/claude/skills/milestone-audit/SKILL.md +10 -10
  67. package/dist/claude/skills/milestone-complete/SKILL.md +11 -7
  68. package/dist/claude/skills/phase-discuss/SKILL.md +2 -2
  69. package/dist/claude/skills/phase-execute/SKILL.md +111 -46
  70. package/dist/claude/skills/phase-plan/SKILL.md +27 -9
  71. package/dist/claude/skills/project-new/SKILL.md +20 -7
  72. package/dist/claude/skills/quick/SKILL.md +4 -4
  73. package/dist/claude/skills/repo-cleanup/SKILL.md +1 -1
  74. package/dist/claude/skills/seed-memory/SKILL.md +42 -35
  75. package/dist/claude/skills/session-pause/SKILL.md +1 -1
  76. package/dist/claude/skills/session-plan/SKILL.md +5 -4
  77. package/dist/claude/skills/session-resume/SKILL.md +41 -5
  78. package/dist/claude/skills/todo-add/SKILL.md +0 -23
  79. package/dist/claude/skills/todo-check/SKILL.md +6 -2
  80. package/dist/index.mjs +6 -4
  81. package/dist/shared/{luca.BHov6l1O.mjs → luca.41dcdyd6.mjs} +33 -19
  82. package/dist/shared/luca.BIOOhA-l.mjs +249 -0
  83. package/dist/shared/{luca.ChU85KSu.mjs → luca.BZc5veeE.mjs} +4 -0
  84. package/dist/shared/luca.Bdq1J16r.mjs +21 -0
  85. package/dist/shared/luca.BpClq9Sj.mjs +9 -0
  86. package/dist/shared/luca.CIYP8N2e.mjs +116 -0
  87. package/dist/shared/{luca.DhWPHlqL.mjs → luca.CWvhdASW.mjs} +667 -69
  88. package/dist/shared/luca.DDb4WHV5.mjs +102 -0
  89. package/dist/shared/{luca.Dtt-5Dh0.mjs → luca.DdFkobL1.mjs} +1 -1
  90. package/dist/shared/{luca.CgZgNO4A.mjs → luca.Dqloj2-2.mjs} +72 -72
  91. package/dist/shared/{luca.Cah4kvKS.mjs → luca.E5xdUA-2.mjs} +862 -153
  92. package/dist/shared/luca.F3D90t11.mjs +52 -0
  93. package/dist/shared/{luca.CvfK4ZaL.mjs → luca.LXh1v8zr.mjs} +3 -60
  94. package/dist/shared/{luca.8yAUP8PG.mjs → luca.X1ymL7m1.mjs} +7 -4
  95. package/dist/shared/luca.raYjreBY.mjs +122 -0
  96. package/package.json +4 -4
  97. package/dist/shared/luca.BiCFpsE9.mjs +0 -57
  98. package/dist/shared/luca.CXJxgzAE.mjs +0 -56
@@ -0,0 +1,77 @@
1
+ import { defineCommand } from 'citty';
2
+ import { r as rejectUnknownFlags, a as runWriteHandler } from '../shared/luca.CIYP8N2e.mjs';
3
+ import 'zod';
4
+ import '../shared/luca.CWvhdASW.mjs';
5
+ import 'node:fs';
6
+ import 'node:fs/promises';
7
+ import 'node:path';
8
+ import 'node:crypto';
9
+ import 'node:module';
10
+ import 'node:url';
11
+ import 'node:child_process';
12
+ import { t as lucaBrainRecallRootTool, u as lucaBrainSetRootTool } from '../shared/luca.E5xdUA-2.mjs';
13
+ import '../shared/luca.LXh1v8zr.mjs';
14
+ import '../shared/luca.raYjreBY.mjs';
15
+ import 'node:os';
16
+ import '../shared/luca.CQ3g1xrD.mjs';
17
+ import '../shared/luca.Dqloj2-2.mjs';
18
+ import '../shared/luca.DdFkobL1.mjs';
19
+ import '../shared/luca.BpClq9Sj.mjs';
20
+ import '../shared/luca.F3D90t11.mjs';
21
+
22
+ const setRootCommand = defineCommand({
23
+ meta: {
24
+ name: "set-root",
25
+ description: "Persist a brain tree root engram ULID (returned by muninn_remember_tree) to .luca/config.json#muninn.brainRoots for the current vault. Local write \u2014 run once per brain tree at creation (project-new / seed-memory) so readers resolve the root by ULID. Phase-agnostic."
26
+ },
27
+ args: {
28
+ concept: {
29
+ type: "string",
30
+ required: true,
31
+ description: 'The brain tree root concept (e.g. "brain:project-identity", "brain:project-requirements").'
32
+ },
33
+ id: {
34
+ type: "string",
35
+ required: true,
36
+ description: "The brain tree root engram ULID (the root_id returned by muninn_remember_tree)."
37
+ }
38
+ },
39
+ async run({ args, rawArgs, cmd }) {
40
+ rejectUnknownFlags("brain set-root", cmd, rawArgs);
41
+ await runWriteHandler("brain set-root", lucaBrainSetRootTool, {
42
+ concept: args.concept,
43
+ id: args.id
44
+ });
45
+ }
46
+ });
47
+ const recallRootCommand = defineCommand({
48
+ meta: {
49
+ name: "recall-root",
50
+ description: 'Emit a muninn_recall_tree procedure for a brain tree, resolved by its cached root ULID (replaces the broken recall_tree(id="brain:project-identity")). Uninitialized \u2192 a plain notice. Phase-agnostic.'
51
+ },
52
+ args: {
53
+ concept: {
54
+ type: "string",
55
+ required: true,
56
+ description: 'The brain tree root concept to recall (e.g. "brain:project-identity").'
57
+ }
58
+ },
59
+ async run({ args, rawArgs, cmd }) {
60
+ rejectUnknownFlags("brain recall-root", cmd, rawArgs);
61
+ await runWriteHandler("brain recall-root", lucaBrainRecallRootTool, {
62
+ concept: args.concept
63
+ });
64
+ }
65
+ });
66
+ const brainCommand = defineCommand({
67
+ meta: {
68
+ name: "brain",
69
+ description: "Resolve MuninnDB brain trees (project identity) by cached root ULID"
70
+ },
71
+ subCommands: {
72
+ "set-root": setRootCommand,
73
+ "recall-root": recallRootCommand
74
+ }
75
+ });
76
+
77
+ export { brainCommand };
@@ -1,6 +1,7 @@
1
1
  import { defineCommand } from 'citty';
2
+ import { r as rejectUnknownFlags, a as runWriteHandler } from '../shared/luca.CIYP8N2e.mjs';
2
3
  import 'zod';
3
- import '../shared/luca.DhWPHlqL.mjs';
4
+ import '../shared/luca.CWvhdASW.mjs';
4
5
  import 'node:fs';
5
6
  import 'node:fs/promises';
6
7
  import 'node:path';
@@ -8,13 +9,15 @@ import 'node:crypto';
8
9
  import 'node:module';
9
10
  import 'node:url';
10
11
  import 'node:child_process';
11
- import { r as runWriteHandler, u as lucaBranchGuardTool } from '../shared/luca.Cah4kvKS.mjs';
12
- import '../shared/luca.CvfK4ZaL.mjs';
12
+ import { A as lucaBranchGuardTool } from '../shared/luca.E5xdUA-2.mjs';
13
+ import '../shared/luca.LXh1v8zr.mjs';
14
+ import '../shared/luca.raYjreBY.mjs';
13
15
  import 'node:os';
14
- import '../shared/luca.BiCFpsE9.mjs';
15
- import '../shared/luca.CgZgNO4A.mjs';
16
- import '../shared/luca.Dtt-5Dh0.mjs';
17
16
  import '../shared/luca.CQ3g1xrD.mjs';
17
+ import '../shared/luca.Dqloj2-2.mjs';
18
+ import '../shared/luca.DdFkobL1.mjs';
19
+ import '../shared/luca.BpClq9Sj.mjs';
20
+ import '../shared/luca.F3D90t11.mjs';
18
21
 
19
22
  const guardCommand = defineCommand({
20
23
  meta: {
@@ -28,7 +31,8 @@ const guardCommand = defineCommand({
28
31
  description: 'Branch name that must NOT equal the current branch (typically the repository default branch, default "main").'
29
32
  }
30
33
  },
31
- async run({ args }) {
34
+ async run({ args, rawArgs, cmd }) {
35
+ rejectUnknownFlags("branch guard", cmd, rawArgs);
32
36
  await runWriteHandler("branch guard", lucaBranchGuardTool, {
33
37
  default_branch: args["default-branch"]
34
38
  });
@@ -1,6 +1,7 @@
1
1
  import { defineCommand } from 'citty';
2
+ import { r as rejectUnknownFlags, b as readJsonPayload, a as runWriteHandler } from '../shared/luca.CIYP8N2e.mjs';
2
3
  import 'zod';
3
- import '../shared/luca.DhWPHlqL.mjs';
4
+ import '../shared/luca.CWvhdASW.mjs';
4
5
  import 'node:fs';
5
6
  import 'node:fs/promises';
6
7
  import 'node:path';
@@ -8,13 +9,15 @@ import 'node:crypto';
8
9
  import 'node:module';
9
10
  import 'node:url';
10
11
  import 'node:child_process';
11
- import { f as readJsonPayload, r as runWriteHandler, t as lucaChecksRunTool } from '../shared/luca.Cah4kvKS.mjs';
12
- import '../shared/luca.CvfK4ZaL.mjs';
12
+ import { z as lucaChecksRunTool } from '../shared/luca.E5xdUA-2.mjs';
13
+ import '../shared/luca.LXh1v8zr.mjs';
14
+ import '../shared/luca.raYjreBY.mjs';
13
15
  import 'node:os';
14
- import '../shared/luca.BiCFpsE9.mjs';
15
- import '../shared/luca.CgZgNO4A.mjs';
16
- import '../shared/luca.Dtt-5Dh0.mjs';
17
16
  import '../shared/luca.CQ3g1xrD.mjs';
17
+ import '../shared/luca.Dqloj2-2.mjs';
18
+ import '../shared/luca.DdFkobL1.mjs';
19
+ import '../shared/luca.BpClq9Sj.mjs';
20
+ import '../shared/luca.F3D90t11.mjs';
18
21
 
19
22
  const runCommand = defineCommand({
20
23
  meta: {
@@ -32,7 +35,8 @@ const runCommand = defineCommand({
32
35
  description: "Per-command timeout in milliseconds (range 100-600000, default 90000). On timeout the process is killed."
33
36
  }
34
37
  },
35
- async run({ args }) {
38
+ async run({ args, rawArgs, cmd }) {
39
+ rejectUnknownFlags("checks run", cmd, rawArgs);
36
40
  const commands = await readJsonPayload("checks run", args.file);
37
41
  await runWriteHandler("checks run", lucaChecksRunTool, {
38
42
  commands,
@@ -1,420 +1,19 @@
1
- import { defineCommand } from 'citty';
2
- import { resolve } from 'pathe';
3
- import '../shared/luca.DhWPHlqL.mjs';
4
- import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
1
+ import { v as verifyFile } from '../shared/luca.CWvhdASW.mjs';
2
+ import 'node:fs';
5
3
  import 'node:fs/promises';
6
- import { join } from 'node:path';
4
+ import 'node:path';
7
5
  import 'node:crypto';
8
6
  import 'node:module';
9
7
  import 'node:url';
10
- import { spawnSync } from 'node:child_process';
8
+ import 'node:child_process';
9
+ import { defineCommand } from 'citty';
10
+ import { resolve } from 'pathe';
11
11
  import { l as logger } from '../shared/luca.dM-MKlNE.mjs';
12
+ import { s as sanitizeControlChars } from '../shared/luca.BpClq9Sj.mjs';
12
13
  import 'zod';
13
14
  import 'node:os';
14
15
  import 'consola';
15
16
 
16
- const SYMBOL_STOPWORDS = /* @__PURE__ */ new Set([
17
- "true",
18
- "false",
19
- "null",
20
- "undefined",
21
- "void",
22
- "any",
23
- "unknown",
24
- "never",
25
- "string",
26
- "number",
27
- "boolean",
28
- "object",
29
- "array",
30
- "bun",
31
- "npm",
32
- "pnpm",
33
- "yarn",
34
- "git",
35
- "gh",
36
- "pr",
37
- "ci",
38
- "cd",
39
- "repo",
40
- "main",
41
- "master",
42
- "dev",
43
- "build",
44
- "test",
45
- "lint",
46
- "tsc",
47
- "eslint",
48
- "todo",
49
- "fixme",
50
- "note",
51
- "warn",
52
- "info",
53
- "log",
54
- "http",
55
- "https",
56
- "json",
57
- "yaml",
58
- "toml",
59
- "env",
60
- "src",
61
- "dist",
62
- "lib",
63
- "app",
64
- "apps"
65
- ]);
66
- const COUNTABLE_NOUNS = /* @__PURE__ */ new Set([
67
- "file",
68
- "files",
69
- "test",
70
- "tests",
71
- "table",
72
- "tables",
73
- "index",
74
- "indexes",
75
- "indices",
76
- "function",
77
- "functions",
78
- "endpoint",
79
- "endpoints",
80
- "route",
81
- "routes",
82
- "mutation",
83
- "mutations",
84
- "query",
85
- "queries",
86
- "action",
87
- "actions",
88
- "tool",
89
- "tools",
90
- "mode",
91
- "modes",
92
- "phase",
93
- "phases",
94
- "wave",
95
- "waves",
96
- "commit",
97
- "commits",
98
- "package",
99
- "packages",
100
- "module",
101
- "modules",
102
- "class",
103
- "classes",
104
- "method",
105
- "methods",
106
- "component",
107
- "components",
108
- "hook",
109
- "hooks"
110
- ]);
111
- function singularize(noun) {
112
- const lower = noun.toLowerCase();
113
- if (lower === "indices") return "index";
114
- if (lower === "queries") return "query";
115
- if (lower === "classes") return "class";
116
- if (lower === "indexes") return "index";
117
- if (lower.endsWith("ies") && lower.length > 3) {
118
- return `${lower.slice(0, -3)}y`;
119
- }
120
- if (lower.endsWith("s") && !lower.endsWith("ss")) return lower.slice(0, -1);
121
- return lower;
122
- }
123
- const BACKTICK_RE = /`([^`\n]+)`/g;
124
- const FILE_PATH_RE = /(?:^|[\s(`'"])((?:packages|src|apps|lib|tests?|docs?|\.planning|\.luca|\.changeset|\.github)\/[\w./-]+\.[\w]+)(?=[\s)`'".,;!?]|$)/g;
125
- const QUANTITATIVE_RE = /\b(\d+)\s+([a-zA-Z]+)\b/g;
126
- const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
127
- function extractClaims(text) {
128
- if (!text) return [];
129
- const lines = text.split("\n");
130
- const seen = /* @__PURE__ */ new Set();
131
- const claims = [];
132
- const pushIfNew = (key, claim) => {
133
- if (seen.has(key)) return;
134
- seen.add(key);
135
- claims.push(claim);
136
- };
137
- for (let i = 0; i < lines.length; i++) {
138
- const line = lines[i] ?? "";
139
- const sourceLine = i + 1;
140
- const sourceContext = line.trim().slice(0, 240);
141
- for (const match of line.matchAll(BACKTICK_RE)) {
142
- const inner = (match[1] ?? "").trim();
143
- if (inner.includes("/") && /\.\w+(?:[#?:]|$)/.test(inner)) continue;
144
- if (!IDENTIFIER_RE.test(inner)) continue;
145
- if (inner.length < 3) continue;
146
- if (SYMBOL_STOPWORDS.has(inner.toLowerCase())) continue;
147
- pushIfNew(`symbol:${inner}`, {
148
- type: "symbol",
149
- text: match[0],
150
- identifier: inner,
151
- sourceLine,
152
- sourceContext
153
- });
154
- }
155
- for (const match of line.matchAll(FILE_PATH_RE)) {
156
- const path = match[1];
157
- if (!path) continue;
158
- pushIfNew(`path:${path}`, {
159
- type: "file-path",
160
- text: path,
161
- path,
162
- sourceLine,
163
- sourceContext
164
- });
165
- }
166
- for (const match of line.matchAll(QUANTITATIVE_RE)) {
167
- const numStr = match[1];
168
- const noun = match[2];
169
- if (!numStr || !noun) continue;
170
- if (numStr.length === 4) {
171
- const n = Number(numStr);
172
- if (n >= 1900 && n <= 2200) continue;
173
- }
174
- const lowerNoun = noun.toLowerCase();
175
- if (!COUNTABLE_NOUNS.has(lowerNoun)) continue;
176
- const num = Number(numStr);
177
- if (!Number.isFinite(num)) continue;
178
- pushIfNew(`count:${num}:${lowerNoun}`, {
179
- type: "quantitative",
180
- text: `${numStr} ${noun}`,
181
- number: num,
182
- noun: singularize(lowerNoun),
183
- sourceLine,
184
- sourceContext
185
- });
186
- }
187
- }
188
- return claims;
189
- }
190
- function gitAvailable(repoRoot) {
191
- const r = spawnSync(
192
- "git",
193
- ["-C", repoRoot, "rev-parse", "--is-inside-work-tree"],
194
- { encoding: "utf-8" }
195
- );
196
- return r.status === 0;
197
- }
198
- function gitGrepFiles(repoRoot, needle, timeoutMs) {
199
- const r = spawnSync(
200
- "git",
201
- ["-C", repoRoot, "grep", "--untracked", "-l", "--fixed-strings", needle],
202
- { encoding: "utf-8", timeout: timeoutMs }
203
- );
204
- if (r.signal === "SIGTERM" || r.error?.message?.includes("ETIMEDOUT")) {
205
- return { ok: false, matchedFiles: [], timedOut: true };
206
- }
207
- if (r.status === 1) return { ok: true, matchedFiles: [], timedOut: false };
208
- if (r.status !== 0) return { ok: false, matchedFiles: [], timedOut: false };
209
- const matchedFiles = (r.stdout ?? "").split("\n").map((s) => s.trim()).filter(Boolean);
210
- return { ok: true, matchedFiles, timedOut: false };
211
- }
212
- const FS_SKIP_DIRS = /* @__PURE__ */ new Set([
213
- "node_modules",
214
- ".git",
215
- "dist",
216
- "build",
217
- ".next",
218
- ".turbo",
219
- "coverage",
220
- ".cache"
221
- ]);
222
- const FS_SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
223
- ".png",
224
- ".jpg",
225
- ".jpeg",
226
- ".gif",
227
- ".webp",
228
- ".ico",
229
- ".pdf",
230
- ".zip",
231
- ".tar",
232
- ".gz",
233
- ".lock",
234
- ".lockb"
235
- ]);
236
- const FS_MAX_FILE_BYTES = 5 * 1024 * 1024;
237
- function fsGrepFiles(repoRoot, needle, timeoutMs) {
238
- const start = Date.now();
239
- const matchedFiles = [];
240
- const stack = [repoRoot];
241
- while (stack.length > 0) {
242
- if (Date.now() - start > timeoutMs) {
243
- return { ok: false, matchedFiles, timedOut: true };
244
- }
245
- const dir = stack.pop();
246
- if (dir === void 0) break;
247
- let entries;
248
- try {
249
- entries = readdirSync(dir);
250
- } catch {
251
- continue;
252
- }
253
- for (const name of entries) {
254
- const full = join(dir, name);
255
- let info;
256
- try {
257
- info = statSync(full);
258
- } catch {
259
- continue;
260
- }
261
- if (info.isDirectory()) {
262
- if (FS_SKIP_DIRS.has(name)) continue;
263
- stack.push(full);
264
- continue;
265
- }
266
- if (!info.isFile()) continue;
267
- const dotIdx = name.lastIndexOf(".");
268
- if (dotIdx >= 0) {
269
- const ext = name.slice(dotIdx).toLowerCase();
270
- if (FS_SKIP_EXTENSIONS.has(ext)) continue;
271
- }
272
- if (info.size > FS_MAX_FILE_BYTES) continue;
273
- try {
274
- if (readFileSync(full).includes(needle)) {
275
- matchedFiles.push(full);
276
- }
277
- } catch {
278
- }
279
- }
280
- }
281
- return { ok: true, matchedFiles, timedOut: false };
282
- }
283
- function searchFiles(repoRoot, needle, timeoutMs, useGit) {
284
- return useGit ? gitGrepFiles(repoRoot, needle, timeoutMs) : fsGrepFiles(repoRoot, needle, timeoutMs);
285
- }
286
- function verifyClaims(claims, opts) {
287
- const totalBudgetMs = opts.totalBudgetMs ?? 3e4;
288
- const perClaimBudgetMs = opts.perClaimBudgetMs ?? 5e3;
289
- const useGit = gitAvailable(opts.repoRoot);
290
- const start = Date.now();
291
- const failures = [];
292
- let timedOut = false;
293
- const breakdown = { symbols: 0, filePaths: 0, quantitative: 0 };
294
- for (const claim of claims) {
295
- if (claim.type === "symbol") breakdown.symbols++;
296
- else if (claim.type === "file-path") breakdown.filePaths++;
297
- else breakdown.quantitative++;
298
- if (Date.now() - start > totalBudgetMs) {
299
- timedOut = true;
300
- failures.push({
301
- claim,
302
- reason: "timeout",
303
- evidence: `Total budget ${totalBudgetMs}ms exhausted before this claim was checked.`
304
- });
305
- continue;
306
- }
307
- if (claim.type === "symbol" && claim.identifier) {
308
- const r = searchFiles(
309
- opts.repoRoot,
310
- claim.identifier,
311
- perClaimBudgetMs,
312
- useGit
313
- );
314
- if (r.timedOut) {
315
- timedOut = true;
316
- failures.push({
317
- claim,
318
- reason: "timeout",
319
- evidence: `Search for symbol "${claim.identifier}" exceeded ${perClaimBudgetMs}ms.`
320
- });
321
- continue;
322
- }
323
- if (!r.ok || r.matchedFiles.length === 0) {
324
- failures.push({
325
- claim,
326
- reason: "symbol-not-found",
327
- evidence: `git grep for "${claim.identifier}" returned 0 hits.`
328
- });
329
- }
330
- continue;
331
- }
332
- if (claim.type === "file-path" && claim.path) {
333
- const abs = join(opts.repoRoot, claim.path);
334
- if (!existsSync(abs)) {
335
- failures.push({
336
- claim,
337
- reason: "file-not-found",
338
- evidence: `Path "${claim.path}" does not exist (resolved to ${abs}).`
339
- });
340
- }
341
- continue;
342
- }
343
- if (claim.type === "quantitative" && claim.noun && typeof claim.number === "number") {
344
- const r = searchFiles(
345
- opts.repoRoot,
346
- claim.noun,
347
- perClaimBudgetMs,
348
- useGit
349
- );
350
- if (r.timedOut) {
351
- timedOut = true;
352
- failures.push({
353
- claim,
354
- reason: "timeout",
355
- evidence: `Search for noun "${claim.noun}" exceeded ${perClaimBudgetMs}ms.`
356
- });
357
- continue;
358
- }
359
- if (!r.ok) {
360
- failures.push({
361
- claim,
362
- reason: "count-mismatch",
363
- evidence: `grep for "${claim.noun}" failed; cannot verify "${claim.text}".`
364
- });
365
- continue;
366
- }
367
- const found = r.matchedFiles.length;
368
- if (Math.abs(found - claim.number) > 1) {
369
- failures.push({
370
- claim,
371
- reason: "count-mismatch",
372
- evidence: `Claim says "${claim.text}", repo has ${found} file(s) mentioning "${claim.noun}" (tolerance \xB11).`
373
- });
374
- }
375
- continue;
376
- }
377
- }
378
- return {
379
- passed: failures.length === 0,
380
- totalClaims: claims.length,
381
- failures,
382
- extractedBreakdown: breakdown,
383
- timedOut
384
- };
385
- }
386
- function verifyTextArtifact(text, opts) {
387
- return verifyClaims(extractClaims(text), opts);
388
- }
389
- function verifyFile(filePath, opts) {
390
- let text;
391
- try {
392
- text = readFileSync(filePath, "utf-8");
393
- } catch (err) {
394
- const msg = err instanceof Error ? err.message : String(err);
395
- return {
396
- passed: false,
397
- totalClaims: 0,
398
- failures: [
399
- {
400
- claim: {
401
- type: "file-path",
402
- text: filePath,
403
- path: filePath,
404
- sourceLine: 0,
405
- sourceContext: ""
406
- },
407
- reason: "artifact-unreadable",
408
- evidence: `Could not read "${filePath}": ${msg}`
409
- }
410
- ],
411
- extractedBreakdown: { symbols: 0, filePaths: 0, quantitative: 0 },
412
- timedOut: false
413
- };
414
- }
415
- return verifyTextArtifact(text, opts);
416
- }
417
-
418
17
  const claimVerifyCommand = defineCommand({
419
18
  meta: {
420
19
  name: "claim-verify",
@@ -446,8 +45,14 @@ const claimVerifyCommand = defineCommand({
446
45
  for (const failure of report.failures) {
447
46
  const loc = failure.claim.sourceLine > 0 ? ` (line ${failure.claim.sourceLine})` : "";
448
47
  logger.error(
449
- `${failure.reason}: ${failure.claim.text}${loc}
450
- ${failure.evidence}`
48
+ `${failure.reason}: ${sanitizeControlChars(failure.claim.text)}${loc}
49
+ ${sanitizeControlChars(failure.evidence)}`
50
+ );
51
+ }
52
+ for (const warning of report.forbiddenLanguage) {
53
+ logger.warn(
54
+ `forbidden-language: "${warning.phrase}" (line ${warning.sourceLine}) has no nearby probe evidence
55
+ ${sanitizeControlChars(warning.sourceContext)}`
451
56
  );
452
57
  }
453
58
  if (report.passed) {
@@ -1,5 +1,4 @@
1
- import { defineCommand } from 'citty';
2
- import { C as ClassifyComplexityInputSchema } from '../shared/luca.DhWPHlqL.mjs';
1
+ import { C as ClassifyComplexityInputSchema } from '../shared/luca.CWvhdASW.mjs';
3
2
  import 'node:fs';
4
3
  import 'node:fs/promises';
5
4
  import 'node:path';
@@ -7,6 +6,7 @@ import 'node:crypto';
7
6
  import 'node:module';
8
7
  import 'node:url';
9
8
  import 'node:child_process';
9
+ import { defineCommand } from 'citty';
10
10
  import { l as logger } from '../shared/luca.dM-MKlNE.mjs';
11
11
  import 'zod';
12
12
  import 'node:os';