@wrongstack/tools 0.313.0 → 0.316.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/dist/_edit-match.d.ts +3 -2
- package/dist/_output-spool.d.ts +4 -3
- package/dist/_shell-pick.d.ts +2 -1
- package/dist/_spawn-stream.d.ts +2 -1
- package/dist/_syntax-check.d.ts +2 -1
- package/dist/audit.js +10 -4055
- package/dist/bash-kill-guard.d.ts +3 -6
- package/dist/bash.js +9 -2308
- package/dist/batch-tool-use.js +2 -187
- package/dist/browser/index.js +25 -1339
- package/dist/browser/network-guard-proxy.d.ts +2 -1
- package/dist/builtin.js +69 -33949
- package/dist/chunk-22B7GE5T.js +260 -0
- package/dist/chunk-2VSZC6QM.js +113 -0
- package/dist/chunk-3PVYQFRQ.js +472 -0
- package/dist/chunk-42BHI7TN.js +337 -0
- package/dist/chunk-4BHUSMNL.js +104 -0
- package/dist/chunk-4I4LWFOO.js +431 -0
- package/dist/chunk-4UESMQDP.js +122 -0
- package/dist/chunk-553IQUMO.js +478 -0
- package/dist/chunk-5H4GSJAI.js +223 -0
- package/dist/chunk-6RNIFLG4.js +305 -0
- package/dist/chunk-7EY5DCRO.js +434 -0
- package/dist/chunk-A6BRWIXN.js +317 -0
- package/dist/chunk-AHHDWNXC.js +200 -0
- package/dist/chunk-BSLVR56S.js +199 -0
- package/dist/chunk-BTSFTGIL.js +7 -0
- package/dist/chunk-D33S4RQG.js +222 -0
- package/dist/chunk-DA4KNE2H.js +4412 -0
- package/dist/chunk-DR7JOQTC.js +520 -0
- package/dist/chunk-EEOFQDT6.js +898 -0
- package/dist/chunk-EJWSCQ6D.js +137 -0
- package/dist/chunk-EMRGN4KM.js +170 -0
- package/dist/chunk-FQZOHLG3.js +89 -0
- package/dist/chunk-GOIZNNOA.js +221 -0
- package/dist/chunk-HFXSNKCV.js +196 -0
- package/dist/chunk-HYX2TZ7O.js +1272 -0
- package/dist/chunk-I64BS3VC.js +15 -0
- package/dist/chunk-IVWV2UM6.js +955 -0
- package/dist/chunk-IXMNTNW5.js +238 -0
- package/dist/chunk-IYK7XGDZ.js +892 -0
- package/dist/chunk-J5SKD6LN.js +235 -0
- package/dist/chunk-JNNVRHMV.js +2111 -0
- package/dist/chunk-JOHXDF3D.js +295 -0
- package/dist/chunk-JRJJX3L5.js +424 -0
- package/dist/chunk-K3WNSHUF.js +1371 -0
- package/dist/chunk-L25CVUVR.js +2830 -0
- package/dist/chunk-LIJQMYYX.js +59 -0
- package/dist/chunk-LWKUTMD5.js +197 -0
- package/dist/chunk-MQX2KXVW.js +54 -0
- package/dist/chunk-MZZN7VCD.js +359 -0
- package/dist/chunk-NBDZMMG2.js +169 -0
- package/dist/chunk-NKJW5GFO.js +366 -0
- package/dist/chunk-NKNOBUBU.js +522 -0
- package/dist/chunk-O4MBV4AX.js +134 -0
- package/dist/chunk-OUBDL3WH.js +3515 -0
- package/dist/chunk-PQKK5JH7.js +101 -0
- package/dist/chunk-PY2IOS7C.js +1000 -0
- package/dist/chunk-Q5JPPCZC.js +12 -0
- package/dist/chunk-QBV725YG.js +128 -0
- package/dist/chunk-QZQSPUBF.js +315 -0
- package/dist/chunk-RGISBZBD.js +231 -0
- package/dist/chunk-RJZQWEMS.js +84 -0
- package/dist/chunk-RXISBFR5.js +315 -0
- package/dist/chunk-S5CCLZL3.js +99 -0
- package/dist/chunk-SDLVH5RZ.js +178 -0
- package/dist/chunk-T2F4S7U4.js +1723 -0
- package/dist/chunk-TEBQ4VPZ.js +163 -0
- package/dist/chunk-U4D5S3VQ.js +21 -0
- package/dist/chunk-U4VAATKP.js +193 -0
- package/dist/chunk-UCYQTZ7F.js +113 -0
- package/dist/chunk-UKU2ZX4C.js +203 -0
- package/dist/chunk-UNSKG6DC.js +159 -0
- package/dist/chunk-V43EQ6VR.js +68 -0
- package/dist/chunk-VB3MJGCV.js +610 -0
- package/dist/chunk-VFKPWSOX.js +154 -0
- package/dist/chunk-VJNHFZ7J.js +190 -0
- package/dist/chunk-VXECAJJ7.js +1091 -0
- package/dist/chunk-WHIIIREL.js +223 -0
- package/dist/chunk-Z6DAKQ3U.js +1 -0
- package/dist/chunk-ZJQ4IGXK.js +474 -0
- package/dist/chunk-ZLTFLC25.js +385 -0
- package/dist/circuit-breaker.js +3 -191
- package/dist/codebase-index/ast-invariant-engine.d.ts +5 -4
- package/dist/codebase-index/binary-frame.d.ts +0 -2
- package/dist/codebase-index/bm25.d.ts +1 -1
- package/dist/codebase-index/circuit-breaker.d.ts +2 -1
- package/dist/codebase-index/codebase-ast-replace-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-impact-analysis-tool.d.ts +4 -3
- package/dist/codebase-index/codebase-invariant-check-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-repo-map-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-skeleton-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-targeted-test-tool.d.ts +3 -2
- package/dist/codebase-index/content-hash.d.ts +0 -2
- package/dist/codebase-index/import-extractor.d.ts +0 -13
- package/dist/codebase-index/index-service.d.ts +1 -1
- package/dist/codebase-index/index.js +84 -13688
- package/dist/codebase-index/languages.d.ts +2 -1
- package/dist/codebase-index/module-roots.d.ts +3 -2
- package/dist/codebase-index/parser-batch.d.ts +0 -5
- package/dist/codebase-index/parser-dispatch.d.ts +2 -1
- package/dist/codebase-index/parser-output.d.ts +4 -3
- package/dist/codebase-index/parser-worker-pool.d.ts +0 -2
- package/dist/codebase-index/parser-worker-script.d.ts +1 -16
- package/dist/codebase-index/parser-worker-script.js +8 -3995
- package/dist/codebase-index/project-server-client-state.d.ts +2 -2
- package/dist/codebase-index/project-server-endpoint.d.ts +0 -15
- package/dist/codebase-index/project-server.js +60 -8548
- package/dist/codebase-index/tree-sitter/visitor.d.ts +2 -1
- package/dist/codebase-index/tree-sitter-parser.d.ts +0 -2
- package/dist/codebase-index/ts-parser.d.ts +1 -1
- package/dist/codebase-index/vector-search.d.ts +2 -1
- package/dist/codebase-index/wal-maintenance.d.ts +3 -2
- package/dist/codebase-index/worker.js +20 -8416
- package/dist/codebase-index/writer-admin.d.ts +0 -1
- package/dist/codebase-index/writer-search.d.ts +0 -9
- package/dist/design.js +2 -299
- package/dist/diff.js +4 -256
- package/dist/document.js +4 -222
- package/dist/e2e.js +5 -476
- package/dist/edit.js +14 -10200
- package/dist/exec.js +16 -3131
- package/dist/fetch.js +8 -398
- package/dist/format.js +10 -3750
- package/dist/generic-parser-VUXDIAOZ.js +13 -0
- package/dist/git.js +4 -380
- package/dist/glob.js +6 -280
- package/dist/go-parser-JXYKJHRC.js +469 -0
- package/dist/grep.js +7 -604
- package/dist/index.js +318 -35387
- package/dist/install.js +10 -4091
- package/dist/json-parser-IBHZ4BZC.js +213 -0
- package/dist/json.js +4 -665
- package/dist/kanban-contract-actions.d.ts +1 -1
- package/dist/kanban-detail-actions.d.ts +2 -1
- package/dist/kanban-presence.d.ts +2 -1
- package/dist/kanban-serializer.d.ts +0 -8
- package/dist/kanban-split-task-handler.d.ts +2 -2
- package/dist/kanban-tool-types.d.ts +2 -1
- package/dist/kanban.js +3 -2096
- package/dist/languages/diagnostics.d.ts +2 -1
- package/dist/languages/index.js +23 -4664
- package/dist/languages/legacy-bridge.d.ts +3 -2
- package/dist/languages/types.d.ts +2 -1
- package/dist/legacy-bridge-7M6QLUQN.js +18 -0
- package/dist/lint.js +10 -3706
- package/dist/logs.js +4 -287
- package/dist/memory.js +6 -215
- package/dist/mode.js +3 -99
- package/dist/outdated.js +4 -5170
- package/dist/pack.js +65 -33897
- package/dist/parser-dispatch-QH4XPGG6.js +14 -0
- package/dist/patch.js +13 -9935
- package/dist/plan.js +5 -3286
- package/dist/process-registry.js +8 -651
- package/dist/ps-slash.js +12 -1439
- package/dist/pwsh.d.ts +0 -1
- package/dist/py-parser-IS4HKYMF.js +16 -0
- package/dist/read.js +13 -9816
- package/dist/replace.js +15 -10014
- package/dist/rs-parser-XHUUO2KZ.js +81 -0
- package/dist/scaffold.js +4 -253
- package/dist/search.js +5 -585
- package/dist/session-kanban-sync.d.ts +0 -6
- package/dist/session-kanban.js +30 -972
- package/dist/skill.js +2 -215
- package/dist/spawn-background.d.ts +1 -1
- package/dist/task.js +5 -3410
- package/dist/test.js +10 -3773
- package/dist/todo.js +4 -2891
- package/dist/tool-help.js +3 -123
- package/dist/tool-search.js +3 -79
- package/dist/tool-tier.js +68 -33984
- package/dist/tool-use.js +2 -93
- package/dist/tree-sitter-parser-AIERW3YI.js +15 -0
- package/dist/tree.js +4 -288
- package/dist/ts-parser-RCTYKSVI.js +232 -0
- package/dist/typecheck.js +10 -3753
- package/dist/win32.js +6 -60
- package/dist/write.js +14 -9882
- package/dist/yaml-parser-4SAXV3PL.js +157 -0
- package/package.json +5 -5
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import {
|
|
2
|
+
kanbanTool
|
|
3
|
+
} from "./chunk-JNNVRHMV.js";
|
|
4
|
+
import {
|
|
5
|
+
applyManagedKanbanBoardToTodos,
|
|
6
|
+
blockingTitles,
|
|
7
|
+
mirrorSessionTodosToKanban,
|
|
8
|
+
takeSessionMirrorFailure
|
|
9
|
+
} from "./chunk-PY2IOS7C.js";
|
|
10
|
+
|
|
11
|
+
// src/todo.ts
|
|
12
|
+
import {
|
|
13
|
+
loadPlan,
|
|
14
|
+
loadTasks,
|
|
15
|
+
savePlan,
|
|
16
|
+
saveTasks,
|
|
17
|
+
setPlanItemStatus
|
|
18
|
+
} from "@wrongstack/core/storage";
|
|
19
|
+
import { addTask, getBoard } from "@wrongstack/kanban";
|
|
20
|
+
function normalizedTitle(value) {
|
|
21
|
+
return value.trim().toLocaleLowerCase().replace(/\s+/g, " ");
|
|
22
|
+
}
|
|
23
|
+
function activeBoardId(items, ctx) {
|
|
24
|
+
const metaKanban = ctx.meta?.["kanban"];
|
|
25
|
+
const metaBoardId = metaKanban && typeof metaKanban === "object" ? metaKanban["boardId"] : void 0;
|
|
26
|
+
return ctx.currentKanbanBoardId ?? (typeof metaBoardId === "string" ? metaBoardId : void 0) ?? items.find((item) => item.kanbanBoardId)?.kanbanBoardId ?? "";
|
|
27
|
+
}
|
|
28
|
+
function bindTodosToBoard(items, previous, board) {
|
|
29
|
+
const previousById = new Map(previous.map((item) => [item.id, item]));
|
|
30
|
+
const available = board.tasks.filter(
|
|
31
|
+
(task) => task.status !== "archived" && task.mergedIntoTaskId === void 0 && (!task.childTaskIds || task.childTaskIds.length === 0)
|
|
32
|
+
).sort(
|
|
33
|
+
(left, right) => left.createdAt.localeCompare(right.createdAt) || left.order - right.order
|
|
34
|
+
);
|
|
35
|
+
const used = /* @__PURE__ */ new Set();
|
|
36
|
+
return items.map((item) => {
|
|
37
|
+
const previousItem = previousById.get(item.id);
|
|
38
|
+
const requestedTaskId = item.kanbanBoardId === board.id ? item.kanbanTaskId : previousItem?.kanbanBoardId === board.id ? previousItem.kanbanTaskId : void 0;
|
|
39
|
+
const title = normalizedTitle(item.content);
|
|
40
|
+
const candidates = [
|
|
41
|
+
requestedTaskId ? board.tasks.find((task2) => task2.id === requestedTaskId) : void 0,
|
|
42
|
+
board.tasks.find((task2) => task2.id === item.id),
|
|
43
|
+
board.tasks.find((task2) => task2.origin?.taskId === item.id),
|
|
44
|
+
available.find((task2) => !used.has(task2.id) && normalizedTitle(task2.title) === title)
|
|
45
|
+
];
|
|
46
|
+
const task = candidates.find((candidate) => candidate && !used.has(candidate.id));
|
|
47
|
+
if (!task) {
|
|
48
|
+
const { blockedBy: _discarded, ...rest } = item;
|
|
49
|
+
return { ...rest };
|
|
50
|
+
}
|
|
51
|
+
used.add(task.id);
|
|
52
|
+
const blockedBy = blockingTitles(board, task);
|
|
53
|
+
return {
|
|
54
|
+
...item,
|
|
55
|
+
kanbanBoardId: board.id,
|
|
56
|
+
kanbanTaskId: task.id,
|
|
57
|
+
...blockedBy.length ? { blockedBy } : { blockedBy: void 0 }
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function demoteBlockedInProgress(items, warnings) {
|
|
62
|
+
return items.map((item) => {
|
|
63
|
+
if (item.status !== "in_progress" || !item.blockedBy?.length) return item;
|
|
64
|
+
warnings.push(
|
|
65
|
+
`"${item.content}" cannot start yet \u2014 it waits on: ${item.blockedBy.join("; ")}. Kept as pending; complete the blocking work first.`
|
|
66
|
+
);
|
|
67
|
+
return { ...item, status: "pending" };
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async function createMissingManagedCards(items, board, ctx, warnings) {
|
|
71
|
+
const created = /* @__PURE__ */ new Map();
|
|
72
|
+
for (const item of items) {
|
|
73
|
+
if (item.kanbanBoardId === board.id && item.kanbanTaskId) continue;
|
|
74
|
+
try {
|
|
75
|
+
const result = await addTask(
|
|
76
|
+
ctx.projectRoot,
|
|
77
|
+
board.id,
|
|
78
|
+
{
|
|
79
|
+
title: item.content,
|
|
80
|
+
description: item.activeForm?.trim() || `Added from the session todo list: ${item.content}`
|
|
81
|
+
},
|
|
82
|
+
// The card mirrors this session's todo row, so the session owns it.
|
|
83
|
+
{ sessionId: ctx.eventSessionId(), actor: "todo" }
|
|
84
|
+
);
|
|
85
|
+
if (!result) {
|
|
86
|
+
warnings.push(`Could not open a Kanban card for "${item.content}": board not found.`);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
created.set(item.id, result.task.id);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
warnings.push(
|
|
92
|
+
`Could not open a Kanban card for "${item.content}": ${error instanceof Error ? error.message : String(error)}`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return created;
|
|
97
|
+
}
|
|
98
|
+
async function synchronizeManagedKanban(items, board, ctx, signal) {
|
|
99
|
+
let synced = 0;
|
|
100
|
+
const warnings = [];
|
|
101
|
+
const actor = ctx.agentId?.trim() || ctx.agentName?.trim() || "kanban-agent";
|
|
102
|
+
const execute = async (input) => {
|
|
103
|
+
const result = await kanbanTool.execute(input, ctx, { signal });
|
|
104
|
+
if (!result.ok) warnings.push(result.message);
|
|
105
|
+
else {
|
|
106
|
+
synced++;
|
|
107
|
+
if (result.message.includes("Warning:")) warnings.push(result.message);
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
};
|
|
111
|
+
for (const item of items) {
|
|
112
|
+
if (item.status !== "pending" || item.kanbanBoardId !== board.id || !item.kanbanTaskId) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const task = board.tasks.find((candidate) => candidate.id === item.kanbanTaskId);
|
|
116
|
+
if (task?.lifecycle?.currentStage !== "running") continue;
|
|
117
|
+
const released = await execute({
|
|
118
|
+
action: "mark_assignment",
|
|
119
|
+
boardId: board.id,
|
|
120
|
+
taskId: task.id,
|
|
121
|
+
assignmentStatus: "assigned",
|
|
122
|
+
agentId: actor,
|
|
123
|
+
lastResult: `Todo returned to queue: ${item.content}`
|
|
124
|
+
});
|
|
125
|
+
if (!released.ok) continue;
|
|
126
|
+
await execute({
|
|
127
|
+
action: "transition_task",
|
|
128
|
+
boardId: board.id,
|
|
129
|
+
taskId: task.id,
|
|
130
|
+
lifecycleStage: "todo",
|
|
131
|
+
author: actor,
|
|
132
|
+
transitionComment: `Todo returned to queue: ${item.content}`
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
for (const item of items) {
|
|
136
|
+
if (item.status === "completed" || item.kanbanBoardId !== board.id || !item.kanbanTaskId) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const task = board.tasks.find((candidate) => candidate.id === item.kanbanTaskId);
|
|
140
|
+
if (task?.status !== "completed") continue;
|
|
141
|
+
warnings.push(
|
|
142
|
+
`"${item.content}" is already Done on the Kanban board and a completed card cannot be reopened; the row stays completed. Create a follow-up card for any remaining work.`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
for (const item of items) {
|
|
146
|
+
if (item.status !== "completed" || item.kanbanBoardId !== board.id || !item.kanbanTaskId) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const task = board.tasks.find((candidate) => candidate.id === item.kanbanTaskId);
|
|
150
|
+
if (!task || task.status === "completed") continue;
|
|
151
|
+
const stage = task.lifecycle?.currentStage;
|
|
152
|
+
if (stage === "backlog" || stage === "todo") {
|
|
153
|
+
const started = await execute({
|
|
154
|
+
action: "start_task",
|
|
155
|
+
boardId: board.id,
|
|
156
|
+
taskId: task.id,
|
|
157
|
+
author: actor,
|
|
158
|
+
agentId: actor,
|
|
159
|
+
transitionComment: `Auto-started for completion: ${item.content}`
|
|
160
|
+
});
|
|
161
|
+
if (!started.ok) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
await execute({
|
|
166
|
+
action: "mark_assignment",
|
|
167
|
+
boardId: board.id,
|
|
168
|
+
taskId: task.id,
|
|
169
|
+
assignmentStatus: "completed",
|
|
170
|
+
agentId: actor,
|
|
171
|
+
lastResult: `Todo completed: ${item.content}`
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const attemptedParents = /* @__PURE__ */ new Set();
|
|
175
|
+
let afterCompletions = await getBoard(ctx.projectRoot, board.id);
|
|
176
|
+
while (afterCompletions) {
|
|
177
|
+
const parent = afterCompletions.tasks.find(
|
|
178
|
+
(task) => task.atomic === true && task.status !== "completed" && Boolean(task.childTaskIds?.length) && !attemptedParents.has(task.id) && task.childTaskIds?.every(
|
|
179
|
+
(childId) => afterCompletions?.tasks.find((candidate) => candidate.id === childId)?.status === "completed"
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
if (!parent) break;
|
|
183
|
+
attemptedParents.add(parent.id);
|
|
184
|
+
const started = await execute({
|
|
185
|
+
action: "start_task",
|
|
186
|
+
boardId: board.id,
|
|
187
|
+
taskId: parent.id,
|
|
188
|
+
author: actor,
|
|
189
|
+
agentId: actor,
|
|
190
|
+
transitionComment: "All child tasks completed; validating composite parent."
|
|
191
|
+
});
|
|
192
|
+
if (started.ok) {
|
|
193
|
+
await execute({
|
|
194
|
+
action: "mark_assignment",
|
|
195
|
+
boardId: board.id,
|
|
196
|
+
taskId: parent.id,
|
|
197
|
+
assignmentStatus: "completed",
|
|
198
|
+
agentId: actor,
|
|
199
|
+
lastResult: "All child tasks completed; composite result ready for verification."
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
afterCompletions = await getBoard(ctx.projectRoot, board.id);
|
|
203
|
+
}
|
|
204
|
+
const completionPending = items.some(
|
|
205
|
+
(item) => item.status === "completed" && item.kanbanBoardId === board.id && Boolean(item.kanbanTaskId) && afterCompletions?.tasks.find((task) => task.id === item.kanbanTaskId)?.status !== "completed"
|
|
206
|
+
);
|
|
207
|
+
const active = items.find(
|
|
208
|
+
(item) => item.status === "in_progress" && item.kanbanBoardId === board.id && Boolean(item.kanbanTaskId)
|
|
209
|
+
);
|
|
210
|
+
const activeStage = active?.kanbanTaskId ? afterCompletions?.tasks.find((task) => task.id === active.kanbanTaskId)?.lifecycle?.currentStage : void 0;
|
|
211
|
+
if (active?.kanbanTaskId) {
|
|
212
|
+
if (activeStage === "review" || activeStage === "done") {
|
|
213
|
+
warnings.push(
|
|
214
|
+
`"${active.content}" is in ${activeStage === "review" ? "Review" : "Done"} awaiting acceptance; not re-activating it from the todo list. ` + (activeStage === "review" ? "Call kanban start_task explicitly to reopen it as a repair." : "Done is terminal; reopen only by creating a follow-up card.")
|
|
215
|
+
);
|
|
216
|
+
} else {
|
|
217
|
+
await execute({
|
|
218
|
+
action: "start_task",
|
|
219
|
+
boardId: board.id,
|
|
220
|
+
taskId: active.kanbanTaskId,
|
|
221
|
+
author: actor,
|
|
222
|
+
agentId: actor,
|
|
223
|
+
transitionComment: `Todo activated: ${active.content}`
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (active?.kanbanTaskId && activeStage === "review") {
|
|
228
|
+
} else if (active?.kanbanTaskId && completionPending) {
|
|
229
|
+
warnings.push(
|
|
230
|
+
"A completed todo is still awaiting acceptance; the next independent Kanban task was started."
|
|
231
|
+
);
|
|
232
|
+
} else if (!active && ctx.currentKanbanBoardId === board.id) {
|
|
233
|
+
ctx.setCurrentKanbanTask(void 0, board.id);
|
|
234
|
+
}
|
|
235
|
+
return { synced, warnings };
|
|
236
|
+
}
|
|
237
|
+
var todoTool = {
|
|
238
|
+
name: "todo",
|
|
239
|
+
category: "Session",
|
|
240
|
+
description: "Manage the compact active-work list. With a managed Kanban binding, every row resolves to a real card and status changes advance that card; without Kanban it remains session-only state. Each call replaces ordering and supplied fields, but unfinished omitted rows are retained until completed.",
|
|
241
|
+
usageHint: "BEST PRACTICE for complex tasks:\n- At the beginning of a non-trivial task, create a clear todo list with specific, actionable items.\n- Only **one** item should be `in_progress` at any time.\n- Update the list frequently as work progresses (mark items done, add new ones, change status).\n- **Re-order items** to reflect current priorities. Omission is not cancellation: unfinished rows are retained until completed.\n- When all items are completed the board auto-clears \u2014 you do NOT need to send an empty list.\n- The system and user can see this list, so keep it honest and up-to-date.\nThis tool is extremely valuable for maintaining focus and giving the user visibility into your plan.",
|
|
242
|
+
permission: "confirm",
|
|
243
|
+
mutating: true,
|
|
244
|
+
timeoutMs: 3e4,
|
|
245
|
+
capabilities: ["session.todo", "fs.write"],
|
|
246
|
+
subjectKey: "todos",
|
|
247
|
+
icon: "todo",
|
|
248
|
+
inputSchema: {
|
|
249
|
+
type: "object",
|
|
250
|
+
properties: {
|
|
251
|
+
todos: {
|
|
252
|
+
type: "array",
|
|
253
|
+
items: {
|
|
254
|
+
type: "object",
|
|
255
|
+
properties: {
|
|
256
|
+
id: {
|
|
257
|
+
type: "string",
|
|
258
|
+
description: 'Unique identifier for the todo item (e.g. "1", "auth-flow").'
|
|
259
|
+
},
|
|
260
|
+
content: {
|
|
261
|
+
type: "string",
|
|
262
|
+
description: "Clear, actionable description of the task."
|
|
263
|
+
},
|
|
264
|
+
status: {
|
|
265
|
+
type: "string",
|
|
266
|
+
enum: ["pending", "in_progress", "completed"],
|
|
267
|
+
description: 'Current status. Only one item should be "in_progress" at a time.'
|
|
268
|
+
},
|
|
269
|
+
activeForm: {
|
|
270
|
+
type: "string",
|
|
271
|
+
description: 'Optional present-tense form shown while the task is active (e.g. "Fixing auth bug").'
|
|
272
|
+
},
|
|
273
|
+
kanbanBoardId: {
|
|
274
|
+
type: "string",
|
|
275
|
+
description: "Kanban board that owns this UI row when Kanban is active."
|
|
276
|
+
},
|
|
277
|
+
kanbanTaskId: {
|
|
278
|
+
type: "string",
|
|
279
|
+
description: "Real Kanban task represented by this UI row."
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
required: ["id", "content", "status"]
|
|
283
|
+
},
|
|
284
|
+
description: "The desired todo list. Supplied rows are replaced/reordered; unfinished omitted rows are retained."
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
required: ["todos"]
|
|
288
|
+
},
|
|
289
|
+
async execute(input, ctx, call) {
|
|
290
|
+
if (!Array.isArray(input?.todos)) {
|
|
291
|
+
throw new Error("todo: todos must be an array");
|
|
292
|
+
}
|
|
293
|
+
const items = input.todos.filter((t) => Boolean(t?.id && t.content));
|
|
294
|
+
const todoIdentity = (item) => item.kanbanBoardId && item.kanbanTaskId ? `kanban:${item.kanbanBoardId}:${item.kanbanTaskId}` : item.promotedFromTask ? `task:${item.promotedFromTask}` : item.promotedFromPlan ? `plan:${item.promotedFromPlan}` : `todo:${item.id}`;
|
|
295
|
+
const requestedIdentities = new Set(items.map(todoIdentity));
|
|
296
|
+
for (const previous of ctx.todos ?? []) {
|
|
297
|
+
const identity = todoIdentity(previous);
|
|
298
|
+
if (previous.status === "completed" || requestedIdentities.has(identity)) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
items.push({ ...previous });
|
|
302
|
+
requestedIdentities.add(identity);
|
|
303
|
+
}
|
|
304
|
+
const inProgress = items.filter((t) => t.status === "in_progress");
|
|
305
|
+
if (inProgress.length > 1) {
|
|
306
|
+
let seenInProgress = false;
|
|
307
|
+
for (const item of items) {
|
|
308
|
+
if (item.status === "in_progress") {
|
|
309
|
+
if (seenInProgress) item.status = "pending";
|
|
310
|
+
seenInProgress = true;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
const boardId = activeBoardId(items, ctx);
|
|
315
|
+
let board = boardId ? await getBoard(ctx.projectRoot, boardId) : null;
|
|
316
|
+
const managed = board?.lifecycle?.mode === "managed";
|
|
317
|
+
let boundItems = managed && board ? bindTodosToBoard(items, ctx.todos ?? [], board) : items;
|
|
318
|
+
const creationWarnings = [];
|
|
319
|
+
if (managed && board) {
|
|
320
|
+
const managedBoardId = board.id;
|
|
321
|
+
const created = await createMissingManagedCards(boundItems, board, ctx, creationWarnings);
|
|
322
|
+
if (created.size > 0) {
|
|
323
|
+
boundItems = boundItems.map((item) => {
|
|
324
|
+
const taskId = created.get(item.id);
|
|
325
|
+
return taskId ? { ...item, kanbanBoardId: managedBoardId, kanbanTaskId: taskId } : item;
|
|
326
|
+
});
|
|
327
|
+
board = await getBoard(ctx.projectRoot, managedBoardId) ?? board;
|
|
328
|
+
boundItems = bindTodosToBoard(boundItems, ctx.todos ?? [], board);
|
|
329
|
+
}
|
|
330
|
+
boundItems = demoteBlockedInProgress(boundItems, creationWarnings);
|
|
331
|
+
}
|
|
332
|
+
ctx.state.replaceTodos(boundItems);
|
|
333
|
+
const kanbanSync = managed && board ? await synchronizeManagedKanban(boundItems, board, ctx, call.signal) : { synced: 0, warnings: [] };
|
|
334
|
+
kanbanSync.warnings.unshift(...creationWarnings);
|
|
335
|
+
if (managed && board) {
|
|
336
|
+
const unresolved = boundItems.filter(
|
|
337
|
+
(item) => item.kanbanBoardId !== board.id || !item.kanbanTaskId
|
|
338
|
+
);
|
|
339
|
+
if (unresolved.length > 0) {
|
|
340
|
+
kanbanSync.warnings.push(
|
|
341
|
+
`${unresolved.length} Todo row(s) could not be bound to a Kanban task and were not applied. Preserve kanbanBoardId/kanbanTaskId when updating the projection.`
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const mirrorFailure = takeSessionMirrorFailure(ctx.projectRoot, ctx.session?.id ?? "");
|
|
346
|
+
if (mirrorFailure) kanbanSync.warnings.push(mirrorFailure);
|
|
347
|
+
let projectedBoard = board;
|
|
348
|
+
if (managed && board) {
|
|
349
|
+
const refreshed = await getBoard(ctx.projectRoot, board.id);
|
|
350
|
+
if (refreshed) {
|
|
351
|
+
projectedBoard = refreshed;
|
|
352
|
+
applyManagedKanbanBoardToTodos(ctx, refreshed);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (!managed) {
|
|
356
|
+
mirrorSessionTodosToKanban(ctx.projectRoot, items, ctx.session?.id ?? "session");
|
|
357
|
+
}
|
|
358
|
+
const completedPlanIds = /* @__PURE__ */ new Set();
|
|
359
|
+
const completedTaskIds = /* @__PURE__ */ new Set();
|
|
360
|
+
const pendingPlanIds = /* @__PURE__ */ new Set();
|
|
361
|
+
const pendingTaskIds = /* @__PURE__ */ new Set();
|
|
362
|
+
for (const item of items) {
|
|
363
|
+
if (item.promotedFromPlan) {
|
|
364
|
+
(item.status === "completed" ? completedPlanIds : pendingPlanIds).add(
|
|
365
|
+
item.promotedFromPlan
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
if (item.promotedFromTask) {
|
|
369
|
+
(item.status === "completed" ? completedTaskIds : pendingTaskIds).add(
|
|
370
|
+
item.promotedFromTask
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
for (const planId of completedPlanIds) {
|
|
375
|
+
if (pendingPlanIds.has(planId)) continue;
|
|
376
|
+
const meta = ctx.meta;
|
|
377
|
+
const planPath = meta["plan.path.resolved"] ?? meta["plan.path"];
|
|
378
|
+
if (typeof planPath !== "string" || !planPath) continue;
|
|
379
|
+
try {
|
|
380
|
+
const plan = await loadPlan(planPath);
|
|
381
|
+
if (plan) {
|
|
382
|
+
const updated = setPlanItemStatus(plan, planId, "done");
|
|
383
|
+
await savePlan(planPath, updated);
|
|
384
|
+
}
|
|
385
|
+
} catch {
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
for (const taskId of completedTaskIds) {
|
|
389
|
+
if (pendingTaskIds.has(taskId)) continue;
|
|
390
|
+
const meta = ctx.meta;
|
|
391
|
+
const taskPath = meta["task.path.resolved"] ?? meta["task.path"];
|
|
392
|
+
if (typeof taskPath !== "string" || !taskPath) continue;
|
|
393
|
+
try {
|
|
394
|
+
const file = await loadTasks(taskPath);
|
|
395
|
+
if (file) {
|
|
396
|
+
const task = file.tasks.find((t) => t.id === taskId);
|
|
397
|
+
if (task && task.status !== "completed") {
|
|
398
|
+
task.status = "completed";
|
|
399
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
400
|
+
await saveTasks(taskPath, file);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} catch {
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return {
|
|
407
|
+
count: items.length,
|
|
408
|
+
in_progress: (ctx.todos ?? boundItems).filter((t) => t.status === "in_progress").length,
|
|
409
|
+
...kanbanSync.synced > 0 ? { kanban_synced: kanbanSync.synced } : {},
|
|
410
|
+
...kanbanSync.warnings.length > 0 ? { kanban_warnings: kanbanSync.warnings } : {},
|
|
411
|
+
...projectedBoard?.lifecycle?.mode === "managed" ? {
|
|
412
|
+
kanban_bindings: boundItems.flatMap((item) => {
|
|
413
|
+
if (item.kanbanBoardId !== projectedBoard.id || !item.kanbanTaskId) return [];
|
|
414
|
+
const task = projectedBoard.tasks.find(
|
|
415
|
+
(candidate) => candidate.id === item.kanbanTaskId
|
|
416
|
+
);
|
|
417
|
+
return task ? [
|
|
418
|
+
{
|
|
419
|
+
todoId: item.id,
|
|
420
|
+
boardId: projectedBoard.id,
|
|
421
|
+
taskId: task.id,
|
|
422
|
+
taskStatus: task.status
|
|
423
|
+
}
|
|
424
|
+
] : [];
|
|
425
|
+
})
|
|
426
|
+
} : {}
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
export {
|
|
432
|
+
todoTool
|
|
433
|
+
};
|
|
434
|
+
//# sourceMappingURL=chunk-7EY5DCRO.js.map
|