@cydm/pie 1.0.2 → 1.0.4
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/builtin/extensions/subagent/index.js +10 -0
- package/dist/cli.js +334 -16
- package/package.json +1 -1
|
@@ -3716,6 +3716,9 @@ function logHttp(level, message, data) {
|
|
|
3716
3716
|
}
|
|
3717
3717
|
}
|
|
3718
3718
|
}
|
|
3719
|
+
function isAbortErrorMessage(message) {
|
|
3720
|
+
return message === "Request was aborted" || message === "Operation aborted";
|
|
3721
|
+
}
|
|
3719
3722
|
initLogFile();
|
|
3720
3723
|
var nodeHttps = null;
|
|
3721
3724
|
var nodeHttp = null;
|
|
@@ -3976,6 +3979,13 @@ var NodeHttpClient = class {
|
|
|
3976
3979
|
resolve(response);
|
|
3977
3980
|
});
|
|
3978
3981
|
req.on("error", (e) => {
|
|
3982
|
+
if (isAbortErrorMessage(e.message) && options.signal?.aborted) {
|
|
3983
|
+
logHttp("DEBUG", `Request [${requestId}] aborted`, {
|
|
3984
|
+
totalTimeMs: Date.now() - requestStartTime
|
|
3985
|
+
});
|
|
3986
|
+
reject(e);
|
|
3987
|
+
return;
|
|
3988
|
+
}
|
|
3979
3989
|
logHttp("ERROR", `Request [${requestId}] failed`, {
|
|
3980
3990
|
error: e.message,
|
|
3981
3991
|
totalTimeMs: Date.now() - requestStartTime
|
package/dist/cli.js
CHANGED
|
@@ -4914,6 +4914,9 @@ function logHttp(level, message, data) {
|
|
|
4914
4914
|
}
|
|
4915
4915
|
}
|
|
4916
4916
|
}
|
|
4917
|
+
function isAbortErrorMessage(message) {
|
|
4918
|
+
return message === "Request was aborted" || message === "Operation aborted";
|
|
4919
|
+
}
|
|
4917
4920
|
function getNodeHttps() {
|
|
4918
4921
|
if (!nodeHttps) {
|
|
4919
4922
|
nodeHttps = require3("https");
|
|
@@ -5184,6 +5187,13 @@ var init_http = __esm({
|
|
|
5184
5187
|
resolve5(response);
|
|
5185
5188
|
});
|
|
5186
5189
|
req.on("error", (e) => {
|
|
5190
|
+
if (isAbortErrorMessage(e.message) && options.signal?.aborted) {
|
|
5191
|
+
logHttp("DEBUG", `Request [${requestId}] aborted`, {
|
|
5192
|
+
totalTimeMs: Date.now() - requestStartTime
|
|
5193
|
+
});
|
|
5194
|
+
reject(e);
|
|
5195
|
+
return;
|
|
5196
|
+
}
|
|
5187
5197
|
logHttp("ERROR", `Request [${requestId}] failed`, {
|
|
5188
5198
|
error: e.message,
|
|
5189
5199
|
totalTimeMs: Date.now() - requestStartTime
|
|
@@ -65450,6 +65460,7 @@ init_file_logger();
|
|
|
65450
65460
|
import * as fs4 from "node:fs";
|
|
65451
65461
|
import * as os2 from "os";
|
|
65452
65462
|
import * as path3 from "path";
|
|
65463
|
+
import { pathToFileURL } from "node:url";
|
|
65453
65464
|
|
|
65454
65465
|
// src/config.ts
|
|
65455
65466
|
import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync2, renameSync } from "fs";
|
|
@@ -65823,7 +65834,7 @@ function resolvePath(extPath, cwd) {
|
|
|
65823
65834
|
}
|
|
65824
65835
|
async function loadExtensionModule(extensionPath) {
|
|
65825
65836
|
try {
|
|
65826
|
-
const module = await import(extensionPath);
|
|
65837
|
+
const module = await import(normalizeExtensionImportPath(extensionPath));
|
|
65827
65838
|
const factory = module.default ?? module;
|
|
65828
65839
|
if (typeof factory !== "function") {
|
|
65829
65840
|
return null;
|
|
@@ -65835,6 +65846,15 @@ async function loadExtensionModule(extensionPath) {
|
|
|
65835
65846
|
);
|
|
65836
65847
|
}
|
|
65837
65848
|
}
|
|
65849
|
+
function normalizeExtensionImportPath(extensionPath) {
|
|
65850
|
+
if (/^[a-zA-Z]:[\\/]/.test(extensionPath)) {
|
|
65851
|
+
return `file:///${extensionPath.replace(/\\/g, "/")}`;
|
|
65852
|
+
}
|
|
65853
|
+
if (path3.isAbsolute(extensionPath)) {
|
|
65854
|
+
return pathToFileURL(extensionPath).href;
|
|
65855
|
+
}
|
|
65856
|
+
return extensionPath;
|
|
65857
|
+
}
|
|
65838
65858
|
async function loadExtension2(extensionPath, cwd, uiContext, abortFn, isIdleFn, hasUI, actions) {
|
|
65839
65859
|
const resolvedPath = resolvePath(extensionPath, cwd);
|
|
65840
65860
|
if (!fs4.existsSync(resolvedPath)) {
|
|
@@ -69294,6 +69314,75 @@ function findFirstChangedLine(oldText, newText) {
|
|
|
69294
69314
|
return void 0;
|
|
69295
69315
|
}
|
|
69296
69316
|
|
|
69317
|
+
// src/todo/render.ts
|
|
69318
|
+
var TODO_STATUS_ICONS = {
|
|
69319
|
+
completed: "\u2713",
|
|
69320
|
+
"in-progress": "\u25C9",
|
|
69321
|
+
"not-started": "\u25CB"
|
|
69322
|
+
};
|
|
69323
|
+
function formatTodoTitle(todo, activeTheme) {
|
|
69324
|
+
if (todo.status === "completed") {
|
|
69325
|
+
return activeTheme.fg("dim", activeTheme.strikethrough(todo.title));
|
|
69326
|
+
}
|
|
69327
|
+
if (todo.status === "in-progress") {
|
|
69328
|
+
return activeTheme.fg("warning", todo.title);
|
|
69329
|
+
}
|
|
69330
|
+
return activeTheme.fg("muted", todo.title);
|
|
69331
|
+
}
|
|
69332
|
+
function formatTodoIcon(todo, activeTheme) {
|
|
69333
|
+
const icon = TODO_STATUS_ICONS[todo.status] ?? "?";
|
|
69334
|
+
if (todo.status === "completed") {
|
|
69335
|
+
return activeTheme.fg("success", icon);
|
|
69336
|
+
}
|
|
69337
|
+
if (todo.status === "in-progress") {
|
|
69338
|
+
return activeTheme.fg("warning", icon);
|
|
69339
|
+
}
|
|
69340
|
+
return activeTheme.fg("muted", icon);
|
|
69341
|
+
}
|
|
69342
|
+
function renderManageTodoListCall(args, activeTheme = theme) {
|
|
69343
|
+
let text = activeTheme.fg("toolTitle", activeTheme.bold("manage_todo_list "));
|
|
69344
|
+
text += activeTheme.fg("muted", args.operation || "...");
|
|
69345
|
+
if (args.operation === "write" && Array.isArray(args.todoList)) {
|
|
69346
|
+
const count = args.todoList.length;
|
|
69347
|
+
text += activeTheme.fg("dim", ` (${count} item${count === 1 ? "" : "s"})`);
|
|
69348
|
+
}
|
|
69349
|
+
return text;
|
|
69350
|
+
}
|
|
69351
|
+
function renderManageTodoListResult(result, options, activeTheme = theme) {
|
|
69352
|
+
const details = result.details;
|
|
69353
|
+
if (!details) {
|
|
69354
|
+
const firstText = result.content?.find((item) => item.type === "text" && typeof item.text === "string")?.text;
|
|
69355
|
+
return firstText || "";
|
|
69356
|
+
}
|
|
69357
|
+
if (details.error) {
|
|
69358
|
+
return activeTheme.fg("error", `\u2717 ${details.error}`);
|
|
69359
|
+
}
|
|
69360
|
+
const todos = details.todos || [];
|
|
69361
|
+
if (todos.length === 0) {
|
|
69362
|
+
return activeTheme.fg("dim", "No todos");
|
|
69363
|
+
}
|
|
69364
|
+
const completed = todos.filter((todo) => todo.status === "completed").length;
|
|
69365
|
+
let text = `${activeTheme.fg("success", "\u2713")} ${activeTheme.fg("muted", `${completed}/${todos.length} completed`)}`;
|
|
69366
|
+
if (options.expanded) {
|
|
69367
|
+
for (const todo of todos) {
|
|
69368
|
+
text += `
|
|
69369
|
+
${formatTodoIcon(todo, activeTheme)} ${activeTheme.fg("accent", `${todo.id}.`)} ${formatTodoTitle(todo, activeTheme)}`;
|
|
69370
|
+
}
|
|
69371
|
+
}
|
|
69372
|
+
return text;
|
|
69373
|
+
}
|
|
69374
|
+
function renderTodoWidgetLines(todos, activeTheme = theme) {
|
|
69375
|
+
if (todos.length === 0) {
|
|
69376
|
+
return [];
|
|
69377
|
+
}
|
|
69378
|
+
const completed = todos.filter((todo) => todo.status === "completed").length;
|
|
69379
|
+
const lines = [activeTheme.fg("muted", `Todo List - ${completed}/${todos.length} completed`)];
|
|
69380
|
+
for (const todo of todos) {
|
|
69381
|
+
lines.push(` ${formatTodoIcon(todo, activeTheme)} ${activeTheme.fg("accent", `${todo.id}.`)} ${formatTodoTitle(todo, activeTheme)}`);
|
|
69382
|
+
}
|
|
69383
|
+
return lines;
|
|
69384
|
+
}
|
|
69385
|
+
|
|
69297
69386
|
// src/components/tool-execution.ts
|
|
69298
69387
|
function formatSize3(bytes) {
|
|
69299
69388
|
if (bytes < 1024) return `${bytes}B`;
|
|
@@ -69376,7 +69465,13 @@ var ToolExecutionComponent = class extends Container {
|
|
|
69376
69465
|
formatToolExecution() {
|
|
69377
69466
|
let text = "";
|
|
69378
69467
|
const invalidArg = theme.fg("error", "[invalid arg]");
|
|
69379
|
-
if (this.toolName === "
|
|
69468
|
+
if (this.toolName === "manage_todo_list") {
|
|
69469
|
+
text = renderManageTodoListCall(this.args, theme);
|
|
69470
|
+
if (this.result) {
|
|
69471
|
+
text += `
|
|
69472
|
+
${renderManageTodoListResult(this.result, { expanded: this.expanded }, theme)}`;
|
|
69473
|
+
}
|
|
69474
|
+
} else if (this.toolName === "read") {
|
|
69380
69475
|
const rawPath = str(this.args?.file_path ?? this.args?.path);
|
|
69381
69476
|
const path18 = rawPath !== null ? shortenPath(rawPath) : null;
|
|
69382
69477
|
const offset = this.args?.offset;
|
|
@@ -69712,6 +69807,27 @@ ${theme.fg("error", "Error: " + errorOutput)}`;
|
|
|
69712
69807
|
}
|
|
69713
69808
|
};
|
|
69714
69809
|
|
|
69810
|
+
// src/components/todo-widget.ts
|
|
69811
|
+
var TodoWidgetComponent = class extends Container {
|
|
69812
|
+
state;
|
|
69813
|
+
constructor(state) {
|
|
69814
|
+
super();
|
|
69815
|
+
this.state = state;
|
|
69816
|
+
this.refresh();
|
|
69817
|
+
}
|
|
69818
|
+
refresh() {
|
|
69819
|
+
this.clear();
|
|
69820
|
+
const lines = renderTodoWidgetLines(this.state.read());
|
|
69821
|
+
if (lines.length === 0) {
|
|
69822
|
+
return;
|
|
69823
|
+
}
|
|
69824
|
+
this.addChild(new Spacer(1));
|
|
69825
|
+
for (const line of lines) {
|
|
69826
|
+
this.addChild(new Text(line, 1, 0));
|
|
69827
|
+
}
|
|
69828
|
+
}
|
|
69829
|
+
};
|
|
69830
|
+
|
|
69715
69831
|
// src/components/tree-selector.ts
|
|
69716
69832
|
var RESET = "\x1B[0m";
|
|
69717
69833
|
var INVERSE = "\x1B[7m";
|
|
@@ -74698,6 +74814,8 @@ var InteractiveMode = class {
|
|
|
74698
74814
|
footerData;
|
|
74699
74815
|
editor;
|
|
74700
74816
|
editorContainer;
|
|
74817
|
+
todoWidgetContainer;
|
|
74818
|
+
todoWidget;
|
|
74701
74819
|
keybindings;
|
|
74702
74820
|
sessionManager;
|
|
74703
74821
|
skills;
|
|
@@ -74807,6 +74925,11 @@ var InteractiveMode = class {
|
|
|
74807
74925
|
this.skillsSection = formatSkillsForPrompt(options.skills);
|
|
74808
74926
|
}
|
|
74809
74927
|
options;
|
|
74928
|
+
refreshTodoWidget() {
|
|
74929
|
+
this.todoWidget.refresh();
|
|
74930
|
+
this.todoWidgetContainer.invalidate?.();
|
|
74931
|
+
this.safeRequestRender();
|
|
74932
|
+
}
|
|
74810
74933
|
async init() {
|
|
74811
74934
|
const savedLevel = this.options.settingsManager.getDefaultThinkingLevel();
|
|
74812
74935
|
if (savedLevel) {
|
|
@@ -74827,6 +74950,8 @@ var InteractiveMode = class {
|
|
|
74827
74950
|
});
|
|
74828
74951
|
this.updateTerminalTitle();
|
|
74829
74952
|
this.setupUI();
|
|
74953
|
+
this.options.todoState.restoreFromMessages(this.sessionManager.getMessages());
|
|
74954
|
+
this.refreshTodoWidget();
|
|
74830
74955
|
this.setupAutocomplete();
|
|
74831
74956
|
this.setupAgentSubscription();
|
|
74832
74957
|
this.setupEditorSubmitHandler();
|
|
@@ -75724,6 +75849,9 @@ ${newToolsSection}`
|
|
|
75724
75849
|
contextWindow: this.options.model?.contextWindow ?? 0
|
|
75725
75850
|
});
|
|
75726
75851
|
this.editorContainer = new Container();
|
|
75852
|
+
this.todoWidgetContainer = new Container();
|
|
75853
|
+
this.todoWidget = new TodoWidgetComponent(this.options.todoState);
|
|
75854
|
+
this.todoWidgetContainer.addChild(this.todoWidget);
|
|
75727
75855
|
const editorTheme = getEditorTheme();
|
|
75728
75856
|
this.editor = new Editor(this.ui, editorTheme, { paddingX: 1 });
|
|
75729
75857
|
this.editor.onChange = () => {
|
|
@@ -75737,6 +75865,7 @@ ${newToolsSection}`
|
|
|
75737
75865
|
this.editorContainer.addChild(this.editor);
|
|
75738
75866
|
this.ui.addChild(this.chatContainer);
|
|
75739
75867
|
this.ui.addChild(this.pendingMessagesContainer);
|
|
75868
|
+
this.ui.addChild(this.todoWidgetContainer);
|
|
75740
75869
|
this.ui.addChild(new Spacer());
|
|
75741
75870
|
this.ui.addChild(this.footer);
|
|
75742
75871
|
this.ui.addChild(this.editorContainer);
|
|
@@ -75976,7 +76105,18 @@ ${newToolsSection}`
|
|
|
75976
76105
|
thinkingContent += content2.thinking || "";
|
|
75977
76106
|
} else if (content2.type === "toolCall") {
|
|
75978
76107
|
const toolCall = content2;
|
|
75979
|
-
const
|
|
76108
|
+
const contentIndex = message.content.indexOf(content2);
|
|
76109
|
+
const partialToolId = `__partial_tool__:${contentIndex}`;
|
|
76110
|
+
const toolId = toolCall.id || partialToolId;
|
|
76111
|
+
if (toolCall.id && !this.pendingTools.has(toolCall.id) && this.pendingTools.has(partialToolId)) {
|
|
76112
|
+
const existingComponent = this.pendingTools.get(partialToolId);
|
|
76113
|
+
if (existingComponent) {
|
|
76114
|
+
this.pendingTools.delete(partialToolId);
|
|
76115
|
+
this.pendingTools.set(toolCall.id, existingComponent);
|
|
76116
|
+
existingComponent.updateArgs(toolCall.arguments);
|
|
76117
|
+
}
|
|
76118
|
+
continue;
|
|
76119
|
+
}
|
|
75980
76120
|
if (!this.pendingTools.has(toolId)) {
|
|
75981
76121
|
const toolComponent = new ToolExecutionComponent(
|
|
75982
76122
|
toolCall.name,
|
|
@@ -76064,6 +76204,9 @@ ${newToolsSection}`
|
|
|
76064
76204
|
);
|
|
76065
76205
|
}
|
|
76066
76206
|
}
|
|
76207
|
+
if (event.toolName === "manage_todo_list") {
|
|
76208
|
+
this.refreshTodoWidget();
|
|
76209
|
+
}
|
|
76067
76210
|
this.footer.setState({ lifecycleEvent: "tool_execution_end" });
|
|
76068
76211
|
this.emitExtensionEvent({
|
|
76069
76212
|
type: "tool:execution:end",
|
|
@@ -76868,6 +77011,7 @@ ${newToolsSection}`
|
|
|
76868
77011
|
this.safeRequestRender();
|
|
76869
77012
|
this.agent.abort();
|
|
76870
77013
|
const cleanup = async () => {
|
|
77014
|
+
let timedOut = false;
|
|
76871
77015
|
try {
|
|
76872
77016
|
await Promise.race([
|
|
76873
77017
|
this.agent.waitForIdle(),
|
|
@@ -76876,12 +77020,19 @@ ${newToolsSection}`
|
|
|
76876
77020
|
)
|
|
76877
77021
|
]);
|
|
76878
77022
|
} catch {
|
|
77023
|
+
timedOut = true;
|
|
77024
|
+
}
|
|
77025
|
+
if (timedOut || this.agent.state.isStreaming) {
|
|
77026
|
+
this.agent.resetProcessingState();
|
|
76879
77027
|
}
|
|
76880
77028
|
this.isProcessing = false;
|
|
76881
77029
|
this.setSubmitLock(false);
|
|
76882
77030
|
this.isInterrupting = false;
|
|
77031
|
+
this.streamingComponent = void 0;
|
|
77032
|
+
this.queuedSubmissionText = void 0;
|
|
76883
77033
|
this.footer.setState({ status: "ready" });
|
|
76884
77034
|
this.pendingTools.clear();
|
|
77035
|
+
this.ui.setFocus(this.editor);
|
|
76885
77036
|
this.chatContainer.addChild(new Text(theme.fg("warning", "\u26A0 Interrupted by user"), 1, 0));
|
|
76886
77037
|
this.chatContainer.addChild(new Spacer(1));
|
|
76887
77038
|
this.safeRequestRender();
|
|
@@ -77399,11 +77550,13 @@ ${newToolsSection}`
|
|
|
77399
77550
|
this.updateTerminalTitle();
|
|
77400
77551
|
const messages = this.sessionManager.getMessages();
|
|
77401
77552
|
const conversationMessages = messages.filter(
|
|
77402
|
-
(msg) => msg.role === "user" || msg.role === "assistant"
|
|
77553
|
+
(msg) => msg.role === "user" || msg.role === "assistant" || msg.role === "toolResult"
|
|
77403
77554
|
);
|
|
77404
77555
|
this.agent.resetProcessingState();
|
|
77405
77556
|
this.agent.replaceMessages(conversationMessages);
|
|
77406
77557
|
this.lastSyncedMessageCount = this.agent.state.messages.length;
|
|
77558
|
+
this.options.todoState.restoreFromMessages(messages);
|
|
77559
|
+
this.refreshTodoWidget();
|
|
77407
77560
|
this.chatContainer.clear();
|
|
77408
77561
|
this.lastStatusSpacer = void 0;
|
|
77409
77562
|
this.lastStatusText = void 0;
|
|
@@ -78053,6 +78206,9 @@ ${args}` : skillBlock;
|
|
|
78053
78206
|
this.chatContainer.addChild(new Spacer(1));
|
|
78054
78207
|
this.chatContainer.addChild(new Text(theme.fg("accent", `$ ${command}`), 1, 0));
|
|
78055
78208
|
this.editor.setText("");
|
|
78209
|
+
const outputContainer = new Container();
|
|
78210
|
+
outputContainer.addChild(new Spacer(1));
|
|
78211
|
+
this.chatContainer.addChild(outputContainer);
|
|
78056
78212
|
this.ui.requestRender();
|
|
78057
78213
|
const { spawn: spawn3 } = await import("child_process");
|
|
78058
78214
|
const child = spawn3("bash", ["-c", command], {
|
|
@@ -78060,16 +78216,9 @@ ${args}` : skillBlock;
|
|
|
78060
78216
|
env: process.env
|
|
78061
78217
|
});
|
|
78062
78218
|
let output = "";
|
|
78063
|
-
let isOutputAdded = false;
|
|
78064
|
-
const outputContainer = new Container();
|
|
78065
|
-
outputContainer.addChild(new Spacer(1));
|
|
78066
78219
|
child.stdout.on("data", (data) => {
|
|
78067
78220
|
const text = data.toString();
|
|
78068
78221
|
output += text;
|
|
78069
|
-
if (!isOutputAdded) {
|
|
78070
|
-
this.chatContainer.addChild(outputContainer);
|
|
78071
|
-
isOutputAdded = true;
|
|
78072
|
-
}
|
|
78073
78222
|
const lines = text.split("\n");
|
|
78074
78223
|
for (const line of lines) {
|
|
78075
78224
|
if (line || lines.indexOf(line) < lines.length - 1) {
|
|
@@ -78081,10 +78230,6 @@ ${args}` : skillBlock;
|
|
|
78081
78230
|
child.stderr.on("data", (data) => {
|
|
78082
78231
|
const text = data.toString();
|
|
78083
78232
|
output += text;
|
|
78084
|
-
if (!isOutputAdded) {
|
|
78085
|
-
this.chatContainer.addChild(outputContainer);
|
|
78086
|
-
isOutputAdded = true;
|
|
78087
|
-
}
|
|
78088
78233
|
const lines = text.split("\n");
|
|
78089
78234
|
for (const line of lines) {
|
|
78090
78235
|
if (line || lines.indexOf(line) < lines.length - 1) {
|
|
@@ -78719,6 +78864,177 @@ var ModelRegistry = class {
|
|
|
78719
78864
|
}
|
|
78720
78865
|
};
|
|
78721
78866
|
|
|
78867
|
+
// src/todo/state-manager.ts
|
|
78868
|
+
var TodoStateManager = class {
|
|
78869
|
+
todos = [];
|
|
78870
|
+
read() {
|
|
78871
|
+
return this.todos.map((todo) => ({ ...todo }));
|
|
78872
|
+
}
|
|
78873
|
+
write(todos) {
|
|
78874
|
+
this.todos = todos.map((todo) => ({ ...todo }));
|
|
78875
|
+
}
|
|
78876
|
+
clear() {
|
|
78877
|
+
this.todos = [];
|
|
78878
|
+
}
|
|
78879
|
+
getStats() {
|
|
78880
|
+
const total = this.todos.length;
|
|
78881
|
+
const completed = this.todos.filter((todo) => todo.status === "completed").length;
|
|
78882
|
+
const inProgress = this.todos.filter((todo) => todo.status === "in-progress").length;
|
|
78883
|
+
return {
|
|
78884
|
+
total,
|
|
78885
|
+
completed,
|
|
78886
|
+
inProgress,
|
|
78887
|
+
notStarted: total - completed - inProgress
|
|
78888
|
+
};
|
|
78889
|
+
}
|
|
78890
|
+
validate(todos) {
|
|
78891
|
+
const errors = [];
|
|
78892
|
+
if (!Array.isArray(todos)) {
|
|
78893
|
+
return { valid: false, errors: ["todoList must be an array"] };
|
|
78894
|
+
}
|
|
78895
|
+
const validStatuses = /* @__PURE__ */ new Set(["not-started", "in-progress", "completed"]);
|
|
78896
|
+
for (let index = 0; index < todos.length; index++) {
|
|
78897
|
+
const item = todos[index];
|
|
78898
|
+
const prefix = `Item ${index + 1}`;
|
|
78899
|
+
if (typeof item?.id !== "number" || !Number.isInteger(item.id) || item.id < 1) {
|
|
78900
|
+
errors.push(`${prefix}: 'id' must be a positive integer`);
|
|
78901
|
+
}
|
|
78902
|
+
if (typeof item?.title !== "string" || item.title.trim().length === 0) {
|
|
78903
|
+
errors.push(`${prefix}: 'title' is required`);
|
|
78904
|
+
}
|
|
78905
|
+
if (typeof item?.description !== "string") {
|
|
78906
|
+
errors.push(`${prefix}: 'description' must be a string`);
|
|
78907
|
+
}
|
|
78908
|
+
if (!validStatuses.has(item?.status)) {
|
|
78909
|
+
errors.push(`${prefix}: 'status' must be one of: not-started, in-progress, completed`);
|
|
78910
|
+
}
|
|
78911
|
+
if (item?.id !== index + 1) {
|
|
78912
|
+
errors.push(`${prefix}: ids must be sequential starting from 1`);
|
|
78913
|
+
}
|
|
78914
|
+
}
|
|
78915
|
+
return { valid: errors.length === 0, errors };
|
|
78916
|
+
}
|
|
78917
|
+
restoreFromMessages(messages) {
|
|
78918
|
+
this.todos = [];
|
|
78919
|
+
for (const message of messages) {
|
|
78920
|
+
if (message.role !== "toolResult" || message.toolName !== "manage_todo_list") {
|
|
78921
|
+
continue;
|
|
78922
|
+
}
|
|
78923
|
+
const details = message.details;
|
|
78924
|
+
if (details?.todos) {
|
|
78925
|
+
this.todos = details.todos.map((todo) => ({ ...todo }));
|
|
78926
|
+
}
|
|
78927
|
+
}
|
|
78928
|
+
}
|
|
78929
|
+
};
|
|
78930
|
+
|
|
78931
|
+
// src/todo/tool.ts
|
|
78932
|
+
init_esm();
|
|
78933
|
+
function stringEnum(values, description) {
|
|
78934
|
+
return Type.Union(values.map((value) => Type.Literal(value)), { description });
|
|
78935
|
+
}
|
|
78936
|
+
var TodoItemSchema = Type.Object({
|
|
78937
|
+
id: Type.Number({ description: "Unique identifier for the todo. Use sequential numbers starting from 1." }),
|
|
78938
|
+
title: Type.String({ description: "Concise action-oriented todo label (3-7 words). Displayed in the UI." }),
|
|
78939
|
+
description: Type.String({
|
|
78940
|
+
description: "Detailed context, requirements, or implementation notes for the todo item."
|
|
78941
|
+
}),
|
|
78942
|
+
status: stringEnum(
|
|
78943
|
+
["not-started", "in-progress", "completed"],
|
|
78944
|
+
"not-started: Not begun | in-progress: Currently working | completed: Fully finished with no blockers"
|
|
78945
|
+
)
|
|
78946
|
+
});
|
|
78947
|
+
var ManageTodoListParams = Type.Object({
|
|
78948
|
+
operation: stringEnum(
|
|
78949
|
+
["write", "read"],
|
|
78950
|
+
"write: Replace entire todo list with new content. read: Retrieve current todo list."
|
|
78951
|
+
),
|
|
78952
|
+
todoList: Type.Optional(
|
|
78953
|
+
Type.Array(TodoItemSchema, {
|
|
78954
|
+
description: "Complete array of all todo items. Required for write. Must include ALL items."
|
|
78955
|
+
})
|
|
78956
|
+
)
|
|
78957
|
+
});
|
|
78958
|
+
var MANAGE_TODO_LIST_DESCRIPTION = `Manage a structured todo list to track progress and plan tasks throughout your coding session.
|
|
78959
|
+
|
|
78960
|
+
Use this tool for complex multi-step work, especially when:
|
|
78961
|
+
- The task needs planning or progress tracking
|
|
78962
|
+
- The user provided multiple requests
|
|
78963
|
+
- You need to break a larger change into actionable steps
|
|
78964
|
+
- You are about to start or finish a tracked step
|
|
78965
|
+
|
|
78966
|
+
Workflow:
|
|
78967
|
+
1. Write the full todo list with clear action items
|
|
78968
|
+
2. Mark the current item as in-progress before starting it
|
|
78969
|
+
3. Complete the work for that item
|
|
78970
|
+
4. Mark it completed immediately
|
|
78971
|
+
5. Continue until all items are done
|
|
78972
|
+
|
|
78973
|
+
Always send the full list on write. Partial updates are not supported.`;
|
|
78974
|
+
function successMessage(todos) {
|
|
78975
|
+
const total = todos.length;
|
|
78976
|
+
const completed = todos.filter((todo) => todo.status === "completed").length;
|
|
78977
|
+
let message = `Todos have been modified successfully. ${completed}/${total} completed. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable.`;
|
|
78978
|
+
if (total < 3) {
|
|
78979
|
+
message += "\n\nWarning: Small todo list (<3 items). This task might not need a todo list.";
|
|
78980
|
+
}
|
|
78981
|
+
return message;
|
|
78982
|
+
}
|
|
78983
|
+
function createManageTodoListTool(state) {
|
|
78984
|
+
return {
|
|
78985
|
+
name: "manage_todo_list",
|
|
78986
|
+
label: "manage_todo_list",
|
|
78987
|
+
description: MANAGE_TODO_LIST_DESCRIPTION,
|
|
78988
|
+
parameters: ManageTodoListParams,
|
|
78989
|
+
execute: async (_toolCallId, params) => {
|
|
78990
|
+
if (params.operation === "read") {
|
|
78991
|
+
const todos2 = state.read();
|
|
78992
|
+
return {
|
|
78993
|
+
content: [
|
|
78994
|
+
{
|
|
78995
|
+
type: "text",
|
|
78996
|
+
text: todos2.length ? JSON.stringify(todos2, null, 2) : "No todos. Use write operation to create a todo list."
|
|
78997
|
+
}
|
|
78998
|
+
],
|
|
78999
|
+
details: { operation: "read", todos: todos2 }
|
|
79000
|
+
};
|
|
79001
|
+
}
|
|
79002
|
+
if (!params.todoList || !Array.isArray(params.todoList)) {
|
|
79003
|
+
return {
|
|
79004
|
+
content: [{ type: "text", text: "Error: todoList is required for write operation." }],
|
|
79005
|
+
details: { operation: "write", todos: state.read(), error: "todoList required" },
|
|
79006
|
+
isError: true
|
|
79007
|
+
};
|
|
79008
|
+
}
|
|
79009
|
+
const todoList = params.todoList;
|
|
79010
|
+
const validation = state.validate(todoList);
|
|
79011
|
+
if (!validation.valid) {
|
|
79012
|
+
return {
|
|
79013
|
+
content: [
|
|
79014
|
+
{
|
|
79015
|
+
type: "text",
|
|
79016
|
+
text: `Validation failed:
|
|
79017
|
+
${validation.errors.map((error) => ` - ${error}`).join("\n")}`
|
|
79018
|
+
}
|
|
79019
|
+
],
|
|
79020
|
+
details: {
|
|
79021
|
+
operation: "write",
|
|
79022
|
+
todos: state.read(),
|
|
79023
|
+
error: validation.errors.join("; ")
|
|
79024
|
+
},
|
|
79025
|
+
isError: true
|
|
79026
|
+
};
|
|
79027
|
+
}
|
|
79028
|
+
state.write(todoList);
|
|
79029
|
+
const todos = state.read();
|
|
79030
|
+
return {
|
|
79031
|
+
content: [{ type: "text", text: successMessage(todos) }],
|
|
79032
|
+
details: { operation: "write", todos }
|
|
79033
|
+
};
|
|
79034
|
+
}
|
|
79035
|
+
};
|
|
79036
|
+
}
|
|
79037
|
+
|
|
78722
79038
|
// src/cli.ts
|
|
78723
79039
|
enableEarlyLogBuffer();
|
|
78724
79040
|
var logError = (msg, err) => {
|
|
@@ -78810,7 +79126,8 @@ async function startChat(initialPrompt, testCommand) {
|
|
|
78810
79126
|
settingsManager.setDefaultModelAndProvider(provider, modelId);
|
|
78811
79127
|
const cwd = process.cwd();
|
|
78812
79128
|
const fsOptions = { allowlistedDirs: [CONFIG_DIR] };
|
|
78813
|
-
const
|
|
79129
|
+
const todoState = new TodoStateManager();
|
|
79130
|
+
const tools = [...createFileSystemTools(cwd, fsOptions), createBashTool(cwd), createManageTodoListTool(todoState)];
|
|
78814
79131
|
const sessionManager = createSessionManager({ sessionsDir: SESSIONS_DIR });
|
|
78815
79132
|
if (explicitSessionId) {
|
|
78816
79133
|
const loaded = await sessionManager.loadSession(explicitSessionId);
|
|
@@ -78928,6 +79245,7 @@ Current working directory: ${cwd}${skillsSection}`;
|
|
|
78928
79245
|
model,
|
|
78929
79246
|
apiKey,
|
|
78930
79247
|
tools,
|
|
79248
|
+
todoState,
|
|
78931
79249
|
sessionManager,
|
|
78932
79250
|
skills,
|
|
78933
79251
|
cwd,
|