agent-yes 1.226.1 → 1.227.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/dist/{SUPPORTED_CLIS-xM8S6xqo.js → SUPPORTED_CLIS-CFd1V2O5.js} +2 -2
- package/dist/SUPPORTED_CLIS-CJiLdm01.js +11 -0
- package/dist/{agent-yes.config-DmW_TVFI.js → agent-yes.config-0G11aPWe.js} +2 -2
- package/dist/{agentShare-DSYm87vH.js → agentShare-C_rydTfo.js} +7 -7
- package/dist/{callback-DlzoC3fU.js → callback-BV3AhWeB.js} +5 -5
- package/dist/callback-BgWZmpqC.js +12 -0
- package/dist/{callbackCore-DSxbO_6a.js → callbackCore-B3DNe_SP.js} +1 -1
- package/dist/{callbackCore-CgnOT1Cp.js → callbackCore-BlNdDILy.js} +1 -1
- package/dist/cli.js +9 -9
- package/dist/{configShared-nnIwDYY-.js → configShared-YKfS6NMe.js} +1 -1
- package/dist/{cwdConflictWarn-CnxkLs7T.js → cwdConflictWarn-CJZRp4S0.js} +1 -1
- package/dist/{e2e-D5jaSHyY.js → e2e-DB7BDdEA.js} +1 -1
- package/dist/{expose-CKWXAkzK.js → expose-GWRXTGpj.js} +1 -1
- package/dist/{forkNested-DJ2QzBsc.js → forkNested-BWdv2mXC.js} +1 -1
- package/dist/index.js +3 -4
- package/dist/{nodeRuntime-CR0NwnnO.js → nodeRuntime-CgXWHe9p.js} +1 -1
- package/dist/{notifyDaemon-4qxS_FjO.js → notifyDaemon-CyLvaAwc.js} +6 -6
- package/dist/{openBrowser-D9VVE6cq.js → openBrowser-CSfI8zCG.js} +1 -1
- package/dist/{remotes-DZ_G2wkM.js → remotes-BCgowsiu.js} +3 -3
- package/dist/{remotes-BtXom7Rp.js → remotes-ZqD0FHl_.js} +3 -3
- package/dist/{rustBinary-KhrcXvB6.js → rustBinary-DHgMI1uK.js} +2 -2
- package/dist/{schedule-HHBT6eGE.js → schedule-0p4V_khb.js} +7 -7
- package/dist/{serve-BeHHkDIn.js → serve-C2KCUxTP.js} +29 -29
- package/dist/{setup-nAQJffmr.js → setup-CCKS4rtW.js} +3 -3
- package/dist/{share-BwEUEHfy.js → share-CrvxZO7o.js} +2 -2
- package/dist/{share-B7VP4_UF.js → share-ieE7D0C1.js} +2 -2
- package/dist/{spawnGate-CfcpmQmk.js → spawnGate-CIC7b7nm.js} +1 -1
- package/dist/{spawnGate-ChYCj6Dm.js → spawnGate-CeFYH-Ib.js} +2 -2
- package/dist/{subcommands-DYeJJ2Rh.js → subcommands-CsVEI376.js} +5 -5
- package/dist/{subcommands-D8zJxifk.js → subcommands-FTkZkqKy.js} +15 -15
- package/dist/{todoAutomation-fxtfT_44.js → todoAutomation-Cwnh4aWo.js} +300 -4
- package/dist/todoCli-6v1CYdxN.js +427 -0
- package/dist/{tray-BZqJtEmL.js → tray-DBt8X-Qj.js} +1 -1
- package/dist/{ts-yPc1x2-7.js → ts-CT75CpeM.js} +3 -3
- package/dist/{versionChecker-8h_rjT-C.js → versionChecker-Bd09mqkZ.js} +2 -2
- package/dist/{webrtcLink-DknpJQ0y.js → webrtcLink-CjckgIZe.js} +2 -2
- package/dist/{webrtcRemote-C974zjSt.js → webrtcRemote-BMzEt9Cp.js} +3 -3
- package/dist/{workspaceConfig-BRl-GwSN.js → workspaceConfig-De5tgjeO.js} +1 -1
- package/dist/{ws-YCNt3rNz.js → ws-B4zHUwc6.js} +7 -7
- package/package.json +1 -1
- package/ts/todoCli.spec.ts +80 -5
- package/ts/todoCli.ts +471 -336
- package/ts/todoStore.spec.ts +58 -0
- package/ts/todoStore.ts +42 -0
- package/dist/SUPPORTED_CLIS-C5vBin1r.js +0 -11
- package/dist/callback-BmgFE-2j.js +0 -12
- package/dist/todoCli-D8bL164c.js +0 -336
- package/dist/todoDigest-DSTG9ksk.js +0 -284
- package/dist/todoDigest-gKP1XibO.js +0 -3
|
@@ -3,9 +3,9 @@ import { n as logger } from "./logger-CDIsZ-Pp.js";
|
|
|
3
3
|
import { t as agentYesHome } from "./agentYesHome-CtHb5b71.js";
|
|
4
4
|
import { a as updateGlobalPidStatus, i as readGlobalPids } from "./globalPidIndex-CoNr7tS8.js";
|
|
5
5
|
import { a as recordOutbox, i as recordMessage, n as readMailbox, t as partyMatches } from "./messageLog-Bb_Ie3Ck.js";
|
|
6
|
-
import { t as loadSharedCliDefaults } from "./configShared-
|
|
7
|
-
import { n as isWebrtcSpec } from "./webrtcLink-
|
|
8
|
-
import { a as resolveRemoteSpec, i as readRemotes } from "./remotes-
|
|
6
|
+
import { t as loadSharedCliDefaults } from "./configShared-YKfS6NMe.js";
|
|
7
|
+
import { n as isWebrtcSpec } from "./webrtcLink-CjckgIZe.js";
|
|
8
|
+
import { a as resolveRemoteSpec, i as readRemotes } from "./remotes-ZqD0FHl_.js";
|
|
9
9
|
import ms from "ms";
|
|
10
10
|
import yargs from "yargs";
|
|
11
11
|
import { appendFile, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "fs/promises";
|
|
@@ -1239,35 +1239,35 @@ async function runSubcommand(argv) {
|
|
|
1239
1239
|
case "restart": return await cmdRestart(rest);
|
|
1240
1240
|
case "note": return await cmdNote(rest);
|
|
1241
1241
|
case "todo": {
|
|
1242
|
-
const { runTodoSubcommand } = await import("./todoCli-
|
|
1242
|
+
const { runTodoSubcommand } = await import("./todoCli-6v1CYdxN.js");
|
|
1243
1243
|
return runTodoSubcommand(rest);
|
|
1244
1244
|
}
|
|
1245
1245
|
case "serve": {
|
|
1246
|
-
const { cmdServe } = await import("./serve-
|
|
1246
|
+
const { cmdServe } = await import("./serve-C2KCUxTP.js");
|
|
1247
1247
|
return cmdServe(rest);
|
|
1248
1248
|
}
|
|
1249
1249
|
case "setup": {
|
|
1250
|
-
const { cmdSetup } = await import("./setup-
|
|
1250
|
+
const { cmdSetup } = await import("./setup-CCKS4rtW.js");
|
|
1251
1251
|
return cmdSetup(rest);
|
|
1252
1252
|
}
|
|
1253
1253
|
case "ws": {
|
|
1254
|
-
const { cmdWs } = await import("./ws-
|
|
1254
|
+
const { cmdWs } = await import("./ws-B4zHUwc6.js");
|
|
1255
1255
|
return cmdWs(rest);
|
|
1256
1256
|
}
|
|
1257
1257
|
case "schedule": {
|
|
1258
|
-
const { cmdSchedule } = await import("./schedule-
|
|
1258
|
+
const { cmdSchedule } = await import("./schedule-0p4V_khb.js");
|
|
1259
1259
|
return cmdSchedule(rest);
|
|
1260
1260
|
}
|
|
1261
1261
|
case "remote": {
|
|
1262
|
-
const { cmdRemote } = await import("./remotes-
|
|
1262
|
+
const { cmdRemote } = await import("./remotes-BCgowsiu.js");
|
|
1263
1263
|
return cmdRemote(rest);
|
|
1264
1264
|
}
|
|
1265
1265
|
case "expose": {
|
|
1266
|
-
const { cmdExpose } = await import("./expose-
|
|
1266
|
+
const { cmdExpose } = await import("./expose-GWRXTGpj.js");
|
|
1267
1267
|
return cmdExpose(rest);
|
|
1268
1268
|
}
|
|
1269
1269
|
case "callback": {
|
|
1270
|
-
const { cmdCallback } = await import("./callback-
|
|
1270
|
+
const { cmdCallback } = await import("./callback-BgWZmpqC.js");
|
|
1271
1271
|
return cmdCallback(rest);
|
|
1272
1272
|
}
|
|
1273
1273
|
case "reap":
|
|
@@ -1685,7 +1685,7 @@ async function fetchRemoteRecordsRaw(url, token, opts) {
|
|
|
1685
1685
|
let base = url;
|
|
1686
1686
|
let bearer = token;
|
|
1687
1687
|
if (isWebrtcSpec(url)) {
|
|
1688
|
-
const { startWebrtcBridge } = await import("./webrtcRemote-
|
|
1688
|
+
const { startWebrtcBridge } = await import("./webrtcRemote-BMzEt9Cp.js");
|
|
1689
1689
|
bridge = await startWebrtcBridge(url);
|
|
1690
1690
|
base = bridge.baseUrl;
|
|
1691
1691
|
bearer = bridge.token;
|
|
@@ -4305,7 +4305,7 @@ async function cmdNotify(rest) {
|
|
|
4305
4305
|
}
|
|
4306
4306
|
const ensure = async () => {
|
|
4307
4307
|
if (!argv["ensure-daemon"]) return;
|
|
4308
|
-
const { ensureDaemon } = await import("./notifyDaemon-
|
|
4308
|
+
const { ensureDaemon } = await import("./notifyDaemon-CyLvaAwc.js");
|
|
4309
4309
|
await ensureDaemon().catch(() => null);
|
|
4310
4310
|
};
|
|
4311
4311
|
await heartbeatWatcher(parent, selfStartedAt);
|
|
@@ -4379,7 +4379,7 @@ async function cmdNotifyCursor(args) {
|
|
|
4379
4379
|
}
|
|
4380
4380
|
async function cmdNotifyd(rest) {
|
|
4381
4381
|
const sub = rest[0] ?? "status";
|
|
4382
|
-
const daemon = await import("./notifyDaemon-
|
|
4382
|
+
const daemon = await import("./notifyDaemon-CyLvaAwc.js");
|
|
4383
4383
|
switch (sub) {
|
|
4384
4384
|
case "run": return daemon.runDaemon();
|
|
4385
4385
|
case "once": return daemon.runDaemon({ once: true });
|
|
@@ -4406,4 +4406,4 @@ async function cmdNotifyd(rest) {
|
|
|
4406
4406
|
|
|
4407
4407
|
//#endregion
|
|
4408
4408
|
export { daemonLockDir as $, renderLogTailLines as A, stopTipForCli as B, matchKeyword as C, readPtysize as D, readNotes as E, resolveResumeArgs as F, appendEvent as G, waitForLogQuiet as H, restartHintLines as I, isTransientLockMkdirError as J, gcInboxes as K, runSubcommand as L, renderRawLogLines as M, resolveOne as N, recentMessageEdges as O, resolveReadWindow as P, shouldStealLock as Q, snapshotStatus as R, listRecords as S, readAgentPtysize as T, writeKeysPaced as U, submitAndConfirm as V, writeToIpc as W, liveWatchers as X, listInboxParents as Y, readInbox as Z, isPidAlive as _, cmdHelp as a, isUserTyping as b, deriveLiveState as c, extractMenu as d, daemonLockOwnerPath as et, extractNeedsInput as f, isExitRequest as g, isAgentStuck as h, backoffWhileTyping as i, renderRawLog as j, recentReadEdges as k, deriveLiveStatus as l, finalizedLines as m, READ_PAGE_DEFAULT as n, TYPING_BADGE as nt, controlCodeFromName as o, extractTaskCounts as p, hostId as q, TYPING_WINDOW_MS as r, cursorAbs as s, GRACEFUL_EXIT_COMMANDS as t, notifyDir as tt, extractBadges as u, isSlashCommand as v, menuSelectKeys as w, lastStdinAt as x, isSubcommand as y, stdinActivityPath as z };
|
|
4409
|
-
//# sourceMappingURL=subcommands-
|
|
4409
|
+
//# sourceMappingURL=subcommands-FTkZkqKy.js.map
|
|
@@ -1,8 +1,152 @@
|
|
|
1
1
|
import { t as JsonlStore } from "./JsonlStore-Bm5Nj6v_.js";
|
|
2
|
-
import { a as unblockedTasks, c as ORPHANED_STATE, l as canTransition, o as DONE_STATE, p as requiredGate, s as LIFECYCLES, u as initialState } from "./todoDigest-DSTG9ksk.js";
|
|
3
2
|
import path from "path";
|
|
4
3
|
import { lock } from "proper-lockfile";
|
|
5
4
|
|
|
5
|
+
//#region ts/todoLifecycle.ts
|
|
6
|
+
/**
|
|
7
|
+
* States considered "finished" across every kind. A finished task is never
|
|
8
|
+
* reopened by automation (see `todoAutomation.ts` in a later milestone) —
|
|
9
|
+
* only a human explicitly reopening it (e.g. `verify-failed` → the kind's
|
|
10
|
+
* doing state) moves a task out of a finished-adjacent state.
|
|
11
|
+
*/
|
|
12
|
+
const DONE_STATE = "done";
|
|
13
|
+
/**
|
|
14
|
+
* Not a normal graph edge on any kind's `transitions` list — a task can be
|
|
15
|
+
* moved here from ANY state, by automation only (see `todoAutomation.ts`),
|
|
16
|
+
* when its owning agent process has vanished. Kept out of the declarative
|
|
17
|
+
* graphs deliberately: it is a side-channel signal about the WORKER, not a
|
|
18
|
+
* statement about the work itself, so it should never appear as a normal
|
|
19
|
+
* `canTransition`-reachable edge a human/CLI could aim for directly.
|
|
20
|
+
*/
|
|
21
|
+
const ORPHANED_STATE = "orphaned";
|
|
22
|
+
const LIFECYCLES = {
|
|
23
|
+
code: {
|
|
24
|
+
states: [
|
|
25
|
+
"doing",
|
|
26
|
+
"merged",
|
|
27
|
+
"shipped",
|
|
28
|
+
"verifying",
|
|
29
|
+
"done",
|
|
30
|
+
"verify-failed",
|
|
31
|
+
"orphaned"
|
|
32
|
+
],
|
|
33
|
+
transitions: [
|
|
34
|
+
{
|
|
35
|
+
from: "doing",
|
|
36
|
+
to: "merged"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
from: "merged",
|
|
40
|
+
to: "shipped"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
from: "shipped",
|
|
44
|
+
to: "verifying"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
from: "verifying",
|
|
48
|
+
to: "done",
|
|
49
|
+
gate: "verify-green"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
from: "verifying",
|
|
53
|
+
to: "verify-failed",
|
|
54
|
+
gate: "verify-red"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
from: "verify-failed",
|
|
58
|
+
to: "doing"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
decision: {
|
|
63
|
+
states: [
|
|
64
|
+
"deciding",
|
|
65
|
+
"decided",
|
|
66
|
+
"done"
|
|
67
|
+
],
|
|
68
|
+
transitions: [{
|
|
69
|
+
from: "deciding",
|
|
70
|
+
to: "decided",
|
|
71
|
+
gate: "human-decided"
|
|
72
|
+
}, {
|
|
73
|
+
from: "decided",
|
|
74
|
+
to: "done"
|
|
75
|
+
}]
|
|
76
|
+
},
|
|
77
|
+
doc: {
|
|
78
|
+
states: [
|
|
79
|
+
"drafting",
|
|
80
|
+
"review",
|
|
81
|
+
"done"
|
|
82
|
+
],
|
|
83
|
+
transitions: [{
|
|
84
|
+
from: "drafting",
|
|
85
|
+
to: "review"
|
|
86
|
+
}, {
|
|
87
|
+
from: "review",
|
|
88
|
+
to: "done",
|
|
89
|
+
gate: "human-approved"
|
|
90
|
+
}]
|
|
91
|
+
},
|
|
92
|
+
investigation: {
|
|
93
|
+
states: [
|
|
94
|
+
"investigating",
|
|
95
|
+
"reported",
|
|
96
|
+
"done"
|
|
97
|
+
],
|
|
98
|
+
transitions: [{
|
|
99
|
+
from: "investigating",
|
|
100
|
+
to: "reported"
|
|
101
|
+
}, {
|
|
102
|
+
from: "reported",
|
|
103
|
+
to: "done",
|
|
104
|
+
gate: "human-acknowledged"
|
|
105
|
+
}]
|
|
106
|
+
},
|
|
107
|
+
human: {
|
|
108
|
+
states: [
|
|
109
|
+
"pending",
|
|
110
|
+
"decided",
|
|
111
|
+
"done"
|
|
112
|
+
],
|
|
113
|
+
transitions: [{
|
|
114
|
+
from: "pending",
|
|
115
|
+
to: "decided",
|
|
116
|
+
gate: "human-replied"
|
|
117
|
+
}, {
|
|
118
|
+
from: "decided",
|
|
119
|
+
to: "done"
|
|
120
|
+
}]
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
/** The state a newly-created task of `kind` starts in (its graph's first state). */
|
|
124
|
+
function initialState(kind) {
|
|
125
|
+
const first = LIFECYCLES[kind].states[0];
|
|
126
|
+
if (!first) throw new Error(`lifecycle kind "${kind}" has no states`);
|
|
127
|
+
return first;
|
|
128
|
+
}
|
|
129
|
+
/** States reachable in one transition from `currentState`, regardless of gates. */
|
|
130
|
+
function nextStates(kind, currentState) {
|
|
131
|
+
return LIFECYCLES[kind].transitions.filter((t) => t.from === currentState).map((t) => t.to);
|
|
132
|
+
}
|
|
133
|
+
/** Whether `from`→`to` is a transition that exists in `kind`'s graph at all (gate not evaluated here). */
|
|
134
|
+
function canTransition(kind, from, to) {
|
|
135
|
+
return LIFECYCLES[kind].transitions.some((t) => t.from === from && t.to === to);
|
|
136
|
+
}
|
|
137
|
+
/** The gate name required for `from`→`to`, or null if the edge is ungated or does not exist. */
|
|
138
|
+
function requiredGate(kind, from, to) {
|
|
139
|
+
return LIFECYCLES[kind].transitions.find((e) => e.from === from && e.to === to)?.gate ?? null;
|
|
140
|
+
}
|
|
141
|
+
/** All valid state names for `kind` — used to validate stored records and CLI input. */
|
|
142
|
+
function statesOf(kind) {
|
|
143
|
+
return LIFECYCLES[kind].states;
|
|
144
|
+
}
|
|
145
|
+
function isKnownKind(value) {
|
|
146
|
+
return value in LIFECYCLES;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
//#endregion
|
|
6
150
|
//#region ts/todoStore.ts
|
|
7
151
|
/**
|
|
8
152
|
* Persistence and gate-enforcement layer for the `ay todo` engine.
|
|
@@ -180,6 +324,7 @@ var TodoStore = class TodoStore {
|
|
|
180
324
|
createdAt: now,
|
|
181
325
|
updatedAt: now,
|
|
182
326
|
...input.targetTier ? { targetTier: input.targetTier } : {},
|
|
327
|
+
...input.acceptanceCriteria?.trim() ? { acceptanceCriteria: input.acceptanceCriteria.trim() } : {},
|
|
183
328
|
...input.owner ? { owner: input.owner } : {}
|
|
184
329
|
};
|
|
185
330
|
await this.jsonl.append({
|
|
@@ -240,7 +385,8 @@ var TodoStore = class TodoStore {
|
|
|
240
385
|
link: evidence?.link,
|
|
241
386
|
gate: gateName,
|
|
242
387
|
passedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
243
|
-
validator
|
|
388
|
+
validator,
|
|
389
|
+
...rec.acceptanceCriteria ? { acceptanceCriteriaAtApproval: rec.acceptanceCriteria } : {}
|
|
244
390
|
};
|
|
245
391
|
await this.jsonl.updateById(id, {
|
|
246
392
|
satisfiedGates: [...satisfied],
|
|
@@ -339,6 +485,19 @@ var TodoStore = class TodoStore {
|
|
|
339
485
|
return this.mustGet(id);
|
|
340
486
|
});
|
|
341
487
|
}
|
|
488
|
+
/**
|
|
489
|
+
* Set or update a task's acceptance criteria (free text) after creation —
|
|
490
|
+
* criteria may firm up once a task is underway, not just at `create()`
|
|
491
|
+
* time. Requires non-empty text: this method updates criteria, it does
|
|
492
|
+
* not clear them (clearing would need a `null`-vs-`undefined` sentinel
|
|
493
|
+
* distinction, like `block` has, which isn't needed for the "set/update"
|
|
494
|
+
* use case this exists for).
|
|
495
|
+
*/
|
|
496
|
+
async setAcceptanceCriteria(id, text) {
|
|
497
|
+
const trimmed = text.trim();
|
|
498
|
+
if (!trimmed) throw new Error(`task ${id}: acceptance criteria text must not be empty`);
|
|
499
|
+
return this.rawUpdate(id, () => ({ acceptanceCriteria: trimmed }));
|
|
500
|
+
}
|
|
342
501
|
setBlock(id, block) {
|
|
343
502
|
return this.rawUpdate(id, () => ({ block }));
|
|
344
503
|
}
|
|
@@ -449,6 +608,143 @@ function describeBlock(block) {
|
|
|
449
608
|
}
|
|
450
609
|
}
|
|
451
610
|
|
|
611
|
+
//#endregion
|
|
612
|
+
//#region ts/todoDigest.ts
|
|
613
|
+
/**
|
|
614
|
+
* Read-side views over the `ay todo` store: dependency-tree rendering and a
|
|
615
|
+
* per-tag digest board. Pure functions over an already-loaded task list — no
|
|
616
|
+
* I/O — so they are trivially unit-testable and reusable by both the CLI
|
|
617
|
+
* (`todoCli.ts`) and any future web view.
|
|
618
|
+
*
|
|
619
|
+
* `unblockedTasks` is ported near-verbatim from the equivalent, already
|
|
620
|
+
* mutation-tested algorithm in symval-dev-cli's `deps.ts` (a closed-source
|
|
621
|
+
* downstream consumer of an earlier, less general version of this idea) —
|
|
622
|
+
* generalized here to the new `TodoRecord` shape and the `done` state name
|
|
623
|
+
* from `todoLifecycle.ts` instead of a hardcoded status string.
|
|
624
|
+
*/
|
|
625
|
+
/**
|
|
626
|
+
* The full set of task ids `t` is waiting on: the structural `blockedBy`
|
|
627
|
+
* array PLUS, if `t.block` is a `blocked-by-task` shape, that block's own
|
|
628
|
+
* `taskId`. Without folding the latter in, a task blocked via
|
|
629
|
+
* `ay todo block --type blocked-by-task --task X` would never be detected
|
|
630
|
+
* as resolved once X reaches `done` — contradicting `todoBlock.ts`'s own
|
|
631
|
+
* documented promise that this block type "clears itself... pure data, no
|
|
632
|
+
* monitor needed" (codex-review Important: the two mechanisms didn't
|
|
633
|
+
* actually talk to each other, making `blocked-by-task` blocks inert).
|
|
634
|
+
*/
|
|
635
|
+
function declaredTaskDeps(t) {
|
|
636
|
+
return t.block?.type === "blocked-by-task" ? [...t.blockedBy, t.block.taskId] : t.blockedBy;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* A task is "unblocked" when it is not itself finished, it declares task
|
|
640
|
+
* dependencies (via `blockedBy` and/or a `blocked-by-task` block), and
|
|
641
|
+
* every one of them has reached `done`.
|
|
642
|
+
* Surfaced for a caller to act on — never auto-applied here (deciding to
|
|
643
|
+
* resume a task is left to automation, a later milestone, or a human).
|
|
644
|
+
*/
|
|
645
|
+
function unblockedTasks(tasks) {
|
|
646
|
+
const byId = new Map(tasks.map((t) => [t._id, t]));
|
|
647
|
+
return tasks.filter((t) => {
|
|
648
|
+
if (t.state === DONE_STATE) return false;
|
|
649
|
+
const deps = declaredTaskDeps(t);
|
|
650
|
+
if (deps.length === 0) return false;
|
|
651
|
+
return deps.every((d) => byId.get(d)?.state === DONE_STATE);
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
/** Blockers of `t` that have not reached `done` yet (a missing id is reported as-is, still "open"). */
|
|
655
|
+
function openBlockers(t, byId) {
|
|
656
|
+
return declaredTaskDeps(t).filter((d) => byId.get(d)?.state !== DONE_STATE);
|
|
657
|
+
}
|
|
658
|
+
function nodeLine(t) {
|
|
659
|
+
return [`${t._id} [${t.state}] ${t.summary}`, ...t.owner ? [`owner:${t.owner}`] : []].join(" ");
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Dependency tree, rendered from roots (tasks nothing depends on, that
|
|
663
|
+
* themselves declare at least one dependency) down their `blockedBy` edges:
|
|
664
|
+
* a parent's children are what it is waiting for.
|
|
665
|
+
*/
|
|
666
|
+
function renderTree(tasks, rootId) {
|
|
667
|
+
const byId = new Map(tasks.map((t) => [t._id, t]));
|
|
668
|
+
const dependedOn = new Set(tasks.flatMap((t) => t.blockedBy));
|
|
669
|
+
const roots = rootId ? [byId.get(rootId) ?? null].filter((t) => t !== null) : tasks.filter((t) => !dependedOn.has(t._id) && t.blockedBy.length > 0);
|
|
670
|
+
if (rootId && roots.length === 0) throw new Error(`no such task: ${rootId}`);
|
|
671
|
+
if (roots.length === 0) return "(no dependency links)";
|
|
672
|
+
const lines = [];
|
|
673
|
+
const walk = (t, prefix, isLast, depth, seen) => {
|
|
674
|
+
lines.push(depth === 0 ? nodeLine(t) : `${prefix}${isLast ? "└─" : "├─"} ${nodeLine(t)}`);
|
|
675
|
+
if (seen.has(t._id)) return;
|
|
676
|
+
seen.add(t._id);
|
|
677
|
+
const deps = t.blockedBy.map((d) => byId.get(d)).filter((x) => x !== void 0);
|
|
678
|
+
deps.forEach((d, i) => {
|
|
679
|
+
walk(d, depth === 0 ? "" : `${prefix}${isLast ? " " : "│ "}`, i === deps.length - 1, depth + 1, seen);
|
|
680
|
+
});
|
|
681
|
+
};
|
|
682
|
+
roots.forEach((r) => walk(r, "", true, 0, /* @__PURE__ */ new Set()));
|
|
683
|
+
return lines.join("\n");
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* JSON-shaped equivalent of `renderTree` — same roots-and-edges logic, built
|
|
687
|
+
* as data instead of formatted lines, so a machine caller (`--format json`)
|
|
688
|
+
* gets the actual tree structure rather than a string it would have to
|
|
689
|
+
* re-parse (codex-review Important: the CLI previously ignored --format for
|
|
690
|
+
* this command entirely, always writing the human text with exit code 0).
|
|
691
|
+
*/
|
|
692
|
+
function buildTreeJSON(tasks, rootId) {
|
|
693
|
+
const byId = new Map(tasks.map((t) => [t._id, t]));
|
|
694
|
+
const dependedOn = new Set(tasks.flatMap((t) => t.blockedBy));
|
|
695
|
+
const roots = rootId ? [byId.get(rootId) ?? null].filter((t) => t !== null) : tasks.filter((t) => !dependedOn.has(t._id) && t.blockedBy.length > 0);
|
|
696
|
+
if (rootId && roots.length === 0) throw new Error(`no such task: ${rootId}`);
|
|
697
|
+
const build = (t, ancestors) => {
|
|
698
|
+
const node = {
|
|
699
|
+
id: t._id,
|
|
700
|
+
state: t.state,
|
|
701
|
+
summary: t.summary,
|
|
702
|
+
...t.owner ? { owner: t.owner } : {},
|
|
703
|
+
children: []
|
|
704
|
+
};
|
|
705
|
+
if (ancestors.has(t._id)) return node;
|
|
706
|
+
const nextAncestors = new Set(ancestors).add(t._id);
|
|
707
|
+
node.children = t.blockedBy.map((d) => byId.get(d)).filter((x) => x !== void 0).map((d) => build(d, nextAncestors));
|
|
708
|
+
return node;
|
|
709
|
+
};
|
|
710
|
+
return roots.map((r) => build(r, /* @__PURE__ */ new Set()));
|
|
711
|
+
}
|
|
712
|
+
/** Per-tag board: state counts per tag, plus an unblocked-tasks callout. */
|
|
713
|
+
function renderDigest(tasks) {
|
|
714
|
+
if (tasks.length === 0) return "(no tasks)";
|
|
715
|
+
const byId = new Map(tasks.map((t) => [t._id, t]));
|
|
716
|
+
const unblocked = new Set(unblockedTasks(tasks).map((t) => t._id));
|
|
717
|
+
const streams = /* @__PURE__ */ new Map();
|
|
718
|
+
for (const t of tasks) for (const tag of t.tags.length ? t.tags : ["(untagged)"]) {
|
|
719
|
+
if (!streams.has(tag)) streams.set(tag, []);
|
|
720
|
+
streams.get(tag).push(t);
|
|
721
|
+
}
|
|
722
|
+
const lines = [];
|
|
723
|
+
for (const [stream, items] of [...streams.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
724
|
+
const allStates = [...new Set(items.map((t) => t.state))].sort();
|
|
725
|
+
const counts = allStates.map((s) => `${s}:${items.filter((t) => t.state === s).length}`).join(" ");
|
|
726
|
+
lines.push(`## ${stream} (${counts})`);
|
|
727
|
+
for (const s of allStates) for (const t of items.filter((x) => x.state === s)) {
|
|
728
|
+
const open = openBlockers(t, byId);
|
|
729
|
+
const extra = [
|
|
730
|
+
...t.owner ? [`owner:${t.owner}`] : [],
|
|
731
|
+
...t.block ? [`block:${t.block.type}`] : [],
|
|
732
|
+
...open.length ? [`blockedBy:${open.join(",")}`] : [],
|
|
733
|
+
...unblocked.has(t._id) ? ["UNBLOCKED"] : []
|
|
734
|
+
];
|
|
735
|
+
lines.push(` [${t.state}] ${t._id} ${t.summary}${extra.length ? ` [${extra.join(" ")}]` : ""}`);
|
|
736
|
+
}
|
|
737
|
+
lines.push("");
|
|
738
|
+
}
|
|
739
|
+
const ub = unblockedTasks(tasks);
|
|
740
|
+
if (ub.length) {
|
|
741
|
+
lines.push("## unblocked (all blockers reached done — resume these)");
|
|
742
|
+
for (const t of ub) lines.push(` ${t._id} ${t.summary}${t.owner ? ` (owner:${t.owner})` : ""}`);
|
|
743
|
+
lines.push("");
|
|
744
|
+
}
|
|
745
|
+
return lines.join("\n").trimEnd();
|
|
746
|
+
}
|
|
747
|
+
|
|
452
748
|
//#endregion
|
|
453
749
|
//#region ts/todoAutomation.ts
|
|
454
750
|
/**
|
|
@@ -552,5 +848,5 @@ function reconcileTodos(tasks, agents, registeredGates) {
|
|
|
552
848
|
}
|
|
553
849
|
|
|
554
850
|
//#endregion
|
|
555
|
-
export {
|
|
556
|
-
//# sourceMappingURL=todoAutomation-
|
|
851
|
+
export { isKnownKind as _, renderTree as a, statesOf as b, monitorHint as c, openStore as d, DONE_STATE as f, initialState as g, canTransition as h, renderDigest as i, CycleError as l, ORPHANED_STATE as m, buildTreeJSON as n, unblockedTasks as o, LIFECYCLES as p, openBlockers as r, describeBlock as s, reconcileTodos as t, TodoStore as u, nextStates as v, requiredGate as y };
|
|
852
|
+
//# sourceMappingURL=todoAutomation-Cwnh4aWo.js.map
|