@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,300 @@
1
+ import { existsSync, chmodSync, mkdirSync } from 'node:fs';
2
+ import '../shared/luca.CRmaAfXR.mjs';
3
+ import 'node:fs/promises';
4
+ import 'node:path';
5
+ import { l as loadCurrentConfig } from '../shared/luca.CQ3g1xrD.mjs';
6
+ import 'node:crypto';
7
+ import 'node:module';
8
+ import 'node:url';
9
+ import 'node:child_process';
10
+ import * as p from '@clack/prompts';
11
+ import { defineCommand } from 'citty';
12
+ import { join, basename } from 'pathe';
13
+ import { z } from 'zod';
14
+ import { d as checkMuninndbService, c as resolveMuninndbPort } from '../shared/luca.BYdjkfnz.mjs';
15
+ import 'node:os';
16
+ import '../shared/luca.DTomPq7I.mjs';
17
+ import 'semver';
18
+
19
+ function sanitizeVaultName(name) {
20
+ return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
21
+ }
22
+
23
+ async function detectProjectContext(cwd = process.cwd()) {
24
+ const context = {
25
+ hasPackageJson: false,
26
+ hasGit: false,
27
+ hasLuca: false,
28
+ detectedStack: "unknown",
29
+ hasTypeScript: false,
30
+ projectName: null
31
+ };
32
+ const pkgPath = join(cwd, "package.json");
33
+ try {
34
+ const pkgFile = Bun.file(pkgPath);
35
+ if (await pkgFile.exists()) {
36
+ const pkg = JSON.parse(await pkgFile.text());
37
+ context.hasPackageJson = true;
38
+ context.projectName = typeof pkg.name === "string" ? pkg.name : null;
39
+ context.projectDescription = typeof pkg.description === "string" ? pkg.description : null;
40
+ const deps = {
41
+ ...pkg.dependencies,
42
+ ...pkg.devDependencies
43
+ };
44
+ if (deps["react"] || deps["@types/react"]) {
45
+ context.detectedStack = deps["typescript"] ? "react-ts" : "react";
46
+ } else if (deps["typescript"]) {
47
+ context.detectedStack = "node-ts";
48
+ } else if (context.hasPackageJson) {
49
+ context.detectedStack = "node";
50
+ }
51
+ context.hasTypeScript = !!(deps["typescript"] || await Bun.file(join(cwd, "tsconfig.json")).exists());
52
+ }
53
+ } catch {
54
+ }
55
+ context.hasGit = existsSync(join(cwd, ".git"));
56
+ context.hasLuca = existsSync(join(cwd, ".luca"));
57
+ context.hasExistingSource = existsSync(join(cwd, "src")) || existsSync(join(cwd, "app")) || existsSync(join(cwd, "lib"));
58
+ return context;
59
+ }
60
+
61
+ const DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
62
+ function stripPrototypeKeys(obj) {
63
+ if (obj === null || typeof obj !== "object") {
64
+ return obj;
65
+ }
66
+ if (Array.isArray(obj)) {
67
+ return obj.map(stripPrototypeKeys);
68
+ }
69
+ const cleaned = {};
70
+ for (const key of Object.keys(obj)) {
71
+ if (!DANGEROUS_KEYS.has(key)) {
72
+ cleaned[key] = stripPrototypeKeys(
73
+ obj[key]
74
+ );
75
+ }
76
+ }
77
+ return cleaned;
78
+ }
79
+ function sanitizeJsonParse(json) {
80
+ const parsed = JSON.parse(json);
81
+ return stripPrototypeKeys(parsed);
82
+ }
83
+
84
+ const VaultConfigSchema = z.object({
85
+ /** Sanitized vault name in lowercase kebab-case. */
86
+ vaultName: z.string().min(1),
87
+ /** MuninnDB API key for authentication. */
88
+ apiKey: z.string().min(1)
89
+ });
90
+ const MUNINNDB_WEB_UI_URL = "http://localhost:8477";
91
+ function suggestVaultName(context, cwd = process.cwd()) {
92
+ const raw = context.projectName ?? basename(cwd);
93
+ return sanitizeVaultName(raw);
94
+ }
95
+ async function runVaultWizard(context, cwd = process.cwd()) {
96
+ const suggested = suggestVaultName(context, cwd);
97
+ const serviceStatus = await checkMuninndbService();
98
+ if (!serviceStatus.healthy) {
99
+ p.log.warn(
100
+ "MuninnDB is not running. Vault setup requires MuninnDB to be active."
101
+ );
102
+ p.log.info(
103
+ "Start MuninnDB and run `luca vault:init` to complete vault setup."
104
+ );
105
+ return null;
106
+ }
107
+ p.log.info("MuninnDB Vault Setup");
108
+ p.log.message(
109
+ "MuninnDB provides cognitive memory for Luca -- patterns, decisions, and pitfalls persist across sessions."
110
+ );
111
+ const vaultName = await p.text({
112
+ message: "Vault name",
113
+ placeholder: suggested,
114
+ defaultValue: suggested,
115
+ validate: (value) => {
116
+ const sanitized = sanitizeVaultName(value ?? "");
117
+ if (sanitized.length === 0) {
118
+ return "Vault name must contain at least one alphanumeric character.";
119
+ }
120
+ }
121
+ });
122
+ if (p.isCancel(vaultName)) return null;
123
+ const finalVaultName = sanitizeVaultName(String(vaultName));
124
+ p.note(
125
+ [
126
+ "To generate an API key, open the MuninnDB Web UI:",
127
+ "",
128
+ ` ${MUNINNDB_WEB_UI_URL}`,
129
+ "",
130
+ "Navigate to Settings > API Keys and create a new key.",
131
+ "If MuninnDB is not running, you can set this up later."
132
+ ].join("\n"),
133
+ "API Key"
134
+ );
135
+ const apiKey = await p.password({
136
+ message: "MuninnDB API key (leave empty to skip)"
137
+ });
138
+ if (p.isCancel(apiKey)) return null;
139
+ const trimmedKey = String(apiKey ?? "").trim();
140
+ if (trimmedKey.length === 0) {
141
+ p.log.warn(
142
+ "No API key provided. You can set MUNINN_API_KEY in .env later."
143
+ );
144
+ return null;
145
+ }
146
+ const confirmed = await p.confirm({
147
+ message: `Set vault "${finalVaultName}" with the provided API key?`,
148
+ initialValue: true
149
+ });
150
+ if (p.isCancel(confirmed) || !confirmed) return null;
151
+ const parseResult = VaultConfigSchema.safeParse({
152
+ vaultName: finalVaultName,
153
+ apiKey: trimmedKey
154
+ });
155
+ if (!parseResult.success) {
156
+ p.log.error("Invalid vault configuration. Please try again.");
157
+ return null;
158
+ }
159
+ return parseResult.data;
160
+ }
161
+ async function writeVaultConfig(vaultName, configPath) {
162
+ let config = {};
163
+ const file = Bun.file(configPath);
164
+ if (await file.exists()) {
165
+ try {
166
+ config = sanitizeJsonParse(await file.text());
167
+ } catch {
168
+ }
169
+ }
170
+ const existing = typeof config.muninn === "object" && config.muninn !== null ? config.muninn : {};
171
+ config.muninn = { ...existing, vault: vaultName };
172
+ await Bun.write(configPath, JSON.stringify(config, null, 2) + "\n");
173
+ }
174
+ async function writeApiKeyToEnv(apiKey, envPath, vaultName) {
175
+ const vaultKey = vaultName ? `MUNINN_DB_${vaultName.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_API_KEY` : "MUNINN_DB_API_KEY";
176
+ const envLines = [
177
+ `${vaultKey}=${apiKey}`,
178
+ // Default vault key is always needed for cross-cutting memories
179
+ ...vaultName && vaultName !== "default" ? [`MUNINN_DB_DEFAULT_API_KEY=${apiKey}`] : [],
180
+ `MUNINN_DB_API_KEY=${apiKey}`
181
+ ];
182
+ const file = Bun.file(envPath);
183
+ if (await file.exists()) {
184
+ let content = await file.text();
185
+ for (const envLine of envLines) {
186
+ const keyName = envLine.split("=")[0];
187
+ const lines = content.split("\n");
188
+ const existingIndex = lines.findIndex(
189
+ (line) => line.startsWith(`${keyName}=`)
190
+ );
191
+ if (existingIndex >= 0) {
192
+ lines[existingIndex] = envLine;
193
+ content = lines.join("\n");
194
+ } else {
195
+ const separator = content.endsWith("\n") ? "" : "\n";
196
+ content = content + separator + envLine + "\n";
197
+ }
198
+ }
199
+ await Bun.write(envPath, content);
200
+ } else {
201
+ await Bun.write(envPath, envLines.join("\n") + "\n");
202
+ }
203
+ chmodSync(envPath, 384);
204
+ }
205
+ async function ensureEnvInGitignore(cwd) {
206
+ const gitignorePath = join(cwd, ".gitignore");
207
+ const file = Bun.file(gitignorePath);
208
+ if (await file.exists()) {
209
+ const content = await file.text();
210
+ const lines = content.split("\n").map((l) => l.trim());
211
+ const hasEnvEntry = lines.some((line) => line === ".env");
212
+ if (!hasEnvEntry) {
213
+ const separator = content.endsWith("\n") ? "" : "\n";
214
+ await Bun.write(
215
+ gitignorePath,
216
+ content + separator + "\n# Environment secrets\n.env\n"
217
+ );
218
+ }
219
+ } else {
220
+ await Bun.write(gitignorePath, "# Environment secrets\n.env\n");
221
+ }
222
+ }
223
+ async function verifyVaultConnection(vaultName, port) {
224
+ const resolvedPort = resolveMuninndbPort(port);
225
+ const status = await checkMuninndbService(resolvedPort);
226
+ return status.healthy;
227
+ }
228
+
229
+ const vaultInitCommand = defineCommand({
230
+ meta: {
231
+ name: "vault:init",
232
+ description: "Set up MuninnDB vault for a project"
233
+ },
234
+ args: {
235
+ "skip-vault": {
236
+ type: "boolean",
237
+ description: "Skip MuninnDB vault setup (only create .luca/ directory)",
238
+ default: false
239
+ }
240
+ },
241
+ async run({ args }) {
242
+ p.intro("luca vault:init");
243
+ const cwd = process.cwd();
244
+ const context = await detectProjectContext();
245
+ const configPath = join(cwd, ".luca", "config.json");
246
+ const config = await loadCurrentConfig({ cwd });
247
+ const existingVault = config.muninn?.vault;
248
+ if (typeof existingVault === "string" && existingVault.length > 0) {
249
+ p.log.warn(
250
+ `Vault already configured (muninn.vault = "${existingVault}").`
251
+ );
252
+ p.log.info(
253
+ "To reconfigure, edit the muninn.vault field in .luca/config.json and run again."
254
+ );
255
+ p.outro("Vault setup skipped.");
256
+ return;
257
+ }
258
+ const lucaDir = join(cwd, ".luca");
259
+ if (!existsSync(lucaDir)) {
260
+ mkdirSync(lucaDir, { recursive: true });
261
+ p.log.success("Created .luca/ directory");
262
+ }
263
+ if (args["skip-vault"]) {
264
+ p.log.info("Skipping MuninnDB vault setup (--skip-vault)");
265
+ p.outro(
266
+ "Project directory prepared. Run `luca vault:init` later to set up the vault."
267
+ );
268
+ return;
269
+ }
270
+ const vaultResult = await runVaultWizard(context, cwd);
271
+ if (!vaultResult) {
272
+ p.outro("Vault setup cancelled.");
273
+ return;
274
+ }
275
+ const envPath = join(cwd, ".env");
276
+ await writeVaultConfig(vaultResult.vaultName, configPath);
277
+ p.log.success("Vault name written to .luca/config.json");
278
+ await writeApiKeyToEnv(
279
+ vaultResult.apiKey,
280
+ envPath,
281
+ vaultResult.vaultName
282
+ );
283
+ p.log.success("API key written to .env");
284
+ await ensureEnvInGitignore(cwd);
285
+ p.log.success(".env protected in .gitignore");
286
+ const reachable = await verifyVaultConnection(vaultResult.vaultName);
287
+ if (reachable) {
288
+ p.log.success(
289
+ `MuninnDB reachable \u2014 vault "${vaultResult.vaultName}" is ready`
290
+ );
291
+ } else {
292
+ p.log.warn(
293
+ `MuninnDB not reachable. Vault "${vaultResult.vaultName}" will activate when MuninnDB starts.`
294
+ );
295
+ }
296
+ p.outro('Vault configured! Run `lu "<your task>"` to start the pipeline.');
297
+ }
298
+ });
299
+
300
+ export { vaultInitCommand };
@@ -0,0 +1,95 @@
1
+ import { defineCommand } from 'citty';
2
+ import '../shared/luca.CRmaAfXR.mjs';
3
+ import { l as loadCurrentState, r as resolveActiveSlug } from '../shared/luca.CrXzXueR.mjs';
4
+ import 'node:fs';
5
+ import 'node:fs/promises';
6
+ import 'node:path';
7
+ import 'node:crypto';
8
+ import 'node:module';
9
+ import 'node:url';
10
+ import 'node:child_process';
11
+ import { l as listPhaseSlugs, r as readVerificationResult, a as aggregateVerificationResults } from '../shared/luca.BmhNkYe2.mjs';
12
+ import { l as logger } from '../shared/luca.dM-MKlNE.mjs';
13
+ import 'zod';
14
+ import 'node:os';
15
+ import '../shared/luca.TSMg1t7I.mjs';
16
+ import 'pathe';
17
+ import 'consola';
18
+
19
+ async function resolveSlug(opts) {
20
+ if (opts.explicit) return opts.explicit;
21
+ const state = await loadCurrentState({ cwd: opts.cwd });
22
+ const r = resolveActiveSlug(state);
23
+ if (!r.ok) {
24
+ logger.error(`luca verification: ${r.error}`);
25
+ process.exit(1);
26
+ }
27
+ return r.slug;
28
+ }
29
+ const readCommand = defineCommand({
30
+ meta: {
31
+ name: "read",
32
+ description: "Read a phase's verify.json as JSON (null if no result exists)."
33
+ },
34
+ args: {
35
+ slug: {
36
+ type: "string",
37
+ description: "Phase slug to read (default: the active phase)."
38
+ },
39
+ "run-id": {
40
+ type: "string",
41
+ description: "Current run id. When set, results whose stamped runId does not match are treated as stale and yield null."
42
+ }
43
+ },
44
+ async run({ args }) {
45
+ const cwd = process.cwd();
46
+ const slug = await resolveSlug({ explicit: args.slug, cwd });
47
+ const result = readVerificationResult({
48
+ cwd,
49
+ slug,
50
+ currentRunId: args["run-id"]
51
+ });
52
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
53
+ `);
54
+ }
55
+ });
56
+ const aggregateCommand = defineCommand({
57
+ meta: {
58
+ name: "aggregate",
59
+ description: "Aggregate every phase's verify.json into milestone-level stats (totalWaves, passCount, failCount, stalledCount, allCriteriaMet, blockingGaps from the latest result)."
60
+ },
61
+ args: {
62
+ "run-id": {
63
+ type: "string",
64
+ description: "Current run id; filters out stale per-phase verify.json."
65
+ }
66
+ },
67
+ run({ args }) {
68
+ const cwd = process.cwd();
69
+ const results = [];
70
+ for (const slug of listPhaseSlugs(cwd)) {
71
+ const result = readVerificationResult({
72
+ cwd,
73
+ slug,
74
+ currentRunId: args["run-id"]
75
+ });
76
+ if (result) results.push(result);
77
+ }
78
+ process.stdout.write(
79
+ `${JSON.stringify(aggregateVerificationResults(results), null, 2)}
80
+ `
81
+ );
82
+ }
83
+ });
84
+ const verificationCommand = defineCommand({
85
+ meta: {
86
+ name: "verification",
87
+ description: "Read and aggregate Luca workflow verification results."
88
+ },
89
+ subCommands: {
90
+ read: readCommand,
91
+ aggregate: aggregateCommand
92
+ }
93
+ });
94
+
95
+ export { verificationCommand };
@@ -0,0 +1,70 @@
1
+ import { defineCommand } from 'citty';
2
+ import { l as logger } from '../shared/luca.dM-MKlNE.mjs';
3
+ import { LUCA_VERSION } from '../index.mjs';
4
+ import { a as checkPlatform } from '../shared/luca.DTomPq7I.mjs';
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname, join } from 'pathe';
7
+ import 'consola';
8
+ import 'node:os';
9
+ import '@clack/prompts';
10
+ import 'semver';
11
+ import 'zod';
12
+
13
+ async function checkForUpdates() {
14
+ try {
15
+ const { default: updateNotifier } = await import('update-notifier');
16
+ const currentDir = dirname(fileURLToPath(import.meta.url));
17
+ const possiblePaths = [
18
+ join(currentDir, "..", "..", "package.json"),
19
+ // from dist/utils/
20
+ join(currentDir, "..", "package.json")
21
+ // from src/utils/
22
+ ];
23
+ let pkg = null;
24
+ for (const pkgPath of possiblePaths) {
25
+ try {
26
+ pkg = JSON.parse(await Bun.file(pkgPath).text());
27
+ break;
28
+ } catch {
29
+ }
30
+ }
31
+ if (!pkg) {
32
+ return;
33
+ }
34
+ const notifier = updateNotifier({
35
+ pkg,
36
+ updateCheckInterval: 1e3 * 60 * 60 * 24
37
+ // 24 hours
38
+ });
39
+ notifier.notify({
40
+ message: `New Luca CLI version available: {currentVersion} \u2192 {latestVersion}
41
+ Run: bun add -g @alecsibilia/luca@latest`,
42
+ defer: false
43
+ });
44
+ } catch {
45
+ }
46
+ }
47
+
48
+ const versionCommand = defineCommand({
49
+ meta: {
50
+ name: "version",
51
+ description: "Show Luca version and platform info"
52
+ },
53
+ async run() {
54
+ const platform = checkPlatform();
55
+ logger.box(
56
+ [
57
+ `Luca CLI v${LUCA_VERSION}`,
58
+ "",
59
+ `Platform: ${platform.os} / ${platform.arch}`,
60
+ `Home: ${platform.homeDir}`
61
+ ].join("\n")
62
+ );
63
+ try {
64
+ await checkForUpdates();
65
+ } catch {
66
+ }
67
+ }
68
+ });
69
+
70
+ export { versionCommand };
@@ -0,0 +1,47 @@
1
+ import { defineCommand } from 'citty';
2
+ import 'zod';
3
+ import '../shared/luca.CRmaAfXR.mjs';
4
+ import 'node:fs';
5
+ import 'node:fs/promises';
6
+ import 'node:path';
7
+ import 'node:crypto';
8
+ import 'node:module';
9
+ import 'node:url';
10
+ import 'node:child_process';
11
+ import { r as runWriteHandler, s as lucaWorkflowResetTool } from '../shared/luca.DjDTeDCi.mjs';
12
+ import 'node:os';
13
+ import '../shared/luca.CrXzXueR.mjs';
14
+ import '../shared/luca.HZxBTBgD.mjs';
15
+ import '../shared/luca.TSMg1t7I.mjs';
16
+ import '../shared/luca.CQ3g1xrD.mjs';
17
+ import '../shared/luca.B3Mimc0P.mjs';
18
+
19
+ const resetCommand = defineCommand({
20
+ meta: {
21
+ name: "reset",
22
+ description: "Reset .luca/state.json to schema defaults (idle) and remove the pipeline lock. Destructive but recoverable \u2014 only resets workflow bookkeeping, no source-tree changes. Requires --confirm. Phase-agnostic."
23
+ },
24
+ args: {
25
+ confirm: {
26
+ type: "boolean",
27
+ default: false,
28
+ description: "Must be passed to actually perform the reset. Without it the command refuses so an accidental call cannot wipe the workflow."
29
+ }
30
+ },
31
+ async run({ args }) {
32
+ await runWriteHandler("workflow reset", lucaWorkflowResetTool, {
33
+ confirm: args.confirm
34
+ });
35
+ }
36
+ });
37
+ const workflowCommand = defineCommand({
38
+ meta: {
39
+ name: "workflow",
40
+ description: "Luca workflow lifecycle operations"
41
+ },
42
+ subCommands: {
43
+ reset: resetCommand
44
+ }
45
+ });
46
+
47
+ export { workflowCommand };