@alecsibilia/luca 13.0.0-alpha.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.
Files changed (128) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +47 -0
  3. package/bin/luca.js +3 -0
  4. package/dist/chunks/branch.mjs +47 -0
  5. package/dist/chunks/bun-runtime.mjs +46 -0
  6. package/dist/chunks/checks.mjs +53 -0
  7. package/dist/chunks/claim-verify.mjs +465 -0
  8. package/dist/chunks/classify.mjs +105 -0
  9. package/dist/chunks/confidence.mjs +199 -0
  10. package/dist/chunks/doctor.mjs +158 -0
  11. package/dist/chunks/hook.mjs +696 -0
  12. package/dist/chunks/init.mjs +715 -0
  13. package/dist/chunks/muninndb-health.mjs +66 -0
  14. package/dist/chunks/phase.mjs +38 -0
  15. package/dist/chunks/pr-review.mjs +122 -0
  16. package/dist/chunks/preferences.mjs +61 -0
  17. package/dist/chunks/repair.mjs +111 -0
  18. package/dist/chunks/repo.mjs +58 -0
  19. package/dist/chunks/retro.mjs +86 -0
  20. package/dist/chunks/roadmap.mjs +58 -0
  21. package/dist/chunks/rules.mjs +527 -0
  22. package/dist/chunks/stale-mcp-server.mjs +90 -0
  23. package/dist/chunks/state.mjs +57 -0
  24. package/dist/chunks/stray-local-install.mjs +200 -0
  25. package/dist/chunks/telemetry.mjs +165 -0
  26. package/dist/chunks/todo.mjs +151 -0
  27. package/dist/chunks/vault-init.mjs +300 -0
  28. package/dist/chunks/verification.mjs +95 -0
  29. package/dist/chunks/version.mjs +70 -0
  30. package/dist/chunks/workflow.mjs +47 -0
  31. package/dist/claude/.claude/agents/architect.md +410 -0
  32. package/dist/claude/.claude/agents/build.md +111 -0
  33. package/dist/claude/.claude/agents/discuss.md +93 -0
  34. package/dist/claude/.claude/agents/discussion.md +149 -0
  35. package/dist/claude/.claude/agents/execute.md +416 -0
  36. package/dist/claude/.claude/agents/executor.md +161 -0
  37. package/dist/claude/.claude/agents/fast.md +84 -0
  38. package/dist/claude/.claude/agents/finalize.md +484 -0
  39. package/dist/claude/.claude/agents/learner.md +160 -0
  40. package/dist/claude/.claude/agents/plan-reviewer.md +129 -0
  41. package/dist/claude/.claude/agents/plan.md +96 -0
  42. package/dist/claude/.claude/agents/research.md +327 -0
  43. package/dist/claude/.claude/agents/researcher.md +78 -0
  44. package/dist/claude/.claude/agents/review.md +283 -0
  45. package/dist/claude/.claude/agents/reviewer.md +163 -0
  46. package/dist/claude/.claude/agents/shadow-scanner.md +257 -0
  47. package/dist/claude/.claude/agents/triage.md +230 -0
  48. package/dist/claude/.claude/agents/verifier.md +131 -0
  49. package/dist/claude/.claude/commands/bug-diagnose.md +12 -0
  50. package/dist/claude/.claude/commands/gh-issue-triage.md +14 -0
  51. package/dist/claude/.claude/commands/gh-pr-address.md +235 -0
  52. package/dist/claude/.claude/commands/gh-prepare.md +12 -0
  53. package/dist/claude/.claude/commands/grill-me.md +12 -0
  54. package/dist/claude/.claude/commands/lu-review.md +51 -0
  55. package/dist/claude/.claude/commands/lu.md +75 -0
  56. package/dist/claude/.claude/commands/luca-init.md +14 -0
  57. package/dist/claude/.claude/commands/luca-telemetry-report.md +12 -0
  58. package/dist/claude/.claude/commands/memory-audit.md +12 -0
  59. package/dist/claude/.claude/commands/milestone-new.md +122 -0
  60. package/dist/claude/.claude/commands/phase-discuss.md +45 -0
  61. package/dist/claude/.claude/commands/phase-execute.md +39 -0
  62. package/dist/claude/.claude/commands/phase-plan.md +53 -0
  63. package/dist/claude/.claude/commands/repo-cleanup.md +80 -0
  64. package/dist/claude/.claude/commands/todo-add.md +28 -0
  65. package/dist/claude/.claude/commands/todo-check.md +36 -0
  66. package/dist/claude/.claude/hooks/context-refresher.ts +285 -0
  67. package/dist/claude/.claude/hooks/continuation-messages.ts +215 -0
  68. package/dist/claude/.claude/hooks/pipeline-guard.ts +182 -0
  69. package/dist/claude/.claude/settings.json +41 -0
  70. package/dist/claude/skills/arch-audit/SKILL.md +161 -0
  71. package/dist/claude/skills/autopilot/SKILL.md +1299 -0
  72. package/dist/claude/skills/bug-diagnose/SKILL.md +102 -0
  73. package/dist/claude/skills/choose/SKILL.md +124 -0
  74. package/dist/claude/skills/gh-issue-triage/SKILL.md +97 -0
  75. package/dist/claude/skills/gh-pr-address/SKILL.md +235 -0
  76. package/dist/claude/skills/gh-prepare/SKILL.md +209 -0
  77. package/dist/claude/skills/grill-me/SKILL.md +46 -0
  78. package/dist/claude/skills/lu/SKILL.md +112 -0
  79. package/dist/claude/skills/lu-review/SKILL.md +51 -0
  80. package/dist/claude/skills/luca-init/SKILL.md +91 -0
  81. package/dist/claude/skills/luca-telemetry-report/SKILL.md +145 -0
  82. package/dist/claude/skills/luca-write-surface/SKILL.md +213 -0
  83. package/dist/claude/skills/memory-audit/SKILL.md +217 -0
  84. package/dist/claude/skills/milestone-audit/SKILL.md +545 -0
  85. package/dist/claude/skills/milestone-complete/SKILL.md +168 -0
  86. package/dist/claude/skills/milestone-gaps/SKILL.md +60 -0
  87. package/dist/claude/skills/milestone-new/SKILL.md +125 -0
  88. package/dist/claude/skills/note/SKILL.md +162 -0
  89. package/dist/claude/skills/phase-add/SKILL.md +91 -0
  90. package/dist/claude/skills/phase-assumptions/SKILL.md +92 -0
  91. package/dist/claude/skills/phase-discuss/SKILL.md +165 -0
  92. package/dist/claude/skills/phase-execute/SKILL.md +1786 -0
  93. package/dist/claude/skills/phase-insert/SKILL.md +100 -0
  94. package/dist/claude/skills/phase-plan/SKILL.md +461 -0
  95. package/dist/claude/skills/phase-remove/SKILL.md +113 -0
  96. package/dist/claude/skills/phase-research/SKILL.md +80 -0
  97. package/dist/claude/skills/post-init-tour/SKILL.md +58 -0
  98. package/dist/claude/skills/progress/SKILL.md +271 -0
  99. package/dist/claude/skills/project-new/SKILL.md +609 -0
  100. package/dist/claude/skills/quick/SKILL.md +256 -0
  101. package/dist/claude/skills/rename-audit/SKILL.md +52 -0
  102. package/dist/claude/skills/repo-audit/SKILL.md +88 -0
  103. package/dist/claude/skills/repo-cleanup/SKILL.md +80 -0
  104. package/dist/claude/skills/seed-memory/SKILL.md +235 -0
  105. package/dist/claude/skills/session-pause/SKILL.md +126 -0
  106. package/dist/claude/skills/session-plan/SKILL.md +112 -0
  107. package/dist/claude/skills/session-resume/SKILL.md +75 -0
  108. package/dist/claude/skills/todo-add/SKILL.md +85 -0
  109. package/dist/claude/skills/todo-check/SKILL.md +77 -0
  110. package/dist/claude/skills/workflow-save/SKILL.md +277 -0
  111. package/dist/index.d.mts +33 -0
  112. package/dist/index.d.ts +33 -0
  113. package/dist/index.mjs +69 -0
  114. package/dist/shared/luca.B3Mimc0P.mjs +52 -0
  115. package/dist/shared/luca.B3saVjJm.mjs +163 -0
  116. package/dist/shared/luca.BYdjkfnz.mjs +217 -0
  117. package/dist/shared/luca.BmhNkYe2.mjs +56 -0
  118. package/dist/shared/luca.C4gMUoBd.mjs +358 -0
  119. package/dist/shared/luca.CQ3g1xrD.mjs +19 -0
  120. package/dist/shared/luca.CRmaAfXR.mjs +713 -0
  121. package/dist/shared/luca.CrXzXueR.mjs +57 -0
  122. package/dist/shared/luca.DTomPq7I.mjs +91 -0
  123. package/dist/shared/luca.DjDTeDCi.mjs +1904 -0
  124. package/dist/shared/luca.HZxBTBgD.mjs +201 -0
  125. package/dist/shared/luca.TSMg1t7I.mjs +10 -0
  126. package/dist/shared/luca.dM-MKlNE.mjs +25 -0
  127. package/dist/shared/luca.naWEcQ4B.mjs +7 -0
  128. package/package.json +76 -0
@@ -0,0 +1,465 @@
1
+ import { defineCommand } from 'citty';
2
+ import { resolve } from 'pathe';
3
+ import '../shared/luca.CRmaAfXR.mjs';
4
+ import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
5
+ import 'node:fs/promises';
6
+ import { join } from 'node:path';
7
+ import 'node:crypto';
8
+ import 'node:module';
9
+ import 'node:url';
10
+ import { spawnSync } from 'node:child_process';
11
+ import { l as logger } from '../shared/luca.dM-MKlNE.mjs';
12
+ import 'zod';
13
+ import 'node:os';
14
+ import 'consola';
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
+ const claimVerifyCommand = defineCommand({
419
+ meta: {
420
+ name: "claim-verify",
421
+ description: "Verify that a text artifact cites symbols, file paths and counts that exist in the working tree."
422
+ },
423
+ args: {
424
+ file: {
425
+ type: "positional",
426
+ required: true,
427
+ description: "Path to the text artifact to verify (changeset, PR body, plan)."
428
+ },
429
+ "repo-root": {
430
+ type: "string",
431
+ description: "Repo root that claims are resolved against (default: cwd)."
432
+ }
433
+ },
434
+ run({ args }) {
435
+ const repoRoot = args["repo-root"] ? resolve(args["repo-root"]) : process.cwd();
436
+ const report = verifyFile(resolve(args.file), { repoRoot });
437
+ const b = report.extractedBreakdown;
438
+ logger.info(
439
+ `Verified ${report.totalClaims} claim(s): ${b.symbols} symbol, ${b.filePaths} path, ${b.quantitative} quantitative.`
440
+ );
441
+ if (report.timedOut) {
442
+ logger.warn(
443
+ "Time budget exhausted \u2014 some claims were not verified."
444
+ );
445
+ }
446
+ for (const failure of report.failures) {
447
+ const loc = failure.claim.sourceLine > 0 ? ` (line ${failure.claim.sourceLine})` : "";
448
+ logger.error(
449
+ `${failure.reason}: ${failure.claim.text}${loc}
450
+ ${failure.evidence}`
451
+ );
452
+ }
453
+ if (report.passed) {
454
+ logger.success("All claims verified.");
455
+ process.exitCode = 0;
456
+ } else {
457
+ logger.error(
458
+ `${report.failures.length} claim(s) failed verification.`
459
+ );
460
+ process.exitCode = 1;
461
+ }
462
+ }
463
+ });
464
+
465
+ export { claimVerifyCommand };
@@ -0,0 +1,105 @@
1
+ import { defineCommand } from 'citty';
2
+ import { C as ClassifyComplexityInputSchema } from '../shared/luca.CRmaAfXR.mjs';
3
+ import 'node:fs';
4
+ import 'node:fs/promises';
5
+ import 'node:path';
6
+ import 'node:crypto';
7
+ import 'node:module';
8
+ import 'node:url';
9
+ import 'node:child_process';
10
+ import { l as logger } from '../shared/luca.dM-MKlNE.mjs';
11
+ import 'zod';
12
+ import 'node:os';
13
+ import 'consola';
14
+
15
+ function classifyComplexity(input) {
16
+ const {
17
+ taskDescription,
18
+ estimatedFileCount,
19
+ crossCuttingConcerns,
20
+ hasBreakingChanges,
21
+ affectedDomains
22
+ } = ClassifyComplexityInputSchema.parse(input);
23
+ let score = 0;
24
+ if (estimatedFileCount <= 2) score += 0;
25
+ else if (estimatedFileCount <= 5) score += 1;
26
+ else if (estimatedFileCount <= 15) score += 2;
27
+ else if (estimatedFileCount <= 30) score += 3;
28
+ else score += 4;
29
+ score += Math.min(crossCuttingConcerns.length, 4);
30
+ if (hasBreakingChanges) score += 2;
31
+ if (affectedDomains.length > 3) score += 2;
32
+ else if (affectedDomains.length > 1) score += 1;
33
+ const desc = taskDescription.toLowerCase();
34
+ if (desc.includes("refactor") || desc.includes("migration")) score += 1;
35
+ if (desc.includes("security") || desc.includes("auth")) score += 1;
36
+ if (desc.includes("database") || desc.includes("schema")) score += 1;
37
+ const complexity = score <= 1 ? "TRIVIAL" : score <= 3 ? "SIMPLE" : score <= 6 ? "MODERATE" : score <= 9 ? "COMPLEX" : "CRITICAL";
38
+ const fileScope = estimatedFileCount <= 3 ? "small" : estimatedFileCount <= 15 ? "medium" : "large";
39
+ const dependencyDepth = crossCuttingConcerns.length <= 1 ? "shallow" : crossCuttingConcerns.length <= 3 ? "moderate" : "deep";
40
+ const riskLevel = hasBreakingChanges || score > 6 ? "high" : score > 3 ? "medium" : "low";
41
+ return {
42
+ complexity,
43
+ reasoning: `Score ${score}: ${estimatedFileCount} files, ${crossCuttingConcerns.length} cross-cutting concerns, ${affectedDomains.length} domains${hasBreakingChanges ? ", has breaking changes" : ""}`,
44
+ factors: { fileScope, dependencyDepth, riskLevel }
45
+ };
46
+ }
47
+
48
+ function splitList(value) {
49
+ if (!value) return [];
50
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
51
+ }
52
+ const classifyCommand = defineCommand({
53
+ meta: {
54
+ name: "classify",
55
+ description: "Classify a development task\u2019s complexity (TRIVIAL \u2192 CRITICAL)."
56
+ },
57
+ args: {
58
+ task: {
59
+ type: "string",
60
+ required: true,
61
+ description: "Task description."
62
+ },
63
+ files: {
64
+ type: "string",
65
+ description: "Estimated number of files affected."
66
+ },
67
+ concerns: {
68
+ type: "string",
69
+ description: "Comma-separated cross-cutting concerns."
70
+ },
71
+ domains: {
72
+ type: "string",
73
+ description: "Comma-separated affected architectural domains."
74
+ },
75
+ breaking: {
76
+ type: "boolean",
77
+ description: "The task introduces breaking changes."
78
+ },
79
+ json: {
80
+ type: "boolean",
81
+ description: "Emit the full ComplexityResult as JSON."
82
+ }
83
+ },
84
+ run({ args }) {
85
+ const result = classifyComplexity({
86
+ taskDescription: args.task,
87
+ estimatedFileCount: args.files !== void 0 ? Number(args.files) : void 0,
88
+ crossCuttingConcerns: splitList(args.concerns),
89
+ hasBreakingChanges: args.breaking,
90
+ affectedDomains: splitList(args.domains)
91
+ });
92
+ if (args.json) {
93
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
94
+ `);
95
+ return;
96
+ }
97
+ logger.info(`Complexity: ${result.complexity}`);
98
+ logger.info(result.reasoning);
99
+ logger.info(
100
+ `Factors: fileScope=${result.factors.fileScope}, dependencyDepth=${result.factors.dependencyDepth}, riskLevel=${result.factors.riskLevel}`
101
+ );
102
+ }
103
+ });
104
+
105
+ export { classifyCommand };