@alecsibilia/luca 13.0.0-alpha.1 → 13.0.0-alpha.12

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 (103) hide show
  1. package/dist/chunks/branch.mjs +9 -7
  2. package/dist/chunks/checks.mjs +9 -7
  3. package/dist/chunks/claim-verify.mjs +10 -406
  4. package/dist/chunks/classify.mjs +1 -1
  5. package/dist/chunks/confidence.mjs +88 -12
  6. package/dist/chunks/config-version-skew.mjs +97 -0
  7. package/dist/chunks/doctor.mjs +17 -1
  8. package/dist/chunks/hook.mjs +148 -17
  9. package/dist/chunks/init.mjs +396 -98
  10. package/dist/chunks/legacy-claude-artifacts.mjs +105 -0
  11. package/dist/chunks/legacy-package.mjs +47 -0
  12. package/dist/chunks/luca-gitignore.mjs +93 -0
  13. package/dist/chunks/muninn-mcp.mjs +81 -0
  14. package/dist/chunks/muninndb-health.mjs +1 -1
  15. package/dist/chunks/phase.mjs +33 -9
  16. package/dist/chunks/plan.mjs +49 -0
  17. package/dist/chunks/pr-review.mjs +13 -9
  18. package/dist/chunks/preferences.mjs +11 -8
  19. package/dist/chunks/repair.mjs +1 -1
  20. package/dist/chunks/repo.mjs +9 -7
  21. package/dist/chunks/retro.mjs +5 -5
  22. package/dist/chunks/roadmap.mjs +11 -8
  23. package/dist/chunks/rules.mjs +5 -5
  24. package/dist/chunks/shared-tmp-payloads.mjs +72 -0
  25. package/dist/chunks/stale-global-symlinks.mjs +78 -0
  26. package/dist/chunks/state.mjs +54 -9
  27. package/dist/chunks/stray-local-install.mjs +17 -9
  28. package/dist/chunks/telemetry.mjs +1 -1
  29. package/dist/chunks/todo.mjs +52 -15
  30. package/dist/chunks/vault-config-location.mjs +96 -0
  31. package/dist/chunks/vault-init.mjs +15 -223
  32. package/dist/chunks/verification.mjs +9 -6
  33. package/dist/chunks/workflow.mjs +9 -7
  34. package/dist/claude/.claude/agents/architect.md +99 -10
  35. package/dist/claude/.claude/agents/debater.md +89 -0
  36. package/dist/claude/.claude/agents/discussion.md +14 -26
  37. package/dist/claude/.claude/agents/execute.md +24 -5
  38. package/dist/claude/.claude/agents/executor.md +42 -22
  39. package/dist/claude/.claude/agents/finalize.md +66 -42
  40. package/dist/claude/.claude/agents/learner.md +32 -71
  41. package/dist/claude/.claude/agents/plan-reviewer.md +11 -9
  42. package/dist/claude/.claude/agents/research.md +2 -2
  43. package/dist/claude/.claude/agents/researcher.md +6 -9
  44. package/dist/claude/.claude/agents/review.md +46 -21
  45. package/dist/claude/.claude/agents/reviewer.md +19 -15
  46. package/dist/claude/.claude/agents/shadow-scanner.md +25 -27
  47. package/dist/claude/.claude/agents/test-writer.md +90 -0
  48. package/dist/claude/.claude/agents/triage.md +6 -7
  49. package/dist/claude/.claude/agents/verifier.md +68 -12
  50. package/dist/claude/.claude/commands/gh-pr-address.md +10 -10
  51. package/dist/claude/.claude/commands/lu-review.md +1 -1
  52. package/dist/claude/.claude/commands/lu.md +38 -15
  53. package/dist/claude/.claude/commands/milestone-new.md +4 -4
  54. package/dist/claude/.claude/commands/phase-execute.md +1 -1
  55. package/dist/claude/.claude/commands/phase-plan.md +6 -2
  56. package/dist/claude/.claude/commands/repo-cleanup.md +10 -7
  57. package/dist/claude/.claude/commands/todo-check.md +2 -2
  58. package/dist/claude/.claude/hooks/context-refresher.ts +14046 -252
  59. package/dist/claude/.claude/hooks/continuation-messages.ts +14023 -190
  60. package/dist/claude/.claude/hooks/pipeline-guard.ts +14015 -162
  61. package/dist/claude/.claude/luca-statusline.ts +146 -0
  62. package/dist/claude/skills/caveman/SKILL.md +58 -0
  63. package/dist/claude/skills/gh-issue-triage/SKILL.md +6 -6
  64. package/dist/claude/skills/gh-pr-address/SKILL.md +10 -10
  65. package/dist/claude/skills/gh-prepare/SKILL.md +1 -1
  66. package/dist/claude/skills/lu/SKILL.md +70 -19
  67. package/dist/claude/skills/lu-review/SKILL.md +1 -1
  68. package/dist/claude/skills/luca-init/SKILL.md +2 -2
  69. package/dist/claude/skills/luca-write-surface/SKILL.md +19 -3
  70. package/dist/claude/skills/milestone-audit/SKILL.md +41 -61
  71. package/dist/claude/skills/milestone-complete/SKILL.md +10 -3
  72. package/dist/claude/skills/phase-discuss/SKILL.md +16 -6
  73. package/dist/claude/skills/phase-execute/SKILL.md +93 -86
  74. package/dist/claude/skills/phase-plan/SKILL.md +43 -19
  75. package/dist/claude/skills/phase-research/SKILL.md +9 -1
  76. package/dist/claude/skills/post-init-tour/SKILL.md +1 -1
  77. package/dist/claude/skills/project-new/SKILL.md +21 -79
  78. package/dist/claude/skills/quick/SKILL.md +10 -15
  79. package/dist/claude/skills/repo-audit/SKILL.md +5 -5
  80. package/dist/claude/skills/repo-cleanup/SKILL.md +10 -7
  81. package/dist/claude/skills/session-pause/SKILL.md +1 -1
  82. package/dist/claude/skills/session-plan/SKILL.md +12 -11
  83. package/dist/claude/skills/todo-add/SKILL.md +0 -23
  84. package/dist/claude/skills/workflow-save/SKILL.md +4 -4
  85. package/dist/index.mjs +4 -1
  86. package/dist/shared/luca.1Es9-Gvz.mjs +102 -0
  87. package/dist/shared/{luca.B3saVjJm.mjs → luca.41dcdyd6.mjs} +58 -24
  88. package/dist/shared/{luca.C4gMUoBd.mjs → luca.BSiiWgBd.mjs} +3 -3
  89. package/dist/shared/{luca.DjDTeDCi.mjs → luca.BYM-DeyF.mjs} +479 -142
  90. package/dist/shared/luca.BZc5veeE.mjs +36 -0
  91. package/dist/shared/{luca.BYdjkfnz.mjs → luca.BfjhRHhj.mjs} +1 -1
  92. package/dist/shared/{luca.CRmaAfXR.mjs → luca.CGOV-hrW.mjs} +595 -30
  93. package/dist/shared/luca.CvcFKGbj.mjs +249 -0
  94. package/dist/shared/{luca.TSMg1t7I.mjs → luca.CzglpVsy.mjs} +1 -1
  95. package/dist/shared/{luca.BmhNkYe2.mjs → luca.D5Rk3yXn.mjs} +5 -5
  96. package/dist/shared/luca.DHbTnxmf.mjs +114 -0
  97. package/dist/shared/luca.DQSuoQwr.mjs +118 -0
  98. package/dist/shared/luca.NNGS-NSE.mjs +127 -0
  99. package/dist/shared/{luca.HZxBTBgD.mjs → luca.juAH_s77.mjs} +2 -2
  100. package/package.json +4 -4
  101. package/dist/claude/skills/autopilot/SKILL.md +0 -1299
  102. package/dist/shared/luca.B3Mimc0P.mjs +0 -52
  103. package/dist/shared/luca.CrXzXueR.mjs +0 -57
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import 'zod';
3
- import '../shared/luca.CRmaAfXR.mjs';
3
+ import '../shared/luca.CGOV-hrW.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import 'node:path';
@@ -8,13 +8,14 @@ import 'node:crypto';
8
8
  import 'node:module';
9
9
  import 'node:url';
10
10
  import 'node:child_process';
11
- import { r as runWriteHandler, q as lucaBranchGuardTool } from '../shared/luca.DjDTeDCi.mjs';
11
+ import { t as lucaBranchGuardTool } from '../shared/luca.BYM-DeyF.mjs';
12
+ import '../shared/luca.NNGS-NSE.mjs';
13
+ import { r as rejectUnknownFlags, a as runWriteHandler } from '../shared/luca.DHbTnxmf.mjs';
12
14
  import 'node:os';
13
- import '../shared/luca.CrXzXueR.mjs';
14
- import '../shared/luca.HZxBTBgD.mjs';
15
- import '../shared/luca.TSMg1t7I.mjs';
15
+ import '../shared/luca.DQSuoQwr.mjs';
16
+ import '../shared/luca.juAH_s77.mjs';
17
+ import '../shared/luca.CzglpVsy.mjs';
16
18
  import '../shared/luca.CQ3g1xrD.mjs';
17
- import '../shared/luca.B3Mimc0P.mjs';
18
19
 
19
20
  const guardCommand = defineCommand({
20
21
  meta: {
@@ -28,7 +29,8 @@ const guardCommand = defineCommand({
28
29
  description: 'Branch name that must NOT equal the current branch (typically the repository default branch, default "main").'
29
30
  }
30
31
  },
31
- async run({ args }) {
32
+ async run({ args, rawArgs, cmd }) {
33
+ rejectUnknownFlags("branch guard", cmd, rawArgs);
32
34
  await runWriteHandler("branch guard", lucaBranchGuardTool, {
33
35
  default_branch: args["default-branch"]
34
36
  });
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import 'zod';
3
- import '../shared/luca.CRmaAfXR.mjs';
3
+ import '../shared/luca.CGOV-hrW.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import 'node:path';
@@ -8,13 +8,14 @@ import 'node:crypto';
8
8
  import 'node:module';
9
9
  import 'node:url';
10
10
  import 'node:child_process';
11
- import { c as readJsonPayload, r as runWriteHandler, p as lucaChecksRunTool } from '../shared/luca.DjDTeDCi.mjs';
11
+ import { s as lucaChecksRunTool } from '../shared/luca.BYM-DeyF.mjs';
12
+ import '../shared/luca.NNGS-NSE.mjs';
13
+ import { r as rejectUnknownFlags, b as readJsonPayload, a as runWriteHandler } from '../shared/luca.DHbTnxmf.mjs';
12
14
  import 'node:os';
13
- import '../shared/luca.CrXzXueR.mjs';
14
- import '../shared/luca.HZxBTBgD.mjs';
15
- import '../shared/luca.TSMg1t7I.mjs';
15
+ import '../shared/luca.DQSuoQwr.mjs';
16
+ import '../shared/luca.juAH_s77.mjs';
17
+ import '../shared/luca.CzglpVsy.mjs';
16
18
  import '../shared/luca.CQ3g1xrD.mjs';
17
- import '../shared/luca.B3Mimc0P.mjs';
18
19
 
19
20
  const runCommand = defineCommand({
20
21
  meta: {
@@ -32,7 +33,8 @@ const runCommand = defineCommand({
32
33
  description: "Per-command timeout in milliseconds (range 100-600000, default 90000). On timeout the process is killed."
33
34
  }
34
35
  },
35
- async run({ args }) {
36
+ async run({ args, rawArgs, cmd }) {
37
+ rejectUnknownFlags("checks run", cmd, rawArgs);
36
38
  const commands = await readJsonPayload("checks run", args.file);
37
39
  await runWriteHandler("checks run", lucaChecksRunTool, {
38
40
  commands,
@@ -1,420 +1,18 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import { resolve } from 'pathe';
3
- import '../shared/luca.CRmaAfXR.mjs';
4
- import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
3
+ import { v as verifyFile } from '../shared/luca.CGOV-hrW.mjs';
4
+ import 'node:fs';
5
5
  import 'node:fs/promises';
6
- import { join } from 'node:path';
6
+ import 'node:path';
7
7
  import 'node:crypto';
8
8
  import 'node:module';
9
9
  import 'node:url';
10
- import { spawnSync } from 'node:child_process';
10
+ import 'node:child_process';
11
11
  import { l as logger } from '../shared/luca.dM-MKlNE.mjs';
12
12
  import 'zod';
13
13
  import 'node:os';
14
14
  import 'consola';
15
15
 
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
16
  const claimVerifyCommand = defineCommand({
419
17
  meta: {
420
18
  name: "claim-verify",
@@ -450,6 +48,12 @@ const claimVerifyCommand = defineCommand({
450
48
  ${failure.evidence}`
451
49
  );
452
50
  }
51
+ for (const warning of report.forbiddenLanguage) {
52
+ logger.warn(
53
+ `forbidden-language: "${warning.phrase}" (line ${warning.sourceLine}) has no nearby probe evidence
54
+ ${warning.sourceContext}`
55
+ );
56
+ }
453
57
  if (report.passed) {
454
58
  logger.success("All claims verified.");
455
59
  process.exitCode = 0;
@@ -1,5 +1,5 @@
1
1
  import { defineCommand } from 'citty';
2
- import { C as ClassifyComplexityInputSchema } from '../shared/luca.CRmaAfXR.mjs';
2
+ import { C as ClassifyComplexityInputSchema } from '../shared/luca.CGOV-hrW.mjs';
3
3
  import 'node:fs';
4
4
  import 'node:fs/promises';
5
5
  import 'node:path';
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from 'citty';
2
- import '../shared/luca.CRmaAfXR.mjs';
3
- import { l as loadCurrentState, r as resolveActiveSlug } from '../shared/luca.CrXzXueR.mjs';
2
+ import '../shared/luca.CGOV-hrW.mjs';
3
+ import { l as loadCurrentState, r as resolveActiveSlug } from '../shared/luca.DQSuoQwr.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import 'node:path';
@@ -8,15 +8,47 @@ import 'node:crypto';
8
8
  import 'node:module';
9
9
  import 'node:url';
10
10
  import 'node:child_process';
11
- import { b as renderConfidenceJournalMarkdown, r as readConfidenceJournal, g as getConfidenceSummary } from '../shared/luca.HZxBTBgD.mjs';
11
+ import { r as readConfidenceJournal, b as renderConfidenceJournalMarkdown, g as getConfidenceSummary } from '../shared/luca.juAH_s77.mjs';
12
12
  import { l as logger } from '../shared/luca.dM-MKlNE.mjs';
13
13
  import 'zod';
14
- import { c as readJsonPayload, r as runWriteHandler, t as lucaConfidenceLogTool } from '../shared/luca.DjDTeDCi.mjs';
14
+ import { v as lucaConfidenceLogTool } from '../shared/luca.BYM-DeyF.mjs';
15
+ import '../shared/luca.NNGS-NSE.mjs';
16
+ import { r as rejectUnknownFlags, b as readJsonPayload, a as runWriteHandler } from '../shared/luca.DHbTnxmf.mjs';
15
17
  import 'node:os';
16
- import '../shared/luca.TSMg1t7I.mjs';
18
+ import '../shared/luca.CzglpVsy.mjs';
17
19
  import 'consola';
18
20
  import '../shared/luca.CQ3g1xrD.mjs';
19
- import '../shared/luca.B3Mimc0P.mjs';
21
+
22
+ function selectConfidenceGateActions(entries) {
23
+ const auto = [];
24
+ const research = [];
25
+ const ask = [];
26
+ for (const entry of entries) {
27
+ if (entry.resolution) {
28
+ if (entry.resolution === "auto") auto.push(entry);
29
+ else if (entry.resolution === "research") research.push(entry);
30
+ else if (entry.resolution === "ask") ask.push(entry);
31
+ else {
32
+ entry.resolution;
33
+ ask.push(entry);
34
+ }
35
+ } else if (entry.confidence === "high") {
36
+ auto.push(entry);
37
+ } else if (entry.confidence === "medium") {
38
+ auto.push(entry);
39
+ } else if (entry.confidence === "low" && entry.researchable === true) {
40
+ research.push(entry);
41
+ } else {
42
+ ask.push(entry);
43
+ }
44
+ }
45
+ return {
46
+ auto,
47
+ research,
48
+ ask,
49
+ counts: { auto: auto.length, research: research.length, ask: ask.length }
50
+ };
51
+ }
20
52
 
21
53
  async function resolveSlug(opts) {
22
54
  if (opts.explicit) return opts.explicit;
@@ -87,9 +119,18 @@ const logCommand = defineCommand({
87
119
  "review-hint": {
88
120
  type: "string",
89
121
  description: "Optional. Suggested focus area for a human reviewer."
122
+ },
123
+ researchable: {
124
+ type: "boolean",
125
+ description: "Optional. Set true when the ambiguity is factual and resolvable by automated research; absent/false means human judgment is required (gate routes to ask)."
126
+ },
127
+ resolution: {
128
+ type: "string",
129
+ description: "Optional. Explicit gate routing override: auto | research | ask. Overrides confidence-derived bucketing when set."
90
130
  }
91
131
  },
92
- async run({ args }) {
132
+ async run({ args, rawArgs, cmd }) {
133
+ rejectUnknownFlags("confidence log", cmd, rawArgs);
93
134
  let payload;
94
135
  if (args.file) {
95
136
  const fromFile = await readJsonPayload("confidence log", args.file);
@@ -101,6 +142,13 @@ const logCommand = defineCommand({
101
142
  }
102
143
  payload = fromFile;
103
144
  } else {
145
+ const resolution = args.resolution;
146
+ if (resolution !== void 0 && resolution !== "auto" && resolution !== "research" && resolution !== "ask") {
147
+ logger.error(
148
+ `luca confidence log: --resolution must be one of: auto, research, ask (got "${resolution}"). Run \`luca confidence log --help\` for usage.`
149
+ );
150
+ process.exit(1);
151
+ }
104
152
  payload = {
105
153
  phase: args.phase,
106
154
  wave: args.wave !== void 0 ? Number(args.wave) : void 0,
@@ -112,7 +160,9 @@ const logCommand = defineCommand({
112
160
  reasoning: args.reasoning,
113
161
  risk: args.risk,
114
162
  files: splitCsv(args.files),
115
- ...args["review-hint"] !== void 0 ? { reviewHint: args["review-hint"] } : {}
163
+ ...args["review-hint"] !== void 0 ? { reviewHint: args["review-hint"] } : {},
164
+ ...args.researchable !== void 0 ? { researchable: args.researchable } : {},
165
+ ...resolution !== void 0 ? { resolution } : {}
116
166
  };
117
167
  }
118
168
  await runWriteHandler(
@@ -133,7 +183,8 @@ const readCommand = defineCommand({
133
183
  description: "Phase slug to read (default: the active phase)."
134
184
  }
135
185
  },
136
- async run({ args }) {
186
+ async run({ args, rawArgs, cmd }) {
187
+ rejectUnknownFlags("confidence read", cmd, rawArgs);
137
188
  const cwd = process.cwd();
138
189
  const slug = await resolveSlug({ explicit: args.slug, cwd });
139
190
  const entries = readConfidenceJournal({ cwd, slug });
@@ -152,7 +203,8 @@ const summaryCommand = defineCommand({
152
203
  description: "Phase slug to summarize (default: the active phase)."
153
204
  }
154
205
  },
155
- async run({ args }) {
206
+ async run({ args, rawArgs, cmd }) {
207
+ rejectUnknownFlags("confidence summary", cmd, rawArgs);
156
208
  const cwd = process.cwd();
157
209
  const slug = await resolveSlug({ explicit: args.slug, cwd });
158
210
  const summary = getConfidenceSummary(
@@ -173,7 +225,8 @@ const renderCommand = defineCommand({
173
225
  description: "Phase slug to render (default: the active phase)."
174
226
  }
175
227
  },
176
- async run({ args }) {
228
+ async run({ args, rawArgs, cmd }) {
229
+ rejectUnknownFlags("confidence render", cmd, rawArgs);
177
230
  const cwd = process.cwd();
178
231
  const slug = await resolveSlug({ explicit: args.slug, cwd });
179
232
  const md = renderConfidenceJournalMarkdown(
@@ -183,6 +236,28 @@ const renderCommand = defineCommand({
183
236
  `);
184
237
  }
185
238
  });
239
+ const gateCommand = defineCommand({
240
+ meta: {
241
+ name: "gate",
242
+ description: "Bucket a phase's confidence entries into gate actions and emit a JSON object shaped { auto: [...], research: [...], ask: [...], counts: { auto, research, ask } } to stdout. Each entry lands in exactly one bucket: explicit resolution overrides first, then high/medium \u2192 auto, low+researchable \u2192 research, otherwise \u2192 ask (fail-toward-human)."
243
+ },
244
+ args: {
245
+ slug: {
246
+ type: "string",
247
+ description: "Phase slug (defaults to active phase)."
248
+ }
249
+ },
250
+ async run({ args, rawArgs, cmd }) {
251
+ rejectUnknownFlags("confidence gate", cmd, rawArgs);
252
+ const cwd = process.cwd();
253
+ const slug = await resolveSlug({ explicit: args.slug, cwd });
254
+ const actions = selectConfidenceGateActions(
255
+ readConfidenceJournal({ cwd, slug })
256
+ );
257
+ process.stdout.write(`${JSON.stringify(actions, null, 2)}
258
+ `);
259
+ }
260
+ });
186
261
  const confidenceCommand = defineCommand({
187
262
  meta: {
188
263
  name: "confidence",
@@ -192,7 +267,8 @@ const confidenceCommand = defineCommand({
192
267
  log: logCommand,
193
268
  read: readCommand,
194
269
  summary: summaryCommand,
195
- render: renderCommand
270
+ render: renderCommand,
271
+ gate: gateCommand
196
272
  }
197
273
  });
198
274