@alecsibilia/luca 13.0.0-alpha.3 → 13.0.0-alpha.5

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 (73) hide show
  1. package/dist/chunks/branch.mjs +6 -6
  2. package/dist/chunks/checks.mjs +6 -6
  3. package/dist/chunks/claim-verify.mjs +1 -1
  4. package/dist/chunks/classify.mjs +1 -1
  5. package/dist/chunks/confidence.mjs +6 -6
  6. package/dist/chunks/config-version-skew.mjs +97 -0
  7. package/dist/chunks/doctor.mjs +3 -1
  8. package/dist/chunks/hook.mjs +86 -10
  9. package/dist/chunks/init.mjs +28 -12
  10. package/dist/chunks/phase.mjs +6 -6
  11. package/dist/chunks/pr-review.mjs +6 -6
  12. package/dist/chunks/preferences.mjs +6 -6
  13. package/dist/chunks/repair.mjs +1 -1
  14. package/dist/chunks/repo.mjs +6 -6
  15. package/dist/chunks/retro.mjs +5 -5
  16. package/dist/chunks/roadmap.mjs +6 -6
  17. package/dist/chunks/rules.mjs +5 -5
  18. package/dist/chunks/state.mjs +47 -7
  19. package/dist/chunks/stray-local-install.mjs +1 -1
  20. package/dist/chunks/telemetry.mjs +1 -1
  21. package/dist/chunks/todo.mjs +6 -6
  22. package/dist/chunks/vault-init.mjs +1 -1
  23. package/dist/chunks/verification.mjs +4 -4
  24. package/dist/chunks/workflow.mjs +6 -6
  25. package/dist/claude/.claude/agents/debater.md +88 -0
  26. package/dist/claude/.claude/agents/discussion.md +13 -26
  27. package/dist/claude/.claude/agents/executor.md +6 -18
  28. package/dist/claude/.claude/agents/learner.md +31 -71
  29. package/dist/claude/.claude/agents/plan-reviewer.md +5 -9
  30. package/dist/claude/.claude/agents/researcher.md +5 -9
  31. package/dist/claude/.claude/agents/reviewer.md +18 -15
  32. package/dist/claude/.claude/agents/shadow-scanner.md +23 -26
  33. package/dist/claude/.claude/agents/test-writer.md +89 -0
  34. package/dist/claude/.claude/agents/verifier.md +5 -5
  35. package/dist/claude/.claude/commands/gh-pr-address.md +4 -4
  36. package/dist/claude/.claude/commands/lu-review.md +1 -1
  37. package/dist/claude/.claude/commands/lu.md +5 -5
  38. package/dist/claude/.claude/commands/milestone-new.md +1 -1
  39. package/dist/claude/.claude/commands/phase-execute.md +1 -1
  40. package/dist/claude/.claude/commands/phase-plan.md +1 -1
  41. package/dist/claude/.claude/commands/repo-cleanup.md +8 -5
  42. package/dist/claude/.claude/hooks/context-refresher.ts +1 -0
  43. package/dist/claude/.claude/hooks/continuation-messages.ts +1 -0
  44. package/dist/claude/.claude/hooks/pipeline-guard.ts +1 -0
  45. package/dist/claude/skills/autopilot/SKILL.md +22 -32
  46. package/dist/claude/skills/gh-pr-address/SKILL.md +4 -4
  47. package/dist/claude/skills/lu/SKILL.md +8 -8
  48. package/dist/claude/skills/lu-review/SKILL.md +1 -1
  49. package/dist/claude/skills/luca-write-surface/SKILL.md +8 -3
  50. package/dist/claude/skills/milestone-audit/SKILL.md +41 -61
  51. package/dist/claude/skills/milestone-complete/SKILL.md +1 -1
  52. package/dist/claude/skills/phase-discuss/SKILL.md +5 -5
  53. package/dist/claude/skills/phase-execute/SKILL.md +60 -67
  54. package/dist/claude/skills/phase-plan/SKILL.md +11 -13
  55. package/dist/claude/skills/phase-research/SKILL.md +1 -1
  56. package/dist/claude/skills/post-init-tour/SKILL.md +1 -1
  57. package/dist/claude/skills/project-new/SKILL.md +19 -77
  58. package/dist/claude/skills/quick/SKILL.md +2 -8
  59. package/dist/claude/skills/repo-audit/SKILL.md +5 -5
  60. package/dist/claude/skills/repo-cleanup/SKILL.md +8 -5
  61. package/dist/claude/skills/session-plan/SKILL.md +9 -9
  62. package/dist/claude/skills/workflow-save/SKILL.md +4 -4
  63. package/dist/index.mjs +1 -1
  64. package/dist/shared/{luca.BQXFn5yo.mjs → luca.0ssL9rcP.mjs} +2 -2
  65. package/dist/shared/{luca.pqZahLS5.mjs → luca.B-w_fieK.mjs} +1 -1
  66. package/dist/shared/luca.BTcIy-Mh.mjs +183 -0
  67. package/dist/shared/{luca.BhM9TDAo.mjs → luca.BiHycbFi.mjs} +3 -3
  68. package/dist/shared/{luca.BH2GZl5z.mjs → luca.Cf93m-DM.mjs} +1 -1
  69. package/dist/shared/{luca.CuvqWf4b.mjs → luca.D7w2gjQ5.mjs} +2 -2
  70. package/dist/shared/{luca.DXUcpbIe.mjs → luca.DZxAJmyM.mjs} +28 -2
  71. package/dist/shared/{luca.DykMxS_D.mjs → luca.lLQ2ZIAB.mjs} +159 -100
  72. package/package.json +1 -1
  73. package/dist/shared/luca.Djs7oPPj.mjs +0 -57
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import 'zod';
3
- import '../shared/luca.DXUcpbIe.mjs';
3
+ import '../shared/luca.DZxAJmyM.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import 'node:path';
@@ -8,13 +8,13 @@ import 'node:crypto';
8
8
  import 'node:module';
9
9
  import 'node:url';
10
10
  import 'node:child_process';
11
- import { r as runWriteHandler, t as lucaBranchGuardTool } from '../shared/luca.DykMxS_D.mjs';
11
+ import { r as runWriteHandler, u as lucaBranchGuardTool } from '../shared/luca.lLQ2ZIAB.mjs';
12
+ import '../shared/luca.BTcIy-Mh.mjs';
12
13
  import 'node:os';
13
- import '../shared/luca.BH2GZl5z.mjs';
14
- import '../shared/luca.CuvqWf4b.mjs';
15
- import '../shared/luca.pqZahLS5.mjs';
14
+ import '../shared/luca.Cf93m-DM.mjs';
15
+ import '../shared/luca.D7w2gjQ5.mjs';
16
+ import '../shared/luca.B-w_fieK.mjs';
16
17
  import '../shared/luca.CQ3g1xrD.mjs';
17
- import '../shared/luca.Djs7oPPj.mjs';
18
18
 
19
19
  const guardCommand = defineCommand({
20
20
  meta: {
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import 'zod';
3
- import '../shared/luca.DXUcpbIe.mjs';
3
+ import '../shared/luca.DZxAJmyM.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import 'node:path';
@@ -8,13 +8,13 @@ import 'node:crypto';
8
8
  import 'node:module';
9
9
  import 'node:url';
10
10
  import 'node:child_process';
11
- import { e as readJsonPayload, r as runWriteHandler, s as lucaChecksRunTool } from '../shared/luca.DykMxS_D.mjs';
11
+ import { f as readJsonPayload, r as runWriteHandler, t as lucaChecksRunTool } from '../shared/luca.lLQ2ZIAB.mjs';
12
+ import '../shared/luca.BTcIy-Mh.mjs';
12
13
  import 'node:os';
13
- import '../shared/luca.BH2GZl5z.mjs';
14
- import '../shared/luca.CuvqWf4b.mjs';
15
- import '../shared/luca.pqZahLS5.mjs';
14
+ import '../shared/luca.Cf93m-DM.mjs';
15
+ import '../shared/luca.D7w2gjQ5.mjs';
16
+ import '../shared/luca.B-w_fieK.mjs';
16
17
  import '../shared/luca.CQ3g1xrD.mjs';
17
- import '../shared/luca.Djs7oPPj.mjs';
18
18
 
19
19
  const runCommand = defineCommand({
20
20
  meta: {
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import { resolve } from 'pathe';
3
- import '../shared/luca.DXUcpbIe.mjs';
3
+ import '../shared/luca.DZxAJmyM.mjs';
4
4
  import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import { join } from 'node:path';
@@ -1,5 +1,5 @@
1
1
  import { defineCommand } from 'citty';
2
- import { C as ClassifyComplexityInputSchema } from '../shared/luca.DXUcpbIe.mjs';
2
+ import { C as ClassifyComplexityInputSchema } from '../shared/luca.DZxAJmyM.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.DXUcpbIe.mjs';
3
- import { l as loadCurrentState, r as resolveActiveSlug } from '../shared/luca.BH2GZl5z.mjs';
2
+ import '../shared/luca.DZxAJmyM.mjs';
3
+ import { l as loadCurrentState, r as resolveActiveSlug } from '../shared/luca.Cf93m-DM.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import 'node:path';
@@ -8,15 +8,15 @@ 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.CuvqWf4b.mjs';
11
+ import { b as renderConfidenceJournalMarkdown, r as readConfidenceJournal, g as getConfidenceSummary } from '../shared/luca.D7w2gjQ5.mjs';
12
12
  import { l as logger } from '../shared/luca.dM-MKlNE.mjs';
13
13
  import 'zod';
14
- import { e as readJsonPayload, r as runWriteHandler, v as lucaConfidenceLogTool } from '../shared/luca.DykMxS_D.mjs';
14
+ import { f as readJsonPayload, r as runWriteHandler, w as lucaConfidenceLogTool } from '../shared/luca.lLQ2ZIAB.mjs';
15
+ import '../shared/luca.BTcIy-Mh.mjs';
15
16
  import 'node:os';
16
- import '../shared/luca.pqZahLS5.mjs';
17
+ import '../shared/luca.B-w_fieK.mjs';
17
18
  import 'consola';
18
19
  import '../shared/luca.CQ3g1xrD.mjs';
19
- import '../shared/luca.Djs7oPPj.mjs';
20
20
 
21
21
  async function resolveSlug(opts) {
22
22
  if (opts.explicit) return opts.explicit;
@@ -0,0 +1,97 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { writeFile, readFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { LUCA_VERSION } from '../index.mjs';
5
+ import 'citty';
6
+ import 'pathe';
7
+
8
+ const CHECK_NAME = "Config version skew";
9
+ function configPath() {
10
+ return join(process.cwd(), ".luca", "config.json");
11
+ }
12
+ function isDevVersion(v) {
13
+ return v === "0.0.0-dev" || v.includes("dev");
14
+ }
15
+ async function readConfig() {
16
+ const p = configPath();
17
+ if (!existsSync(p)) return null;
18
+ try {
19
+ const raw = JSON.parse(await readFile(p, "utf-8"));
20
+ return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+ const configVersionSkewCheck = {
26
+ name: CHECK_NAME,
27
+ scope: "project",
28
+ async run() {
29
+ const pass = (message) => ({
30
+ name: CHECK_NAME,
31
+ status: "pass",
32
+ message,
33
+ fixCommand: null,
34
+ details: null
35
+ });
36
+ if (isDevVersion(LUCA_VERSION)) {
37
+ return pass("dev CLI build \u2014 version skew not checked");
38
+ }
39
+ const config = await readConfig();
40
+ if (config === null) {
41
+ return pass("no .luca/config.json to check");
42
+ }
43
+ const stored = config.lucaVersion;
44
+ if (typeof stored !== "string" || stored.length === 0) {
45
+ return pass("config.json has no lucaVersion to compare");
46
+ }
47
+ if (stored === LUCA_VERSION) {
48
+ return pass(`config.lucaVersion matches CLI (${LUCA_VERSION})`);
49
+ }
50
+ return {
51
+ name: CHECK_NAME,
52
+ status: "warning",
53
+ message: `config.lucaVersion ${stored} \u2260 installed CLI ${LUCA_VERSION}`,
54
+ fixCommand: "luca doctor --fix",
55
+ details: [
56
+ `.luca/config.json records lucaVersion="${stored}" but the installed CLI is ${LUCA_VERSION}.`,
57
+ `Skills/agents that branch on lucaVersion may behave as if on the older version.`,
58
+ `Run \`luca doctor --fix\` to reconcile (rewrites lucaVersion, preserves all other config keys).`
59
+ ].join("\n ")
60
+ };
61
+ },
62
+ async fix() {
63
+ if (isDevVersion(LUCA_VERSION)) {
64
+ return { applied: [], errors: [] };
65
+ }
66
+ const config = await readConfig();
67
+ if (config === null) {
68
+ return { applied: [], errors: [] };
69
+ }
70
+ const stored = config.lucaVersion;
71
+ if (stored === LUCA_VERSION) {
72
+ return { applied: [], errors: [] };
73
+ }
74
+ try {
75
+ const next = { ...config, lucaVersion: LUCA_VERSION };
76
+ await writeFile(
77
+ configPath(),
78
+ JSON.stringify(next, null, 2) + "\n"
79
+ );
80
+ return {
81
+ applied: [
82
+ `Reconciled .luca/config.json lucaVersion ${typeof stored === "string" ? stored : "unset"} \u2192 ${LUCA_VERSION}`
83
+ ],
84
+ errors: []
85
+ };
86
+ } catch (err) {
87
+ return {
88
+ applied: [],
89
+ errors: [
90
+ `Failed to rewrite .luca/config.json lucaVersion: ${err.message}`
91
+ ]
92
+ };
93
+ }
94
+ }
95
+ };
96
+
97
+ export { configVersionSkewCheck };
@@ -12,6 +12,7 @@ async function executeDoctor(options = {}) {
12
12
  const { staleGlobalSymlinksCheck } = await import('./stale-global-symlinks.mjs');
13
13
  const { legacyPackageCheck } = await import('./legacy-package.mjs');
14
14
  const { strayLocalInstallCheck } = await import('./stray-local-install.mjs');
15
+ const { configVersionSkewCheck } = await import('./config-version-skew.mjs');
15
16
  const allChecks = [
16
17
  // Prerequisites
17
18
  bunRuntimeCheck,
@@ -22,7 +23,8 @@ async function executeDoctor(options = {}) {
22
23
  staleGlobalSymlinksCheck,
23
24
  legacyPackageCheck,
24
25
  // Project (cwd-dependent)
25
- strayLocalInstallCheck
26
+ strayLocalInstallCheck,
27
+ configVersionSkewCheck
26
28
  ];
27
29
  const checks = scope ? allChecks.filter((check) => check.scope === scope) : allChecks;
28
30
  if (checks.length === 0) {
@@ -1,11 +1,11 @@
1
1
  import { defineCommand } from 'citty';
2
- import { c as coarsePhaseOf, a as classifyWritePath, A as AUDIT_PATH_PATTERN, W as WAVE_FILE_RE } from '../shared/luca.DXUcpbIe.mjs';
3
- import { l as loadCurrentState, r as resolveActiveSlug } from '../shared/luca.BH2GZl5z.mjs';
2
+ import { c as coarsePhaseOf, t as toLucaRelative, a as classifyWritePath, A as AUDIT_PATH_PATTERN, W as WAVE_FILE_RE } from '../shared/luca.DZxAJmyM.mjs';
3
+ import { l as loadCurrentState, r as resolveActiveSlug } from '../shared/luca.Cf93m-DM.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:fs/promises';
6
- import { isAbsolute, relative } from 'node:path';
7
- import { S as STEP_ARTIFACTS } from '../shared/luca.Djs7oPPj.mjs';
8
- import { p as phasePathFor } from '../shared/luca.pqZahLS5.mjs';
6
+ import 'node:path';
7
+ import { l as lucaStateClaimOwnerTool, S as STEP_ARTIFACTS } from '../shared/luca.BTcIy-Mh.mjs';
8
+ import { p as phasePathFor } from '../shared/luca.B-w_fieK.mjs';
9
9
  import 'node:crypto';
10
10
  import 'node:module';
11
11
  import 'node:url';
@@ -94,7 +94,29 @@ const READONLY_COMMANDS = /* @__PURE__ */ new Set([
94
94
  "echo",
95
95
  "printf",
96
96
  "true",
97
- "false"
97
+ "false",
98
+ // Read-only text filters. These read stdin/files and write to stdout —
99
+ // they never mutate files on their own. Omitting them made any pipeline
100
+ // through a filter (e.g. `find . | sort`) promote to bash-mutate via the
101
+ // unknown-command fallback, blocking read-only inspection in restrictive
102
+ // phases (v13 run report, M1). `tee`/`xargs` are deliberately EXCLUDED:
103
+ // `tee` writes files and `xargs` runs an arbitrary (possibly mutating)
104
+ // command, so they stay conservative.
105
+ "sort",
106
+ "uniq",
107
+ "cut",
108
+ "tr",
109
+ "comm",
110
+ "diff",
111
+ "jq",
112
+ "rg",
113
+ "column",
114
+ "nl",
115
+ "tac",
116
+ "rev",
117
+ "paste",
118
+ "fold",
119
+ "join"
98
120
  ]);
99
121
  const GIT_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set([
100
122
  "status",
@@ -191,17 +213,28 @@ const PKG_MUTATE_PATTERNS = [
191
213
  ["pnpm", "add"]
192
214
  ];
193
215
  const ALWAYS_DENIED_COMMANDS = /* @__PURE__ */ new Set(["eval", "source", "."]);
216
+ const LUCA_TOPLEVEL_READ = /* @__PURE__ */ new Set(["version", "telemetry", "rules"]);
217
+ const LUCA_TOPLEVEL_WRITE = /* @__PURE__ */ new Set([
218
+ "init",
219
+ "vault:init",
220
+ "retro",
221
+ "claim-verify",
222
+ "classify",
223
+ "doctor",
224
+ "repair"
225
+ ]);
194
226
  const LUCA_READ_VERBS = /* @__PURE__ */ new Set([
195
227
  "read",
196
228
  "current",
197
229
  "list",
198
230
  "guard",
231
+ "aggregate",
199
232
  "filter-stale",
200
233
  "detect-convergence",
201
234
  "regression-check"
202
235
  ]);
203
236
  const LUCA_NOUN_VERBS = {
204
- state: /* @__PURE__ */ new Set(["read", "advance"]),
237
+ state: /* @__PURE__ */ new Set(["read", "advance", "claim-owner", "set-current-phase"]),
205
238
  phase: /* @__PURE__ */ new Set(["current", "advance", "archive"]),
206
239
  roadmap: /* @__PURE__ */ new Set(["read", "create"]),
207
240
  preferences: /* @__PURE__ */ new Set(["read", "write"]),
@@ -215,13 +248,22 @@ const LUCA_NOUN_VERBS = {
215
248
  checks: /* @__PURE__ */ new Set(["run"]),
216
249
  branch: /* @__PURE__ */ new Set(["guard"]),
217
250
  workflow: /* @__PURE__ */ new Set(["reset"]),
218
- confidence: /* @__PURE__ */ new Set(["log"])
251
+ confidence: /* @__PURE__ */ new Set(["log"]),
252
+ // Read-side surfaces over the per-phase verify.json files.
253
+ verification: /* @__PURE__ */ new Set(["read", "aggregate"])
219
254
  };
220
255
  function classifyLucaCommand(rest) {
256
+ if (rest.some((t) => ["--help", "-h", "--version"].includes(t))) {
257
+ return "bash-readonly";
258
+ }
221
259
  const noun = rest.find((t) => !t.startsWith("-"));
222
260
  if (!noun) return "bash-readonly";
223
261
  const verbs = LUCA_NOUN_VERBS[noun];
224
- if (!verbs) return void 0;
262
+ if (!verbs) {
263
+ if (LUCA_TOPLEVEL_READ.has(noun)) return "bash-readonly";
264
+ if (LUCA_TOPLEVEL_WRITE.has(noun)) return "luca-write";
265
+ return void 0;
266
+ }
225
267
  const afterNoun = rest.slice(rest.indexOf(noun) + 1);
226
268
  const verb = afterNoun.find((t) => !t.startsWith("-"));
227
269
  if (!verb || !verbs.has(verb)) {
@@ -493,9 +535,21 @@ async function handleStageGateHook(opts) {
493
535
  }
494
536
  const toolName = parsed.tool_name ?? parsed.toolName;
495
537
  const toolInput = parsed.tool_input ?? parsed.toolInput;
538
+ const sessionId = parsed.session_id ?? parsed.sessionId;
496
539
  const cwd = opts.cwd ?? process.cwd();
497
540
  const homedir = opts.homedir ?? process.env.HOME;
498
541
  const state = await loadCurrentState({ cwd });
542
+ if (toolName === "Bash" && sessionId && state.ownerSessionId !== sessionId && isStateAdvanceCommand(
543
+ toolInput?.command ?? ""
544
+ )) {
545
+ try {
546
+ await lucaStateClaimOwnerTool.handler({ sessionId }, { cwd });
547
+ state.ownerSessionId = sessionId;
548
+ } catch (err) {
549
+ log(`stage-gate: owner stamp skipped (${err.message})`);
550
+ }
551
+ }
552
+ const isBystander = Boolean(state.ownerSessionId) && Boolean(sessionId) && state.ownerSessionId !== sessionId;
499
553
  const phase = coarsePhaseOf(state.pipelineStep);
500
554
  if (phase === "IDLE") {
501
555
  log(
@@ -511,7 +565,7 @@ async function handleStageGateHook(opts) {
511
565
  log(`stage-gate: ${toolName} without file_path \u2014 allowing`);
512
566
  return { exitCode: 0, toolName, toolInput, decision: "allow" };
513
567
  }
514
- const relTarget = isAbsolute(targetPath) ? relative(cwd, targetPath) : targetPath;
568
+ const relTarget = toLucaRelative(targetPath, cwd);
515
569
  const pc = classifyWritePath(targetPath, { homedir, cwd });
516
570
  if (pc.class === "denied") {
517
571
  pathBlockReason = `${toolName} to '${targetPath}' is always denied: ${pc.reason ?? "forbidden path"}`;
@@ -573,6 +627,12 @@ async function handleStageGateHook(opts) {
573
627
  log("stage-gate: could not classify tool \u2014 allowing");
574
628
  return { exitCode: 0, toolName, toolInput, decision: "allow" };
575
629
  }
630
+ if (isBystander) {
631
+ log(
632
+ `stage-gate: session ${sessionId} is not the run owner (${state.ownerSessionId}) \u2014 exempting ${toolName} (category=${category}) from the phase=${phase} matrix`
633
+ );
634
+ return { exitCode: 0, toolName, toolInput, decision: "allow" };
635
+ }
576
636
  const allowed = isToolAllowed({ phase, category });
577
637
  if (!allowed) {
578
638
  const msg = `stage-gate BLOCK: ${toolName} (category=${category}) is not allowed in phase=${phase} (pipelineStep=${state.pipelineStep})`;
@@ -590,6 +650,22 @@ async function handleStageGateHook(opts) {
590
650
  );
591
651
  return { exitCode: 0, toolName, toolInput, decision: "allow" };
592
652
  }
653
+ function isStateAdvanceCommand(command) {
654
+ if (!command.includes("advance")) return false;
655
+ let entries;
656
+ try {
657
+ entries = parse(command);
658
+ } catch {
659
+ return false;
660
+ }
661
+ const toks = entries.filter((e) => typeof e === "string");
662
+ for (let i = 0; i <= toks.length - 3; i += 1) {
663
+ if (toks[i] === "luca" && toks[i + 1] === "state" && toks[i + 2] === "advance") {
664
+ return true;
665
+ }
666
+ }
667
+ return false;
668
+ }
593
669
  const FIXED_PHASE_FILE_ARTIFACTS = /* @__PURE__ */ new Set([
594
670
  "research",
595
671
  "context",
@@ -1,9 +1,9 @@
1
1
  import * as p from '@clack/prompts';
2
2
  import { defineCommand, runMain } from 'citty';
3
3
  import { existsSync, chmodSync } from 'node:fs';
4
- import { mkdir, writeFile, readFile, readdir, copyFile } from 'node:fs/promises';
4
+ import { mkdir, readFile, writeFile, readdir, copyFile } from 'node:fs/promises';
5
5
  import { join, dirname, delimiter } from 'node:path';
6
- import { l as lucaStateSchema } from '../shared/luca.DXUcpbIe.mjs';
6
+ import { l as lucaStateSchema } from '../shared/luca.DZxAJmyM.mjs';
7
7
  import 'node:crypto';
8
8
  import 'node:module';
9
9
  import 'node:url';
@@ -25,16 +25,21 @@ async function writeProjectSkeleton(opts) {
25
25
  });
26
26
  const lucaDir = join(opts.cwd, ".luca");
27
27
  await mkdir(lucaDir, { recursive: true });
28
- await writeIfMissing({
29
- path: join(lucaDir, "state.json"),
30
- contents: JSON.stringify(
31
- lucaStateSchema.parse({ sessionId: generateRunId() }),
32
- null,
33
- 2
34
- ) + "\n",
35
- force: opts.force ?? false,
36
- log
37
- });
28
+ const statePath = join(lucaDir, "state.json");
29
+ if (existsSync(statePath) && await isActiveState(statePath)) {
30
+ log(` skip: ${statePath} (active workflow \u2014 refusing to overwrite)`);
31
+ } else {
32
+ await writeIfMissing({
33
+ path: statePath,
34
+ contents: JSON.stringify(
35
+ lucaStateSchema.parse({ sessionId: generateRunId() }),
36
+ null,
37
+ 2
38
+ ) + "\n",
39
+ force: opts.force ?? false,
40
+ log
41
+ });
42
+ }
38
43
  await writeIfMissing({
39
44
  path: join(lucaDir, "config.json"),
40
45
  contents: JSON.stringify(
@@ -50,6 +55,17 @@ async function writeProjectSkeleton(opts) {
50
55
  log
51
56
  });
52
57
  }
58
+ async function isActiveState(statePath) {
59
+ try {
60
+ const raw = JSON.parse(await readFile(statePath, "utf-8"));
61
+ const step = raw.pipelineStep;
62
+ const roadmap = raw.roadmap;
63
+ const currentPhase = raw.currentPhase;
64
+ return typeof step === "string" && step !== "idle" || Array.isArray(roadmap) && roadmap.length > 0 || typeof currentPhase === "number" && currentPhase > 0;
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
53
69
  async function writeIfMissing(args) {
54
70
  if (existsSync(args.path) && !args.force) {
55
71
  args.log(` skip: ${args.path} (already exists)`);
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import 'zod';
3
- import '../shared/luca.DXUcpbIe.mjs';
3
+ import '../shared/luca.DZxAJmyM.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import 'node:path';
@@ -8,13 +8,13 @@ import 'node:crypto';
8
8
  import 'node:module';
9
9
  import 'node:url';
10
10
  import 'node:child_process';
11
- import { r as runWriteHandler, b as lucaPhaseCurrentTool, c as lucaPhaseAdvanceTool, d as lucaPhaseArchiveTool } from '../shared/luca.DykMxS_D.mjs';
11
+ import { r as runWriteHandler, c as lucaPhaseCurrentTool, d as lucaPhaseAdvanceTool, e as lucaPhaseArchiveTool } from '../shared/luca.lLQ2ZIAB.mjs';
12
+ import '../shared/luca.BTcIy-Mh.mjs';
12
13
  import 'node:os';
13
- import '../shared/luca.BH2GZl5z.mjs';
14
- import '../shared/luca.CuvqWf4b.mjs';
15
- import '../shared/luca.pqZahLS5.mjs';
14
+ import '../shared/luca.Cf93m-DM.mjs';
15
+ import '../shared/luca.D7w2gjQ5.mjs';
16
+ import '../shared/luca.B-w_fieK.mjs';
16
17
  import '../shared/luca.CQ3g1xrD.mjs';
17
- import '../shared/luca.Djs7oPPj.mjs';
18
18
 
19
19
  const currentCommand = defineCommand({
20
20
  meta: {
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import 'zod';
3
- import '../shared/luca.DXUcpbIe.mjs';
3
+ import '../shared/luca.DZxAJmyM.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import 'node:path';
@@ -8,13 +8,13 @@ import 'node:crypto';
8
8
  import 'node:module';
9
9
  import 'node:url';
10
10
  import 'node:child_process';
11
- import { e as readJsonPayload, r as runWriteHandler, n as lucaPrReviewDetectConvergenceTool, o as lucaPrReviewFilterStaleTool, p as lucaPrReviewRegressionCheckTool } from '../shared/luca.DykMxS_D.mjs';
11
+ import { f as readJsonPayload, r as runWriteHandler, o as lucaPrReviewDetectConvergenceTool, p as lucaPrReviewFilterStaleTool, q as lucaPrReviewRegressionCheckTool } from '../shared/luca.lLQ2ZIAB.mjs';
12
+ import '../shared/luca.BTcIy-Mh.mjs';
12
13
  import 'node:os';
13
- import '../shared/luca.BH2GZl5z.mjs';
14
- import '../shared/luca.CuvqWf4b.mjs';
15
- import '../shared/luca.pqZahLS5.mjs';
14
+ import '../shared/luca.Cf93m-DM.mjs';
15
+ import '../shared/luca.D7w2gjQ5.mjs';
16
+ import '../shared/luca.B-w_fieK.mjs';
16
17
  import '../shared/luca.CQ3g1xrD.mjs';
17
- import '../shared/luca.Djs7oPPj.mjs';
18
18
 
19
19
  const filterStaleCommand = defineCommand({
20
20
  meta: {
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import 'zod';
3
- import '../shared/luca.DXUcpbIe.mjs';
3
+ import '../shared/luca.DZxAJmyM.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import 'node:path';
@@ -8,13 +8,13 @@ import 'node:crypto';
8
8
  import 'node:module';
9
9
  import 'node:url';
10
10
  import 'node:child_process';
11
- import { e as readJsonPayload, r as runWriteHandler, h as lucaPreferencesReadTool, i as lucaPreferencesWriteTool } from '../shared/luca.DykMxS_D.mjs';
11
+ import { f as readJsonPayload, r as runWriteHandler, i as lucaPreferencesReadTool, j as lucaPreferencesWriteTool } from '../shared/luca.lLQ2ZIAB.mjs';
12
+ import '../shared/luca.BTcIy-Mh.mjs';
12
13
  import 'node:os';
13
- import '../shared/luca.BH2GZl5z.mjs';
14
- import '../shared/luca.CuvqWf4b.mjs';
15
- import '../shared/luca.pqZahLS5.mjs';
14
+ import '../shared/luca.Cf93m-DM.mjs';
15
+ import '../shared/luca.D7w2gjQ5.mjs';
16
+ import '../shared/luca.B-w_fieK.mjs';
16
17
  import '../shared/luca.CQ3g1xrD.mjs';
17
- import '../shared/luca.Djs7oPPj.mjs';
18
18
 
19
19
  const readCommand = defineCommand({
20
20
  meta: {
@@ -2,7 +2,7 @@ import { defineCommand } from 'citty';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { readFile, rm } from 'node:fs/promises';
4
4
  import { join } from 'node:path';
5
- import { l as lucaStateSchema } from '../shared/luca.DXUcpbIe.mjs';
5
+ import { l as lucaStateSchema } from '../shared/luca.DZxAJmyM.mjs';
6
6
  import 'node:crypto';
7
7
  import 'node:module';
8
8
  import 'node:url';
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import 'zod';
3
- import '../shared/luca.DXUcpbIe.mjs';
3
+ import '../shared/luca.DZxAJmyM.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import 'node:path';
@@ -8,13 +8,13 @@ import 'node:crypto';
8
8
  import 'node:module';
9
9
  import 'node:url';
10
10
  import 'node:child_process';
11
- import { e as readJsonPayload, r as runWriteHandler, q as lucaRepoCleanupApplyTool } from '../shared/luca.DykMxS_D.mjs';
11
+ import { f as readJsonPayload, r as runWriteHandler, s as lucaRepoCleanupApplyTool } from '../shared/luca.lLQ2ZIAB.mjs';
12
+ import '../shared/luca.BTcIy-Mh.mjs';
12
13
  import 'node:os';
13
- import '../shared/luca.BH2GZl5z.mjs';
14
- import '../shared/luca.CuvqWf4b.mjs';
15
- import '../shared/luca.pqZahLS5.mjs';
14
+ import '../shared/luca.Cf93m-DM.mjs';
15
+ import '../shared/luca.D7w2gjQ5.mjs';
16
+ import '../shared/luca.B-w_fieK.mjs';
16
17
  import '../shared/luca.CQ3g1xrD.mjs';
17
- import '../shared/luca.Djs7oPPj.mjs';
18
18
 
19
19
  const cleanupApplyCommand = defineCommand({
20
20
  meta: {
@@ -1,5 +1,5 @@
1
1
  import { defineCommand } from 'citty';
2
- import '../shared/luca.DXUcpbIe.mjs';
2
+ import '../shared/luca.DZxAJmyM.mjs';
3
3
  import 'node:fs';
4
4
  import 'node:fs/promises';
5
5
  import 'node:path';
@@ -7,13 +7,13 @@ import 'node:crypto';
7
7
  import 'node:module';
8
8
  import 'node:url';
9
9
  import 'node:child_process';
10
- import { a as analyzeRun, g as gatherRunArtifacts, r as renderPostmortemMarkdown, c as computePostmortemExitCode } from '../shared/luca.BhM9TDAo.mjs';
11
- import { l as listRuns } from '../shared/luca.CuvqWf4b.mjs';
10
+ import { a as analyzeRun, g as gatherRunArtifacts, r as renderPostmortemMarkdown, c as computePostmortemExitCode } from '../shared/luca.BiHycbFi.mjs';
11
+ import { l as listRuns } from '../shared/luca.D7w2gjQ5.mjs';
12
12
  import { l as logger } from '../shared/luca.dM-MKlNE.mjs';
13
13
  import 'zod';
14
14
  import 'node:os';
15
- import '../shared/luca.BQXFn5yo.mjs';
16
- import '../shared/luca.pqZahLS5.mjs';
15
+ import '../shared/luca.0ssL9rcP.mjs';
16
+ import '../shared/luca.B-w_fieK.mjs';
17
17
  import 'pathe';
18
18
  import 'consola';
19
19
 
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import 'zod';
3
- import '../shared/luca.DXUcpbIe.mjs';
3
+ import '../shared/luca.DZxAJmyM.mjs';
4
4
  import 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import 'node:path';
@@ -8,13 +8,13 @@ import 'node:crypto';
8
8
  import 'node:module';
9
9
  import 'node:url';
10
10
  import 'node:child_process';
11
- import { e as readJsonPayload, r as runWriteHandler, f as lucaRoadmapReadTool, g as lucaRoadmapCreateTool } from '../shared/luca.DykMxS_D.mjs';
11
+ import { f as readJsonPayload, r as runWriteHandler, g as lucaRoadmapReadTool, h as lucaRoadmapCreateTool } from '../shared/luca.lLQ2ZIAB.mjs';
12
+ import '../shared/luca.BTcIy-Mh.mjs';
12
13
  import 'node:os';
13
- import '../shared/luca.BH2GZl5z.mjs';
14
- import '../shared/luca.CuvqWf4b.mjs';
15
- import '../shared/luca.pqZahLS5.mjs';
14
+ import '../shared/luca.Cf93m-DM.mjs';
15
+ import '../shared/luca.D7w2gjQ5.mjs';
16
+ import '../shared/luca.B-w_fieK.mjs';
16
17
  import '../shared/luca.CQ3g1xrD.mjs';
17
- import '../shared/luca.Djs7oPPj.mjs';
18
18
 
19
19
  const readCommand = defineCommand({
20
20
  meta: {
@@ -1,6 +1,6 @@
1
1
  import { defineCommand } from 'citty';
2
2
  import { join as join$1 } from 'pathe';
3
- import '../shared/luca.DXUcpbIe.mjs';
3
+ import '../shared/luca.DZxAJmyM.mjs';
4
4
  import { existsSync, readdirSync, statSync, readFileSync } from 'node:fs';
5
5
  import 'node:fs/promises';
6
6
  import { isAbsolute, resolve, join, extname } from 'node:path';
@@ -8,13 +8,13 @@ import 'node:crypto';
8
8
  import { createRequire } from 'node:module';
9
9
  import { pathToFileURL } from 'node:url';
10
10
  import 'node:child_process';
11
- import { a as analyzeRun, g as gatherRunArtifacts } from '../shared/luca.BhM9TDAo.mjs';
12
- import { l as listRuns } from '../shared/luca.CuvqWf4b.mjs';
11
+ import { a as analyzeRun, g as gatherRunArtifacts } from '../shared/luca.BiHycbFi.mjs';
12
+ import { l as listRuns } from '../shared/luca.D7w2gjQ5.mjs';
13
13
  import { l as logger } from '../shared/luca.dM-MKlNE.mjs';
14
14
  import 'zod';
15
15
  import 'node:os';
16
- import '../shared/luca.BQXFn5yo.mjs';
17
- import '../shared/luca.pqZahLS5.mjs';
16
+ import '../shared/luca.0ssL9rcP.mjs';
17
+ import '../shared/luca.B-w_fieK.mjs';
18
18
  import 'consola';
19
19
 
20
20
  let tsModuleCache = void 0;