@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.
- package/LICENSE +201 -0
- package/README.md +47 -0
- package/bin/luca.js +3 -0
- package/dist/chunks/branch.mjs +47 -0
- package/dist/chunks/bun-runtime.mjs +46 -0
- package/dist/chunks/checks.mjs +53 -0
- package/dist/chunks/claim-verify.mjs +465 -0
- package/dist/chunks/classify.mjs +105 -0
- package/dist/chunks/confidence.mjs +199 -0
- package/dist/chunks/doctor.mjs +158 -0
- package/dist/chunks/hook.mjs +696 -0
- package/dist/chunks/init.mjs +715 -0
- package/dist/chunks/muninndb-health.mjs +66 -0
- package/dist/chunks/phase.mjs +38 -0
- package/dist/chunks/pr-review.mjs +122 -0
- package/dist/chunks/preferences.mjs +61 -0
- package/dist/chunks/repair.mjs +111 -0
- package/dist/chunks/repo.mjs +58 -0
- package/dist/chunks/retro.mjs +86 -0
- package/dist/chunks/roadmap.mjs +58 -0
- package/dist/chunks/rules.mjs +527 -0
- package/dist/chunks/stale-mcp-server.mjs +90 -0
- package/dist/chunks/state.mjs +57 -0
- package/dist/chunks/stray-local-install.mjs +200 -0
- package/dist/chunks/telemetry.mjs +165 -0
- package/dist/chunks/todo.mjs +151 -0
- package/dist/chunks/vault-init.mjs +300 -0
- package/dist/chunks/verification.mjs +95 -0
- package/dist/chunks/version.mjs +70 -0
- package/dist/chunks/workflow.mjs +47 -0
- package/dist/claude/.claude/agents/architect.md +410 -0
- package/dist/claude/.claude/agents/build.md +111 -0
- package/dist/claude/.claude/agents/discuss.md +93 -0
- package/dist/claude/.claude/agents/discussion.md +149 -0
- package/dist/claude/.claude/agents/execute.md +416 -0
- package/dist/claude/.claude/agents/executor.md +161 -0
- package/dist/claude/.claude/agents/fast.md +84 -0
- package/dist/claude/.claude/agents/finalize.md +484 -0
- package/dist/claude/.claude/agents/learner.md +160 -0
- package/dist/claude/.claude/agents/plan-reviewer.md +129 -0
- package/dist/claude/.claude/agents/plan.md +96 -0
- package/dist/claude/.claude/agents/research.md +327 -0
- package/dist/claude/.claude/agents/researcher.md +78 -0
- package/dist/claude/.claude/agents/review.md +283 -0
- package/dist/claude/.claude/agents/reviewer.md +163 -0
- package/dist/claude/.claude/agents/shadow-scanner.md +257 -0
- package/dist/claude/.claude/agents/triage.md +230 -0
- package/dist/claude/.claude/agents/verifier.md +131 -0
- package/dist/claude/.claude/commands/bug-diagnose.md +12 -0
- package/dist/claude/.claude/commands/gh-issue-triage.md +14 -0
- package/dist/claude/.claude/commands/gh-pr-address.md +235 -0
- package/dist/claude/.claude/commands/gh-prepare.md +12 -0
- package/dist/claude/.claude/commands/grill-me.md +12 -0
- package/dist/claude/.claude/commands/lu-review.md +51 -0
- package/dist/claude/.claude/commands/lu.md +75 -0
- package/dist/claude/.claude/commands/luca-init.md +14 -0
- package/dist/claude/.claude/commands/luca-telemetry-report.md +12 -0
- package/dist/claude/.claude/commands/memory-audit.md +12 -0
- package/dist/claude/.claude/commands/milestone-new.md +122 -0
- package/dist/claude/.claude/commands/phase-discuss.md +45 -0
- package/dist/claude/.claude/commands/phase-execute.md +39 -0
- package/dist/claude/.claude/commands/phase-plan.md +53 -0
- package/dist/claude/.claude/commands/repo-cleanup.md +80 -0
- package/dist/claude/.claude/commands/todo-add.md +28 -0
- package/dist/claude/.claude/commands/todo-check.md +36 -0
- package/dist/claude/.claude/hooks/context-refresher.ts +285 -0
- package/dist/claude/.claude/hooks/continuation-messages.ts +215 -0
- package/dist/claude/.claude/hooks/pipeline-guard.ts +182 -0
- package/dist/claude/.claude/settings.json +41 -0
- package/dist/claude/skills/arch-audit/SKILL.md +161 -0
- package/dist/claude/skills/autopilot/SKILL.md +1299 -0
- package/dist/claude/skills/bug-diagnose/SKILL.md +102 -0
- package/dist/claude/skills/choose/SKILL.md +124 -0
- package/dist/claude/skills/gh-issue-triage/SKILL.md +97 -0
- package/dist/claude/skills/gh-pr-address/SKILL.md +235 -0
- package/dist/claude/skills/gh-prepare/SKILL.md +209 -0
- package/dist/claude/skills/grill-me/SKILL.md +46 -0
- package/dist/claude/skills/lu/SKILL.md +112 -0
- package/dist/claude/skills/lu-review/SKILL.md +51 -0
- package/dist/claude/skills/luca-init/SKILL.md +91 -0
- package/dist/claude/skills/luca-telemetry-report/SKILL.md +145 -0
- package/dist/claude/skills/luca-write-surface/SKILL.md +213 -0
- package/dist/claude/skills/memory-audit/SKILL.md +217 -0
- package/dist/claude/skills/milestone-audit/SKILL.md +545 -0
- package/dist/claude/skills/milestone-complete/SKILL.md +168 -0
- package/dist/claude/skills/milestone-gaps/SKILL.md +60 -0
- package/dist/claude/skills/milestone-new/SKILL.md +125 -0
- package/dist/claude/skills/note/SKILL.md +162 -0
- package/dist/claude/skills/phase-add/SKILL.md +91 -0
- package/dist/claude/skills/phase-assumptions/SKILL.md +92 -0
- package/dist/claude/skills/phase-discuss/SKILL.md +165 -0
- package/dist/claude/skills/phase-execute/SKILL.md +1786 -0
- package/dist/claude/skills/phase-insert/SKILL.md +100 -0
- package/dist/claude/skills/phase-plan/SKILL.md +461 -0
- package/dist/claude/skills/phase-remove/SKILL.md +113 -0
- package/dist/claude/skills/phase-research/SKILL.md +80 -0
- package/dist/claude/skills/post-init-tour/SKILL.md +58 -0
- package/dist/claude/skills/progress/SKILL.md +271 -0
- package/dist/claude/skills/project-new/SKILL.md +609 -0
- package/dist/claude/skills/quick/SKILL.md +256 -0
- package/dist/claude/skills/rename-audit/SKILL.md +52 -0
- package/dist/claude/skills/repo-audit/SKILL.md +88 -0
- package/dist/claude/skills/repo-cleanup/SKILL.md +80 -0
- package/dist/claude/skills/seed-memory/SKILL.md +235 -0
- package/dist/claude/skills/session-pause/SKILL.md +126 -0
- package/dist/claude/skills/session-plan/SKILL.md +112 -0
- package/dist/claude/skills/session-resume/SKILL.md +75 -0
- package/dist/claude/skills/todo-add/SKILL.md +85 -0
- package/dist/claude/skills/todo-check/SKILL.md +77 -0
- package/dist/claude/skills/workflow-save/SKILL.md +277 -0
- package/dist/index.d.mts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.mjs +69 -0
- package/dist/shared/luca.B3Mimc0P.mjs +52 -0
- package/dist/shared/luca.B3saVjJm.mjs +163 -0
- package/dist/shared/luca.BYdjkfnz.mjs +217 -0
- package/dist/shared/luca.BmhNkYe2.mjs +56 -0
- package/dist/shared/luca.C4gMUoBd.mjs +358 -0
- package/dist/shared/luca.CQ3g1xrD.mjs +19 -0
- package/dist/shared/luca.CRmaAfXR.mjs +713 -0
- package/dist/shared/luca.CrXzXueR.mjs +57 -0
- package/dist/shared/luca.DTomPq7I.mjs +91 -0
- package/dist/shared/luca.DjDTeDCi.mjs +1904 -0
- package/dist/shared/luca.HZxBTBgD.mjs +201 -0
- package/dist/shared/luca.TSMg1t7I.mjs +10 -0
- package/dist/shared/luca.dM-MKlNE.mjs +25 -0
- package/dist/shared/luca.naWEcQ4B.mjs +7 -0
- package/package.json +76 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { rm, writeFile, readdir, rmdir } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import '../shared/luca.CRmaAfXR.mjs';
|
|
5
|
+
import 'node:crypto';
|
|
6
|
+
import 'node:module';
|
|
7
|
+
import 'node:url';
|
|
8
|
+
import 'node:child_process';
|
|
9
|
+
import '../index.mjs';
|
|
10
|
+
import { l as listBundledArtifacts } from '../shared/luca.B3saVjJm.mjs';
|
|
11
|
+
import 'zod';
|
|
12
|
+
import 'node:os';
|
|
13
|
+
import 'citty';
|
|
14
|
+
import 'pathe';
|
|
15
|
+
|
|
16
|
+
const CHECK_NAME = "Stray local install";
|
|
17
|
+
async function readJsonObject(path) {
|
|
18
|
+
try {
|
|
19
|
+
const file = Bun.file(path);
|
|
20
|
+
if (!await file.exists()) return null;
|
|
21
|
+
const parsed = JSON.parse(await file.text());
|
|
22
|
+
return parsed !== null && typeof parsed === "object" ? parsed : null;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function isStageGateEntry(entry) {
|
|
28
|
+
if (entry === null || typeof entry !== "object") return false;
|
|
29
|
+
const { hooks } = entry;
|
|
30
|
+
if (!Array.isArray(hooks)) return false;
|
|
31
|
+
return hooks.some((h) => {
|
|
32
|
+
const command = h?.command;
|
|
33
|
+
return typeof command === "string" && command.includes("stage-gate");
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async function scanStray(cwd) {
|
|
37
|
+
const claudeDir = join(cwd, ".claude");
|
|
38
|
+
const items = [];
|
|
39
|
+
if (!existsSync(claudeDir)) {
|
|
40
|
+
return { items, settingsStageGate: false };
|
|
41
|
+
}
|
|
42
|
+
const bundled = await listBundledArtifacts();
|
|
43
|
+
if (bundled) {
|
|
44
|
+
for (const name of bundled.commands) {
|
|
45
|
+
const path = join(claudeDir, "commands", name);
|
|
46
|
+
if (existsSync(path)) {
|
|
47
|
+
items.push({
|
|
48
|
+
path,
|
|
49
|
+
label: `.claude/commands/${name}`,
|
|
50
|
+
kind: "file"
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (const name of bundled.agents) {
|
|
55
|
+
const path = join(claudeDir, "agents", name);
|
|
56
|
+
if (existsSync(path)) {
|
|
57
|
+
items.push({
|
|
58
|
+
path,
|
|
59
|
+
label: `.claude/agents/${name}`,
|
|
60
|
+
kind: "file"
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const name of bundled.skills) {
|
|
65
|
+
const path = join(claudeDir, "skills", name);
|
|
66
|
+
if (existsSync(path)) {
|
|
67
|
+
items.push({
|
|
68
|
+
path,
|
|
69
|
+
label: `.claude/skills/${name}/`,
|
|
70
|
+
kind: "dir"
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const hookScript = join(claudeDir, "hooks", "stage-gate.sh");
|
|
76
|
+
if (existsSync(hookScript)) {
|
|
77
|
+
items.push({
|
|
78
|
+
path: hookScript,
|
|
79
|
+
label: ".claude/hooks/stage-gate.sh",
|
|
80
|
+
kind: "file"
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
const settings = await readJsonObject(join(claudeDir, "settings.json"));
|
|
84
|
+
const hooks = settings?.hooks;
|
|
85
|
+
const preToolUse = hooks !== null && typeof hooks === "object" ? hooks.PreToolUse : void 0;
|
|
86
|
+
const settingsStageGate = Array.isArray(preToolUse) && preToolUse.some(isStageGateEntry);
|
|
87
|
+
return { items, settingsStageGate };
|
|
88
|
+
}
|
|
89
|
+
async function stripStageGate(settingsPath) {
|
|
90
|
+
const settings = await readJsonObject(settingsPath);
|
|
91
|
+
if (!settings) return;
|
|
92
|
+
const hooks = settings.hooks;
|
|
93
|
+
if (hooks !== null && typeof hooks === "object") {
|
|
94
|
+
const hooksObj = hooks;
|
|
95
|
+
const preToolUse = hooksObj.PreToolUse;
|
|
96
|
+
if (Array.isArray(preToolUse)) {
|
|
97
|
+
const kept = preToolUse.filter((e) => !isStageGateEntry(e));
|
|
98
|
+
if (kept.length > 0) {
|
|
99
|
+
hooksObj.PreToolUse = kept;
|
|
100
|
+
} else {
|
|
101
|
+
delete hooksObj.PreToolUse;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (Object.keys(hooksObj).length === 0) {
|
|
105
|
+
delete settings.hooks;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (Object.keys(settings).length === 0) {
|
|
109
|
+
await rm(settingsPath, { force: true });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
await writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
113
|
+
}
|
|
114
|
+
async function pruneEmptyDirs(claudeDir, applied) {
|
|
115
|
+
for (const sub of ["commands", "agents", "skills", "hooks"]) {
|
|
116
|
+
const dir = join(claudeDir, sub);
|
|
117
|
+
if (existsSync(dir) && (await readdir(dir)).length === 0) {
|
|
118
|
+
await rmdir(dir);
|
|
119
|
+
applied.push(`removed empty .claude/${sub}/`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (existsSync(claudeDir) && (await readdir(claudeDir)).length === 0) {
|
|
123
|
+
await rmdir(claudeDir);
|
|
124
|
+
applied.push("removed empty .claude/");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const strayLocalInstallCheck = {
|
|
128
|
+
name: CHECK_NAME,
|
|
129
|
+
scope: "project",
|
|
130
|
+
async run() {
|
|
131
|
+
const { items, settingsStageGate } = await scanStray(process.cwd());
|
|
132
|
+
const count = items.length + (settingsStageGate ? 1 : 0);
|
|
133
|
+
if (count === 0) {
|
|
134
|
+
return {
|
|
135
|
+
name: CHECK_NAME,
|
|
136
|
+
status: "pass",
|
|
137
|
+
message: "no stray luca artifacts in ./.claude",
|
|
138
|
+
fixCommand: null,
|
|
139
|
+
details: null
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
const detailLines = [
|
|
143
|
+
"Through v12, `luca init` copied the luca skill set into this",
|
|
144
|
+
"repo. v13 installs it globally into ~/.claude/ \u2014 a repo should",
|
|
145
|
+
"hold only .luca/ planning files. Stray artifacts found:",
|
|
146
|
+
...items.map((item) => `- ${item.label}`)
|
|
147
|
+
];
|
|
148
|
+
if (settingsStageGate) {
|
|
149
|
+
detailLines.push("- .claude/settings.json (stage-gate hook entry)");
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
name: CHECK_NAME,
|
|
153
|
+
status: "warning",
|
|
154
|
+
message: `${count} stray luca artifact(s) installed locally in ./.claude`,
|
|
155
|
+
fixCommand: "luca doctor --fix",
|
|
156
|
+
details: detailLines.join("\n ")
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
async fix() {
|
|
160
|
+
const cwd = process.cwd();
|
|
161
|
+
const applied = [];
|
|
162
|
+
const errors = [];
|
|
163
|
+
const { items, settingsStageGate } = await scanStray(cwd);
|
|
164
|
+
for (const item of items) {
|
|
165
|
+
try {
|
|
166
|
+
await rm(item.path, {
|
|
167
|
+
recursive: item.kind === "dir",
|
|
168
|
+
force: true
|
|
169
|
+
});
|
|
170
|
+
applied.push(`removed ${item.label}`);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
errors.push(
|
|
173
|
+
`could not remove ${item.label}: ${err.message}`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (settingsStageGate) {
|
|
178
|
+
try {
|
|
179
|
+
await stripStageGate(join(cwd, ".claude", "settings.json"));
|
|
180
|
+
applied.push(
|
|
181
|
+
"removed stage-gate hook entry from .claude/settings.json"
|
|
182
|
+
);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
errors.push(
|
|
185
|
+
`could not update .claude/settings.json: ${err.message}`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
await pruneEmptyDirs(join(cwd, ".claude"), applied);
|
|
191
|
+
} catch (err) {
|
|
192
|
+
errors.push(
|
|
193
|
+
`could not prune empty .claude/ directories: ${err.message}`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
return { applied, errors };
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export { strayLocalInstallCheck };
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { defineCommand } from 'citty';
|
|
2
|
+
import { R as RunIdSchema, L as LUCA_DIR_ROOT, T as TelemetryRecordSchema } from '../shared/luca.CRmaAfXR.mjs';
|
|
3
|
+
import { mkdirSync, appendFileSync } from 'node:fs';
|
|
4
|
+
import 'node:fs/promises';
|
|
5
|
+
import { join, dirname } from 'node:path';
|
|
6
|
+
import 'node:crypto';
|
|
7
|
+
import 'node:module';
|
|
8
|
+
import 'node:url';
|
|
9
|
+
import 'node:child_process';
|
|
10
|
+
import { g as generateRunId } from '../shared/luca.naWEcQ4B.mjs';
|
|
11
|
+
import { l as logger } from '../shared/luca.dM-MKlNE.mjs';
|
|
12
|
+
import 'zod';
|
|
13
|
+
import 'node:os';
|
|
14
|
+
import 'consola';
|
|
15
|
+
|
|
16
|
+
function telemetryPathFor(runId) {
|
|
17
|
+
RunIdSchema.parse(runId);
|
|
18
|
+
return `${LUCA_DIR_ROOT}/telemetry/${runId}.jsonl`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function sanitizeForLog(value) {
|
|
22
|
+
return String(value instanceof Error ? value.message : value).replace(/[\r\n\t]/g, " ").slice(0, 200);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function buildTelemetryRecord(kind, ctx, meta = {}, overrides = {}) {
|
|
26
|
+
return {
|
|
27
|
+
v: 1,
|
|
28
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29
|
+
runId: overrides.runId ?? ctx.runId ?? "",
|
|
30
|
+
kind,
|
|
31
|
+
phase: overrides.phase ?? ctx.phase ?? null,
|
|
32
|
+
slug: overrides.slug ?? ctx.slug ?? null,
|
|
33
|
+
wave: overrides.wave ?? ctx.wave ?? null,
|
|
34
|
+
complexity: overrides.complexity ?? ctx.complexity ?? null,
|
|
35
|
+
oversight: overrides.oversight ?? ctx.oversight ?? null,
|
|
36
|
+
durationMs: overrides.durationMs ?? null,
|
|
37
|
+
meta
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function appendTelemetry(opts) {
|
|
41
|
+
try {
|
|
42
|
+
const record = buildTelemetryRecord(
|
|
43
|
+
opts.kind,
|
|
44
|
+
opts.ctx,
|
|
45
|
+
opts.meta ?? {},
|
|
46
|
+
opts.overrides ?? {}
|
|
47
|
+
);
|
|
48
|
+
const parsed = TelemetryRecordSchema.safeParse(record);
|
|
49
|
+
if (!parsed.success) {
|
|
50
|
+
console.warn(
|
|
51
|
+
`[telemetry] dropped malformed record: ${sanitizeForLog(parsed.error.message)}`
|
|
52
|
+
);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (!parsed.data.runId) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const runIdCheck = RunIdSchema.safeParse(parsed.data.runId);
|
|
59
|
+
if (!runIdCheck.success) {
|
|
60
|
+
console.warn(
|
|
61
|
+
`[telemetry] dropped record with invalid runId: ${sanitizeForLog(runIdCheck.error.message)}`
|
|
62
|
+
);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const p = join(opts.cwd, telemetryPathFor(parsed.data.runId));
|
|
66
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
67
|
+
appendFileSync(p, `${JSON.stringify(parsed.data)}
|
|
68
|
+
`, "utf-8");
|
|
69
|
+
} catch (err) {
|
|
70
|
+
console.warn(`[telemetry] write failed: ${sanitizeForLog(err)}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const emitCommand = defineCommand({
|
|
75
|
+
meta: {
|
|
76
|
+
name: "emit",
|
|
77
|
+
description: "Append one telemetry record to .luca/telemetry/<runId>.jsonl."
|
|
78
|
+
},
|
|
79
|
+
args: {
|
|
80
|
+
kind: {
|
|
81
|
+
type: "string",
|
|
82
|
+
required: true,
|
|
83
|
+
description: "Event kind, e.g. phase.start, wave.end, mode.start, recall.hit."
|
|
84
|
+
},
|
|
85
|
+
"run-id": {
|
|
86
|
+
type: "string",
|
|
87
|
+
required: true,
|
|
88
|
+
description: "Run identifier (see `luca telemetry new-run`)."
|
|
89
|
+
},
|
|
90
|
+
phase: { type: "string", description: "Phase name from the roadmap." },
|
|
91
|
+
slug: { type: "string", description: "Phase slug." },
|
|
92
|
+
wave: { type: "string", description: "Wave number." },
|
|
93
|
+
complexity: {
|
|
94
|
+
type: "string",
|
|
95
|
+
description: "Triage complexity classification."
|
|
96
|
+
},
|
|
97
|
+
oversight: { type: "string", description: "Oversight mode." },
|
|
98
|
+
"duration-ms": {
|
|
99
|
+
type: "string",
|
|
100
|
+
description: "Duration in milliseconds (for .end events)."
|
|
101
|
+
},
|
|
102
|
+
meta: {
|
|
103
|
+
type: "string",
|
|
104
|
+
description: "Free-form per-event metadata as a JSON object."
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
run({ args }) {
|
|
108
|
+
let meta = {};
|
|
109
|
+
if (args.meta) {
|
|
110
|
+
try {
|
|
111
|
+
const parsed = JSON.parse(args.meta);
|
|
112
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
113
|
+
throw new Error("not a JSON object");
|
|
114
|
+
}
|
|
115
|
+
meta = parsed;
|
|
116
|
+
} catch (err) {
|
|
117
|
+
logger.error(
|
|
118
|
+
`luca telemetry emit: --meta is not a valid JSON object \u2014 ${err instanceof Error ? err.message : String(err)}`
|
|
119
|
+
);
|
|
120
|
+
process.exitCode = 1;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const ctx = {
|
|
125
|
+
runId: args["run-id"],
|
|
126
|
+
phase: args.phase ?? null,
|
|
127
|
+
slug: args.slug ?? null,
|
|
128
|
+
wave: args.wave !== void 0 ? Number(args.wave) : null,
|
|
129
|
+
complexity: args.complexity ?? null,
|
|
130
|
+
oversight: args.oversight ?? null
|
|
131
|
+
};
|
|
132
|
+
appendTelemetry({
|
|
133
|
+
cwd: process.cwd(),
|
|
134
|
+
kind: args.kind,
|
|
135
|
+
ctx,
|
|
136
|
+
meta,
|
|
137
|
+
overrides: args["duration-ms"] !== void 0 ? { durationMs: Number(args["duration-ms"]) } : {}
|
|
138
|
+
});
|
|
139
|
+
logger.success(
|
|
140
|
+
`telemetry: ${args.kind} emitted for run ${args["run-id"]}.`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
const newRunCommand = defineCommand({
|
|
145
|
+
meta: {
|
|
146
|
+
name: "new-run",
|
|
147
|
+
description: "Mint and print a fresh telemetry run identifier."
|
|
148
|
+
},
|
|
149
|
+
run() {
|
|
150
|
+
process.stdout.write(`${generateRunId()}
|
|
151
|
+
`);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
const telemetryCommand = defineCommand({
|
|
155
|
+
meta: {
|
|
156
|
+
name: "telemetry",
|
|
157
|
+
description: "Emit Luca pipeline telemetry."
|
|
158
|
+
},
|
|
159
|
+
subCommands: {
|
|
160
|
+
emit: emitCommand,
|
|
161
|
+
"new-run": newRunCommand
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
export { telemetryCommand };
|
|
@@ -0,0 +1,151 @@
|
|
|
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 { c as readJsonPayload, r as runWriteHandler, h as lucaTodoUpdateTool, i as lucaTodoListTool, j as lucaTodoAddTool } 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 addCommand = defineCommand({
|
|
20
|
+
meta: {
|
|
21
|
+
name: "add",
|
|
22
|
+
description: "Create a new todo in MuninnDB. Validates the shape and emits a muninn_remember instruction for the agent to execute. Concept: todo:<id> in the repo vault. Phase-agnostic."
|
|
23
|
+
},
|
|
24
|
+
args: {
|
|
25
|
+
title: {
|
|
26
|
+
type: "string",
|
|
27
|
+
required: true,
|
|
28
|
+
description: "Short imperative description of the todo. Used to derive the id when --id is omitted."
|
|
29
|
+
},
|
|
30
|
+
body: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "Optional longer markdown body \u2014 context, acceptance criteria, references."
|
|
33
|
+
},
|
|
34
|
+
status: {
|
|
35
|
+
type: "string",
|
|
36
|
+
default: "pending",
|
|
37
|
+
description: 'Initial status: "pending" or "backlog" (default pending). Promotion to "done" happens via `todo update`.'
|
|
38
|
+
},
|
|
39
|
+
source: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: 'Where the todo originated \u2014 e.g. "gh-issue-#42", "phase-research", "manual".'
|
|
42
|
+
},
|
|
43
|
+
id: {
|
|
44
|
+
type: "string",
|
|
45
|
+
description: "Optional explicit id (kebab-case). Derived from the title when omitted."
|
|
46
|
+
},
|
|
47
|
+
"metadata-file": {
|
|
48
|
+
type: "string",
|
|
49
|
+
description: "Optional path to a JSON file of arbitrary structured fields to store alongside the todo."
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
async run({ args }) {
|
|
53
|
+
const metadata = args["metadata-file"] ? await readJsonPayload("todo add", args["metadata-file"]) : void 0;
|
|
54
|
+
await runWriteHandler("todo add", lucaTodoAddTool, {
|
|
55
|
+
title: args.title,
|
|
56
|
+
body: args.body,
|
|
57
|
+
status: args.status,
|
|
58
|
+
source: args.source,
|
|
59
|
+
id: args.id,
|
|
60
|
+
metadata
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const listCommand = defineCommand({
|
|
65
|
+
meta: {
|
|
66
|
+
name: "list",
|
|
67
|
+
description: 'List todos from MuninnDB. Emits a muninn_recall instruction with context ["todo:"] in the repo vault. Phase-agnostic.'
|
|
68
|
+
},
|
|
69
|
+
args: {
|
|
70
|
+
status: {
|
|
71
|
+
type: "string",
|
|
72
|
+
description: "Optional status filter (pending, backlog, done, ...). Applied by the agent post-recall."
|
|
73
|
+
},
|
|
74
|
+
limit: {
|
|
75
|
+
type: "string",
|
|
76
|
+
default: "50",
|
|
77
|
+
description: "Max todos to recall (range 1-200, default 50)."
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
async run({ args }) {
|
|
81
|
+
await runWriteHandler("todo list", lucaTodoListTool, {
|
|
82
|
+
status: args.status,
|
|
83
|
+
limit: Number(args.limit)
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
const updateCommand = defineCommand({
|
|
88
|
+
meta: {
|
|
89
|
+
name: "update",
|
|
90
|
+
description: 'Update an existing todo. Validates the new shape, enforces the verification-ref guard when promoting to "done", and emits a muninn_remember instruction. Phase-agnostic.'
|
|
91
|
+
},
|
|
92
|
+
args: {
|
|
93
|
+
id: {
|
|
94
|
+
type: "string",
|
|
95
|
+
required: true,
|
|
96
|
+
description: "Existing todo id (kebab-case). Used as the muninn concept suffix: todo:<id>."
|
|
97
|
+
},
|
|
98
|
+
title: {
|
|
99
|
+
type: "string",
|
|
100
|
+
required: true,
|
|
101
|
+
description: "Full title of the todo. Pass the existing title unchanged unless renaming."
|
|
102
|
+
},
|
|
103
|
+
status: {
|
|
104
|
+
type: "string",
|
|
105
|
+
required: true,
|
|
106
|
+
description: 'New status. Promoting to "done" requires --verification-criterion pointing at a met PASS criterion.'
|
|
107
|
+
},
|
|
108
|
+
body: {
|
|
109
|
+
type: "string",
|
|
110
|
+
description: "Optional updated markdown body."
|
|
111
|
+
},
|
|
112
|
+
source: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "Optional updated origin label."
|
|
115
|
+
},
|
|
116
|
+
"verification-criterion": {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: `Required when --status=done. A criterionId (e.g. "ac-03") in the active phase's verify.json that is met=true with non-empty evidence and parent status=PASS.`
|
|
119
|
+
},
|
|
120
|
+
"metadata-file": {
|
|
121
|
+
type: "string",
|
|
122
|
+
description: "Optional path to a JSON file of arbitrary structured fields to store alongside the todo."
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
async run({ args }) {
|
|
126
|
+
const metadata = args["metadata-file"] ? await readJsonPayload("todo update", args["metadata-file"]) : void 0;
|
|
127
|
+
const verificationRef = args["verification-criterion"] ? { criterionId: args["verification-criterion"] } : void 0;
|
|
128
|
+
await runWriteHandler("todo update", lucaTodoUpdateTool, {
|
|
129
|
+
id: args.id,
|
|
130
|
+
title: args.title,
|
|
131
|
+
status: args.status,
|
|
132
|
+
body: args.body,
|
|
133
|
+
source: args.source,
|
|
134
|
+
metadata,
|
|
135
|
+
verificationRef
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
const todoCommand = defineCommand({
|
|
140
|
+
meta: {
|
|
141
|
+
name: "todo",
|
|
142
|
+
description: "Manage the Luca development backlog (MuninnDB todos)"
|
|
143
|
+
},
|
|
144
|
+
subCommands: {
|
|
145
|
+
add: addCommand,
|
|
146
|
+
list: listCommand,
|
|
147
|
+
update: updateCommand
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
export { todoCommand };
|