@astralyn/pi 0.82.5 → 0.82.7
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/CHANGELOG.md +55 -0
- package/README.md +1 -1
- package/dist/cli/project-trust.d.ts.map +1 -1
- package/dist/cli/project-trust.js +4 -1
- package/dist/cli/project-trust.js.map +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +11 -13
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +4 -22
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/types.d.ts +32 -5
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +2 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/tools/bash.d.ts +1 -5
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +16 -41
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +42 -11
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +6 -7
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +17 -8
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +3 -3
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +3 -13
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +6 -36
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +4 -4
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +7 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js +11 -0
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -3
- package/dist/core/tools/write.js.map +1 -1
- package/dist/extensions/deepwiki/render.d.ts.map +1 -1
- package/dist/extensions/deepwiki/render.js +34 -22
- package/dist/extensions/deepwiki/render.js.map +1 -1
- package/dist/extensions/plan/constants.d.ts +43 -6
- package/dist/extensions/plan/constants.d.ts.map +1 -1
- package/dist/extensions/plan/constants.js +51 -8
- package/dist/extensions/plan/constants.js.map +1 -1
- package/dist/extensions/plan/index.d.ts +12 -3
- package/dist/extensions/plan/index.d.ts.map +1 -1
- package/dist/extensions/plan/index.js +173 -35
- package/dist/extensions/plan/index.js.map +1 -1
- package/dist/extensions/plan/schema.d.ts +5 -0
- package/dist/extensions/plan/schema.d.ts.map +1 -1
- package/dist/extensions/plan/schema.js.map +1 -1
- package/dist/extensions/plan/state.d.ts +11 -2
- package/dist/extensions/plan/state.d.ts.map +1 -1
- package/dist/extensions/plan/state.js +31 -9
- package/dist/extensions/plan/state.js.map +1 -1
- package/dist/extensions/plan/storage.d.ts +11 -3
- package/dist/extensions/plan/storage.d.ts.map +1 -1
- package/dist/extensions/plan/storage.js +45 -20
- package/dist/extensions/plan/storage.js.map +1 -1
- package/dist/extensions/plan/view.d.ts +44 -0
- package/dist/extensions/plan/view.d.ts.map +1 -0
- package/dist/extensions/plan/view.js +149 -0
- package/dist/extensions/plan/view.js.map +1 -0
- package/dist/extensions/question/dialog.d.ts.map +1 -1
- package/dist/extensions/question/dialog.js +29 -10
- package/dist/extensions/question/dialog.js.map +1 -1
- package/dist/extensions/question/index.d.ts.map +1 -1
- package/dist/extensions/question/index.js +7 -2
- package/dist/extensions/question/index.js.map +1 -1
- package/dist/extensions/question/render.d.ts +4 -8
- package/dist/extensions/question/render.d.ts.map +1 -1
- package/dist/extensions/question/render.js +202 -29
- package/dist/extensions/question/render.js.map +1 -1
- package/dist/extensions/question/results.d.ts.map +1 -1
- package/dist/extensions/question/results.js +1 -0
- package/dist/extensions/question/results.js.map +1 -1
- package/dist/extensions/question/schema.d.ts.map +1 -1
- package/dist/extensions/question/schema.js +2 -2
- package/dist/extensions/question/schema.js.map +1 -1
- package/dist/extensions/question/types.d.ts +1 -0
- package/dist/extensions/question/types.d.ts.map +1 -1
- package/dist/extensions/question/types.js +1 -1
- package/dist/extensions/question/types.js.map +1 -1
- package/dist/extensions/router/stream.d.ts +1 -0
- package/dist/extensions/router/stream.d.ts.map +1 -1
- package/dist/extensions/router/stream.js +75 -0
- package/dist/extensions/router/stream.js.map +1 -1
- package/dist/extensions/subagent/activity.d.ts.map +1 -1
- package/dist/extensions/subagent/activity.js +25 -4
- package/dist/extensions/subagent/activity.js.map +1 -1
- package/dist/extensions/subagent/constants.d.ts +1 -0
- package/dist/extensions/subagent/constants.d.ts.map +1 -1
- package/dist/extensions/subagent/constants.js +1 -0
- package/dist/extensions/subagent/constants.js.map +1 -1
- package/dist/extensions/subagent/index.d.ts.map +1 -1
- package/dist/extensions/subagent/index.js +16 -0
- package/dist/extensions/subagent/index.js.map +1 -1
- package/dist/extensions/subagent/render.d.ts.map +1 -1
- package/dist/extensions/subagent/render.js +352 -152
- package/dist/extensions/subagent/render.js.map +1 -1
- package/dist/extensions/subagent/resolve.d.ts.map +1 -1
- package/dist/extensions/subagent/resolve.js +13 -9
- package/dist/extensions/subagent/resolve.js.map +1 -1
- package/dist/extensions/subagent/retry.d.ts +17 -0
- package/dist/extensions/subagent/retry.d.ts.map +1 -0
- package/dist/extensions/subagent/retry.js +41 -0
- package/dist/extensions/subagent/retry.js.map +1 -0
- package/dist/extensions/subagent/runner.d.ts.map +1 -1
- package/dist/extensions/subagent/runner.js +37 -7
- package/dist/extensions/subagent/runner.js.map +1 -1
- package/dist/extensions/subagent/schema.d.ts.map +1 -1
- package/dist/extensions/subagent/schema.js +2 -2
- package/dist/extensions/subagent/schema.js.map +1 -1
- package/dist/extensions/subagent/sdk-runner.d.ts +6 -0
- package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
- package/dist/extensions/subagent/sdk-runner.js +46 -24
- package/dist/extensions/subagent/sdk-runner.js.map +1 -1
- package/dist/extensions/subagent/types.d.ts +7 -0
- package/dist/extensions/subagent/types.d.ts.map +1 -1
- package/dist/extensions/subagent/types.js.map +1 -1
- package/dist/extensions/todo/constants.d.ts +17 -6
- package/dist/extensions/todo/constants.d.ts.map +1 -1
- package/dist/extensions/todo/constants.js +22 -9
- package/dist/extensions/todo/constants.js.map +1 -1
- package/dist/extensions/todo/index.d.ts +0 -14
- package/dist/extensions/todo/index.d.ts.map +1 -1
- package/dist/extensions/todo/index.js +13 -1
- package/dist/extensions/todo/index.js.map +1 -1
- package/dist/extensions/todo/schema.d.ts +53 -2
- package/dist/extensions/todo/schema.d.ts.map +1 -1
- package/dist/extensions/todo/schema.js +57 -8
- package/dist/extensions/todo/schema.js.map +1 -1
- package/dist/extensions/todo/state.d.ts +16 -5
- package/dist/extensions/todo/state.d.ts.map +1 -1
- package/dist/extensions/todo/state.js +874 -130
- package/dist/extensions/todo/state.js.map +1 -1
- package/dist/extensions/todo/view.d.ts +16 -1
- package/dist/extensions/todo/view.d.ts.map +1 -1
- package/dist/extensions/todo/view.js +428 -1
- package/dist/extensions/todo/view.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +2 -1
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.js +5 -0
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +1 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +4 -2
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts +13 -6
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.js +54 -17
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +6 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +23 -11
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +11 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +56 -0
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +3 -0
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +52 -2
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +10 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +139 -18
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-group.d.ts +1 -0
- package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-group.js +5 -0
- package/dist/modes/interactive/components/tool-group.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +10 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +55 -2
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +79 -19
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +3 -2
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +8 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/paths.d.ts +6 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +8 -0
- package/dist/utils/paths.js.map +1 -1
- package/docs/bundled/README.md +1 -0
- package/docs/bundled/extensions/deepwiki.md +1 -1
- package/docs/bundled/extensions/plan.md +13 -10
- package/docs/bundled/extensions/question.md +7 -5
- package/docs/bundled/extensions/router.md +3 -4
- package/docs/bundled/extensions/statusline.md +0 -1
- package/docs/bundled/extensions/subagent.md +5 -4
- package/docs/bundled/extensions/todo.md +9 -7
- package/docs/bundled/tool-presentation.md +17 -16
- package/docs/extensions.md +34 -5
- package/docs/rpc.md +1 -1
- package/docs/usage.md +1 -1
- package/npm-shrinkwrap.json +6 -6
- package/package.json +1 -1
- package/dist/utils/output-decoder.d.ts +0 -52
- package/dist/utils/output-decoder.d.ts.map +0 -1
- package/dist/utils/output-decoder.js +0 -134
- package/dist/utils/output-decoder.js.map +0 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
2
|
+
import { LIST_DISPLAY_MAX_ITEMS, TODO_MAX_BATCH_ITEMS, TODO_MAX_BLOCKED_BY } from "./constants.js";
|
|
3
|
+
import { TODO_DETAILS_SCHEMA_VERSION, } from "./schema.js";
|
|
2
4
|
import { unresolvedDependencyIds } from "./state.js";
|
|
3
5
|
export const STATUS_MARK = {
|
|
4
6
|
pending: "[ ]",
|
|
@@ -100,24 +102,449 @@ function formatOverlayItem(item, state, theme, showId) {
|
|
|
100
102
|
}
|
|
101
103
|
return text;
|
|
102
104
|
}
|
|
105
|
+
const EDGE_PARAMS = ["blockedBy", "addBlockedBy", "removeBlockedBy", "addBlocks", "removeBlocks"];
|
|
106
|
+
function callText(value) {
|
|
107
|
+
return typeof value === "string" ? value.trim() : "";
|
|
108
|
+
}
|
|
109
|
+
function callSubject(value) {
|
|
110
|
+
return callText(value).replace(/\s+/g, " ");
|
|
111
|
+
}
|
|
112
|
+
function callId(value) {
|
|
113
|
+
return typeof value === "number" && Number.isInteger(value) ? value : undefined;
|
|
114
|
+
}
|
|
115
|
+
function callIds(value) {
|
|
116
|
+
if (!Array.isArray(value))
|
|
117
|
+
return [];
|
|
118
|
+
const ids = [];
|
|
119
|
+
let count;
|
|
120
|
+
try {
|
|
121
|
+
count = Math.min(value.length, TODO_MAX_BLOCKED_BY);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return ids;
|
|
125
|
+
}
|
|
126
|
+
for (let index = 0; index < count; index++) {
|
|
127
|
+
let entry;
|
|
128
|
+
try {
|
|
129
|
+
entry = value[index];
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (typeof entry === "number" && Number.isSafeInteger(entry) && entry >= 1)
|
|
135
|
+
ids.push(entry);
|
|
136
|
+
}
|
|
137
|
+
return ids;
|
|
138
|
+
}
|
|
139
|
+
function callArrayLength(value) {
|
|
140
|
+
try {
|
|
141
|
+
return Array.isArray(value) ? value.length : undefined;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const CALL_BATCH_SUBJECT_PREVIEW_COUNT = 2;
|
|
148
|
+
const CALL_SUBJECT_PREVIEW_WIDTH = 72;
|
|
149
|
+
const CALL_DESCRIPTION_PREVIEW_LENGTH = 120;
|
|
150
|
+
function callStrings(value) {
|
|
151
|
+
if (!Array.isArray(value))
|
|
152
|
+
return [];
|
|
153
|
+
const strings = [];
|
|
154
|
+
let count;
|
|
155
|
+
try {
|
|
156
|
+
count = Math.min(value.length, TODO_MAX_BLOCKED_BY);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return strings;
|
|
160
|
+
}
|
|
161
|
+
for (let index = 0; index < count; index++) {
|
|
162
|
+
let entry;
|
|
163
|
+
try {
|
|
164
|
+
entry = value[index];
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const text = callText(entry);
|
|
170
|
+
if (text)
|
|
171
|
+
strings.push(text);
|
|
172
|
+
}
|
|
173
|
+
return strings;
|
|
174
|
+
}
|
|
175
|
+
function callBatchItems(value) {
|
|
176
|
+
if (!Array.isArray(value))
|
|
177
|
+
return [];
|
|
178
|
+
let count;
|
|
179
|
+
try {
|
|
180
|
+
count = Math.min(value.length, TODO_MAX_BATCH_ITEMS);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
const items = [];
|
|
186
|
+
for (let index = 0; index < count; index++) {
|
|
187
|
+
let valueAtIndex;
|
|
188
|
+
try {
|
|
189
|
+
valueAtIndex = value[index];
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
valueAtIndex = undefined;
|
|
193
|
+
}
|
|
194
|
+
const item = safeRecord(valueAtIndex);
|
|
195
|
+
items.push({
|
|
196
|
+
subject: item ? callSubject(safeValue(item, "subject")) : "",
|
|
197
|
+
description: item ? callText(safeValue(item, "description")) : "",
|
|
198
|
+
blockedBy: item ? callIds(safeValue(item, "blockedBy")) : [],
|
|
199
|
+
blockedByKeys: item ? callStrings(safeValue(item, "blockedByKeys")) : [],
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
return items;
|
|
203
|
+
}
|
|
204
|
+
function formatSubjectPreview(subjects, total) {
|
|
205
|
+
const shown = subjects
|
|
206
|
+
.slice(0, CALL_BATCH_SUBJECT_PREVIEW_COUNT)
|
|
207
|
+
.filter(Boolean)
|
|
208
|
+
.map((subject) => truncateToWidth(subject, CALL_SUBJECT_PREVIEW_WIDTH, "…"));
|
|
209
|
+
if (!shown.length)
|
|
210
|
+
return "";
|
|
211
|
+
const hidden = Math.max(0, total - shown.length);
|
|
212
|
+
return `${shown.join(", ")}${hidden ? `, +${hidden} more` : ""}`;
|
|
213
|
+
}
|
|
214
|
+
function callBatchSubjectPreview(items, total) {
|
|
215
|
+
return formatSubjectPreview(items.map((item) => item.subject), total);
|
|
216
|
+
}
|
|
217
|
+
function callDescriptionPreview(value) {
|
|
218
|
+
const text = value.replace(/\s+/g, " ");
|
|
219
|
+
return text.length > CALL_DESCRIPTION_PREVIEW_LENGTH
|
|
220
|
+
? `${text.slice(0, CALL_DESCRIPTION_PREVIEW_LENGTH - 1)}…`
|
|
221
|
+
: text;
|
|
222
|
+
}
|
|
223
|
+
function callNonNegativeInteger(value) {
|
|
224
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : undefined;
|
|
225
|
+
}
|
|
226
|
+
function isTodoStatus(value) {
|
|
227
|
+
return Object.hasOwn(STATUS_MARK, value);
|
|
228
|
+
}
|
|
229
|
+
function formatCallHeadline(args, theme) {
|
|
230
|
+
const action = callText(args?.action);
|
|
231
|
+
const displayAction = action === "create_many" ? "create" : action;
|
|
232
|
+
const parts = [theme.fg("toolTitle", theme.bold(displayAction ? `todo ${displayAction}` : "todo"))];
|
|
233
|
+
if (action === "create_many") {
|
|
234
|
+
const items = callBatchItems(args?.items);
|
|
235
|
+
const count = callArrayLength(args?.items);
|
|
236
|
+
if (count !== undefined) {
|
|
237
|
+
parts.push(theme.fg("dim", `${count} ${count === 1 ? "task" : "tasks"}`));
|
|
238
|
+
const preview = callBatchSubjectPreview(items, count);
|
|
239
|
+
if (preview) {
|
|
240
|
+
parts.push(theme.fg("dim", "·"));
|
|
241
|
+
parts.push(theme.fg("text", preview));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const id = callId(args?.id);
|
|
246
|
+
if (id !== undefined)
|
|
247
|
+
parts.push(theme.fg("accent", `#${id}`));
|
|
248
|
+
const status = callText(args?.status);
|
|
249
|
+
if (status && isTodoStatus(status))
|
|
250
|
+
parts.push(theme.fg(STATUS_COLOR[status], status));
|
|
251
|
+
const subject = callSubject(args?.subject);
|
|
252
|
+
if (subject)
|
|
253
|
+
parts.push(theme.fg("text", subject));
|
|
254
|
+
// Dependency-only updates would otherwise render as a bare `todo update #3`.
|
|
255
|
+
const hasEdgeChange = EDGE_PARAMS.some((key) => callIds(args?.[key]).length > 0);
|
|
256
|
+
if (!status && !subject && hasEdgeChange)
|
|
257
|
+
parts.push(theme.fg("dim", "dependencies"));
|
|
258
|
+
return parts.join(" ");
|
|
259
|
+
}
|
|
260
|
+
function batchResultIds(result, count) {
|
|
261
|
+
const details = groupDetails(result?.details);
|
|
262
|
+
if (!details || details.action !== "create_many")
|
|
263
|
+
return undefined;
|
|
264
|
+
const ids = groupIds(safeValue(details.operation, "ids"));
|
|
265
|
+
return ids?.length === count ? ids : undefined;
|
|
266
|
+
}
|
|
267
|
+
function formatCallDetails(args, theme, result) {
|
|
268
|
+
const lines = [];
|
|
269
|
+
const push = (label, value) => {
|
|
270
|
+
if (value)
|
|
271
|
+
lines.push(theme.fg("dim", `${label}: ${value}`));
|
|
272
|
+
};
|
|
273
|
+
const action = callText(args?.action);
|
|
274
|
+
if (action === "create_many") {
|
|
275
|
+
const items = callBatchItems(args?.items);
|
|
276
|
+
const ids = batchResultIds(result, items.length);
|
|
277
|
+
for (const [index, item] of items.entries()) {
|
|
278
|
+
const dependencies = [...item.blockedBy.map((id) => `#${id}`), ...item.blockedByKeys];
|
|
279
|
+
let line = theme.fg("accent", ids ? `#${ids[index]}` : `${index + 1}.`);
|
|
280
|
+
if (item.subject)
|
|
281
|
+
line += ` ${theme.fg("text", item.subject)}`;
|
|
282
|
+
if (dependencies.length)
|
|
283
|
+
line += ` ${theme.fg("dim", `· blocked by ${dependencies.join(", ")}`)}`;
|
|
284
|
+
lines.push(line);
|
|
285
|
+
if (item.description)
|
|
286
|
+
lines.push(` ${theme.fg("dim", callDescriptionPreview(item.description))}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
push("description", callText(args?.description));
|
|
290
|
+
push("activeForm", callText(args?.activeForm));
|
|
291
|
+
push("owner", callText(args?.owner));
|
|
292
|
+
for (const key of EDGE_PARAMS) {
|
|
293
|
+
const ids = callIds(args?.[key]);
|
|
294
|
+
if (ids.length)
|
|
295
|
+
push(key, ids.map((id) => `#${id}`).join(","));
|
|
296
|
+
}
|
|
297
|
+
if (args?.includeDeleted === true)
|
|
298
|
+
push("includeDeleted", "true");
|
|
299
|
+
const limit = callId(args?.limit);
|
|
300
|
+
if (limit !== undefined)
|
|
301
|
+
push("limit", String(limit));
|
|
302
|
+
const afterId = callId(args?.afterId);
|
|
303
|
+
if (afterId !== undefined)
|
|
304
|
+
push("afterId", String(afterId));
|
|
305
|
+
push("query", callText(args?.query));
|
|
306
|
+
if (args?.unblockedOnly === true)
|
|
307
|
+
push("unblockedOnly", "true");
|
|
308
|
+
if (args?.confirm === true)
|
|
309
|
+
push("confirm", "true");
|
|
310
|
+
const expectedCount = callNonNegativeInteger(args?.expectedCount);
|
|
311
|
+
if (expectedCount !== undefined)
|
|
312
|
+
push("expectedCount", String(expectedCount));
|
|
313
|
+
const metadata = args?.metadata;
|
|
314
|
+
if (metadata && typeof metadata === "object")
|
|
315
|
+
push("metadata", Object.keys(metadata).join(","));
|
|
316
|
+
return lines;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* One-line call summary when collapsed; the same headline plus the parameters
|
|
320
|
+
* the result never echoes (description, activeForm, dependencies) when expanded.
|
|
321
|
+
*/
|
|
322
|
+
export function formatTodoCall(args, theme, expanded, result) {
|
|
323
|
+
const headline = formatCallHeadline(args, theme);
|
|
324
|
+
if (!expanded)
|
|
325
|
+
return headline;
|
|
326
|
+
return [headline, ...formatCallDetails(args, theme, result)].join("\n");
|
|
327
|
+
}
|
|
328
|
+
const GROUP_FAILURE_MAX_LENGTH = 120;
|
|
329
|
+
const GROUP_MAX_IDS = 20;
|
|
330
|
+
const GROUP_MAX_SNAPSHOT_ITEMS = 10_000;
|
|
331
|
+
const TODO_ACTIONS = new Set(["create", "create_many", "update", "list", "get", "delete", "clear"]);
|
|
332
|
+
function safeRecord(value) {
|
|
333
|
+
try {
|
|
334
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
335
|
+
? value
|
|
336
|
+
: undefined;
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function safeValue(record, key) {
|
|
343
|
+
try {
|
|
344
|
+
return record[key];
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function safeString(value) {
|
|
351
|
+
return typeof value === "string" ? value : undefined;
|
|
352
|
+
}
|
|
353
|
+
function groupDetails(value) {
|
|
354
|
+
const details = safeRecord(value);
|
|
355
|
+
if (!details || safeValue(details, "schemaVersion") !== TODO_DETAILS_SCHEMA_VERSION)
|
|
356
|
+
return undefined;
|
|
357
|
+
const action = safeString(safeValue(details, "action"));
|
|
358
|
+
if (!action || !TODO_ACTIONS.has(action))
|
|
359
|
+
return undefined;
|
|
360
|
+
const operation = safeRecord(safeValue(details, "operation"));
|
|
361
|
+
if (!operation || safeValue(operation, "kind") !== action)
|
|
362
|
+
return undefined;
|
|
363
|
+
return { action, operation, items: safeValue(details, "items") };
|
|
364
|
+
}
|
|
365
|
+
function groupPositiveInteger(value) {
|
|
366
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1 ? value : undefined;
|
|
367
|
+
}
|
|
368
|
+
function groupIds(value) {
|
|
369
|
+
try {
|
|
370
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > GROUP_MAX_IDS)
|
|
371
|
+
return undefined;
|
|
372
|
+
const ids = [];
|
|
373
|
+
for (const valueAtIndex of value) {
|
|
374
|
+
const id = groupPositiveInteger(valueAtIndex);
|
|
375
|
+
if (id === undefined)
|
|
376
|
+
return undefined;
|
|
377
|
+
ids.push(id);
|
|
378
|
+
}
|
|
379
|
+
return ids;
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function groupNonNegativeInteger(value) {
|
|
386
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
|
|
387
|
+
}
|
|
388
|
+
function formatGroupStatusCounts(value, resultCount) {
|
|
389
|
+
const count = groupNonNegativeInteger(resultCount);
|
|
390
|
+
if (count === undefined)
|
|
391
|
+
return undefined;
|
|
392
|
+
if (count === 0)
|
|
393
|
+
return "no tasks";
|
|
394
|
+
const rawCounts = safeRecord(value);
|
|
395
|
+
if (!rawCounts)
|
|
396
|
+
return undefined;
|
|
397
|
+
const parts = [];
|
|
398
|
+
let total = 0;
|
|
399
|
+
for (const status of ["pending", "in_progress", "completed", "deleted"]) {
|
|
400
|
+
const statusCount = groupNonNegativeInteger(safeValue(rawCounts, status));
|
|
401
|
+
if (statusCount === undefined)
|
|
402
|
+
continue;
|
|
403
|
+
total += statusCount;
|
|
404
|
+
if (statusCount > 0)
|
|
405
|
+
parts.push(`${statusCount} ${status}`);
|
|
406
|
+
}
|
|
407
|
+
return total === count && parts.length ? parts.join(", ") : undefined;
|
|
408
|
+
}
|
|
409
|
+
function groupItem(value, id) {
|
|
410
|
+
try {
|
|
411
|
+
if (!Array.isArray(value) || value.length > GROUP_MAX_SNAPSHOT_ITEMS)
|
|
412
|
+
return undefined;
|
|
413
|
+
for (const rawItem of value) {
|
|
414
|
+
const item = safeRecord(rawItem);
|
|
415
|
+
if (item && safeValue(item, "id") === id)
|
|
416
|
+
return item;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
return undefined;
|
|
421
|
+
}
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
function groupItemStatus(value, id) {
|
|
425
|
+
const item = groupItem(value, id);
|
|
426
|
+
if (!item)
|
|
427
|
+
return undefined;
|
|
428
|
+
const status = safeString(safeValue(item, "status"));
|
|
429
|
+
return status && isTodoStatus(status) ? status : undefined;
|
|
430
|
+
}
|
|
431
|
+
function groupItemSubject(value, id) {
|
|
432
|
+
const item = groupItem(value, id);
|
|
433
|
+
if (!item)
|
|
434
|
+
return undefined;
|
|
435
|
+
const subject = callSubject(safeValue(item, "subject"));
|
|
436
|
+
return subject || undefined;
|
|
437
|
+
}
|
|
438
|
+
function formatGroupIds(ids) {
|
|
439
|
+
const consecutive = ids.length > 1 && ids.every((id, index) => id === ids[0] + index);
|
|
440
|
+
return consecutive ? `#${ids[0]}–#${ids.at(-1)}` : ids.map((id) => `#${id}`).join(", ");
|
|
441
|
+
}
|
|
442
|
+
function formatGroupFailure(result) {
|
|
443
|
+
try {
|
|
444
|
+
for (const block of result?.content.slice(0, 8) ?? []) {
|
|
445
|
+
if (block.type !== "text" || typeof block.text !== "string")
|
|
446
|
+
continue;
|
|
447
|
+
const reason = block.text
|
|
448
|
+
.replace(/[\u0000-\u001f\u007f-\u009f]/g, " ")
|
|
449
|
+
.replace(/\s+/g, " ")
|
|
450
|
+
.trim();
|
|
451
|
+
if (!reason)
|
|
452
|
+
continue;
|
|
453
|
+
return reason.length > GROUP_FAILURE_MAX_LENGTH ? `${reason.slice(0, GROUP_FAILURE_MAX_LENGTH - 1)}…` : reason;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
// Tool results can come from historical or third-party sources.
|
|
458
|
+
}
|
|
459
|
+
return "tool failed";
|
|
460
|
+
}
|
|
461
|
+
function formatTodoGroupSuccess(args, result) {
|
|
462
|
+
const details = groupDetails(result.details);
|
|
463
|
+
if (!details)
|
|
464
|
+
return undefined;
|
|
465
|
+
switch (details.action) {
|
|
466
|
+
case "create": {
|
|
467
|
+
const ids = groupIds(safeValue(details.operation, "ids"));
|
|
468
|
+
if (!ids || ids.length !== 1)
|
|
469
|
+
return undefined;
|
|
470
|
+
const subject = groupItemSubject(details.items, ids[0]) ?? callSubject(args?.subject);
|
|
471
|
+
const preview = subject ? truncateToWidth(subject, CALL_SUBJECT_PREVIEW_WIDTH, "…") : "";
|
|
472
|
+
return `todo created #${ids[0]}${preview ? ` ${preview}` : ""}`;
|
|
473
|
+
}
|
|
474
|
+
case "create_many": {
|
|
475
|
+
const ids = groupIds(safeValue(details.operation, "ids"));
|
|
476
|
+
if (!ids)
|
|
477
|
+
return undefined;
|
|
478
|
+
const callItems = callBatchItems(args?.items);
|
|
479
|
+
const subjects = ids.map((id, index) => groupItemSubject(details.items, id) ?? callItems[index]?.subject ?? "");
|
|
480
|
+
const preview = formatSubjectPreview(subjects, ids.length);
|
|
481
|
+
return `todo created ${formatGroupIds(ids)}${preview ? ` · ${preview}` : ""}`;
|
|
482
|
+
}
|
|
483
|
+
case "update": {
|
|
484
|
+
const id = groupPositiveInteger(safeValue(details.operation, "id"));
|
|
485
|
+
const status = safeString(safeValue(details.operation, "status"));
|
|
486
|
+
return id !== undefined && status && isTodoStatus(status) ? `todo updated #${id} ${status}` : undefined;
|
|
487
|
+
}
|
|
488
|
+
case "list": {
|
|
489
|
+
const counts = formatGroupStatusCounts(safeValue(details.operation, "statusCounts"), safeValue(details.operation, "resultCount"));
|
|
490
|
+
return counts === undefined ? undefined : `todo list: ${counts}`;
|
|
491
|
+
}
|
|
492
|
+
case "get": {
|
|
493
|
+
const id = groupPositiveInteger(safeValue(details.operation, "id"));
|
|
494
|
+
const status = id === undefined ? undefined : groupItemStatus(details.items, id);
|
|
495
|
+
return status ? `todo get #${id} ${status}` : undefined;
|
|
496
|
+
}
|
|
497
|
+
case "delete": {
|
|
498
|
+
const id = groupPositiveInteger(safeValue(details.operation, "id"));
|
|
499
|
+
if (id === undefined)
|
|
500
|
+
return undefined;
|
|
501
|
+
const subject = groupItemSubject(details.items, id);
|
|
502
|
+
const preview = subject ? truncateToWidth(subject, CALL_SUBJECT_PREVIEW_WIDTH, "…") : "";
|
|
503
|
+
return `todo deleted #${id}${preview ? ` ${preview}` : ""}`;
|
|
504
|
+
}
|
|
505
|
+
case "clear": {
|
|
506
|
+
const count = callNonNegativeInteger(safeValue(details.operation, "count"));
|
|
507
|
+
return count === undefined ? undefined : `todo cleared ${count} ${count === 1 ? "task" : "tasks"}`;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
/** Compact result-aware summary used only by collapsed todo tool groups. */
|
|
512
|
+
export function formatTodoGroupCall(args, theme, context) {
|
|
513
|
+
if (context.isError) {
|
|
514
|
+
return `${formatTodoCall(args, theme, false)} ${theme.fg("error", `failed: ${formatGroupFailure(context.result)}`)}`;
|
|
515
|
+
}
|
|
516
|
+
if (!context.isPartial && context.result) {
|
|
517
|
+
const summary = formatTodoGroupSuccess(args, context.result);
|
|
518
|
+
if (summary)
|
|
519
|
+
return theme.fg("toolTitle", summary);
|
|
520
|
+
}
|
|
521
|
+
return formatTodoCall(args, theme, false);
|
|
522
|
+
}
|
|
103
523
|
export function formatCommandList(state) {
|
|
104
524
|
const visible = visibleTodos(state);
|
|
105
525
|
if (!visible.length)
|
|
106
526
|
return "No todos yet.";
|
|
107
527
|
const lines = [];
|
|
528
|
+
let shown = 0;
|
|
108
529
|
for (const status of ["in_progress", "pending", "completed"]) {
|
|
109
530
|
const group = visible.filter((item) => item.status === status).sort(byIdAsc);
|
|
110
|
-
if (!group.length)
|
|
531
|
+
if (!group.length || shown >= LIST_DISPLAY_MAX_ITEMS)
|
|
111
532
|
continue;
|
|
112
533
|
lines.push(status);
|
|
113
534
|
for (const item of group) {
|
|
535
|
+
if (shown >= LIST_DISPLAY_MAX_ITEMS)
|
|
536
|
+
break;
|
|
114
537
|
const active = item.status === "in_progress" && item.activeForm ? ` (${item.activeForm})` : "";
|
|
115
538
|
const owner = item.owner ? ` @${item.owner}` : "";
|
|
116
539
|
const unresolved = unresolvedDependencyIds(state, item);
|
|
117
540
|
const deps = unresolved.length ? ` blocked by ${unresolved.map((id) => `#${id}`).join(",")}` : "";
|
|
118
541
|
lines.push(` ${STATUS_MARK[item.status]} #${item.id} ${item.subject}${active}${owner}${deps}`);
|
|
542
|
+
shown++;
|
|
119
543
|
}
|
|
120
544
|
}
|
|
545
|
+
if (visible.length > shown) {
|
|
546
|
+
lines.push(`… and ${visible.length - shown} more; use todo list with limit and afterId to page.`);
|
|
547
|
+
}
|
|
121
548
|
return lines.join("\n");
|
|
122
549
|
}
|
|
123
550
|
//# sourceMappingURL=view.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view.js","sourceRoot":"","sources":["../../../src/extensions/todo/view.ts"],"names":[],"mappings":"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","sourcesContent":["import { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport type { TodoItem, TodoState, TodoStatus } from \"./schema.ts\";\nimport { unresolvedDependencyIds } from \"./state.ts\";\n\nexport const STATUS_MARK: Record<TodoStatus, string> = {\n\tpending: \"[ ]\",\n\tin_progress: \"[>]\",\n\tcompleted: \"[x]\",\n\tdeleted: \"[-]\",\n};\n\nexport const STATUS_COLOR: Record<TodoStatus, \"dim\" | \"warning\" | \"success\" | \"muted\"> = {\n\tpending: \"dim\",\n\tin_progress: \"warning\",\n\tcompleted: \"success\",\n\tdeleted: \"muted\",\n};\n\nconst OVERLAY_BODY_MAX_ITEMS = 10;\n\nfunction visibleTodos(state: TodoState): TodoItem[] {\n\treturn state.items.filter((item) => item.status !== \"deleted\");\n}\n\nexport function hasVisibleOverlayItems(state: TodoState, hiddenCompleted: ReadonlySet<number>): boolean {\n\treturn visibleTodos(state).some((item) => item.status !== \"completed\" || !hiddenCompleted.has(item.id));\n}\n\nfunction todoCounts(items: TodoItem[]): { total: number; pending: number; inProgress: number; completed: number } {\n\tconst counts = { total: items.length, pending: 0, inProgress: 0, completed: 0 };\n\tfor (const item of items) {\n\t\tif (item.status === \"pending\") counts.pending++;\n\t\telse if (item.status === \"in_progress\") counts.inProgress++;\n\t\telse if (item.status === \"completed\") counts.completed++;\n\t}\n\treturn counts;\n}\n\nexport function renderOverlayLines(\n\tstate: TodoState,\n\ttheme: Theme,\n\twidth: number,\n\thiddenCompleted: ReadonlySet<number>,\n): string[] {\n\tconst allVisible = visibleTodos(state);\n\tconst visible = allVisible.filter((item) => item.status !== \"completed\" || !hiddenCompleted.has(item.id));\n\tif (!visible.length) return [];\n\n\tconst counts = todoCounts(allVisible);\n\tconst hasActive = visible.some((item) => item.status === \"pending\" || item.status === \"in_progress\");\n\tconst heading = `${theme.fg(hasActive ? \"accent\" : \"dim\", \"Todos\")} ${theme.fg(\"dim\", `(${counts.completed}/${counts.total})`)}`;\n\tconst lines = [truncateToWidth(heading, width, \"...\")];\n\n\tconst showIds = visible.some((item) => item.blockedBy?.length);\n\tconst body = chooseOverlayItems(visible, state, OVERLAY_BODY_MAX_ITEMS);\n\tfor (const item of body.items) {\n\t\tlines.push(truncateToWidth(` ${formatOverlayItem(item, state, theme, showIds)}`, width, \"...\"));\n\t}\n\tif (body.hidden.length > 0) {\n\t\tlines.push(truncateToWidth(theme.fg(\"dim\", ` ${formatHiddenSummary(body.hidden)}`), width, \"...\"));\n\t}\n\tlines.push(\"\");\n\treturn lines;\n}\n\nfunction byIdAsc(first: TodoItem, second: TodoItem): number {\n\treturn first.id - second.id;\n}\n\nfunction overlayPriority(state: TodoState, item: TodoItem): number {\n\tif (item.status === \"in_progress\") return 0;\n\tif (item.status === \"pending\" && unresolvedDependencyIds(state, item).length === 0) return 1;\n\tif (item.status === \"pending\") return 2;\n\treturn 3; // Completed items here are recent; older ones are hidden by TodoOverlay.\n}\n\nfunction chooseOverlayItems(\n\titems: TodoItem[],\n\tstate: TodoState,\n\tmaxBody: number,\n): { items: TodoItem[]; hidden: TodoItem[] } {\n\tconst prioritized = [...items].sort((first, second) => {\n\t\tconst priorityDelta = overlayPriority(state, first) - overlayPriority(state, second);\n\t\treturn priorityDelta || byIdAsc(first, second);\n\t});\n\tif (prioritized.length <= maxBody) return { items: prioritized, hidden: [] };\n\n\tconst shown = prioritized.slice(0, Math.max(1, maxBody - 1));\n\treturn { items: shown, hidden: prioritized.slice(shown.length) };\n}\n\nfunction formatHiddenSummary(items: TodoItem[]): string {\n\tconst counts = todoCounts(items);\n\tconst parts: string[] = [];\n\tif (counts.inProgress) parts.push(`${counts.inProgress} in progress`);\n\tif (counts.pending) parts.push(`${counts.pending} pending`);\n\tif (counts.completed) parts.push(`${counts.completed} completed`);\n\treturn `+${items.length} more${parts.length ? ` (${parts.join(\", \")})` : \"\"}`;\n}\n\nfunction formatOverlayItem(item: TodoItem, state: TodoState, theme: Theme, showId: boolean): string {\n\tlet text = theme.fg(STATUS_COLOR[item.status], STATUS_MARK[item.status]);\n\tif (showId) text += ` ${theme.fg(\"accent\", `#${item.id}`)}`;\n\tconst subject =\n\t\titem.status === \"completed\" ? theme.strikethrough(theme.fg(\"dim\", item.subject)) : theme.fg(\"text\", item.subject);\n\ttext += ` ${subject}`;\n\tif (item.status === \"in_progress\" && item.activeForm) text += ` ${theme.fg(\"dim\", `(${item.activeForm})`)}`;\n\n\tconst unresolved = unresolvedDependencyIds(state, item);\n\tif (unresolved.length) {\n\t\ttext += ` ${theme.fg(\"dim\", `blocked by ${unresolved.map((id) => `#${id}`).join(\",\")}`)}`;\n\t\tif (item.status === \"pending\" || item.status === \"in_progress\")\n\t\t\ttext += ` ${theme.fg(\"warning\", \"(deps incomplete)\")}`;\n\t}\n\treturn text;\n}\n\nexport function formatCommandList(state: TodoState): string {\n\tconst visible = visibleTodos(state);\n\tif (!visible.length) return \"No todos yet.\";\n\tconst lines: string[] = [];\n\tfor (const status of [\"in_progress\", \"pending\", \"completed\"] as const) {\n\t\tconst group = visible.filter((item) => item.status === status).sort(byIdAsc);\n\t\tif (!group.length) continue;\n\t\tlines.push(status);\n\t\tfor (const item of group) {\n\t\t\tconst active = item.status === \"in_progress\" && item.activeForm ? ` (${item.activeForm})` : \"\";\n\t\t\tconst owner = item.owner ? ` @${item.owner}` : \"\";\n\t\t\tconst unresolved = unresolvedDependencyIds(state, item);\n\t\t\tconst deps = unresolved.length ? ` blocked by ${unresolved.map((id) => `#${id}`).join(\",\")}` : \"\";\n\t\t\tlines.push(` ${STATUS_MARK[item.status]} #${item.id} ${item.subject}${active}${owner}${deps}`);\n\t\t}\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"view.js","sourceRoot":"","sources":["../../../src/extensions/todo/view.ts"],"names":[],"mappings":"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","sourcesContent":["import { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { AgentToolResult } from \"../../core/extensions/types.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { LIST_DISPLAY_MAX_ITEMS, TODO_MAX_BATCH_ITEMS, TODO_MAX_BLOCKED_BY } from \"./constants.ts\";\nimport {\n\tTODO_DETAILS_SCHEMA_VERSION,\n\ttype TodoItem,\n\ttype TodoParams,\n\ttype TodoState,\n\ttype TodoStatus,\n} from \"./schema.ts\";\nimport { unresolvedDependencyIds } from \"./state.ts\";\n\nexport const STATUS_MARK: Record<TodoStatus, string> = {\n\tpending: \"[ ]\",\n\tin_progress: \"[>]\",\n\tcompleted: \"[x]\",\n\tdeleted: \"[-]\",\n};\n\nexport const STATUS_COLOR: Record<TodoStatus, \"dim\" | \"warning\" | \"success\" | \"muted\"> = {\n\tpending: \"dim\",\n\tin_progress: \"warning\",\n\tcompleted: \"success\",\n\tdeleted: \"muted\",\n};\n\nconst OVERLAY_BODY_MAX_ITEMS = 10;\n\nfunction visibleTodos(state: TodoState): TodoItem[] {\n\treturn state.items.filter((item) => item.status !== \"deleted\");\n}\n\nexport function hasVisibleOverlayItems(state: TodoState, hiddenCompleted: ReadonlySet<number>): boolean {\n\treturn visibleTodos(state).some((item) => item.status !== \"completed\" || !hiddenCompleted.has(item.id));\n}\n\nfunction todoCounts(items: TodoItem[]): { total: number; pending: number; inProgress: number; completed: number } {\n\tconst counts = { total: items.length, pending: 0, inProgress: 0, completed: 0 };\n\tfor (const item of items) {\n\t\tif (item.status === \"pending\") counts.pending++;\n\t\telse if (item.status === \"in_progress\") counts.inProgress++;\n\t\telse if (item.status === \"completed\") counts.completed++;\n\t}\n\treturn counts;\n}\n\nexport function renderOverlayLines(\n\tstate: TodoState,\n\ttheme: Theme,\n\twidth: number,\n\thiddenCompleted: ReadonlySet<number>,\n): string[] {\n\tconst allVisible = visibleTodos(state);\n\tconst visible = allVisible.filter((item) => item.status !== \"completed\" || !hiddenCompleted.has(item.id));\n\tif (!visible.length) return [];\n\n\tconst counts = todoCounts(allVisible);\n\tconst hasActive = visible.some((item) => item.status === \"pending\" || item.status === \"in_progress\");\n\tconst heading = `${theme.fg(hasActive ? \"accent\" : \"dim\", \"Todos\")} ${theme.fg(\"dim\", `(${counts.completed}/${counts.total})`)}`;\n\tconst lines = [truncateToWidth(heading, width, \"...\")];\n\n\tconst showIds = visible.some((item) => item.blockedBy?.length);\n\tconst body = chooseOverlayItems(visible, state, OVERLAY_BODY_MAX_ITEMS);\n\tfor (const item of body.items) {\n\t\tlines.push(truncateToWidth(` ${formatOverlayItem(item, state, theme, showIds)}`, width, \"...\"));\n\t}\n\tif (body.hidden.length > 0) {\n\t\tlines.push(truncateToWidth(theme.fg(\"dim\", ` ${formatHiddenSummary(body.hidden)}`), width, \"...\"));\n\t}\n\tlines.push(\"\");\n\treturn lines;\n}\n\nfunction byIdAsc(first: TodoItem, second: TodoItem): number {\n\treturn first.id - second.id;\n}\n\nfunction overlayPriority(state: TodoState, item: TodoItem): number {\n\tif (item.status === \"in_progress\") return 0;\n\tif (item.status === \"pending\" && unresolvedDependencyIds(state, item).length === 0) return 1;\n\tif (item.status === \"pending\") return 2;\n\treturn 3; // Completed items here are recent; older ones are hidden by TodoOverlay.\n}\n\nfunction chooseOverlayItems(\n\titems: TodoItem[],\n\tstate: TodoState,\n\tmaxBody: number,\n): { items: TodoItem[]; hidden: TodoItem[] } {\n\tconst prioritized = [...items].sort((first, second) => {\n\t\tconst priorityDelta = overlayPriority(state, first) - overlayPriority(state, second);\n\t\treturn priorityDelta || byIdAsc(first, second);\n\t});\n\tif (prioritized.length <= maxBody) return { items: prioritized, hidden: [] };\n\n\tconst shown = prioritized.slice(0, Math.max(1, maxBody - 1));\n\treturn { items: shown, hidden: prioritized.slice(shown.length) };\n}\n\nfunction formatHiddenSummary(items: TodoItem[]): string {\n\tconst counts = todoCounts(items);\n\tconst parts: string[] = [];\n\tif (counts.inProgress) parts.push(`${counts.inProgress} in progress`);\n\tif (counts.pending) parts.push(`${counts.pending} pending`);\n\tif (counts.completed) parts.push(`${counts.completed} completed`);\n\treturn `+${items.length} more${parts.length ? ` (${parts.join(\", \")})` : \"\"}`;\n}\n\nfunction formatOverlayItem(item: TodoItem, state: TodoState, theme: Theme, showId: boolean): string {\n\tlet text = theme.fg(STATUS_COLOR[item.status], STATUS_MARK[item.status]);\n\tif (showId) text += ` ${theme.fg(\"accent\", `#${item.id}`)}`;\n\tconst subject =\n\t\titem.status === \"completed\" ? theme.strikethrough(theme.fg(\"dim\", item.subject)) : theme.fg(\"text\", item.subject);\n\ttext += ` ${subject}`;\n\tif (item.status === \"in_progress\" && item.activeForm) text += ` ${theme.fg(\"dim\", `(${item.activeForm})`)}`;\n\n\tconst unresolved = unresolvedDependencyIds(state, item);\n\tif (unresolved.length) {\n\t\ttext += ` ${theme.fg(\"dim\", `blocked by ${unresolved.map((id) => `#${id}`).join(\",\")}`)}`;\n\t\tif (item.status === \"pending\" || item.status === \"in_progress\")\n\t\t\ttext += ` ${theme.fg(\"warning\", \"(deps incomplete)\")}`;\n\t}\n\treturn text;\n}\n\n// ---- tool call rendering ----------------------------------------------------\n// Collapsed todo calls join the `todo` tool group, so each row must survive as a\n// single headline with no result beneath it (ToolGroupComponent drops results and\n// appends one shared expand hint). Args come straight off the wire and can be\n// partial while the call streams, so every field is read defensively.\n\ntype TodoCallArgs = Partial<Record<keyof TodoParams, unknown>>;\n\nconst EDGE_PARAMS = [\"blockedBy\", \"addBlockedBy\", \"removeBlockedBy\", \"addBlocks\", \"removeBlocks\"] as const;\n\nfunction callText(value: unknown): string {\n\treturn typeof value === \"string\" ? value.trim() : \"\";\n}\n\nfunction callSubject(value: unknown): string {\n\treturn callText(value).replace(/\\s+/g, \" \");\n}\n\nfunction callId(value: unknown): number | undefined {\n\treturn typeof value === \"number\" && Number.isInteger(value) ? value : undefined;\n}\n\nfunction callIds(value: unknown): number[] {\n\tif (!Array.isArray(value)) return [];\n\tconst ids: number[] = [];\n\tlet count: number;\n\ttry {\n\t\tcount = Math.min(value.length, TODO_MAX_BLOCKED_BY);\n\t} catch {\n\t\treturn ids;\n\t}\n\tfor (let index = 0; index < count; index++) {\n\t\tlet entry: unknown;\n\t\ttry {\n\t\t\tentry = value[index];\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tif (typeof entry === \"number\" && Number.isSafeInteger(entry) && entry >= 1) ids.push(entry);\n\t}\n\treturn ids;\n}\n\nfunction callArrayLength(value: unknown): number | undefined {\n\ttry {\n\t\treturn Array.isArray(value) ? value.length : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nconst CALL_BATCH_SUBJECT_PREVIEW_COUNT = 2;\nconst CALL_SUBJECT_PREVIEW_WIDTH = 72;\nconst CALL_DESCRIPTION_PREVIEW_LENGTH = 120;\n\ninterface CallBatchItem {\n\tsubject: string;\n\tdescription: string;\n\tblockedBy: number[];\n\tblockedByKeys: string[];\n}\n\nfunction callStrings(value: unknown): string[] {\n\tif (!Array.isArray(value)) return [];\n\tconst strings: string[] = [];\n\tlet count: number;\n\ttry {\n\t\tcount = Math.min(value.length, TODO_MAX_BLOCKED_BY);\n\t} catch {\n\t\treturn strings;\n\t}\n\tfor (let index = 0; index < count; index++) {\n\t\tlet entry: unknown;\n\t\ttry {\n\t\t\tentry = value[index];\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tconst text = callText(entry);\n\t\tif (text) strings.push(text);\n\t}\n\treturn strings;\n}\n\nfunction callBatchItems(value: unknown): CallBatchItem[] {\n\tif (!Array.isArray(value)) return [];\n\tlet count: number;\n\ttry {\n\t\tcount = Math.min(value.length, TODO_MAX_BATCH_ITEMS);\n\t} catch {\n\t\treturn [];\n\t}\n\tconst items: CallBatchItem[] = [];\n\tfor (let index = 0; index < count; index++) {\n\t\tlet valueAtIndex: unknown;\n\t\ttry {\n\t\t\tvalueAtIndex = value[index];\n\t\t} catch {\n\t\t\tvalueAtIndex = undefined;\n\t\t}\n\t\tconst item = safeRecord(valueAtIndex);\n\t\titems.push({\n\t\t\tsubject: item ? callSubject(safeValue(item, \"subject\")) : \"\",\n\t\t\tdescription: item ? callText(safeValue(item, \"description\")) : \"\",\n\t\t\tblockedBy: item ? callIds(safeValue(item, \"blockedBy\")) : [],\n\t\t\tblockedByKeys: item ? callStrings(safeValue(item, \"blockedByKeys\")) : [],\n\t\t});\n\t}\n\treturn items;\n}\n\nfunction formatSubjectPreview(subjects: string[], total: number): string {\n\tconst shown = subjects\n\t\t.slice(0, CALL_BATCH_SUBJECT_PREVIEW_COUNT)\n\t\t.filter(Boolean)\n\t\t.map((subject) => truncateToWidth(subject, CALL_SUBJECT_PREVIEW_WIDTH, \"…\"));\n\tif (!shown.length) return \"\";\n\tconst hidden = Math.max(0, total - shown.length);\n\treturn `${shown.join(\", \")}${hidden ? `, +${hidden} more` : \"\"}`;\n}\n\nfunction callBatchSubjectPreview(items: CallBatchItem[], total: number): string {\n\treturn formatSubjectPreview(\n\t\titems.map((item) => item.subject),\n\t\ttotal,\n\t);\n}\n\nfunction callDescriptionPreview(value: string): string {\n\tconst text = value.replace(/\\s+/g, \" \");\n\treturn text.length > CALL_DESCRIPTION_PREVIEW_LENGTH\n\t\t? `${text.slice(0, CALL_DESCRIPTION_PREVIEW_LENGTH - 1)}…`\n\t\t: text;\n}\n\nfunction callNonNegativeInteger(value: unknown): number | undefined {\n\treturn typeof value === \"number\" && Number.isInteger(value) && value >= 0 ? value : undefined;\n}\n\nfunction isTodoStatus(value: string): value is TodoStatus {\n\treturn Object.hasOwn(STATUS_MARK, value);\n}\n\nfunction formatCallHeadline(args: TodoCallArgs | undefined, theme: Theme): string {\n\tconst action = callText(args?.action);\n\tconst displayAction = action === \"create_many\" ? \"create\" : action;\n\tconst parts = [theme.fg(\"toolTitle\", theme.bold(displayAction ? `todo ${displayAction}` : \"todo\"))];\n\n\tif (action === \"create_many\") {\n\t\tconst items = callBatchItems(args?.items);\n\t\tconst count = callArrayLength(args?.items);\n\t\tif (count !== undefined) {\n\t\t\tparts.push(theme.fg(\"dim\", `${count} ${count === 1 ? \"task\" : \"tasks\"}`));\n\t\t\tconst preview = callBatchSubjectPreview(items, count);\n\t\t\tif (preview) {\n\t\t\t\tparts.push(theme.fg(\"dim\", \"·\"));\n\t\t\t\tparts.push(theme.fg(\"text\", preview));\n\t\t\t}\n\t\t}\n\t}\n\n\tconst id = callId(args?.id);\n\tif (id !== undefined) parts.push(theme.fg(\"accent\", `#${id}`));\n\n\tconst status = callText(args?.status);\n\tif (status && isTodoStatus(status)) parts.push(theme.fg(STATUS_COLOR[status], status));\n\n\tconst subject = callSubject(args?.subject);\n\tif (subject) parts.push(theme.fg(\"text\", subject));\n\n\t// Dependency-only updates would otherwise render as a bare `todo update #3`.\n\tconst hasEdgeChange = EDGE_PARAMS.some((key) => callIds(args?.[key]).length > 0);\n\tif (!status && !subject && hasEdgeChange) parts.push(theme.fg(\"dim\", \"dependencies\"));\n\n\treturn parts.join(\" \");\n}\n\nfunction batchResultIds(result: AgentToolResult<unknown> | undefined, count: number): number[] | undefined {\n\tconst details = groupDetails(result?.details);\n\tif (!details || details.action !== \"create_many\") return undefined;\n\tconst ids = groupIds(safeValue(details.operation, \"ids\"));\n\treturn ids?.length === count ? ids : undefined;\n}\n\nfunction formatCallDetails(args: TodoCallArgs | undefined, theme: Theme, result?: AgentToolResult<unknown>): string[] {\n\tconst lines: string[] = [];\n\tconst push = (label: string, value: string) => {\n\t\tif (value) lines.push(theme.fg(\"dim\", `${label}: ${value}`));\n\t};\n\n\tconst action = callText(args?.action);\n\tif (action === \"create_many\") {\n\t\tconst items = callBatchItems(args?.items);\n\t\tconst ids = batchResultIds(result, items.length);\n\t\tfor (const [index, item] of items.entries()) {\n\t\t\tconst dependencies = [...item.blockedBy.map((id) => `#${id}`), ...item.blockedByKeys];\n\t\t\tlet line = theme.fg(\"accent\", ids ? `#${ids[index]}` : `${index + 1}.`);\n\t\t\tif (item.subject) line += ` ${theme.fg(\"text\", item.subject)}`;\n\t\t\tif (dependencies.length) line += ` ${theme.fg(\"dim\", `· blocked by ${dependencies.join(\", \")}`)}`;\n\t\t\tlines.push(line);\n\t\t\tif (item.description) lines.push(` ${theme.fg(\"dim\", callDescriptionPreview(item.description))}`);\n\t\t}\n\t}\n\n\tpush(\"description\", callText(args?.description));\n\tpush(\"activeForm\", callText(args?.activeForm));\n\tpush(\"owner\", callText(args?.owner));\n\tfor (const key of EDGE_PARAMS) {\n\t\tconst ids = callIds(args?.[key]);\n\t\tif (ids.length) push(key, ids.map((id) => `#${id}`).join(\",\"));\n\t}\n\tif (args?.includeDeleted === true) push(\"includeDeleted\", \"true\");\n\tconst limit = callId(args?.limit);\n\tif (limit !== undefined) push(\"limit\", String(limit));\n\tconst afterId = callId(args?.afterId);\n\tif (afterId !== undefined) push(\"afterId\", String(afterId));\n\tpush(\"query\", callText(args?.query));\n\tif (args?.unblockedOnly === true) push(\"unblockedOnly\", \"true\");\n\tif (args?.confirm === true) push(\"confirm\", \"true\");\n\tconst expectedCount = callNonNegativeInteger(args?.expectedCount);\n\tif (expectedCount !== undefined) push(\"expectedCount\", String(expectedCount));\n\tconst metadata = args?.metadata;\n\tif (metadata && typeof metadata === \"object\") push(\"metadata\", Object.keys(metadata).join(\",\"));\n\treturn lines;\n}\n\n/**\n * One-line call summary when collapsed; the same headline plus the parameters\n * the result never echoes (description, activeForm, dependencies) when expanded.\n */\nexport function formatTodoCall(\n\targs: TodoCallArgs | undefined,\n\ttheme: Theme,\n\texpanded: boolean,\n\tresult?: AgentToolResult<unknown>,\n): string {\n\tconst headline = formatCallHeadline(args, theme);\n\tif (!expanded) return headline;\n\treturn [headline, ...formatCallDetails(args, theme, result)].join(\"\\n\");\n}\n\ninterface TodoGroupRenderContext {\n\tisError: boolean;\n\tisPartial: boolean;\n\tresult?: AgentToolResult<unknown>;\n}\n\ninterface GroupTodoDetails {\n\taction: string;\n\toperation: Record<string, unknown>;\n\titems: unknown;\n}\n\nconst GROUP_FAILURE_MAX_LENGTH = 120;\nconst GROUP_MAX_IDS = 20;\nconst GROUP_MAX_SNAPSHOT_ITEMS = 10_000;\nconst TODO_ACTIONS = new Set([\"create\", \"create_many\", \"update\", \"list\", \"get\", \"delete\", \"clear\"]);\n\nfunction safeRecord(value: unknown): Record<string, unknown> | undefined {\n\ttry {\n\t\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n\t\t\t? (value as Record<string, unknown>)\n\t\t\t: undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction safeValue(record: Record<string, unknown>, key: string): unknown {\n\ttry {\n\t\treturn record[key];\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction safeString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" ? value : undefined;\n}\n\nfunction groupDetails(value: unknown): GroupTodoDetails | undefined {\n\tconst details = safeRecord(value);\n\tif (!details || safeValue(details, \"schemaVersion\") !== TODO_DETAILS_SCHEMA_VERSION) return undefined;\n\tconst action = safeString(safeValue(details, \"action\"));\n\tif (!action || !TODO_ACTIONS.has(action)) return undefined;\n\tconst operation = safeRecord(safeValue(details, \"operation\"));\n\tif (!operation || safeValue(operation, \"kind\") !== action) return undefined;\n\treturn { action, operation, items: safeValue(details, \"items\") };\n}\n\nfunction groupPositiveInteger(value: unknown): number | undefined {\n\treturn typeof value === \"number\" && Number.isSafeInteger(value) && value >= 1 ? value : undefined;\n}\n\nfunction groupIds(value: unknown): number[] | undefined {\n\ttry {\n\t\tif (!Array.isArray(value) || value.length === 0 || value.length > GROUP_MAX_IDS) return undefined;\n\t\tconst ids: number[] = [];\n\t\tfor (const valueAtIndex of value) {\n\t\t\tconst id = groupPositiveInteger(valueAtIndex);\n\t\t\tif (id === undefined) return undefined;\n\t\t\tids.push(id);\n\t\t}\n\t\treturn ids;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction groupNonNegativeInteger(value: unknown): number | undefined {\n\treturn typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0 ? value : undefined;\n}\n\nfunction formatGroupStatusCounts(value: unknown, resultCount: unknown): string | undefined {\n\tconst count = groupNonNegativeInteger(resultCount);\n\tif (count === undefined) return undefined;\n\tif (count === 0) return \"no tasks\";\n\n\tconst rawCounts = safeRecord(value);\n\tif (!rawCounts) return undefined;\n\tconst parts: string[] = [];\n\tlet total = 0;\n\tfor (const status of [\"pending\", \"in_progress\", \"completed\", \"deleted\"] as const) {\n\t\tconst statusCount = groupNonNegativeInteger(safeValue(rawCounts, status));\n\t\tif (statusCount === undefined) continue;\n\t\ttotal += statusCount;\n\t\tif (statusCount > 0) parts.push(`${statusCount} ${status}`);\n\t}\n\treturn total === count && parts.length ? parts.join(\", \") : undefined;\n}\n\nfunction groupItem(value: unknown, id: number): Record<string, unknown> | undefined {\n\ttry {\n\t\tif (!Array.isArray(value) || value.length > GROUP_MAX_SNAPSHOT_ITEMS) return undefined;\n\t\tfor (const rawItem of value) {\n\t\t\tconst item = safeRecord(rawItem);\n\t\t\tif (item && safeValue(item, \"id\") === id) return item;\n\t\t}\n\t} catch {\n\t\treturn undefined;\n\t}\n\treturn undefined;\n}\n\nfunction groupItemStatus(value: unknown, id: number): TodoStatus | undefined {\n\tconst item = groupItem(value, id);\n\tif (!item) return undefined;\n\tconst status = safeString(safeValue(item, \"status\"));\n\treturn status && isTodoStatus(status) ? status : undefined;\n}\n\nfunction groupItemSubject(value: unknown, id: number): string | undefined {\n\tconst item = groupItem(value, id);\n\tif (!item) return undefined;\n\tconst subject = callSubject(safeValue(item, \"subject\"));\n\treturn subject || undefined;\n}\n\nfunction formatGroupIds(ids: number[]): string {\n\tconst consecutive = ids.length > 1 && ids.every((id, index) => id === ids[0] + index);\n\treturn consecutive ? `#${ids[0]}–#${ids.at(-1)}` : ids.map((id) => `#${id}`).join(\", \");\n}\n\nfunction formatGroupFailure(result: AgentToolResult<unknown> | undefined): string {\n\ttry {\n\t\tfor (const block of result?.content.slice(0, 8) ?? []) {\n\t\t\tif (block.type !== \"text\" || typeof block.text !== \"string\") continue;\n\t\t\tconst reason = block.text\n\t\t\t\t.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/g, \" \")\n\t\t\t\t.replace(/\\s+/g, \" \")\n\t\t\t\t.trim();\n\t\t\tif (!reason) continue;\n\t\t\treturn reason.length > GROUP_FAILURE_MAX_LENGTH ? `${reason.slice(0, GROUP_FAILURE_MAX_LENGTH - 1)}…` : reason;\n\t\t}\n\t} catch {\n\t\t// Tool results can come from historical or third-party sources.\n\t}\n\treturn \"tool failed\";\n}\n\nfunction formatTodoGroupSuccess(args: TodoCallArgs | undefined, result: AgentToolResult<unknown>): string | undefined {\n\tconst details = groupDetails(result.details);\n\tif (!details) return undefined;\n\n\tswitch (details.action) {\n\t\tcase \"create\": {\n\t\t\tconst ids = groupIds(safeValue(details.operation, \"ids\"));\n\t\t\tif (!ids || ids.length !== 1) return undefined;\n\t\t\tconst subject = groupItemSubject(details.items, ids[0]) ?? callSubject(args?.subject);\n\t\t\tconst preview = subject ? truncateToWidth(subject, CALL_SUBJECT_PREVIEW_WIDTH, \"…\") : \"\";\n\t\t\treturn `todo created #${ids[0]}${preview ? ` ${preview}` : \"\"}`;\n\t\t}\n\t\tcase \"create_many\": {\n\t\t\tconst ids = groupIds(safeValue(details.operation, \"ids\"));\n\t\t\tif (!ids) return undefined;\n\t\t\tconst callItems = callBatchItems(args?.items);\n\t\t\tconst subjects = ids.map(\n\t\t\t\t(id, index) => groupItemSubject(details.items, id) ?? callItems[index]?.subject ?? \"\",\n\t\t\t);\n\t\t\tconst preview = formatSubjectPreview(subjects, ids.length);\n\t\t\treturn `todo created ${formatGroupIds(ids)}${preview ? ` · ${preview}` : \"\"}`;\n\t\t}\n\t\tcase \"update\": {\n\t\t\tconst id = groupPositiveInteger(safeValue(details.operation, \"id\"));\n\t\t\tconst status = safeString(safeValue(details.operation, \"status\"));\n\t\t\treturn id !== undefined && status && isTodoStatus(status) ? `todo updated #${id} ${status}` : undefined;\n\t\t}\n\t\tcase \"list\": {\n\t\t\tconst counts = formatGroupStatusCounts(\n\t\t\t\tsafeValue(details.operation, \"statusCounts\"),\n\t\t\t\tsafeValue(details.operation, \"resultCount\"),\n\t\t\t);\n\t\t\treturn counts === undefined ? undefined : `todo list: ${counts}`;\n\t\t}\n\t\tcase \"get\": {\n\t\t\tconst id = groupPositiveInteger(safeValue(details.operation, \"id\"));\n\t\t\tconst status = id === undefined ? undefined : groupItemStatus(details.items, id);\n\t\t\treturn status ? `todo get #${id} ${status}` : undefined;\n\t\t}\n\t\tcase \"delete\": {\n\t\t\tconst id = groupPositiveInteger(safeValue(details.operation, \"id\"));\n\t\t\tif (id === undefined) return undefined;\n\t\t\tconst subject = groupItemSubject(details.items, id);\n\t\t\tconst preview = subject ? truncateToWidth(subject, CALL_SUBJECT_PREVIEW_WIDTH, \"…\") : \"\";\n\t\t\treturn `todo deleted #${id}${preview ? ` ${preview}` : \"\"}`;\n\t\t}\n\t\tcase \"clear\": {\n\t\t\tconst count = callNonNegativeInteger(safeValue(details.operation, \"count\"));\n\t\t\treturn count === undefined ? undefined : `todo cleared ${count} ${count === 1 ? \"task\" : \"tasks\"}`;\n\t\t}\n\t}\n}\n\n/** Compact result-aware summary used only by collapsed todo tool groups. */\nexport function formatTodoGroupCall(\n\targs: TodoCallArgs | undefined,\n\ttheme: Theme,\n\tcontext: TodoGroupRenderContext,\n): string {\n\tif (context.isError) {\n\t\treturn `${formatTodoCall(args, theme, false)} ${theme.fg(\"error\", `failed: ${formatGroupFailure(context.result)}`)}`;\n\t}\n\tif (!context.isPartial && context.result) {\n\t\tconst summary = formatTodoGroupSuccess(args, context.result);\n\t\tif (summary) return theme.fg(\"toolTitle\", summary);\n\t}\n\treturn formatTodoCall(args, theme, false);\n}\n\nexport function formatCommandList(state: TodoState): string {\n\tconst visible = visibleTodos(state);\n\tif (!visible.length) return \"No todos yet.\";\n\tconst lines: string[] = [];\n\tlet shown = 0;\n\tfor (const status of [\"in_progress\", \"pending\", \"completed\"] as const) {\n\t\tconst group = visible.filter((item) => item.status === status).sort(byIdAsc);\n\t\tif (!group.length || shown >= LIST_DISPLAY_MAX_ITEMS) continue;\n\t\tlines.push(status);\n\t\tfor (const item of group) {\n\t\t\tif (shown >= LIST_DISPLAY_MAX_ITEMS) break;\n\t\t\tconst active = item.status === \"in_progress\" && item.activeForm ? ` (${item.activeForm})` : \"\";\n\t\t\tconst owner = item.owner ? ` @${item.owner}` : \"\";\n\t\t\tconst unresolved = unresolvedDependencyIds(state, item);\n\t\t\tconst deps = unresolved.length ? ` blocked by ${unresolved.map((id) => `#${id}`).join(\",\")}` : \"\";\n\t\t\tlines.push(` ${STATUS_MARK[item.status]} #${item.id} ${item.subject}${active}${owner}${deps}`);\n\t\t\tshown++;\n\t\t}\n\t}\n\tif (visible.length > shown) {\n\t\tlines.push(`… and ${visible.length - shown} more; use todo list with limit and afterId to page.`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
|
package/dist/migrations.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAoDhD;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,IAAI,IAAI,CA+CnD;AA+ID;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB/E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;IAC3C,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC9B,CAOA","sourcesContent":["/**\n * One-time migrations that run on startup.\n */\n\nimport chalk from \"chalk\";\nimport { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir, getBinDir } from \"./config.ts\";\nimport { migrateKeybindingsConfig } from \"./core/keybindings.ts\";\n\nconst MIGRATION_GUIDE_URL =\n\t\"https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/CHANGELOG.md#extensions-migration\";\nconst EXTENSIONS_DOC_URL =\n\t\"https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/docs/extensions.md\";\n\n/**\n * Migrate legacy oauth.json and settings.json apiKeys to auth.json.\n *\n * @returns Array of provider names that were migrated\n */\nexport function migrateAuthToAuthJson(): string[] {\n\tconst agentDir = getAgentDir();\n\tconst authPath = join(agentDir, \"auth.json\");\n\tconst oauthPath = join(agentDir, \"oauth.json\");\n\tconst settingsPath = join(agentDir, \"settings.json\");\n\n\t// Skip if auth.json already exists\n\tif (existsSync(authPath)) return [];\n\n\tconst migrated: Record<string, unknown> = {};\n\tconst providers: string[] = [];\n\n\t// Migrate oauth.json\n\tif (existsSync(oauthPath)) {\n\t\ttry {\n\t\t\tconst oauth = JSON.parse(readFileSync(oauthPath, \"utf-8\"));\n\t\t\tfor (const [provider, cred] of Object.entries(oauth)) {\n\t\t\t\tmigrated[provider] = { type: \"oauth\", ...(cred as object) };\n\t\t\t\tproviders.push(provider);\n\t\t\t}\n\t\t\trenameSync(oauthPath, `${oauthPath}.migrated`);\n\t\t} catch {\n\t\t\t// Skip on error\n\t\t}\n\t}\n\n\t// Migrate settings.json apiKeys\n\tif (existsSync(settingsPath)) {\n\t\ttry {\n\t\t\tconst content = readFileSync(settingsPath, \"utf-8\");\n\t\t\tconst settings = JSON.parse(content);\n\t\t\tif (settings.apiKeys && typeof settings.apiKeys === \"object\") {\n\t\t\t\tfor (const [provider, key] of Object.entries(settings.apiKeys)) {\n\t\t\t\t\tif (!migrated[provider] && typeof key === \"string\") {\n\t\t\t\t\t\tmigrated[provider] = { type: \"api_key\", key };\n\t\t\t\t\t\tproviders.push(provider);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdelete settings.apiKeys;\n\t\t\t\twriteFileSync(settingsPath, JSON.stringify(settings, null, 2));\n\t\t\t}\n\t\t} catch {\n\t\t\t// Skip on error\n\t\t}\n\t}\n\n\tif (Object.keys(migrated).length > 0) {\n\t\tmkdirSync(dirname(authPath), { recursive: true });\n\t\twriteFileSync(authPath, JSON.stringify(migrated, null, 2), { mode: 0o600 });\n\t}\n\n\treturn providers;\n}\n\n/**\n * Migrate sessions from ~/.pi/agent/*.jsonl to proper session directories.\n *\n * Bug in v0.30.0: Sessions were saved to ~/.pi/agent/ instead of\n * ~/.pi/agent/sessions/<encoded-cwd>/. This migration moves them\n * to the correct location based on the cwd in their session header.\n *\n * See: https://github.com/earendil-works/pi-mono/issues/320\n */\nexport function migrateSessionsFromAgentRoot(): void {\n\tconst agentDir = getAgentDir();\n\n\t// Find all .jsonl files directly in agentDir (not in subdirectories)\n\tlet files: string[];\n\ttry {\n\t\tfiles = readdirSync(agentDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(agentDir, f));\n\t} catch {\n\t\treturn;\n\t}\n\n\tif (files.length === 0) return;\n\n\tfor (const file of files) {\n\t\ttry {\n\t\t\t// Read first line to get session header\n\t\t\tconst content = readFileSync(file, \"utf8\");\n\t\t\tconst firstLine = content.split(\"\\n\")[0];\n\t\t\tif (!firstLine?.trim()) continue;\n\n\t\t\tconst header = JSON.parse(firstLine);\n\t\t\tif (header.type !== \"session\" || !header.cwd) continue;\n\n\t\t\tconst cwd: string = header.cwd;\n\n\t\t\t// Compute the correct session directory (same encoding as session-manager.ts)\n\t\t\tconst safePath = `--${cwd.replace(/^[/\\\\]/, \"\").replace(/[/\\\\:]/g, \"-\")}--`;\n\t\t\tconst correctDir = join(agentDir, \"sessions\", safePath);\n\n\t\t\t// Create directory if needed\n\t\t\tif (!existsSync(correctDir)) {\n\t\t\t\tmkdirSync(correctDir, { recursive: true });\n\t\t\t}\n\n\t\t\t// Move the file\n\t\t\tconst fileName = file.split(\"/\").pop() || file.split(\"\\\\\").pop();\n\t\t\tconst newPath = join(correctDir, fileName!);\n\n\t\t\tif (existsSync(newPath)) continue; // Skip if target exists\n\n\t\t\trenameSync(file, newPath);\n\t\t} catch {\n\t\t\t// Skip files that can't be migrated\n\t\t}\n\t}\n}\n\n/**\n * Migrate commands/ to prompts/ if needed.\n * Works for both regular directories and symlinks.\n */\nfunction migrateCommandsToPrompts(baseDir: string, label: string): boolean {\n\tconst commandsDir = join(baseDir, \"commands\");\n\tconst promptsDir = join(baseDir, \"prompts\");\n\n\tif (existsSync(commandsDir) && !existsSync(promptsDir)) {\n\t\ttry {\n\t\t\trenameSync(commandsDir, promptsDir);\n\t\t\tconsole.log(chalk.green(`Migrated ${label} commands/ → prompts/`));\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\tconsole.log(\n\t\t\t\tchalk.yellow(\n\t\t\t\t\t`Warning: Could not migrate ${label} commands/ to prompts/: ${err instanceof Error ? err.message : err}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\treturn false;\n}\n\nfunction migrateKeybindingsConfigFile(): void {\n\tconst configPath = join(getAgentDir(), \"keybindings.json\");\n\tif (!existsSync(configPath)) return;\n\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(configPath, \"utf-8\")) as unknown;\n\t\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\t\treturn;\n\t\t}\n\t\tconst { config, migrated } = migrateKeybindingsConfig(parsed as Record<string, unknown>);\n\t\tif (!migrated) return;\n\t\twriteFileSync(configPath, `${JSON.stringify(config, null, 2)}\\n`, \"utf-8\");\n\t} catch {\n\t\t// Ignore malformed files during migration\n\t}\n}\n\n/**\n * Move fd/rg binaries from tools/ to bin/ if they exist.\n */\nfunction migrateToolsToBin(): void {\n\tconst agentDir = getAgentDir();\n\tconst toolsDir = join(agentDir, \"tools\");\n\tconst binDir = getBinDir();\n\n\tif (!existsSync(toolsDir)) return;\n\n\tconst binaries = [\"fd\", \"rg\", \"fd.exe\", \"rg.exe\"];\n\tlet movedAny = false;\n\n\tfor (const bin of binaries) {\n\t\tconst oldPath = join(toolsDir, bin);\n\t\tconst newPath = join(binDir, bin);\n\n\t\tif (existsSync(oldPath)) {\n\t\t\tif (!existsSync(binDir)) {\n\t\t\t\tmkdirSync(binDir, { recursive: true });\n\t\t\t}\n\t\t\tif (!existsSync(newPath)) {\n\t\t\t\ttry {\n\t\t\t\t\trenameSync(oldPath, newPath);\n\t\t\t\t\tmovedAny = true;\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore errors\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Target exists, just delete the old one\n\t\t\t\ttry {\n\t\t\t\t\trmSync?.(oldPath, { force: true });\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (movedAny) {\n\t\tconsole.log(chalk.green(`Migrated managed binaries tools/ → bin/`));\n\t}\n}\n\n/**\n * Check for deprecated hooks/ and tools/ directories.\n * Note: tools/ may contain fd/rg binaries extracted by pi, so only warn if it has other files.\n */\nfunction checkDeprecatedExtensionDirs(baseDir: string, label: string): string[] {\n\tconst hooksDir = join(baseDir, \"hooks\");\n\tconst toolsDir = join(baseDir, \"tools\");\n\tconst warnings: string[] = [];\n\n\tif (existsSync(hooksDir)) {\n\t\twarnings.push(`${label} hooks/ directory found. Hooks have been renamed to extensions.`);\n\t}\n\n\tif (existsSync(toolsDir)) {\n\t\t// Check if tools/ contains anything other than fd/rg (which are auto-extracted binaries)\n\t\ttry {\n\t\t\tconst entries = readdirSync(toolsDir);\n\t\t\tconst customTools = entries.filter((e) => {\n\t\t\t\tconst lower = e.toLowerCase();\n\t\t\t\treturn (\n\t\t\t\t\tlower !== \"fd\" && lower !== \"rg\" && lower !== \"fd.exe\" && lower !== \"rg.exe\" && !e.startsWith(\".\") // Ignore .DS_Store and other hidden files\n\t\t\t\t);\n\t\t\t});\n\t\t\tif (customTools.length > 0) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`${label} tools/ directory contains custom tools. Custom tools have been merged into extensions.`,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore read errors\n\t\t}\n\t}\n\n\treturn warnings;\n}\n\n/**\n * Run extension system migrations (commands→prompts) and collect warnings about deprecated directories.\n */\nfunction migrateExtensionSystem(cwd: string): string[] {\n\tconst agentDir = getAgentDir();\n\tconst projectDir = join(cwd, CONFIG_DIR_NAME);\n\n\t// Migrate commands/ to prompts/\n\tmigrateCommandsToPrompts(agentDir, \"Global\");\n\tmigrateCommandsToPrompts(projectDir, \"Project\");\n\n\t// Check for deprecated directories\n\tconst warnings = [\n\t\t...checkDeprecatedExtensionDirs(agentDir, \"Global\"),\n\t\t...checkDeprecatedExtensionDirs(projectDir, \"Project\"),\n\t];\n\n\treturn warnings;\n}\n\n/**\n * Print deprecation warnings and wait for keypress.\n */\nexport async function showDeprecationWarnings(warnings: string[]): Promise<void> {\n\tif (warnings.length === 0) return;\n\n\tfor (const warning of warnings) {\n\t\tconsole.log(chalk.yellow(`Warning: ${warning}`));\n\t}\n\tconsole.log(chalk.yellow(`\\nMove your extensions to the extensions/ directory.`));\n\tconsole.log(chalk.yellow(`Migration guide: ${MIGRATION_GUIDE_URL}`));\n\tconsole.log(chalk.yellow(`Documentation: ${EXTENSIONS_DOC_URL}`));\n\tconsole.log(chalk.dim(`\\nPress any key to continue...`));\n\n\tawait new Promise<void>((resolve) => {\n\t\tprocess.stdin.setRawMode?.(true);\n\t\tprocess.stdin.resume();\n\t\tprocess.stdin.once(\"data\", () => {\n\t\t\tprocess.stdin.setRawMode?.(false);\n\t\t\tprocess.stdin.pause();\n\t\t\tresolve();\n\t\t});\n\t});\n\tconsole.log();\n}\n\n/**\n * Run all migrations. Called once on startup.\n *\n * @returns Object with migration results and deprecation warnings\n */\nexport function runMigrations(cwd: string): {\n\tmigratedAuthProviders: string[];\n\tdeprecationWarnings: string[];\n} {\n\tconst migratedAuthProviders = migrateAuthToAuthJson();\n\tmigrateSessionsFromAgentRoot();\n\tmigrateToolsToBin();\n\tmigrateKeybindingsConfigFile();\n\tconst deprecationWarnings = migrateExtensionSystem(cwd);\n\treturn { migratedAuthProviders, deprecationWarnings };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AAAA;;GAEG;AAcH;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAoDhD;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,IAAI,IAAI,CA+CnD;AA+ID;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB/E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;IAC3C,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC9B,CAOA","sourcesContent":["/**\n * One-time migrations that run on startup.\n */\n\nimport chalk from \"chalk\";\nimport { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir, getBinDir } from \"./config.ts\";\nimport { migrateKeybindingsConfig } from \"./core/keybindings.ts\";\nimport { cwdToSafeDirName } from \"./utils/paths.ts\";\n\nconst MIGRATION_GUIDE_URL =\n\t\"https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/CHANGELOG.md#extensions-migration\";\nconst EXTENSIONS_DOC_URL =\n\t\"https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/docs/extensions.md\";\n\n/**\n * Migrate legacy oauth.json and settings.json apiKeys to auth.json.\n *\n * @returns Array of provider names that were migrated\n */\nexport function migrateAuthToAuthJson(): string[] {\n\tconst agentDir = getAgentDir();\n\tconst authPath = join(agentDir, \"auth.json\");\n\tconst oauthPath = join(agentDir, \"oauth.json\");\n\tconst settingsPath = join(agentDir, \"settings.json\");\n\n\t// Skip if auth.json already exists\n\tif (existsSync(authPath)) return [];\n\n\tconst migrated: Record<string, unknown> = {};\n\tconst providers: string[] = [];\n\n\t// Migrate oauth.json\n\tif (existsSync(oauthPath)) {\n\t\ttry {\n\t\t\tconst oauth = JSON.parse(readFileSync(oauthPath, \"utf-8\"));\n\t\t\tfor (const [provider, cred] of Object.entries(oauth)) {\n\t\t\t\tmigrated[provider] = { type: \"oauth\", ...(cred as object) };\n\t\t\t\tproviders.push(provider);\n\t\t\t}\n\t\t\trenameSync(oauthPath, `${oauthPath}.migrated`);\n\t\t} catch {\n\t\t\t// Skip on error\n\t\t}\n\t}\n\n\t// Migrate settings.json apiKeys\n\tif (existsSync(settingsPath)) {\n\t\ttry {\n\t\t\tconst content = readFileSync(settingsPath, \"utf-8\");\n\t\t\tconst settings = JSON.parse(content);\n\t\t\tif (settings.apiKeys && typeof settings.apiKeys === \"object\") {\n\t\t\t\tfor (const [provider, key] of Object.entries(settings.apiKeys)) {\n\t\t\t\t\tif (!migrated[provider] && typeof key === \"string\") {\n\t\t\t\t\t\tmigrated[provider] = { type: \"api_key\", key };\n\t\t\t\t\t\tproviders.push(provider);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdelete settings.apiKeys;\n\t\t\t\twriteFileSync(settingsPath, JSON.stringify(settings, null, 2));\n\t\t\t}\n\t\t} catch {\n\t\t\t// Skip on error\n\t\t}\n\t}\n\n\tif (Object.keys(migrated).length > 0) {\n\t\tmkdirSync(dirname(authPath), { recursive: true });\n\t\twriteFileSync(authPath, JSON.stringify(migrated, null, 2), { mode: 0o600 });\n\t}\n\n\treturn providers;\n}\n\n/**\n * Migrate sessions from ~/.pi/agent/*.jsonl to proper session directories.\n *\n * Bug in v0.30.0: Sessions were saved to ~/.pi/agent/ instead of\n * ~/.pi/agent/sessions/<encoded-cwd>/. This migration moves them\n * to the correct location based on the cwd in their session header.\n *\n * See: https://github.com/earendil-works/pi-mono/issues/320\n */\nexport function migrateSessionsFromAgentRoot(): void {\n\tconst agentDir = getAgentDir();\n\n\t// Find all .jsonl files directly in agentDir (not in subdirectories)\n\tlet files: string[];\n\ttry {\n\t\tfiles = readdirSync(agentDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(agentDir, f));\n\t} catch {\n\t\treturn;\n\t}\n\n\tif (files.length === 0) return;\n\n\tfor (const file of files) {\n\t\ttry {\n\t\t\t// Read first line to get session header\n\t\t\tconst content = readFileSync(file, \"utf8\");\n\t\t\tconst firstLine = content.split(\"\\n\")[0];\n\t\t\tif (!firstLine?.trim()) continue;\n\n\t\t\tconst header = JSON.parse(firstLine);\n\t\t\tif (header.type !== \"session\" || !header.cwd) continue;\n\n\t\t\tconst cwd: string = header.cwd;\n\n\t\t\t// Compute the correct session directory (same encoding as session-manager.ts)\n\t\t\tconst safePath = cwdToSafeDirName(cwd);\n\t\t\tconst correctDir = join(agentDir, \"sessions\", safePath);\n\n\t\t\t// Create directory if needed\n\t\t\tif (!existsSync(correctDir)) {\n\t\t\t\tmkdirSync(correctDir, { recursive: true });\n\t\t\t}\n\n\t\t\t// Move the file\n\t\t\tconst fileName = file.split(\"/\").pop() || file.split(\"\\\\\").pop();\n\t\t\tconst newPath = join(correctDir, fileName!);\n\n\t\t\tif (existsSync(newPath)) continue; // Skip if target exists\n\n\t\t\trenameSync(file, newPath);\n\t\t} catch {\n\t\t\t// Skip files that can't be migrated\n\t\t}\n\t}\n}\n\n/**\n * Migrate commands/ to prompts/ if needed.\n * Works for both regular directories and symlinks.\n */\nfunction migrateCommandsToPrompts(baseDir: string, label: string): boolean {\n\tconst commandsDir = join(baseDir, \"commands\");\n\tconst promptsDir = join(baseDir, \"prompts\");\n\n\tif (existsSync(commandsDir) && !existsSync(promptsDir)) {\n\t\ttry {\n\t\t\trenameSync(commandsDir, promptsDir);\n\t\t\tconsole.log(chalk.green(`Migrated ${label} commands/ → prompts/`));\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\tconsole.log(\n\t\t\t\tchalk.yellow(\n\t\t\t\t\t`Warning: Could not migrate ${label} commands/ to prompts/: ${err instanceof Error ? err.message : err}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\treturn false;\n}\n\nfunction migrateKeybindingsConfigFile(): void {\n\tconst configPath = join(getAgentDir(), \"keybindings.json\");\n\tif (!existsSync(configPath)) return;\n\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(configPath, \"utf-8\")) as unknown;\n\t\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\t\treturn;\n\t\t}\n\t\tconst { config, migrated } = migrateKeybindingsConfig(parsed as Record<string, unknown>);\n\t\tif (!migrated) return;\n\t\twriteFileSync(configPath, `${JSON.stringify(config, null, 2)}\\n`, \"utf-8\");\n\t} catch {\n\t\t// Ignore malformed files during migration\n\t}\n}\n\n/**\n * Move fd/rg binaries from tools/ to bin/ if they exist.\n */\nfunction migrateToolsToBin(): void {\n\tconst agentDir = getAgentDir();\n\tconst toolsDir = join(agentDir, \"tools\");\n\tconst binDir = getBinDir();\n\n\tif (!existsSync(toolsDir)) return;\n\n\tconst binaries = [\"fd\", \"rg\", \"fd.exe\", \"rg.exe\"];\n\tlet movedAny = false;\n\n\tfor (const bin of binaries) {\n\t\tconst oldPath = join(toolsDir, bin);\n\t\tconst newPath = join(binDir, bin);\n\n\t\tif (existsSync(oldPath)) {\n\t\t\tif (!existsSync(binDir)) {\n\t\t\t\tmkdirSync(binDir, { recursive: true });\n\t\t\t}\n\t\t\tif (!existsSync(newPath)) {\n\t\t\t\ttry {\n\t\t\t\t\trenameSync(oldPath, newPath);\n\t\t\t\t\tmovedAny = true;\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore errors\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Target exists, just delete the old one\n\t\t\t\ttry {\n\t\t\t\t\trmSync?.(oldPath, { force: true });\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (movedAny) {\n\t\tconsole.log(chalk.green(`Migrated managed binaries tools/ → bin/`));\n\t}\n}\n\n/**\n * Check for deprecated hooks/ and tools/ directories.\n * Note: tools/ may contain fd/rg binaries extracted by pi, so only warn if it has other files.\n */\nfunction checkDeprecatedExtensionDirs(baseDir: string, label: string): string[] {\n\tconst hooksDir = join(baseDir, \"hooks\");\n\tconst toolsDir = join(baseDir, \"tools\");\n\tconst warnings: string[] = [];\n\n\tif (existsSync(hooksDir)) {\n\t\twarnings.push(`${label} hooks/ directory found. Hooks have been renamed to extensions.`);\n\t}\n\n\tif (existsSync(toolsDir)) {\n\t\t// Check if tools/ contains anything other than fd/rg (which are auto-extracted binaries)\n\t\ttry {\n\t\t\tconst entries = readdirSync(toolsDir);\n\t\t\tconst customTools = entries.filter((e) => {\n\t\t\t\tconst lower = e.toLowerCase();\n\t\t\t\treturn (\n\t\t\t\t\tlower !== \"fd\" && lower !== \"rg\" && lower !== \"fd.exe\" && lower !== \"rg.exe\" && !e.startsWith(\".\") // Ignore .DS_Store and other hidden files\n\t\t\t\t);\n\t\t\t});\n\t\t\tif (customTools.length > 0) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`${label} tools/ directory contains custom tools. Custom tools have been merged into extensions.`,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore read errors\n\t\t}\n\t}\n\n\treturn warnings;\n}\n\n/**\n * Run extension system migrations (commands→prompts) and collect warnings about deprecated directories.\n */\nfunction migrateExtensionSystem(cwd: string): string[] {\n\tconst agentDir = getAgentDir();\n\tconst projectDir = join(cwd, CONFIG_DIR_NAME);\n\n\t// Migrate commands/ to prompts/\n\tmigrateCommandsToPrompts(agentDir, \"Global\");\n\tmigrateCommandsToPrompts(projectDir, \"Project\");\n\n\t// Check for deprecated directories\n\tconst warnings = [\n\t\t...checkDeprecatedExtensionDirs(agentDir, \"Global\"),\n\t\t...checkDeprecatedExtensionDirs(projectDir, \"Project\"),\n\t];\n\n\treturn warnings;\n}\n\n/**\n * Print deprecation warnings and wait for keypress.\n */\nexport async function showDeprecationWarnings(warnings: string[]): Promise<void> {\n\tif (warnings.length === 0) return;\n\n\tfor (const warning of warnings) {\n\t\tconsole.log(chalk.yellow(`Warning: ${warning}`));\n\t}\n\tconsole.log(chalk.yellow(`\\nMove your extensions to the extensions/ directory.`));\n\tconsole.log(chalk.yellow(`Migration guide: ${MIGRATION_GUIDE_URL}`));\n\tconsole.log(chalk.yellow(`Documentation: ${EXTENSIONS_DOC_URL}`));\n\tconsole.log(chalk.dim(`\\nPress any key to continue...`));\n\n\tawait new Promise<void>((resolve) => {\n\t\tprocess.stdin.setRawMode?.(true);\n\t\tprocess.stdin.resume();\n\t\tprocess.stdin.once(\"data\", () => {\n\t\t\tprocess.stdin.setRawMode?.(false);\n\t\t\tprocess.stdin.pause();\n\t\t\tresolve();\n\t\t});\n\t});\n\tconsole.log();\n}\n\n/**\n * Run all migrations. Called once on startup.\n *\n * @returns Object with migration results and deprecation warnings\n */\nexport function runMigrations(cwd: string): {\n\tmigratedAuthProviders: string[];\n\tdeprecationWarnings: string[];\n} {\n\tconst migratedAuthProviders = migrateAuthToAuthJson();\n\tmigrateSessionsFromAgentRoot();\n\tmigrateToolsToBin();\n\tmigrateKeybindingsConfigFile();\n\tconst deprecationWarnings = migrateExtensionSystem(cwd);\n\treturn { migratedAuthProviders, deprecationWarnings };\n}\n"]}
|