@dadado/agent-kit-cli 5.15.1 → 5.17.0
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/dashboard/dashboard-data.mjs +69 -65
- package/dashboard/lib/guards.d.mts +5 -0
- package/dashboard/lib/guards.mjs +31 -0
- package/dashboard/lib/identity-probe.mjs +56 -0
- package/dashboard/lib/pipeline-cache.mjs +58 -0
- package/dashboard/lib/processes.mjs +71 -0
- package/dashboard/serve.mjs +43 -1
- package/dashboard/start-broadcast.mjs +2 -12
- package/dashboard/start.mjs +2 -13
- package/dist/add-HTYIFKMN.js +150 -0
- package/dist/chunk-2IPXQFJE.js +253 -0
- package/dist/chunk-2RDRMGD3.js +82 -0
- package/dist/chunk-2YU2H7I6.js +42 -0
- package/dist/chunk-4IWG5CVE.js +214 -0
- package/dist/chunk-5VCOWTTP.js +6 -0
- package/dist/chunk-77V75A7O.js +39 -0
- package/dist/chunk-7HDQEK6T.js +26 -0
- package/dist/chunk-AW74INUW.js +1456 -0
- package/dist/chunk-B73KQ5KX.js +972 -0
- package/dist/chunk-CBCW5R5J.js +484 -0
- package/dist/chunk-DE3LQFSJ.js +18 -0
- package/dist/chunk-DQA434Y4.js +106 -0
- package/dist/chunk-F4VMYR7U.js +318 -0
- package/dist/chunk-F6C3GFKV.js +57 -0
- package/dist/chunk-HFUH3HVS.js +282 -0
- package/dist/chunk-HS7LVVLO.js +74 -0
- package/dist/chunk-IHROBTNM.js +135 -0
- package/dist/chunk-KD2MYBB6.js +213 -0
- package/dist/chunk-MAPOQEA2.js +43 -0
- package/dist/chunk-N3HTJVBR.js +341 -0
- package/dist/chunk-NQ7IQ5XT.js +26 -0
- package/dist/chunk-OUQZC744.js +1315 -0
- package/dist/chunk-PCCQUWE5.js +817 -0
- package/dist/chunk-PNFF6OU3.js +273 -0
- package/dist/chunk-PNG7LO2F.js +53 -0
- package/dist/chunk-QTON2GQX.js +203 -0
- package/dist/chunk-S6GMFDKV.js +50 -0
- package/dist/chunk-SMGFN3Z4.js +503 -0
- package/dist/chunk-STGFBAYT.js +641 -0
- package/dist/chunk-TFZ2JUXH.js +1081 -0
- package/dist/chunk-VY7MTPJ2.js +205 -0
- package/dist/chunk-W24CMQ32.js +525 -0
- package/dist/chunk-WW3YZKHW.js +165 -0
- package/dist/chunk-ZB43YMHW.js +20 -0
- package/dist/chunk-ZCTSUQRR.js +874 -0
- package/dist/chunk-ZXKZBTXE.js +17 -0
- package/dist/contribute-C6AW2P6J.js +367 -0
- package/dist/cursor-awareness-KTL2RGKD.js +77 -0
- package/dist/dashboard-YSNUGBFY.js +21 -0
- package/dist/dashboard-broadcast-M4CMW7RX.js +119 -0
- package/dist/diff-VHNR62SI.js +74 -0
- package/dist/doctor-PET2HRRJ.js +236 -0
- package/dist/dogfood-KKFVRWT6.js +366 -0
- package/dist/guard-EHXF76ID.js +530 -0
- package/dist/handoff-3UI6VKVG.js +302 -0
- package/dist/hook-DYIMKJYL.js +1074 -0
- package/dist/index.js +62 -14068
- package/dist/init-Q4LBM6KT.js +127 -0
- package/dist/install-RHOS3OYZ.js +34 -0
- package/dist/mission-control-LJACEVVN.js +89 -0
- package/dist/monitors-QV2IS3MP.js +176 -0
- package/dist/plan-index-JMC7Z4DW.js +44 -0
- package/dist/public-inbound-radar-WP3XF7GS.js +68 -0
- package/dist/remote-issues-SZ4HDQL5.js +44 -0
- package/dist/run-I3BFGULT.js +243 -0
- package/dist/run-plan-FO7CL4M3.js +118 -0
- package/dist/scan-NI2SWJ2E.js +33 -0
- package/dist/setup-global-M42GEODI.js +340 -0
- package/dist/status-XPVQVQMX.js +150 -0
- package/dist/update-FJ2EBZVO.js +283 -0
- package/dist/validate-V2CW7J4B.js +201 -0
- package/package.json +3 -3
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import {
|
|
2
|
+
pruneHandoffText
|
|
3
|
+
} from "./chunk-IHROBTNM.js";
|
|
4
|
+
import {
|
|
5
|
+
extractFieldBlock,
|
|
6
|
+
extractHandoffNamedPlans,
|
|
7
|
+
namedPlanCandidatePaths,
|
|
8
|
+
parsePlanFrontmatter
|
|
9
|
+
} from "./chunk-KD2MYBB6.js";
|
|
10
|
+
import {
|
|
11
|
+
PM_TOOL_LABELS
|
|
12
|
+
} from "./chunk-F6C3GFKV.js";
|
|
13
|
+
import {
|
|
14
|
+
logger
|
|
15
|
+
} from "./chunk-ZB43YMHW.js";
|
|
16
|
+
import {
|
|
17
|
+
HANDOFF_REL
|
|
18
|
+
} from "./chunk-5VCOWTTP.js";
|
|
19
|
+
import {
|
|
20
|
+
ensureDir,
|
|
21
|
+
fileExists,
|
|
22
|
+
readJson
|
|
23
|
+
} from "./chunk-77V75A7O.js";
|
|
24
|
+
|
|
25
|
+
// src/commands/handoff.ts
|
|
26
|
+
import { spawn } from "child_process";
|
|
27
|
+
import { appendFile, readFile, readdir, writeFile } from "fs/promises";
|
|
28
|
+
import path from "path";
|
|
29
|
+
import { defineCommand } from "citty";
|
|
30
|
+
var HANDOFF_PRUNE_DEFAULT_KEEP = 5;
|
|
31
|
+
function pruneArchiveTimestamp(now2 = /* @__PURE__ */ new Date()) {
|
|
32
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
33
|
+
return `${now2.getFullYear()}-${pad(now2.getMonth() + 1)}-${pad(now2.getDate())}-${pad(now2.getHours())}${pad(now2.getMinutes())}`;
|
|
34
|
+
}
|
|
35
|
+
async function runPrune(cwd, keep) {
|
|
36
|
+
const handoffPath = path.join(cwd, ".cursor", "HANDOFF.md");
|
|
37
|
+
if (!await fileExists(handoffPath)) {
|
|
38
|
+
logger.warn("No .cursor/HANDOFF.md to prune.");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const original = await readFile(handoffPath, "utf8");
|
|
42
|
+
const result = pruneHandoffText(original, keep);
|
|
43
|
+
if (!result.ok) {
|
|
44
|
+
logger.warn(`Refusing to prune: ${result.reason}`);
|
|
45
|
+
process.exitCode = 1;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!result.archived || result.archived.length === 0) {
|
|
49
|
+
logger.info(
|
|
50
|
+
`HANDOFF.md already at or under ${keep} narrative ${keep === 1 ? "entry" : "entries"}; nothing to prune.`
|
|
51
|
+
);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const archiveDir = path.join(cwd, ".cursor", "context", "archive");
|
|
55
|
+
await ensureDir(archiveDir);
|
|
56
|
+
const archivePath = path.join(archiveDir, `handoff-${pruneArchiveTimestamp()}.md`);
|
|
57
|
+
const header = [
|
|
58
|
+
`<!-- Pruned from .cursor/HANDOFF.md at ${(/* @__PURE__ */ new Date()).toISOString()} -->`,
|
|
59
|
+
`<!-- ${result.archived.length} narrative ${result.archived.length === 1 ? "entry" : "entries"} archived, keep=${keep} -->`,
|
|
60
|
+
""
|
|
61
|
+
].join("\n");
|
|
62
|
+
const body = `${result.archived.join("\n\n")}
|
|
63
|
+
`;
|
|
64
|
+
if (await fileExists(archivePath)) {
|
|
65
|
+
await appendFile(archivePath, `
|
|
66
|
+
${header}${body}`, "utf8");
|
|
67
|
+
} else {
|
|
68
|
+
await writeFile(archivePath, `${header}${body}`, "utf8");
|
|
69
|
+
}
|
|
70
|
+
await writeFile(handoffPath, result.text ?? original, "utf8");
|
|
71
|
+
logger.success(
|
|
72
|
+
`HANDOFF.md pruned: kept ${result.keptCount} narrative ${result.keptCount === 1 ? "entry" : "entries"}, archived ${result.archived.length} to ${path.relative(cwd, archivePath)}.`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
var PRESERVED_FIELDS = [
|
|
76
|
+
"Run queue",
|
|
77
|
+
"Queue cursor",
|
|
78
|
+
"Queue status",
|
|
79
|
+
"Queue outcomes",
|
|
80
|
+
"Backlog plans",
|
|
81
|
+
"Parked plans"
|
|
82
|
+
];
|
|
83
|
+
function hasFrontmatter(raw) {
|
|
84
|
+
return /^---\r?\n[\s\S]*?\r?\n---/.test(raw);
|
|
85
|
+
}
|
|
86
|
+
function todoText(t) {
|
|
87
|
+
return (t.content ?? t.id).replace(/^["']|["']$/g, "");
|
|
88
|
+
}
|
|
89
|
+
function copyFieldBlock(text, label) {
|
|
90
|
+
if (extractFieldBlock(text, label) === null) return null;
|
|
91
|
+
const lines = text.split("\n");
|
|
92
|
+
const start = lines.findIndex((l) => l.startsWith(`- **${label}:**`));
|
|
93
|
+
if (start < 0) return null;
|
|
94
|
+
let end = start + 1;
|
|
95
|
+
while (end < lines.length) {
|
|
96
|
+
const line = lines[end] ?? "";
|
|
97
|
+
if (/^- \*\*[^*:\n]+:\*\*/.test(line) || line.trim() && !/^\s+\S/.test(line)) break;
|
|
98
|
+
end += 1;
|
|
99
|
+
}
|
|
100
|
+
while (end > start + 1 && !lines[end - 1]?.trim()) end -= 1;
|
|
101
|
+
return lines.slice(start, end).join("\n");
|
|
102
|
+
}
|
|
103
|
+
async function findActivePlan(root, handoff) {
|
|
104
|
+
const active = extractHandoffNamedPlans(handoff).active;
|
|
105
|
+
if (active) {
|
|
106
|
+
for (const candidate of namedPlanCandidatePaths(root, active)) {
|
|
107
|
+
if (await fileExists(candidate)) {
|
|
108
|
+
return { file: active, raw: await readFile(candidate, "utf8") };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const plansDir = path.join(root, ".cursor", "plans");
|
|
113
|
+
if (!await fileExists(plansDir)) return null;
|
|
114
|
+
const files = (await readdir(plansDir)).filter((f) => f.endsWith(".plan.md")).sort().reverse();
|
|
115
|
+
for (const file of files) {
|
|
116
|
+
const raw = await readFile(path.join(plansDir, file), "utf8");
|
|
117
|
+
const fm = parsePlanFrontmatter(raw);
|
|
118
|
+
if (fm?.todos?.some((t) => t.status !== "completed" && t.status !== "cancelled")) {
|
|
119
|
+
return { file, raw };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return files[0] ? { file: files[0], raw: await readFile(path.join(plansDir, files[0]), "utf8") } : null;
|
|
123
|
+
}
|
|
124
|
+
function now() {
|
|
125
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 16);
|
|
126
|
+
}
|
|
127
|
+
async function loadProfile(rootDir) {
|
|
128
|
+
const configPath = path.join(rootDir, ".cursor", "agent-kit.config.json");
|
|
129
|
+
try {
|
|
130
|
+
return await readJson(configPath);
|
|
131
|
+
} catch {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function buildRoutines(profile) {
|
|
136
|
+
const lines = [];
|
|
137
|
+
const workflow = profile?.git.workflow;
|
|
138
|
+
if (workflow === "homolog-prod") {
|
|
139
|
+
lines.push("- [ ] `git staging` - move changes to staging");
|
|
140
|
+
lines.push("- [ ] `git prod` - promote to production (after approval)");
|
|
141
|
+
} else {
|
|
142
|
+
lines.push("- [ ] Commit, push and open PR/MR");
|
|
143
|
+
}
|
|
144
|
+
const pmTools = profile?.services.projectManagement;
|
|
145
|
+
if (pmTools && pmTools.length > 0) {
|
|
146
|
+
const labels = pmTools.map((t) => PM_TOOL_LABELS[t]).join(", ");
|
|
147
|
+
lines.push(`- [ ] Update tasks in ${labels} (if applicable)`);
|
|
148
|
+
}
|
|
149
|
+
lines.push("- [ ] Review CHANGELOG");
|
|
150
|
+
return lines;
|
|
151
|
+
}
|
|
152
|
+
function closingInstruction(profile) {
|
|
153
|
+
const pmTools = profile?.services.projectManagement;
|
|
154
|
+
if (pmTools && pmTools.length > 0) {
|
|
155
|
+
const labels = pmTools.map((t) => PM_TOOL_LABELS[t]).join(", ");
|
|
156
|
+
return `All todos completed. Check git staging/prod and update ${labels} if applicable.`;
|
|
157
|
+
}
|
|
158
|
+
return "All todos completed. Check commit/push and PR/MR.";
|
|
159
|
+
}
|
|
160
|
+
function buildHandoff(planFile, fm, profile, preserved = []) {
|
|
161
|
+
const completed = fm.todos?.filter((t) => t.status === "completed") ?? [];
|
|
162
|
+
const pending = fm.todos?.filter((t) => t.status === "pending") ?? [];
|
|
163
|
+
const inProgress = fm.todos?.filter((t) => t.status === "in_progress") ?? [];
|
|
164
|
+
const nextTodo = inProgress[0] ?? pending[0];
|
|
165
|
+
const completedPhase = completed.length;
|
|
166
|
+
const totalPhases = fm.todos?.length ?? 0;
|
|
167
|
+
const routines = buildRoutines(profile);
|
|
168
|
+
return [
|
|
169
|
+
`# Handoff - ${fm.name ?? planFile}`,
|
|
170
|
+
"",
|
|
171
|
+
`- **Plan:** ${planFile}`,
|
|
172
|
+
`- **Last updated:** ${now()}`,
|
|
173
|
+
`- **Progress:** ${completedPhase}/${totalPhases} todos completed`,
|
|
174
|
+
...preserved,
|
|
175
|
+
"",
|
|
176
|
+
"## Completed",
|
|
177
|
+
...completed.length > 0 ? completed.map((t) => `- [x] \`${t.id}\`: ${todoText(t)}`) : ["- (none)"],
|
|
178
|
+
"",
|
|
179
|
+
"## In progress",
|
|
180
|
+
...inProgress.length > 0 ? inProgress.map((t) => `- [ ] \`${t.id}\`: ${todoText(t)}`) : ["- (none)"],
|
|
181
|
+
"",
|
|
182
|
+
"## Pending",
|
|
183
|
+
...pending.length > 0 ? pending.map((t) => `- [ ] \`${t.id}\`: ${todoText(t)}`) : ["- (none)"],
|
|
184
|
+
"",
|
|
185
|
+
"## Instruction for Next Agent",
|
|
186
|
+
"",
|
|
187
|
+
nextTodo ? `Continue from todo \`${nextTodo.id}\`: ${todoText(nextTodo)}.` : closingInstruction(profile),
|
|
188
|
+
"",
|
|
189
|
+
"## Suggested routines",
|
|
190
|
+
"",
|
|
191
|
+
...routines,
|
|
192
|
+
"",
|
|
193
|
+
"---",
|
|
194
|
+
"*Generated by `agent-kit handoff`. To resume: open new conversation and say `/continue-plan`.*",
|
|
195
|
+
""
|
|
196
|
+
].join("\n");
|
|
197
|
+
}
|
|
198
|
+
function printV3Guidance() {
|
|
199
|
+
logger.info("No plan in .cursor/plans/ and no ./cursor-handoff in this directory.");
|
|
200
|
+
logger.info(
|
|
201
|
+
"Create a plan with todos or update .cursor/HANDOFF.md manually (/handoff command in Cursor)."
|
|
202
|
+
);
|
|
203
|
+
logger.info("Resume in IDE: /resume, summaries and transcripts.");
|
|
204
|
+
}
|
|
205
|
+
function runCursorHandoff(scriptPath, cwd) {
|
|
206
|
+
return new Promise((resolve, reject) => {
|
|
207
|
+
const child = spawn("sh", [scriptPath, "handoff"], {
|
|
208
|
+
cwd,
|
|
209
|
+
stdio: "inherit"
|
|
210
|
+
});
|
|
211
|
+
child.on("error", (err) => reject(err));
|
|
212
|
+
child.on("close", (code) => resolve(code ?? 1));
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
var handoffCommand = defineCommand({
|
|
216
|
+
meta: {
|
|
217
|
+
name: "handoff",
|
|
218
|
+
description: "Write .cursor/HANDOFF.md from the active plan (or cursor-handoff fallback)."
|
|
219
|
+
},
|
|
220
|
+
args: {
|
|
221
|
+
cwd: {
|
|
222
|
+
type: "string",
|
|
223
|
+
description: "Project root directory",
|
|
224
|
+
default: process.cwd()
|
|
225
|
+
},
|
|
226
|
+
prune: {
|
|
227
|
+
type: "boolean",
|
|
228
|
+
description: "Prune .cursor/HANDOFF.md in place instead of regenerating it: keep the most recent narrative entries, archive the rest to .cursor/context/archive/. Never auto-triggered.",
|
|
229
|
+
default: false
|
|
230
|
+
},
|
|
231
|
+
keep: {
|
|
232
|
+
type: "string",
|
|
233
|
+
description: `With --prune, how many recent narrative entries to keep in place (default ${HANDOFF_PRUNE_DEFAULT_KEEP}).`
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
async run({ args }) {
|
|
237
|
+
if (args.prune) {
|
|
238
|
+
const keepArg = typeof args.keep === "string" ? Number.parseInt(args.keep, 10) : void 0;
|
|
239
|
+
const keep = keepArg !== void 0 && Number.isFinite(keepArg) && keepArg >= 0 ? keepArg : HANDOFF_PRUNE_DEFAULT_KEEP;
|
|
240
|
+
await runPrune(args.cwd, keep);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const profile = await loadProfile(args.cwd);
|
|
244
|
+
const handoffPath = path.join(args.cwd, HANDOFF_REL);
|
|
245
|
+
const previous = await fileExists(handoffPath) ? await readFile(handoffPath, "utf8") : "";
|
|
246
|
+
const plan = await findActivePlan(args.cwd, previous);
|
|
247
|
+
if (plan) {
|
|
248
|
+
if (hasFrontmatter(plan.raw)) {
|
|
249
|
+
const fm = parsePlanFrontmatter(plan.raw);
|
|
250
|
+
const preserved = PRESERVED_FIELDS.map((label) => copyFieldBlock(previous, label)).filter(
|
|
251
|
+
(block) => block !== null
|
|
252
|
+
);
|
|
253
|
+
await ensureDir(path.join(args.cwd, ".cursor"));
|
|
254
|
+
const content = buildHandoff(plan.file, fm, profile, preserved);
|
|
255
|
+
await writeFile(handoffPath, content, "utf8");
|
|
256
|
+
logger.success("HANDOFF.md updated: .cursor/HANDOFF.md");
|
|
257
|
+
logger.info(
|
|
258
|
+
`Plan: ${plan.file} (${fm.todos?.filter((t) => t.status === "completed").length}/${fm.todos?.length} completed)`
|
|
259
|
+
);
|
|
260
|
+
logger.info("");
|
|
261
|
+
logger.info("Suggested next steps:");
|
|
262
|
+
let n = 1;
|
|
263
|
+
for (const line of buildRoutines(profile)) {
|
|
264
|
+
logger.info(` ${n}. ${line.replace(/^- \[ \] /, "")}`);
|
|
265
|
+
n += 1;
|
|
266
|
+
}
|
|
267
|
+
logger.info("");
|
|
268
|
+
logger.info("To resume: open new conversation and say /continue-plan");
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
logger.warn(`Plan ${plan.file} without valid frontmatter; trying legacy flow.`);
|
|
272
|
+
}
|
|
273
|
+
const scriptPath = path.join(args.cwd, "cursor-handoff");
|
|
274
|
+
if (!await fileExists(scriptPath)) {
|
|
275
|
+
printV3Guidance();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (process.platform === "win32") {
|
|
279
|
+
logger.warn(
|
|
280
|
+
"cursor-handoff is a shell script. On Windows, run in Git Bash or WSL: sh cursor-handoff handoff"
|
|
281
|
+
);
|
|
282
|
+
printV3Guidance();
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
const code = await runCursorHandoff(scriptPath, args.cwd);
|
|
287
|
+
if (code !== 0) {
|
|
288
|
+
process.exitCode = code;
|
|
289
|
+
}
|
|
290
|
+
} catch (err) {
|
|
291
|
+
logger.warn(`Failed to execute cursor-handoff: ${String(err)}`);
|
|
292
|
+
printV3Guidance();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
export {
|
|
297
|
+
HANDOFF_PRUNE_DEFAULT_KEEP,
|
|
298
|
+
buildRoutines,
|
|
299
|
+
handoffCommand,
|
|
300
|
+
loadProfile,
|
|
301
|
+
runPrune
|
|
302
|
+
};
|