@hasna/coders 0.1.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -7548,7 +7548,7 @@ var init_settings = __esm({
7548
7548
  alwaysThinkingEnabled: external_exports.boolean().optional(),
7549
7549
  // Permissions
7550
7550
  permissions: external_exports.object({
7551
- defaultMode: external_exports.enum(["default", "plan", "acceptEdits", "dontAsk", "auto"]).optional(),
7551
+ defaultMode: external_exports.enum(["default", "plan", "acceptEdits", "dontAsk", "auto", "bypassPermissions"]).optional(),
7552
7552
  allow: external_exports.array(PermissionRuleSchema).optional(),
7553
7553
  deny: external_exports.array(PermissionRuleSchema).optional()
7554
7554
  }).optional(),
@@ -7598,13 +7598,19 @@ var init_settings = __esm({
7598
7598
  // Teammate mode
7599
7599
  teammateMode: external_exports.enum(["auto", "tmux", "in-process"]).optional(),
7600
7600
  // Remote control
7601
- remoteControlAtStartup: external_exports.boolean().optional()
7601
+ remoteControlAtStartup: external_exports.boolean().optional(),
7602
+ // Custom status line — run an external command to render the status bar
7603
+ statusLine: external_exports.object({
7604
+ type: external_exports.literal("command"),
7605
+ command: external_exports.string(),
7606
+ padding: external_exports.number().optional()
7607
+ }).optional()
7602
7608
  }).passthrough();
7603
7609
  DEFAULT_SETTINGS = {
7604
7610
  model: null,
7605
7611
  alwaysThinkingEnabled: void 0,
7606
7612
  permissions: {
7607
- defaultMode: "default",
7613
+ defaultMode: "bypassPermissions",
7608
7614
  allow: [],
7609
7615
  deny: []
7610
7616
  },
@@ -7653,16 +7659,6 @@ function getUserSettingsPath() {
7653
7659
  function getUserConfigPath() {
7654
7660
  return join(getConfigDir(), ".config.json");
7655
7661
  }
7656
- function getTeamsDir() {
7657
- const dir = join(getConfigDir(), "teams");
7658
- ensureDir(dir);
7659
- return dir;
7660
- }
7661
- function getTasksDir() {
7662
- const dir = join(getConfigDir(), "tasks");
7663
- ensureDir(dir);
7664
- return dir;
7665
- }
7666
7662
  function getPluginsDir() {
7667
7663
  const dir = join(getConfigDir(), "plugins");
7668
7664
  ensureDir(dir);
@@ -10168,7 +10164,7 @@ var require_react_development = __commonJS({
10168
10164
  var dispatcher = resolveDispatcher();
10169
10165
  return dispatcher.useReducer(reducer, initialArg, init);
10170
10166
  }
10171
- function useRef(initialValue) {
10167
+ function useRef2(initialValue) {
10172
10168
  var dispatcher = resolveDispatcher();
10173
10169
  return dispatcher.useRef(initialValue);
10174
10170
  }
@@ -10962,7 +10958,7 @@ var require_react_development = __commonJS({
10962
10958
  exports.useLayoutEffect = useLayoutEffect2;
10963
10959
  exports.useMemo = useMemo3;
10964
10960
  exports.useReducer = useReducer;
10965
- exports.useRef = useRef;
10961
+ exports.useRef = useRef2;
10966
10962
  exports.useState = useState3;
10967
10963
  exports.useSyncExternalStore = useSyncExternalStore;
10968
10964
  exports.useTransition = useTransition;
@@ -39039,7 +39035,7 @@ var require_backend = __commonJS({
39039
39035
  return [initialArg, function() {
39040
39036
  }];
39041
39037
  },
39042
- useRef: function useRef(initialValue) {
39038
+ useRef: function useRef2(initialValue) {
39043
39039
  var hook = nextHook();
39044
39040
  initialValue = null !== hook ? hook.memoizedState : {
39045
39041
  current: initialValue
@@ -59191,6 +59187,73 @@ var init_models = __esm({
59191
59187
  maxOutput: 32768,
59192
59188
  supportsThinking: true,
59193
59189
  supportsVision: true
59190
+ },
59191
+ // ── xAI Grok models ──────────────────────────────────────────────
59192
+ grok3: {
59193
+ alias: "grok3",
59194
+ variants: {
59195
+ firstParty: "grok-3",
59196
+ bedrock: "grok-3",
59197
+ vertex: "grok-3",
59198
+ xai: "grok-3"
59199
+ },
59200
+ contextWindow: 131072,
59201
+ maxOutput: 16384,
59202
+ supportsThinking: true,
59203
+ supportsVision: false
59204
+ },
59205
+ grok3mini: {
59206
+ alias: "grok3mini",
59207
+ variants: {
59208
+ firstParty: "grok-3-mini",
59209
+ bedrock: "grok-3-mini",
59210
+ vertex: "grok-3-mini",
59211
+ xai: "grok-3-mini"
59212
+ },
59213
+ contextWindow: 131072,
59214
+ maxOutput: 16384,
59215
+ supportsThinking: true,
59216
+ supportsVision: false
59217
+ },
59218
+ grok2: {
59219
+ alias: "grok2",
59220
+ variants: {
59221
+ firstParty: "grok-2",
59222
+ bedrock: "grok-2",
59223
+ vertex: "grok-2",
59224
+ xai: "grok-2"
59225
+ },
59226
+ contextWindow: 131072,
59227
+ maxOutput: 8192,
59228
+ supportsThinking: false,
59229
+ supportsVision: true
59230
+ },
59231
+ // ── Google Gemini models ──────────────────────────────────────────
59232
+ gemini25pro: {
59233
+ alias: "gemini25pro",
59234
+ variants: {
59235
+ firstParty: "gemini-2.5-pro",
59236
+ bedrock: "gemini-2.5-pro",
59237
+ vertex: "gemini-2.5-pro",
59238
+ gemini: "gemini-2.5-pro"
59239
+ },
59240
+ contextWindow: 1e6,
59241
+ maxOutput: 65536,
59242
+ supportsThinking: true,
59243
+ supportsVision: true
59244
+ },
59245
+ gemini25flash: {
59246
+ alias: "gemini25flash",
59247
+ variants: {
59248
+ firstParty: "gemini-2.5-flash",
59249
+ bedrock: "gemini-2.5-flash",
59250
+ vertex: "gemini-2.5-flash",
59251
+ gemini: "gemini-2.5-flash"
59252
+ },
59253
+ contextWindow: 1e6,
59254
+ maxOutput: 65536,
59255
+ supportsThinking: true,
59256
+ supportsVision: true
59194
59257
  }
59195
59258
  };
59196
59259
  USER_ALIASES = {
@@ -59198,7 +59261,18 @@ var init_models = __esm({
59198
59261
  sonnet: "sonnet46",
59199
59262
  opus: "opus46",
59200
59263
  "sonnet[1m]": "sonnet46",
59201
- "opus[1m]": "opus46"
59264
+ "opus[1m]": "opus46",
59265
+ // xAI Grok aliases
59266
+ grok: "grok3",
59267
+ "grok-3": "grok3",
59268
+ "grok-3-mini": "grok3mini",
59269
+ "grok-2": "grok2",
59270
+ // Gemini aliases
59271
+ gemini: "gemini25pro",
59272
+ "gemini-pro": "gemini25pro",
59273
+ "gemini-flash": "gemini25flash",
59274
+ "gemini-2.5-pro": "gemini25pro",
59275
+ "gemini-2.5-flash": "gemini25flash"
59202
59276
  };
59203
59277
  }
59204
59278
  });
@@ -59893,6 +59967,151 @@ var init_session = __esm({
59893
59967
  }
59894
59968
  });
59895
59969
 
59970
+ // src/core/background-tasks.ts
59971
+ var background_tasks_exports = {};
59972
+ __export(background_tasks_exports, {
59973
+ completeTask: () => completeTask,
59974
+ createTask: () => createTask,
59975
+ failTask: () => failTask,
59976
+ getAllTasks: () => getAllTasks,
59977
+ getOutputPath: () => getOutputPath,
59978
+ getRecentlyCompletedTasks: () => getRecentlyCompletedTasks,
59979
+ getRunningTasks: () => getRunningTasks,
59980
+ getTask: () => getTask,
59981
+ killTask: () => killTask,
59982
+ readTaskOutput: () => readTaskOutput,
59983
+ updateTask: () => updateTask,
59984
+ writeTaskOutput: () => writeTaskOutput
59985
+ });
59986
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4, appendFileSync, readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
59987
+ import { join as join6 } from "path";
59988
+ import { homedir as homedir2 } from "os";
59989
+ function ensureTasksDir() {
59990
+ try {
59991
+ mkdirSync5(TASKS_DIR, { recursive: true });
59992
+ } catch {
59993
+ }
59994
+ }
59995
+ function createTask(type, description) {
59996
+ const id = type === "bash" ? `bg-${nextBashId++}` : `agent-${nextAgentId++}`;
59997
+ ensureTasksDir();
59998
+ const outputPath = getOutputPath(id);
59999
+ const task = {
60000
+ id,
60001
+ type,
60002
+ description,
60003
+ status: "running",
60004
+ startTime: Date.now(),
60005
+ output: "",
60006
+ outputPath
60007
+ };
60008
+ if (type === "agent") {
60009
+ task.progress = { tokenCount: 0 };
60010
+ }
60011
+ tasks.set(id, task);
60012
+ try {
60013
+ writeFileSync4(outputPath, "", "utf-8");
60014
+ } catch {
60015
+ }
60016
+ return task;
60017
+ }
60018
+ function updateTask(id, updates) {
60019
+ const task = tasks.get(id);
60020
+ if (!task) return;
60021
+ Object.assign(task, updates);
60022
+ }
60023
+ function completeTask(id, output, exitCode) {
60024
+ const task = tasks.get(id);
60025
+ if (!task) return;
60026
+ task.status = "completed";
60027
+ task.output = output;
60028
+ task.exitCode = exitCode ?? null;
60029
+ task.endTime = Date.now();
60030
+ if (task.outputPath) {
60031
+ try {
60032
+ writeFileSync4(task.outputPath, output, "utf-8");
60033
+ } catch {
60034
+ }
60035
+ }
60036
+ }
60037
+ function failTask(id, error2) {
60038
+ const task = tasks.get(id);
60039
+ if (!task) return;
60040
+ task.status = "failed";
60041
+ task.error = error2;
60042
+ task.endTime = Date.now();
60043
+ if (task.outputPath) {
60044
+ try {
60045
+ appendFileSync(task.outputPath, `
60046
+
60047
+ --- ERROR ---
60048
+ ${error2}
60049
+ `, "utf-8");
60050
+ } catch {
60051
+ }
60052
+ }
60053
+ }
60054
+ function killTask(id) {
60055
+ const task = tasks.get(id);
60056
+ if (!task) return;
60057
+ task.status = "killed";
60058
+ task.endTime = Date.now();
60059
+ }
60060
+ function getTask(id) {
60061
+ return tasks.get(id);
60062
+ }
60063
+ function getAllTasks() {
60064
+ return [...tasks.values()];
60065
+ }
60066
+ function getRunningTasks() {
60067
+ return [...tasks.values()].filter((t) => t.status === "running");
60068
+ }
60069
+ function getRecentlyCompletedTasks(withinMs = 6e4) {
60070
+ const cutoff = Date.now() - withinMs;
60071
+ return [...tasks.values()].filter(
60072
+ (t) => t.status !== "running" && t.endTime != null && t.endTime >= cutoff
60073
+ );
60074
+ }
60075
+ function getOutputPath(taskId) {
60076
+ return join6(TASKS_DIR, `${taskId}.output`);
60077
+ }
60078
+ function writeTaskOutput(taskId, chunk) {
60079
+ const task = tasks.get(taskId);
60080
+ if (!task) return;
60081
+ task.output += chunk;
60082
+ if (task.outputPath) {
60083
+ try {
60084
+ appendFileSync(task.outputPath, chunk, "utf-8");
60085
+ } catch {
60086
+ }
60087
+ }
60088
+ }
60089
+ function readTaskOutput(taskId) {
60090
+ const task = tasks.get(taskId);
60091
+ if (task?.output) {
60092
+ return task.output;
60093
+ }
60094
+ const path = getOutputPath(taskId);
60095
+ if (existsSync8(path)) {
60096
+ try {
60097
+ return readFileSync7(path, "utf-8");
60098
+ } catch {
60099
+ return "";
60100
+ }
60101
+ }
60102
+ return "";
60103
+ }
60104
+ var tasks, nextBashId, nextAgentId, TASKS_DIR;
60105
+ var init_background_tasks = __esm({
60106
+ "src/core/background-tasks.ts"() {
60107
+ "use strict";
60108
+ tasks = /* @__PURE__ */ new Map();
60109
+ nextBashId = 1;
60110
+ nextAgentId = 1;
60111
+ TASKS_DIR = join6(homedir2(), ".coders", "tasks");
60112
+ }
60113
+ });
60114
+
59896
60115
  // src/ui/screen/terminal.ts
59897
60116
  var terminal_exports = {};
59898
60117
  __export(terminal_exports, {
@@ -60018,8 +60237,82 @@ var init_terminal = __esm({
60018
60237
  }
60019
60238
  });
60020
60239
 
60240
+ // src/ui/themes.ts
60241
+ var themes_exports = {};
60242
+ __export(themes_exports, {
60243
+ DARK_THEME: () => DARK_THEME,
60244
+ DEFAULT_THEME: () => DEFAULT_THEME,
60245
+ LIGHT_THEME: () => LIGHT_THEME,
60246
+ getAvailableThemes: () => getAvailableThemes,
60247
+ getTheme: () => getTheme
60248
+ });
60249
+ function getTheme(name) {
60250
+ return THEMES[name] ?? DEFAULT_THEME;
60251
+ }
60252
+ function getAvailableThemes() {
60253
+ return Object.keys(THEMES);
60254
+ }
60255
+ var DEFAULT_THEME, DARK_THEME, LIGHT_THEME, THEMES;
60256
+ var init_themes = __esm({
60257
+ "src/ui/themes.ts"() {
60258
+ "use strict";
60259
+ DEFAULT_THEME = {
60260
+ name: "default",
60261
+ colors: {
60262
+ primary: "cyan",
60263
+ secondary: "blue",
60264
+ success: "green",
60265
+ error: "red",
60266
+ warning: "yellow",
60267
+ info: "blue",
60268
+ muted: "gray",
60269
+ plan: "magenta",
60270
+ tool: "yellow",
60271
+ thinking: "gray"
60272
+ }
60273
+ };
60274
+ DARK_THEME = {
60275
+ name: "dark",
60276
+ colors: {
60277
+ primary: "#61afef",
60278
+ secondary: "#c678dd",
60279
+ success: "#98c379",
60280
+ error: "#e06c75",
60281
+ warning: "#e5c07b",
60282
+ info: "#56b6c2",
60283
+ muted: "#5c6370",
60284
+ plan: "#c678dd",
60285
+ tool: "#e5c07b",
60286
+ thinking: "#5c6370"
60287
+ }
60288
+ };
60289
+ LIGHT_THEME = {
60290
+ name: "light",
60291
+ colors: {
60292
+ primary: "#0184bc",
60293
+ secondary: "#a626a4",
60294
+ success: "#50a14f",
60295
+ error: "#e45649",
60296
+ warning: "#c18401",
60297
+ info: "#0184bc",
60298
+ muted: "#a0a1a7",
60299
+ plan: "#a626a4",
60300
+ tool: "#c18401",
60301
+ thinking: "#a0a1a7"
60302
+ }
60303
+ };
60304
+ THEMES = {
60305
+ default: DEFAULT_THEME,
60306
+ dark: DARK_THEME,
60307
+ light: LIGHT_THEME
60308
+ };
60309
+ }
60310
+ });
60311
+
60021
60312
  // src/core/slash-commands.ts
60022
- import { writeFileSync as writeFileSync4 } from "fs";
60313
+ import { writeFileSync as writeFileSync5, existsSync as existsSync9, readdirSync as readdirSync2 } from "fs";
60314
+ import { join as join7 } from "path";
60315
+ import { execSync as execSync3 } from "child_process";
60023
60316
  function registerSlashCommand(cmd) {
60024
60317
  commands.set(cmd.name, cmd);
60025
60318
  if (cmd.aliases) {
@@ -60070,8 +60363,12 @@ ${lines.join("\n")}` };
60070
60363
  registerSlashCommand({
60071
60364
  name: "compact",
60072
60365
  category: "core",
60073
- description: "Compact conversation context",
60074
- handler: () => ({ action: "compact", output: "Context compacted." })
60366
+ description: "Compact conversation context [instructions]",
60367
+ handler: (args) => ({
60368
+ action: "compact",
60369
+ data: args.trim() || void 0,
60370
+ output: args.trim() ? `Context compacted with instructions: ${args.trim()}` : "Context compacted."
60371
+ })
60075
60372
  });
60076
60373
  registerSlashCommand({
60077
60374
  name: "exit",
@@ -60091,15 +60388,15 @@ ${lines.join("\n")}` };
60091
60388
  category: "mode",
60092
60389
  description: "View or change the current model",
60093
60390
  handler: (args) => {
60094
- if (args) return { action: "setModel", data: args, output: `Model set to: ${args}` };
60391
+ if (args) return { action: "model", data: args.trim(), output: `Model set to: ${args.trim()}` };
60095
60392
  return { output: "Current model \u2014 use /model <name> to change." };
60096
60393
  }
60097
60394
  });
60098
60395
  registerSlashCommand({
60099
60396
  name: "fast",
60100
60397
  category: "mode",
60101
- description: "Toggle fast mode",
60102
- handler: () => ({ output: "Fast mode toggled." })
60398
+ description: "Toggle fast mode (switch between current model and haiku)",
60399
+ handler: () => ({ action: "fast", output: "Fast mode toggled." })
60103
60400
  });
60104
60401
  registerSlashCommand({
60105
60402
  name: "verbose",
@@ -60135,14 +60432,44 @@ ${lines.join("\n")}` };
60135
60432
  name: "tasks",
60136
60433
  aliases: ["todo", "todos"],
60137
60434
  category: "task",
60138
- description: "Toggle task list view",
60139
- handler: () => ({ action: "toggleView", data: "tasks", output: "Task list toggled." })
60435
+ description: "List all background tasks with their status",
60436
+ handler: () => {
60437
+ try {
60438
+ const { getAllTasks: getAllTasks2 } = (init_background_tasks(), __toCommonJS(background_tasks_exports));
60439
+ const tasks2 = getAllTasks2();
60440
+ if (tasks2.length === 0) return { output: "No background tasks." };
60441
+ const lines = tasks2.map((t) => {
60442
+ const elapsed = t.endTime ? `${((t.endTime - t.startTime) / 1e3).toFixed(1)}s` : `${((Date.now() - t.startTime) / 1e3).toFixed(1)}s`;
60443
+ const status = t.status === "running" ? "running" : t.status === "completed" ? "done" : t.status;
60444
+ return ` ${t.id.padEnd(12)} ${status.padEnd(10)} ${elapsed.padEnd(8)} ${t.description}`;
60445
+ });
60446
+ return { output: `Background tasks:
60447
+ ${"ID".padEnd(12)} ${"Status".padEnd(10)} ${"Time".padEnd(8)} Description
60448
+ ${lines.join("\n")}` };
60449
+ } catch {
60450
+ return { output: "No background tasks." };
60451
+ }
60452
+ }
60140
60453
  });
60141
60454
  registerSlashCommand({
60142
60455
  name: "diff",
60143
60456
  category: "git",
60144
- description: "Show git diff",
60145
- handler: () => ({ output: "Use Bash tool: git diff" })
60457
+ description: "Show uncommitted git changes",
60458
+ handler: () => {
60459
+ try {
60460
+ const unstaged = execSync3("git diff", { encoding: "utf-8", timeout: 1e4 }).trim();
60461
+ const staged = execSync3("git diff --cached", { encoding: "utf-8", timeout: 1e4 }).trim();
60462
+ const parts = [];
60463
+ if (staged) parts.push(`\u2500\u2500 Staged changes \u2500\u2500
60464
+ ${staged}`);
60465
+ if (unstaged) parts.push(`\u2500\u2500 Unstaged changes \u2500\u2500
60466
+ ${unstaged}`);
60467
+ if (parts.length === 0) return { output: "No uncommitted changes." };
60468
+ return { output: parts.join("\n\n") };
60469
+ } catch (e) {
60470
+ return { output: `Failed to get diff: ${e instanceof Error ? e.message : String(e)}` };
60471
+ }
60472
+ }
60146
60473
  });
60147
60474
  registerSlashCommand({
60148
60475
  name: "pr",
@@ -60207,7 +60534,7 @@ ${lines.join("\n")}` };
60207
60534
  if (!isNaN(choice) && choice >= 1 && choice <= checkpoints.length) {
60208
60535
  const cp = checkpoints[choice - 1];
60209
60536
  try {
60210
- writeFileSync4(cp.file_path, cp.original_content, "utf-8");
60537
+ writeFileSync5(cp.file_path, cp.original_content, "utf-8");
60211
60538
  const op = cp.edit_operation ? JSON.parse(cp.edit_operation) : null;
60212
60539
  const summary = op?.old_string ? `Reverted edit: "${truncate(op.old_string, 40)}" -> "${truncate(op.new_string, 40)}"` : "Restored original content";
60213
60540
  return { output: `Restored checkpoint #${choice}: ${cp.file_path}
@@ -60306,6 +60633,181 @@ Use /restore latest or /restore <number> or /restore <id-prefix>`
60306
60633
  return { output: `No checkpoint found matching "${arg}". Use /restore to list available checkpoints.` };
60307
60634
  }
60308
60635
  });
60636
+ registerSlashCommand({
60637
+ name: "cost",
60638
+ category: "system",
60639
+ description: "Show session cost, token usage, and duration",
60640
+ handler: () => {
60641
+ const sessionId = getCurrentSessionId();
60642
+ if (!sessionId) return { output: "No active session." };
60643
+ try {
60644
+ const row = dbGet(
60645
+ `SELECT created_at,
60646
+ COALESCE(SUM(tokens_in), 0) AS total_in,
60647
+ COALESCE(SUM(tokens_out), 0) AS total_out,
60648
+ COALESCE(SUM(cost_usd), 0) AS total_cost
60649
+ FROM messages WHERE session_id = ?`,
60650
+ [sessionId]
60651
+ );
60652
+ const session = dbGet(`SELECT created_at FROM sessions WHERE id = ?`, [sessionId]);
60653
+ const startTime = session?.created_at ? new Date(session.created_at).getTime() : Date.now();
60654
+ const durationMs = Date.now() - startTime;
60655
+ const durationMin = Math.floor(durationMs / 6e4);
60656
+ const durationSec = Math.floor(durationMs % 6e4 / 1e3);
60657
+ const totalIn = row?.total_in ?? 0;
60658
+ const totalOut = row?.total_out ?? 0;
60659
+ const totalCost = row?.total_cost ?? 0;
60660
+ const lines = [
60661
+ `Session cost:`,
60662
+ ` Duration: ${durationMin}m ${durationSec}s`,
60663
+ ` Input tokens: ${totalIn.toLocaleString()}`,
60664
+ ` Output tokens: ${totalOut.toLocaleString()}`,
60665
+ ` Total tokens: ${(totalIn + totalOut).toLocaleString()}`,
60666
+ ` Total cost: $${totalCost.toFixed(4)}`
60667
+ ];
60668
+ return { output: lines.join("\n") };
60669
+ } catch (e) {
60670
+ return { output: `Failed to get cost info: ${e instanceof Error ? e.message : String(e)}` };
60671
+ }
60672
+ }
60673
+ });
60674
+ registerSlashCommand({
60675
+ name: "files",
60676
+ category: "system",
60677
+ description: "List files read/written in this session",
60678
+ handler: () => {
60679
+ const sessionId = getCurrentSessionId();
60680
+ if (!sessionId) return { output: "No active session." };
60681
+ try {
60682
+ const rows = dbAll(
60683
+ `SELECT DISTINCT file_path, edit_operation FROM checkpoints WHERE session_id = ? OR 1=1 ORDER BY created_at DESC LIMIT 50`
60684
+ );
60685
+ const msgRows = dbAll(
60686
+ `SELECT tool_uses FROM messages WHERE session_id = ? AND tool_uses IS NOT NULL`,
60687
+ [sessionId]
60688
+ );
60689
+ const filesRead = /* @__PURE__ */ new Set();
60690
+ const filesWritten = /* @__PURE__ */ new Set();
60691
+ for (const r of msgRows) {
60692
+ try {
60693
+ const uses = JSON.parse(r.tool_uses);
60694
+ if (Array.isArray(uses)) {
60695
+ for (const u of uses) {
60696
+ if (u.name === "Read" && u.input?.file_path) filesRead.add(u.input.file_path);
60697
+ if ((u.name === "Edit" || u.name === "Write") && u.input?.file_path) filesWritten.add(u.input.file_path);
60698
+ }
60699
+ }
60700
+ } catch {
60701
+ }
60702
+ }
60703
+ const parts = [];
60704
+ if (filesRead.size > 0) {
60705
+ parts.push(`Files read (${filesRead.size}):
60706
+ ${Array.from(filesRead).map((f) => ` ${f}`).join("\n")}`);
60707
+ }
60708
+ if (filesWritten.size > 0) {
60709
+ parts.push(`Files written (${filesWritten.size}):
60710
+ ${Array.from(filesWritten).map((f) => ` ${f}`).join("\n")}`);
60711
+ }
60712
+ if (parts.length === 0) return { output: "No files tracked in this session." };
60713
+ return { output: parts.join("\n\n") };
60714
+ } catch (e) {
60715
+ return { output: `Failed to list files: ${e instanceof Error ? e.message : String(e)}` };
60716
+ }
60717
+ }
60718
+ });
60719
+ registerSlashCommand({
60720
+ name: "export",
60721
+ category: "core",
60722
+ description: "Export conversation to a file [path]",
60723
+ handler: (args) => {
60724
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
60725
+ const path = args.trim() || join7(process.cwd(), `coders-export-${timestamp}.md`);
60726
+ return { action: "export", data: path, output: `Exporting conversation to: ${path}` };
60727
+ }
60728
+ });
60729
+ registerSlashCommand({
60730
+ name: "theme",
60731
+ category: "mode",
60732
+ description: "Change UI theme [name]",
60733
+ handler: (args) => {
60734
+ if (!args.trim()) {
60735
+ try {
60736
+ const { getAvailableThemes: getAvailableThemes2 } = (init_themes(), __toCommonJS(themes_exports));
60737
+ const themes = getAvailableThemes2();
60738
+ return { output: `Available themes: ${themes.join(", ")}
60739
+ Use /theme <name> to switch.` };
60740
+ } catch {
60741
+ return { output: "Available themes: default, dark, light\nUse /theme <name> to switch." };
60742
+ }
60743
+ }
60744
+ return { action: "theme", data: args.trim(), output: `Theme set to: ${args.trim()}` };
60745
+ }
60746
+ });
60747
+ registerSlashCommand({
60748
+ name: "effort",
60749
+ category: "mode",
60750
+ description: "Set effort level [low|medium|high]",
60751
+ handler: (args) => {
60752
+ const level = args.trim().toLowerCase();
60753
+ if (!level) return { output: "Usage: /effort <low|medium|high>" };
60754
+ if (!["low", "medium", "high"].includes(level)) {
60755
+ return { output: `Invalid effort level: "${level}". Must be one of: low, medium, high` };
60756
+ }
60757
+ return { action: "effort", data: level, output: `Effort level set to: ${level}` };
60758
+ }
60759
+ });
60760
+ registerSlashCommand({
60761
+ name: "skills",
60762
+ category: "system",
60763
+ description: "List available skills from .coders/skills/ and .claude/skills/",
60764
+ handler: () => {
60765
+ const skillDirs = [
60766
+ join7(process.cwd(), ".coders", "skills"),
60767
+ join7(process.cwd(), ".claude", "skills")
60768
+ ];
60769
+ const skills = [];
60770
+ for (const dir of skillDirs) {
60771
+ if (!existsSync9(dir)) continue;
60772
+ try {
60773
+ const entries = readdirSync2(dir, { withFileTypes: true });
60774
+ for (const entry of entries) {
60775
+ if (entry.isDirectory()) {
60776
+ const skillMd = join7(dir, entry.name, "SKILL.md");
60777
+ if (existsSync9(skillMd)) {
60778
+ skills.push(` ${entry.name} (${dir}/${entry.name}/SKILL.md)`);
60779
+ }
60780
+ }
60781
+ }
60782
+ } catch {
60783
+ }
60784
+ }
60785
+ if (skills.length === 0) return { output: "No skills found. Place SKILL.md files in .coders/skills/<name>/ or .claude/skills/<name>/." };
60786
+ return { output: `Available skills (${skills.length}):
60787
+ ${skills.join("\n")}` };
60788
+ }
60789
+ });
60790
+ registerSlashCommand({
60791
+ name: "rename",
60792
+ category: "core",
60793
+ description: "Rename the current session [name]",
60794
+ handler: (args) => {
60795
+ if (!args.trim()) return { output: "Usage: /rename <name>" };
60796
+ return { action: "rename", data: args.trim(), output: `Session renamed to: ${args.trim()}` };
60797
+ }
60798
+ });
60799
+ registerSlashCommand({
60800
+ name: "resume",
60801
+ category: "navigation",
60802
+ description: "Resume the last session",
60803
+ handler: () => ({ action: "resume", output: "Resuming last session..." })
60804
+ });
60805
+ registerSlashCommand({
60806
+ name: "vim",
60807
+ category: "mode",
60808
+ description: "Toggle vim keybinding mode",
60809
+ handler: () => ({ action: "vim", output: "Vim mode toggled." })
60810
+ });
60309
60811
  registerSlashCommand({
60310
60812
  name: "undo",
60311
60813
  category: "core",
@@ -60318,7 +60820,7 @@ Use /restore latest or /restore <number> or /restore <id-prefix>`
60318
60820
  return { output: "No checkpoints found. Nothing to undo." };
60319
60821
  }
60320
60822
  try {
60321
- writeFileSync4(cp.file_path, cp.original_content, "utf-8");
60823
+ writeFileSync5(cp.file_path, cp.original_content, "utf-8");
60322
60824
  const op = cp.edit_operation ? JSON.parse(cp.edit_operation) : null;
60323
60825
  const summary = op?.old_string ? `Reverted: "${truncate(op.old_string, 50)}" -> "${truncate(op.new_string, 50)}"` : op?.type === "write_overwrite" ? "Restored file before overwrite" : "Restored original content";
60324
60826
  return { output: `Undo successful: ${cp.file_path}
@@ -63023,8 +63525,8 @@ var init_permissions = __esm({
63023
63525
  });
63024
63526
 
63025
63527
  // src/memory/files.ts
63026
- import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
63027
- import { join as join6 } from "path";
63528
+ import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
63529
+ import { join as join8 } from "path";
63028
63530
  function getInstructionsContent(projectDir) {
63029
63531
  const cached2 = _cache.get(projectDir);
63030
63532
  if (cached2 !== void 0) return cached2 || null;
@@ -63034,7 +63536,7 @@ function getInstructionsContent(projectDir) {
63034
63536
  return null;
63035
63537
  }
63036
63538
  try {
63037
- let content = readFileSync7(filePath, "utf-8");
63539
+ let content = readFileSync8(filePath, "utf-8");
63038
63540
  content = stripHtmlComments(content);
63039
63541
  _cache.set(projectDir, content);
63040
63542
  return content;
@@ -63045,11 +63547,11 @@ function getInstructionsContent(projectDir) {
63045
63547
  }
63046
63548
  function getGlobalInstructions() {
63047
63549
  const configDir = getConfigDir();
63048
- const primary = join6(configDir, "CODERS.md");
63049
- const path = existsSync8(primary) ? primary : null;
63550
+ const primary = join8(configDir, "CODERS.md");
63551
+ const path = existsSync10(primary) ? primary : null;
63050
63552
  if (!path) return null;
63051
63553
  try {
63052
- return stripHtmlComments(readFileSync7(path, "utf-8"));
63554
+ return stripHtmlComments(readFileSync8(path, "utf-8"));
63053
63555
  } catch {
63054
63556
  return null;
63055
63557
  }
@@ -63064,13 +63566,13 @@ ${global2}`);
63064
63566
  if (project) parts.push(`# Project Instructions
63065
63567
 
63066
63568
  ${project}`);
63067
- const rulesDir = join6(projectDir, ".coders", "rules");
63068
- if (existsSync8(rulesDir)) {
63569
+ const rulesDir = join8(projectDir, ".coders", "rules");
63570
+ if (existsSync10(rulesDir)) {
63069
63571
  try {
63070
- const { readdirSync: readdirSync2 } = __require("fs");
63071
- for (const file of readdirSync2(rulesDir)) {
63572
+ const { readdirSync: readdirSync4 } = __require("fs");
63573
+ for (const file of readdirSync4(rulesDir)) {
63072
63574
  if (!file.endsWith(".md")) continue;
63073
- const content = readFileSync7(join6(rulesDir, file), "utf-8");
63575
+ const content = readFileSync8(join8(rulesDir, file), "utf-8");
63074
63576
  parts.push(`# Rule: ${file}
63075
63577
 
63076
63578
  ${stripHtmlComments(content)}`);
@@ -63092,6 +63594,257 @@ var init_files = __esm({
63092
63594
  }
63093
63595
  });
63094
63596
 
63597
+ // src/core/constants.ts
63598
+ var BASH_TOOL, READ_TOOL, EDIT_TOOL, WRITE_TOOL, GLOB_TOOL, GREP_TOOL, AGENT_TOOL, WEB_FETCH_TOOL, WEB_SEARCH_TOOL, NOTEBOOK_EDIT_TOOL, LSP_TOOL, TASK_CREATE_TOOL, TASK_GET_TOOL, TASK_LIST_TOOL, TASK_UPDATE_TOOL, TASK_STOP_TOOL, TASK_OUTPUT_TOOL, ENTER_PLAN_MODE_TOOL, EXIT_PLAN_MODE_TOOL, ASK_USER_QUESTION_TOOL, CRON_CREATE_TOOL, CRON_DELETE_TOOL, CRON_LIST_TOOL, ENTER_WORKTREE_TOOL, EXIT_WORKTREE_TOOL, TOOL_SEARCH_TOOL, SEND_MESSAGE_TOOL, CONFIG_TOOL, LIST_MCP_RESOURCES_TOOL, READ_MCP_RESOURCE_TOOL, DEFAULT_BASH_TIMEOUT_MS, MAX_BASH_TIMEOUT_MS, DEFAULT_READ_LINE_LIMIT, DEFAULT_MAX_RESULT_SIZE_CHARS;
63599
+ var init_constants = __esm({
63600
+ "src/core/constants.ts"() {
63601
+ "use strict";
63602
+ BASH_TOOL = "Bash";
63603
+ READ_TOOL = "Read";
63604
+ EDIT_TOOL = "Edit";
63605
+ WRITE_TOOL = "Write";
63606
+ GLOB_TOOL = "Glob";
63607
+ GREP_TOOL = "Grep";
63608
+ AGENT_TOOL = "Agent";
63609
+ WEB_FETCH_TOOL = "WebFetch";
63610
+ WEB_SEARCH_TOOL = "WebSearch";
63611
+ NOTEBOOK_EDIT_TOOL = "NotebookEdit";
63612
+ LSP_TOOL = "LSP";
63613
+ TASK_CREATE_TOOL = "TaskCreate";
63614
+ TASK_GET_TOOL = "TaskGet";
63615
+ TASK_LIST_TOOL = "TaskList";
63616
+ TASK_UPDATE_TOOL = "TaskUpdate";
63617
+ TASK_STOP_TOOL = "TaskStop";
63618
+ TASK_OUTPUT_TOOL = "TaskOutput";
63619
+ ENTER_PLAN_MODE_TOOL = "EnterPlanMode";
63620
+ EXIT_PLAN_MODE_TOOL = "ExitPlanMode";
63621
+ ASK_USER_QUESTION_TOOL = "AskUserQuestion";
63622
+ CRON_CREATE_TOOL = "CronCreate";
63623
+ CRON_DELETE_TOOL = "CronDelete";
63624
+ CRON_LIST_TOOL = "CronList";
63625
+ ENTER_WORKTREE_TOOL = "EnterWorktree";
63626
+ EXIT_WORKTREE_TOOL = "ExitWorktree";
63627
+ TOOL_SEARCH_TOOL = "ToolSearch";
63628
+ SEND_MESSAGE_TOOL = "SendMessage";
63629
+ CONFIG_TOOL = "Config";
63630
+ LIST_MCP_RESOURCES_TOOL = "ListMcpResourcesTool";
63631
+ READ_MCP_RESOURCE_TOOL = "ReadMcpResourceTool";
63632
+ DEFAULT_BASH_TIMEOUT_MS = 12e4;
63633
+ MAX_BASH_TIMEOUT_MS = 6e5;
63634
+ DEFAULT_READ_LINE_LIMIT = 2e3;
63635
+ DEFAULT_MAX_RESULT_SIZE_CHARS = 1e5;
63636
+ }
63637
+ });
63638
+
63639
+ // src/tools/builtin/skill.ts
63640
+ import { readFileSync as readFileSync9, readdirSync as readdirSync3, existsSync as existsSync11, statSync } from "fs";
63641
+ import { join as join9 } from "path";
63642
+ import { homedir as homedir3 } from "os";
63643
+ function parseFrontmatter(content) {
63644
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
63645
+ if (!match) {
63646
+ return { frontmatter: {}, body: content };
63647
+ }
63648
+ const rawYaml = match[1];
63649
+ const body = match[2];
63650
+ const frontmatter = {};
63651
+ for (const line of rawYaml.split("\n")) {
63652
+ const trimmed = line.trim();
63653
+ if (!trimmed || trimmed.startsWith("#")) continue;
63654
+ const colonIdx = trimmed.indexOf(":");
63655
+ if (colonIdx === -1) continue;
63656
+ const key = trimmed.slice(0, colonIdx).trim();
63657
+ let value = trimmed.slice(colonIdx + 1).trim();
63658
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
63659
+ value = value.slice(1, -1);
63660
+ }
63661
+ frontmatter[key] = value;
63662
+ }
63663
+ return { frontmatter, body };
63664
+ }
63665
+ function parseSkillFile(filePath, dirName) {
63666
+ try {
63667
+ const content = readFileSync9(filePath, "utf-8");
63668
+ const { frontmatter, body } = parseFrontmatter(content);
63669
+ return {
63670
+ name: frontmatter.name || dirName,
63671
+ description: frontmatter.description || "",
63672
+ body: body.trim()
63673
+ };
63674
+ } catch {
63675
+ return null;
63676
+ }
63677
+ }
63678
+ function scanSkillsDir(baseDir, source) {
63679
+ if (!existsSync11(baseDir)) return [];
63680
+ const skills = [];
63681
+ try {
63682
+ const entries = readdirSync3(baseDir);
63683
+ for (const entry of entries) {
63684
+ const entryPath = join9(baseDir, entry);
63685
+ try {
63686
+ if (!statSync(entryPath).isDirectory()) continue;
63687
+ } catch {
63688
+ continue;
63689
+ }
63690
+ const skillFile = join9(entryPath, "SKILL.md");
63691
+ if (!existsSync11(skillFile)) continue;
63692
+ const parsed = parseSkillFile(skillFile, entry);
63693
+ if (!parsed) continue;
63694
+ skills.push({
63695
+ name: parsed.name,
63696
+ description: parsed.description,
63697
+ path: skillFile,
63698
+ source
63699
+ });
63700
+ }
63701
+ } catch {
63702
+ }
63703
+ return skills;
63704
+ }
63705
+ function discoverSkills(cwd2) {
63706
+ const projectDir = cwd2 ?? process.cwd();
63707
+ const home = homedir3();
63708
+ const projectCoders = scanSkillsDir(join9(projectDir, ".coders", "skills"), "project");
63709
+ const projectClaude = scanSkillsDir(join9(projectDir, ".claude", "skills"), "project");
63710
+ const userCoders = scanSkillsDir(join9(home, ".coders", "skills"), "user");
63711
+ const userClaude = scanSkillsDir(join9(home, ".claude", "skills"), "user");
63712
+ const seen = /* @__PURE__ */ new Set();
63713
+ const all = [];
63714
+ for (const list2 of [projectCoders, projectClaude, userCoders, userClaude]) {
63715
+ for (const skill of list2) {
63716
+ const key = skill.name.toLowerCase();
63717
+ if (seen.has(key)) continue;
63718
+ seen.add(key);
63719
+ all.push(skill);
63720
+ }
63721
+ }
63722
+ return all;
63723
+ }
63724
+ var SKILL_TOOL, skillInputSchema, skillTool;
63725
+ var init_skill = __esm({
63726
+ "src/tools/builtin/skill.ts"() {
63727
+ "use strict";
63728
+ init_zod();
63729
+ init_constants();
63730
+ SKILL_TOOL = "Skill";
63731
+ skillInputSchema = external_exports.strictObject({
63732
+ skill: external_exports.string().describe("The skill name to invoke (e.g. 'commit', 'review-pr', 'test')"),
63733
+ args: external_exports.string().optional().describe("Optional arguments to pass to the skill prompt")
63734
+ });
63735
+ skillTool = {
63736
+ name: SKILL_TOOL,
63737
+ searchHint: "invoke user-defined skill from SKILL.md",
63738
+ maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
63739
+ shouldDefer: false,
63740
+ strict: false,
63741
+ async description() {
63742
+ return "Execute a skill within the main conversation. Skills are user-defined prompts in .coders/skills/ or .claude/skills/ directories.";
63743
+ },
63744
+ async prompt() {
63745
+ return `Invoke a user-defined skill from .coders/skills/ or .claude/skills/ directories.
63746
+ Skills are directories containing a SKILL.md file with YAML frontmatter (name, description) and a markdown prompt body.
63747
+ When invoked, the skill's prompt is returned with {{args}} replaced by the provided args.
63748
+ Use this tool when the user references a skill by name or uses a "/<skill-name>" slash command pattern.`;
63749
+ },
63750
+ get inputSchema() {
63751
+ return skillInputSchema;
63752
+ },
63753
+ get outputSchema() {
63754
+ return external_exports.any();
63755
+ },
63756
+ userFacingName() {
63757
+ return SKILL_TOOL;
63758
+ },
63759
+ isEnabled() {
63760
+ return true;
63761
+ },
63762
+ isConcurrencySafe() {
63763
+ return true;
63764
+ },
63765
+ isReadOnly() {
63766
+ return true;
63767
+ },
63768
+ toAutoClassifierInput(input) {
63769
+ return `skill:${input.skill}${input.args ? ` ${input.args}` : ""}`;
63770
+ },
63771
+ getActivityDescription(input) {
63772
+ return `Running skill: ${input.skill}`;
63773
+ },
63774
+ getToolUseSummary(input) {
63775
+ return input.skill;
63776
+ },
63777
+ async checkPermissions(input) {
63778
+ return { behavior: "allow", updatedInput: input };
63779
+ },
63780
+ async validateInput(input) {
63781
+ if (!input.skill || typeof input.skill !== "string") {
63782
+ return { result: false, message: "skill name is required" };
63783
+ }
63784
+ return { result: true };
63785
+ },
63786
+ async call(input) {
63787
+ const skillName = input.skill.trim().toLowerCase();
63788
+ const skills = discoverSkills();
63789
+ const match = skills.find((s) => s.name.toLowerCase() === skillName);
63790
+ if (!match) {
63791
+ const available = skills.map((s) => s.name).join(", ");
63792
+ const errorMsg = available ? `Skill "${input.skill}" not found. Available skills: ${available}` : `Skill "${input.skill}" not found. No skills are installed. Create a skill directory with a SKILL.md file in .coders/skills/ or .claude/skills/.`;
63793
+ return {
63794
+ data: {
63795
+ skill: input.skill,
63796
+ prompt: "",
63797
+ source: "project",
63798
+ error: errorMsg
63799
+ }
63800
+ };
63801
+ }
63802
+ const parsed = parseSkillFile(match.path, match.name);
63803
+ if (!parsed || !parsed.body) {
63804
+ return {
63805
+ data: {
63806
+ skill: input.skill,
63807
+ prompt: "",
63808
+ source: match.source,
63809
+ error: `Skill "${match.name}" has an empty or invalid SKILL.md file at ${match.path}`
63810
+ }
63811
+ };
63812
+ }
63813
+ let prompt = parsed.body;
63814
+ if (input.args) {
63815
+ prompt = prompt.replace(/\{\{args\}\}/g, input.args);
63816
+ } else {
63817
+ prompt = prompt.replace(/\{\{args\}\}/g, "");
63818
+ }
63819
+ return {
63820
+ data: {
63821
+ skill: match.name,
63822
+ prompt: prompt.trim(),
63823
+ source: match.source
63824
+ }
63825
+ };
63826
+ },
63827
+ mapToolResultToToolResultBlockParam(result, toolUseId) {
63828
+ if (result.error) {
63829
+ return {
63830
+ type: "tool_result",
63831
+ tool_use_id: toolUseId,
63832
+ content: result.error,
63833
+ is_error: true
63834
+ };
63835
+ }
63836
+ return {
63837
+ type: "tool_result",
63838
+ tool_use_id: toolUseId,
63839
+ content: `<command-name>${result.skill}</command-name>
63840
+
63841
+ ${result.prompt}`
63842
+ };
63843
+ }
63844
+ };
63845
+ }
63846
+ });
63847
+
63095
63848
  // src/core/system-prompt.ts
63096
63849
  function buildSystemPrompt(ctx) {
63097
63850
  const sections = [];
@@ -63120,6 +63873,28 @@ ${t.prompt}`).join("\n\n");
63120
63873
  ${toolSection}`);
63121
63874
  }
63122
63875
  }
63876
+ if (ctx.deferredTools && ctx.deferredTools.length > 0) {
63877
+ const deferredList = ctx.deferredTools.map((t) => `- **${t.name}**: ${t.description}`).join("\n");
63878
+ sections.push(`
63879
+ # Deferred Tools
63880
+
63881
+ The following tools are available but not loaded by default. Use the ToolSearch tool to fetch their full schemas before calling them. Once you call ToolSearch for a tool, you can use it normally.
63882
+
63883
+ ${deferredList}`);
63884
+ }
63885
+ try {
63886
+ const skills = discoverSkills(ctx.projectDir);
63887
+ if (skills.length > 0) {
63888
+ const skillList = skills.map((s) => `- **${s.name}**${s.description ? `: ${s.description}` : ""} _(${s.source})_`).join("\n");
63889
+ sections.push(`
63890
+ # Available Skills
63891
+
63892
+ The following skills are available via the Skill tool. When a user references a skill by name or uses "/<skill-name>", invoke it with the Skill tool.
63893
+
63894
+ ${skillList}`);
63895
+ }
63896
+ } catch {
63897
+ }
63123
63898
  const ctxParts = [];
63124
63899
  ctxParts.push(`Working directory: ${ctx.projectDir}`);
63125
63900
  ctxParts.push(`Model: ${ctx.model}`);
@@ -63147,6 +63922,7 @@ var init_system_prompt = __esm({
63147
63922
  "src/core/system-prompt.ts"() {
63148
63923
  "use strict";
63149
63924
  init_files();
63925
+ init_skill();
63150
63926
  CORE_INSTRUCTIONS = `You are Coders, an open-source interactive CLI agent built by Hasna for software engineering.
63151
63927
  You are an interactive agent that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
63152
63928
 
@@ -63190,44 +63966,6 @@ You are an interactive agent that helps users with software engineering tasks. U
63190
63966
  }
63191
63967
  });
63192
63968
 
63193
- // src/core/constants.ts
63194
- var BASH_TOOL, READ_TOOL, EDIT_TOOL, WRITE_TOOL, GLOB_TOOL, GREP_TOOL, AGENT_TOOL, WEB_FETCH_TOOL, WEB_SEARCH_TOOL, NOTEBOOK_EDIT_TOOL, LSP_TOOL, TASK_CREATE_TOOL, TASK_GET_TOOL, TASK_LIST_TOOL, TASK_UPDATE_TOOL, ENTER_PLAN_MODE_TOOL, EXIT_PLAN_MODE_TOOL, ASK_USER_QUESTION_TOOL, CRON_CREATE_TOOL, CRON_DELETE_TOOL, CRON_LIST_TOOL, ENTER_WORKTREE_TOOL, EXIT_WORKTREE_TOOL, TOOL_SEARCH_TOOL, SEND_MESSAGE_TOOL, CONFIG_TOOL, DEFAULT_BASH_TIMEOUT_MS, MAX_BASH_TIMEOUT_MS, DEFAULT_READ_LINE_LIMIT, DEFAULT_MAX_RESULT_SIZE_CHARS;
63195
- var init_constants = __esm({
63196
- "src/core/constants.ts"() {
63197
- "use strict";
63198
- BASH_TOOL = "Bash";
63199
- READ_TOOL = "Read";
63200
- EDIT_TOOL = "Edit";
63201
- WRITE_TOOL = "Write";
63202
- GLOB_TOOL = "Glob";
63203
- GREP_TOOL = "Grep";
63204
- AGENT_TOOL = "Agent";
63205
- WEB_FETCH_TOOL = "WebFetch";
63206
- WEB_SEARCH_TOOL = "WebSearch";
63207
- NOTEBOOK_EDIT_TOOL = "NotebookEdit";
63208
- LSP_TOOL = "LSP";
63209
- TASK_CREATE_TOOL = "TaskCreate";
63210
- TASK_GET_TOOL = "TaskGet";
63211
- TASK_LIST_TOOL = "TaskList";
63212
- TASK_UPDATE_TOOL = "TaskUpdate";
63213
- ENTER_PLAN_MODE_TOOL = "EnterPlanMode";
63214
- EXIT_PLAN_MODE_TOOL = "ExitPlanMode";
63215
- ASK_USER_QUESTION_TOOL = "AskUserQuestion";
63216
- CRON_CREATE_TOOL = "CronCreate";
63217
- CRON_DELETE_TOOL = "CronDelete";
63218
- CRON_LIST_TOOL = "CronList";
63219
- ENTER_WORKTREE_TOOL = "EnterWorktree";
63220
- EXIT_WORKTREE_TOOL = "ExitWorktree";
63221
- TOOL_SEARCH_TOOL = "ToolSearch";
63222
- SEND_MESSAGE_TOOL = "SendMessage";
63223
- CONFIG_TOOL = "Config";
63224
- DEFAULT_BASH_TIMEOUT_MS = 12e4;
63225
- MAX_BASH_TIMEOUT_MS = 6e5;
63226
- DEFAULT_READ_LINE_LIMIT = 2e3;
63227
- DEFAULT_MAX_RESULT_SIZE_CHARS = 1e5;
63228
- }
63229
- });
63230
-
63231
63969
  // src/tools/builtin/bash.ts
63232
63970
  import { spawn } from "child_process";
63233
63971
  function isDangerousCommand(command) {
@@ -63342,13 +64080,14 @@ function truncateOutput(output, maxChars = DEFAULT_MAX_RESULT_SIZE_CHARS) {
63342
64080
 
63343
64081
  ` + output.slice(-half);
63344
64082
  }
63345
- var DANGEROUS_PATTERNS, BashInputSchema, BashOutputSchema, READ_ONLY_COMMANDS, READ_ONLY_KEYWORDS, GIT_READ_ONLY_SUBCOMMANDS, NPM_READ_ONLY_SUBCOMMANDS, DOCKER_READ_ONLY_SUBCOMMANDS, backgroundTasks, nextBgId, bashTool, BASH_PROMPT;
64083
+ var DANGEROUS_PATTERNS, BashInputSchema, BashOutputSchema, READ_ONLY_COMMANDS, READ_ONLY_KEYWORDS, GIT_READ_ONLY_SUBCOMMANDS, NPM_READ_ONLY_SUBCOMMANDS, DOCKER_READ_ONLY_SUBCOMMANDS, backgroundTasks, bashTool, BASH_PROMPT;
63346
64084
  var init_bash = __esm({
63347
64085
  "src/tools/builtin/bash.ts"() {
63348
64086
  "use strict";
63349
64087
  init_zod();
63350
64088
  init_constants();
63351
64089
  init_db();
64090
+ init_background_tasks();
63352
64091
  DANGEROUS_PATTERNS = [
63353
64092
  { pattern: /\brm\s+-rf\s+\/\s*$/, reason: "Recursive delete of root filesystem" },
63354
64093
  { pattern: /\brm\s+-rf\s+~\//, reason: "Recursive delete of home directory" },
@@ -63541,7 +64280,6 @@ var init_bash = __esm({
63541
64280
  "version"
63542
64281
  ]);
63543
64282
  backgroundTasks = /* @__PURE__ */ new Map();
63544
- nextBgId = 1;
63545
64283
  bashTool = {
63546
64284
  name: BASH_TOOL,
63547
64285
  searchHint: "execute shell commands in the terminal",
@@ -63612,7 +64350,8 @@ var init_bash = __esm({
63612
64350
  const timeoutMs = timeout ?? getDefaultTimeout();
63613
64351
  const startTime = performance.now();
63614
64352
  if (run_in_background) {
63615
- const taskId = `bg-${nextBgId++}`;
64353
+ const bgTask = createTask("bash", command);
64354
+ const taskId = bgTask.id;
63616
64355
  const child = spawn(command, [], {
63617
64356
  shell: getShell(),
63618
64357
  cwd: process.cwd(),
@@ -63621,10 +64360,14 @@ var init_bash = __esm({
63621
64360
  });
63622
64361
  let output = "";
63623
64362
  child.stdout?.on("data", (data) => {
63624
- output += data.toString();
64363
+ const chunk = data.toString();
64364
+ output += chunk;
64365
+ writeTaskOutput(taskId, chunk);
63625
64366
  });
63626
64367
  child.stderr?.on("data", (data) => {
63627
- output += data.toString();
64368
+ const chunk = data.toString();
64369
+ output += chunk;
64370
+ writeTaskOutput(taskId, chunk);
63628
64371
  });
63629
64372
  const task = { process: child, output: "", done: false, exitCode: null };
63630
64373
  backgroundTasks.set(taskId, task);
@@ -63632,10 +64375,16 @@ var init_bash = __esm({
63632
64375
  task.output = output;
63633
64376
  task.done = true;
63634
64377
  task.exitCode = code;
64378
+ completeTask(taskId, output, code ?? void 0);
64379
+ });
64380
+ child.on("error", (err) => {
64381
+ task.done = true;
64382
+ task.exitCode = 1;
64383
+ failTask(taskId, err.message);
63635
64384
  });
63636
64385
  return {
63637
64386
  data: {
63638
- stdout: `Background task started with ID: ${taskId}`,
64387
+ stdout: `Background task started with ID: ${taskId}. Use TaskOutput to check its status.`,
63639
64388
  stderr: "",
63640
64389
  exitCode: null,
63641
64390
  interrupted: false,
@@ -63748,7 +64497,7 @@ Instructions:
63748
64497
  });
63749
64498
 
63750
64499
  // src/tools/builtin/read.ts
63751
- import { readFileSync as readFileSync8, statSync, existsSync as existsSync9 } from "fs";
64500
+ import { readFileSync as readFileSync10, statSync as statSync2, existsSync as existsSync12 } from "fs";
63752
64501
  import { extname, isAbsolute, resolve as resolve2 } from "path";
63753
64502
  function hasFileBeenRead(filePath) {
63754
64503
  return readFiles.has(resolve2(filePath));
@@ -63757,7 +64506,7 @@ function markFileAsRead(filePath) {
63757
64506
  readFiles.add(resolve2(filePath));
63758
64507
  }
63759
64508
  function readTextFile(filePath, input) {
63760
- const rawContent = readFileSync8(filePath, "utf-8");
64509
+ const rawContent = readFileSync10(filePath, "utf-8");
63761
64510
  const allLines = rawContent.split("\n");
63762
64511
  const totalLines = allLines.length;
63763
64512
  const startLine = input.offset ?? 1;
@@ -63780,7 +64529,7 @@ function readTextFile(filePath, input) {
63780
64529
  };
63781
64530
  }
63782
64531
  function readImageFile(filePath, input) {
63783
- const stat = statSync(filePath);
64532
+ const stat = statSync2(filePath);
63784
64533
  const ext = extname(filePath).toLowerCase();
63785
64534
  const content = `[Image file: ${filePath} (${ext}, ${formatBytes(stat.size)})]`;
63786
64535
  return {
@@ -63794,7 +64543,7 @@ function readImageFile(filePath, input) {
63794
64543
  };
63795
64544
  }
63796
64545
  function readPdfFile(filePath, input) {
63797
- const stat = statSync(filePath);
64546
+ const stat = statSync2(filePath);
63798
64547
  const pages = input.pages ?? "1-5";
63799
64548
  const content = `[PDF file: ${filePath} (${formatBytes(stat.size)}, requested pages: ${pages}). PDF reading requires pdf-parse library \u2014 install to enable.]`;
63800
64549
  return {
@@ -63809,7 +64558,7 @@ function readPdfFile(filePath, input) {
63809
64558
  }
63810
64559
  function readNotebookFile(filePath, input) {
63811
64560
  try {
63812
- const rawContent = readFileSync8(filePath, "utf-8");
64561
+ const rawContent = readFileSync10(filePath, "utf-8");
63813
64562
  const notebook = JSON.parse(rawContent);
63814
64563
  if (!notebook.cells || !Array.isArray(notebook.cells)) {
63815
64564
  return {
@@ -63929,11 +64678,11 @@ var init_read = __esm({
63929
64678
  return { result: false, message: "file_path is required", errorCode: 1 };
63930
64679
  }
63931
64680
  const resolved = resolvePath(input.file_path);
63932
- if (!existsSync9(resolved)) {
64681
+ if (!existsSync12(resolved)) {
63933
64682
  return { result: false, message: `File does not exist: ${input.file_path}`, errorCode: 2 };
63934
64683
  }
63935
64684
  try {
63936
- const stat = statSync(resolved);
64685
+ const stat = statSync2(resolved);
63937
64686
  if (stat.isDirectory()) {
63938
64687
  return { result: false, message: `Path is a directory, not a file: ${input.file_path}. Use Bash with ls to list directory contents.`, errorCode: 3 };
63939
64688
  }
@@ -63986,7 +64735,7 @@ Usage:
63986
64735
  });
63987
64736
 
63988
64737
  // src/tools/builtin/edit.ts
63989
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync10, statSync as statSync2 } from "fs";
64738
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync13, statSync as statSync3 } from "fs";
63990
64739
  import { resolve as resolve3, isAbsolute as isAbsolute2 } from "path";
63991
64740
  import { randomUUID as randomUUID2 } from "crypto";
63992
64741
  function resolvePath2(filePath) {
@@ -64126,7 +64875,7 @@ var init_edit = __esm({
64126
64875
  return { result: false, message: "old_string and new_string must be different", errorCode: 2 };
64127
64876
  }
64128
64877
  const resolved = resolvePath2(input.file_path);
64129
- if (!existsSync10(resolved)) {
64878
+ if (!existsSync13(resolved)) {
64130
64879
  return { result: false, message: `File does not exist: ${input.file_path}`, errorCode: 3 };
64131
64880
  }
64132
64881
  if (!hasFileBeenRead(resolved)) {
@@ -64136,7 +64885,7 @@ var init_edit = __esm({
64136
64885
  errorCode: 4
64137
64886
  };
64138
64887
  }
64139
- const content = readFileSync9(resolved, "utf-8");
64888
+ const content = readFileSync11(resolved, "utf-8");
64140
64889
  const count = countOccurrences(content, input.old_string);
64141
64890
  if (count === 0) {
64142
64891
  return {
@@ -64167,7 +64916,7 @@ var init_edit = __esm({
64167
64916
  const resolved = resolvePath2(input.file_path);
64168
64917
  const MAX_FILE_SIZE = 50 * 1024 * 1024;
64169
64918
  try {
64170
- const fileSize = statSync2(resolved).size;
64919
+ const fileSize = statSync3(resolved).size;
64171
64920
  if (fileSize > MAX_FILE_SIZE) {
64172
64921
  return {
64173
64922
  data: {
@@ -64182,7 +64931,7 @@ var init_edit = __esm({
64182
64931
  }
64183
64932
  } catch {
64184
64933
  }
64185
- const originalContent = readFileSync9(resolved, "utf-8");
64934
+ const originalContent = readFileSync11(resolved, "utf-8");
64186
64935
  let newContent;
64187
64936
  let replacements;
64188
64937
  if (input.replace_all) {
@@ -64213,7 +64962,7 @@ var init_edit = __esm({
64213
64962
  );
64214
64963
  } catch {
64215
64964
  }
64216
- writeFileSync5(resolved, newContent, "utf-8");
64965
+ writeFileSync6(resolved, newContent, "utf-8");
64217
64966
  markFileAsRead(resolved);
64218
64967
  const gitDiff = generateUnifiedDiff(originalContent, newContent, input.file_path);
64219
64968
  return {
@@ -64251,7 +65000,7 @@ Usage:
64251
65000
  });
64252
65001
 
64253
65002
  // src/tools/builtin/write.ts
64254
- import { writeFileSync as writeFileSync6, readFileSync as readFileSync10, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
65003
+ import { writeFileSync as writeFileSync7, readFileSync as readFileSync12, existsSync as existsSync14, mkdirSync as mkdirSync6 } from "fs";
64255
65004
  import { dirname as dirname3, resolve as resolve4, isAbsolute as isAbsolute3 } from "path";
64256
65005
  import { randomUUID as randomUUID3 } from "crypto";
64257
65006
  function resolvePath3(filePath) {
@@ -64324,14 +65073,14 @@ var init_write = __esm({
64324
65073
  return { data: { filePath: "", bytesWritten: 0, created: false } };
64325
65074
  }
64326
65075
  const resolved = resolvePath3(input.file_path);
64327
- const created = !existsSync11(resolved);
65076
+ const created = !existsSync14(resolved);
64328
65077
  const dir = dirname3(resolved);
64329
- if (!existsSync11(dir)) {
64330
- mkdirSync5(dir, { recursive: true });
65078
+ if (!existsSync14(dir)) {
65079
+ mkdirSync6(dir, { recursive: true });
64331
65080
  }
64332
65081
  if (!created) {
64333
65082
  try {
64334
- const originalContent = readFileSync10(resolved, "utf-8");
65083
+ const originalContent = readFileSync12(resolved, "utf-8");
64335
65084
  const cpId = randomUUID3().slice(0, 8);
64336
65085
  dbRun(
64337
65086
  "INSERT INTO checkpoints (id, session_id, file_path, original_content, edit_operation) VALUES (?, ?, ?, ?, ?)",
@@ -64340,7 +65089,7 @@ var init_write = __esm({
64340
65089
  } catch {
64341
65090
  }
64342
65091
  }
64343
- writeFileSync6(resolved, input.content, "utf-8");
65092
+ writeFileSync7(resolved, input.content, "utf-8");
64344
65093
  markFileAsRead(resolved);
64345
65094
  return {
64346
65095
  data: {
@@ -67805,18 +68554,18 @@ var require_tasks = __commonJS({
67805
68554
  return patterns.map((pattern) => utils.pattern.removeDuplicateSlashes(pattern));
67806
68555
  }
67807
68556
  function convertPatternsToTasks(positive, negative, dynamic) {
67808
- const tasks = [];
68557
+ const tasks2 = [];
67809
68558
  const patternsOutsideCurrentDirectory = utils.pattern.getPatternsOutsideCurrentDirectory(positive);
67810
68559
  const patternsInsideCurrentDirectory = utils.pattern.getPatternsInsideCurrentDirectory(positive);
67811
68560
  const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory);
67812
68561
  const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory);
67813
- tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic));
68562
+ tasks2.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic));
67814
68563
  if ("." in insideCurrentDirectoryGroup) {
67815
- tasks.push(convertPatternGroupToTask(".", patternsInsideCurrentDirectory, negative, dynamic));
68564
+ tasks2.push(convertPatternGroupToTask(".", patternsInsideCurrentDirectory, negative, dynamic));
67816
68565
  } else {
67817
- tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic));
68566
+ tasks2.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic));
67818
68567
  }
67819
- return tasks;
68568
+ return tasks2;
67820
68569
  }
67821
68570
  exports.convertPatternsToTasks = convertPatternsToTasks;
67822
68571
  function getPositivePatterns(patterns) {
@@ -67994,11 +68743,11 @@ var require_out = __commonJS({
67994
68743
  async.read(path, getSettings2(optionsOrSettingsOrCallback), callback);
67995
68744
  }
67996
68745
  exports.stat = stat;
67997
- function statSync4(path, optionsOrSettings) {
68746
+ function statSync5(path, optionsOrSettings) {
67998
68747
  const settings = getSettings2(optionsOrSettings);
67999
68748
  return sync.read(path, settings);
68000
68749
  }
68001
- exports.statSync = statSync4;
68750
+ exports.statSync = statSync5;
68002
68751
  function getSettings2(settingsOrOptions = {}) {
68003
68752
  if (settingsOrOptions instanceof settings_1.default) {
68004
68753
  return settingsOrOptions;
@@ -68023,14 +68772,14 @@ var require_run_parallel = __commonJS({
68023
68772
  "node_modules/run-parallel/index.js"(exports, module) {
68024
68773
  module.exports = runParallel;
68025
68774
  var queueMicrotask2 = require_queue_microtask();
68026
- function runParallel(tasks, cb) {
68775
+ function runParallel(tasks2, cb) {
68027
68776
  let results, pending, keys;
68028
68777
  let isSync = true;
68029
- if (Array.isArray(tasks)) {
68778
+ if (Array.isArray(tasks2)) {
68030
68779
  results = [];
68031
- pending = tasks.length;
68780
+ pending = tasks2.length;
68032
68781
  } else {
68033
- keys = Object.keys(tasks);
68782
+ keys = Object.keys(tasks2);
68034
68783
  results = {};
68035
68784
  pending = keys.length;
68036
68785
  }
@@ -68052,12 +68801,12 @@ var require_run_parallel = __commonJS({
68052
68801
  done(null);
68053
68802
  } else if (keys) {
68054
68803
  keys.forEach(function(key) {
68055
- tasks[key](function(err, result) {
68804
+ tasks2[key](function(err, result) {
68056
68805
  each(key, err, result);
68057
68806
  });
68058
68807
  });
68059
68808
  } else {
68060
- tasks.forEach(function(task, i) {
68809
+ tasks2.forEach(function(task, i) {
68061
68810
  task(function(err, result) {
68062
68811
  each(i, err, result);
68063
68812
  });
@@ -68174,8 +68923,8 @@ var require_async2 = __commonJS({
68174
68923
  callSuccessCallback(callback, entries);
68175
68924
  return;
68176
68925
  }
68177
- const tasks = entries.map((entry) => makeRplTaskEntry(entry, settings));
68178
- rpl(tasks, (rplError, rplEntries) => {
68926
+ const tasks2 = entries.map((entry) => makeRplTaskEntry(entry, settings));
68927
+ rpl(tasks2, (rplError, rplEntries) => {
68179
68928
  if (rplError !== null) {
68180
68929
  callFailureCallback(callback, rplError);
68181
68930
  return;
@@ -68211,7 +68960,7 @@ var require_async2 = __commonJS({
68211
68960
  callFailureCallback(callback, readdirError);
68212
68961
  return;
68213
68962
  }
68214
- const tasks = names.map((name) => {
68963
+ const tasks2 = names.map((name) => {
68215
68964
  const path = common.joinPathSegments(directory, name, settings.pathSegmentSeparator);
68216
68965
  return (done) => {
68217
68966
  fsStat.stat(path, settings.fsStatSettings, (error2, stats) => {
@@ -68231,7 +68980,7 @@ var require_async2 = __commonJS({
68231
68980
  });
68232
68981
  };
68233
68982
  });
68234
- rpl(tasks, (rplError, entries) => {
68983
+ rpl(tasks2, (rplError, entries) => {
68235
68984
  if (rplError !== null) {
68236
68985
  callFailureCallback(callback, rplError);
68237
68986
  return;
@@ -68496,12 +69245,12 @@ var require_queue = __commonJS({
68496
69245
  }
68497
69246
  function getQueue() {
68498
69247
  var current = queueHead;
68499
- var tasks = [];
69248
+ var tasks2 = [];
68500
69249
  while (current) {
68501
- tasks.push(current.value);
69250
+ tasks2.push(current.value);
68502
69251
  current = current.next;
68503
69252
  }
68504
- return tasks;
69253
+ return tasks2;
68505
69254
  }
68506
69255
  function resume() {
68507
69256
  if (!self2.paused) return;
@@ -69867,9 +70616,9 @@ var require_out4 = __commonJS({
69867
70616
  function getWorks(source, _Provider, options2) {
69868
70617
  const patterns = [].concat(source);
69869
70618
  const settings = new settings_1.default(options2);
69870
- const tasks = taskManager.generate(patterns, settings);
70619
+ const tasks2 = taskManager.generate(patterns, settings);
69871
70620
  const provider = new _Provider(settings);
69872
- return tasks.map(provider.read, provider);
70621
+ return tasks2.map(provider.read, provider);
69873
70622
  }
69874
70623
  function assertPatternsInput(input) {
69875
70624
  const source = [].concat(input);
@@ -69883,7 +70632,7 @@ var require_out4 = __commonJS({
69883
70632
  });
69884
70633
 
69885
70634
  // src/tools/builtin/glob.ts
69886
- import { statSync as statSync3, existsSync as existsSync12 } from "fs";
70635
+ import { statSync as statSync4, existsSync as existsSync15 } from "fs";
69887
70636
  import { resolve as resolve5, isAbsolute as isAbsolute4 } from "path";
69888
70637
  function resolvePath4(p) {
69889
70638
  if (isAbsolute4(p)) return p;
@@ -69949,7 +70698,7 @@ var init_glob = __esm({
69949
70698
  },
69950
70699
  async call(input, context) {
69951
70700
  const cwd2 = input.path ? resolvePath4(input.path) : process.cwd();
69952
- if (input.path && !existsSync12(cwd2)) {
70701
+ if (input.path && !existsSync15(cwd2)) {
69953
70702
  return {
69954
70703
  data: {
69955
70704
  files: [],
@@ -69980,7 +70729,7 @@ var init_glob = __esm({
69980
70729
  });
69981
70730
  const withStats = allFiles.map((f) => {
69982
70731
  try {
69983
- const stat = statSync3(f);
70732
+ const stat = statSync4(f);
69984
70733
  return { path: f, mtime: stat.mtimeMs };
69985
70734
  } catch {
69986
70735
  return { path: f, mtime: 0 };
@@ -70282,7 +71031,7 @@ function filterToolsForAgentType(tools, typeDef) {
70282
71031
  const excluded = new Set(typeDef.excludedTools ?? []);
70283
71032
  return tools.filter((t) => allowed.has(t.name) && !excluded.has(t.name));
70284
71033
  }
70285
- function buildSubAgentSystemPrompt(agentType, typeDef) {
71034
+ function buildSubAgentSystemPrompt(_agentType, typeDef) {
70286
71035
  return `You are a ${typeDef.description}
70287
71036
 
70288
71037
  You are a sub-agent spawned to handle a specific task. Complete the task thoroughly and return your findings/results.
@@ -70301,7 +71050,7 @@ function extractText(message) {
70301
71050
  }
70302
71051
  return "(no text content)";
70303
71052
  }
70304
- function runAgentInBackground(agentId, record2, input, tools, systemPrompt, model, parentContext) {
71053
+ function runAgentInBackground(agentId, bgTaskId, record2, input, tools, systemPrompt, model, parentContext) {
70305
71054
  const abortController = new AbortController();
70306
71055
  runAgentLoop(
70307
71056
  [{ role: "user", content: input.prompt }],
@@ -70314,22 +71063,40 @@ function runAgentInBackground(agentId, record2, input, tools, systemPrompt, mode
70314
71063
  signal: abortController.signal,
70315
71064
  permissionContext: parentContext.getAppState().toolPermissionContext,
70316
71065
  agentId,
70317
- maxTurns: 25
71066
+ maxTurns: 25,
71067
+ onTextDelta: (_text) => {
71068
+ updateTask(bgTaskId, {
71069
+ progress: {
71070
+ lastActivity: (/* @__PURE__ */ new Date()).toISOString()
71071
+ }
71072
+ });
71073
+ }
70318
71074
  }
70319
71075
  ).then((result) => {
70320
71076
  const lastAssistant = [...result.messages].reverse().find((m) => m.role === "assistant");
71077
+ const resultText = extractText(lastAssistant);
70321
71078
  record2.status = "completed";
70322
- record2.result = extractText(lastAssistant);
71079
+ record2.result = resultText;
71080
+ const totalTokens = result.usage.totalInputTokens + result.usage.totalOutputTokens;
71081
+ updateTask(bgTaskId, {
71082
+ progress: {
71083
+ tokenCount: totalTokens,
71084
+ lastActivity: (/* @__PURE__ */ new Date()).toISOString()
71085
+ }
71086
+ });
71087
+ completeTask(bgTaskId, resultText);
70323
71088
  }).catch((error2) => {
71089
+ const errorMsg = error2 instanceof Error ? error2.message : String(error2);
70324
71090
  record2.status = "failed";
70325
- record2.error = error2 instanceof Error ? error2.message : String(error2);
71091
+ record2.error = errorMsg;
71092
+ failTask(bgTaskId, errorMsg);
70326
71093
  });
70327
71094
  }
70328
71095
  function truncate3(str, maxLen) {
70329
71096
  if (str.length <= maxLen) return str;
70330
71097
  return str.slice(0, maxLen - 100) + "\n\n... (truncated)";
70331
71098
  }
70332
- var BUILTIN_AGENT_TYPES, AgentInputSchema, AgentOutputSchema, nextAgentId, runningAgents, agentTool, AGENT_PROMPT;
71099
+ var BUILTIN_AGENT_TYPES, AgentInputSchema, AgentOutputSchema, nextAgentId2, runningAgents, agentTool, AGENT_PROMPT;
70333
71100
  var init_agent = __esm({
70334
71101
  "src/tools/builtin/agent.ts"() {
70335
71102
  "use strict";
@@ -70337,6 +71104,7 @@ var init_agent = __esm({
70337
71104
  init_constants();
70338
71105
  init_agent_loop();
70339
71106
  init_client();
71107
+ init_background_tasks();
70340
71108
  BUILTIN_AGENT_TYPES = {
70341
71109
  "general-purpose": {
70342
71110
  name: "general-purpose",
@@ -70379,7 +71147,7 @@ var init_agent = __esm({
70379
71147
  usage: external_exports.object({ inputTokens: external_exports.number(), outputTokens: external_exports.number() }),
70380
71148
  backgrounded: external_exports.boolean().optional()
70381
71149
  });
70382
- nextAgentId = 1;
71150
+ nextAgentId2 = 1;
70383
71151
  runningAgents = /* @__PURE__ */ new Map();
70384
71152
  agentTool = {
70385
71153
  name: AGENT_TOOL,
@@ -70427,7 +71195,7 @@ var init_agent = __esm({
70427
71195
  return { behavior: "allow", updatedInput: input };
70428
71196
  },
70429
71197
  async call(input, context) {
70430
- const agentId = `agent-${nextAgentId++}`;
71198
+ const agentId = `agent-${nextAgentId2++}`;
70431
71199
  const agentType = input.subagent_type ?? "general-purpose";
70432
71200
  const typeDef = BUILTIN_AGENT_TYPES[agentType] ?? BUILTIN_AGENT_TYPES["general-purpose"];
70433
71201
  const model = input.model ?? "sonnet";
@@ -70448,17 +71216,18 @@ var init_agent = __esm({
70448
71216
  inputSchema: { type: "object", properties: {} },
70449
71217
  isReadOnly: t.isReadOnly(),
70450
71218
  isConcurrencySafe: t.isConcurrencySafe(),
70451
- call: async (toolInput, toolCtx) => {
71219
+ call: async (toolInput, _toolCtx) => {
70452
71220
  const result = await t.call(toolInput, context);
70453
71221
  return { data: result.data };
70454
71222
  }
70455
71223
  }));
70456
71224
  const systemPrompt = buildSubAgentSystemPrompt(agentType, typeDef);
70457
71225
  if (input.run_in_background) {
70458
- runAgentInBackground(agentId, agentRecord, input, toolHandlers, systemPrompt, model, context);
71226
+ const bgTask = createTask("agent", input.prompt.slice(0, 100));
71227
+ runAgentInBackground(agentId, bgTask.id, agentRecord, input, toolHandlers, systemPrompt, model, context);
70459
71228
  return {
70460
71229
  data: {
70461
- result: `Agent ${agentId} (${agentType}) started in background. You will be notified when it completes.`,
71230
+ result: `Agent ${agentId} (${agentType}) started in background. Task ID: ${bgTask.id}. Use TaskOutput to check its status.`,
70462
71231
  agentId,
70463
71232
  agentType,
70464
71233
  model,
@@ -70494,7 +71263,10 @@ var init_agent = __esm({
70494
71263
  agentType,
70495
71264
  model,
70496
71265
  totalTurns: loopResult.totalTurns,
70497
- usage: loopResult.usage
71266
+ usage: {
71267
+ inputTokens: loopResult.usage.totalInputTokens,
71268
+ outputTokens: loopResult.usage.totalOutputTokens
71269
+ }
70498
71270
  }
70499
71271
  };
70500
71272
  } catch (error2) {
@@ -70645,11 +71417,11 @@ var init_todos = __esm({
70645
71417
  });
70646
71418
  return result.map(mapFromHasna);
70647
71419
  }
70648
- let tasks = [...this.fallbackStore.values()];
71420
+ let tasks2 = [...this.fallbackStore.values()];
70649
71421
  if (filter?.status) {
70650
- tasks = tasks.filter((t) => t.status === filter.status);
71422
+ tasks2 = tasks2.filter((t) => t.status === filter.status);
70651
71423
  }
70652
- return tasks;
71424
+ return tasks2;
70653
71425
  }
70654
71426
  async updateTask(id, params) {
70655
71427
  if (this.hasnaTodos) {
@@ -70722,23 +71494,23 @@ var init_todos = __esm({
70722
71494
  await this.hasnaTodos.sync({ taskListId: this.taskListId, direction: "push" });
70723
71495
  return;
70724
71496
  }
70725
- const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync9, existsSync: existsSync17 } = await import("fs");
70726
- const { join: join10 } = await import("path");
70727
- if (!existsSync17(dir)) mkdirSync9(dir, { recursive: true });
70728
- const tasks = await this.listTasks();
70729
- for (const task of tasks) {
70730
- const filePath = join10(dir, `${task.id}.json`);
71497
+ const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync9, existsSync: existsSync19 } = await import("fs");
71498
+ const { join: join12 } = await import("path");
71499
+ if (!existsSync19(dir)) mkdirSync9(dir, { recursive: true });
71500
+ const tasks2 = await this.listTasks();
71501
+ for (const task of tasks2) {
71502
+ const filePath = join12(dir, `${task.id}.json`);
70731
71503
  writeFileSync10(filePath, JSON.stringify(task, null, 2), "utf-8");
70732
71504
  }
70733
71505
  }
70734
71506
  // ── Stats ──────────────────────────────────────────────────────
70735
71507
  async getStats() {
70736
- const tasks = await this.listTasks();
71508
+ const tasks2 = await this.listTasks();
70737
71509
  return {
70738
- total: tasks.length,
70739
- pending: tasks.filter((t) => t.status === "pending").length,
70740
- inProgress: tasks.filter((t) => t.status === "in_progress").length,
70741
- completed: tasks.filter((t) => t.status === "completed").length
71510
+ total: tasks2.length,
71511
+ pending: tasks2.filter((t) => t.status === "pending").length,
71512
+ inProgress: tasks2.filter((t) => t.status === "in_progress").length,
71513
+ completed: tasks2.filter((t) => t.status === "completed").length
70742
71514
  };
70743
71515
  }
70744
71516
  };
@@ -70922,8 +71694,8 @@ var init_tasks = __esm({
70922
71694
  },
70923
71695
  async call() {
70924
71696
  const todos = getTodosIntegration();
70925
- const tasks = await todos.listTasks();
70926
- return { data: { tasks } };
71697
+ const tasks2 = await todos.listTasks();
71698
+ return { data: { tasks: tasks2 } };
70927
71699
  },
70928
71700
  mapToolResultToToolResultBlockParam(result, toolUseId) {
70929
71701
  if (result.tasks.length === 0) {
@@ -71065,6 +71837,251 @@ var init_tasks = __esm({
71065
71837
  }
71066
71838
  });
71067
71839
 
71840
+ // src/tools/builtin/task-output.ts
71841
+ var MAX_OUTPUT_CHARS, TaskOutputInputSchema, TaskOutputOutputSchema, taskOutputTool, TaskStopInputSchema, TaskStopOutputSchema, taskStopTool, TASK_OUTPUT_PROMPT, TASK_STOP_PROMPT;
71842
+ var init_task_output = __esm({
71843
+ "src/tools/builtin/task-output.ts"() {
71844
+ "use strict";
71845
+ init_zod();
71846
+ init_constants();
71847
+ init_background_tasks();
71848
+ MAX_OUTPUT_CHARS = 3e4;
71849
+ TaskOutputInputSchema = external_exports.strictObject({
71850
+ task_id: external_exports.string().describe("The ID of the background task to check (e.g. 'bg-1', 'agent-2')")
71851
+ });
71852
+ TaskOutputOutputSchema = external_exports.object({
71853
+ task_id: external_exports.string(),
71854
+ status: external_exports.string(),
71855
+ output: external_exports.string(),
71856
+ exitCode: external_exports.number().nullable().optional(),
71857
+ durationMs: external_exports.number().optional(),
71858
+ error: external_exports.string().optional(),
71859
+ progress: external_exports.object({
71860
+ tokenCount: external_exports.number().optional(),
71861
+ lastActivity: external_exports.string().optional()
71862
+ }).optional()
71863
+ });
71864
+ taskOutputTool = {
71865
+ name: TASK_OUTPUT_TOOL,
71866
+ searchHint: "check status and output of a background task",
71867
+ maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
71868
+ shouldDefer: true,
71869
+ async description() {
71870
+ return "Check the status and output of a background task";
71871
+ },
71872
+ async prompt() {
71873
+ return TASK_OUTPUT_PROMPT;
71874
+ },
71875
+ get inputSchema() {
71876
+ return TaskOutputInputSchema;
71877
+ },
71878
+ get outputSchema() {
71879
+ return TaskOutputOutputSchema;
71880
+ },
71881
+ userFacingName() {
71882
+ return "TaskOutput";
71883
+ },
71884
+ isEnabled() {
71885
+ return true;
71886
+ },
71887
+ isConcurrencySafe() {
71888
+ return true;
71889
+ },
71890
+ isReadOnly() {
71891
+ return true;
71892
+ },
71893
+ toAutoClassifierInput(input) {
71894
+ return input.task_id;
71895
+ },
71896
+ getActivityDescription(input) {
71897
+ return `Checking task ${input.task_id}`;
71898
+ },
71899
+ async validateInput(input) {
71900
+ if (!input.task_id || !input.task_id.trim()) {
71901
+ return { result: false, message: "task_id is required", errorCode: 1 };
71902
+ }
71903
+ return { result: true };
71904
+ },
71905
+ async checkPermissions(input) {
71906
+ return { behavior: "allow", updatedInput: input };
71907
+ },
71908
+ async call(input) {
71909
+ const task = getTask(input.task_id);
71910
+ if (!task) {
71911
+ return {
71912
+ data: {
71913
+ task_id: input.task_id,
71914
+ status: "not_found",
71915
+ output: `No background task found with ID "${input.task_id}". Use TaskList or check the ID returned when you started the background task.`,
71916
+ error: `Task "${input.task_id}" not found`
71917
+ }
71918
+ };
71919
+ }
71920
+ let output = readTaskOutput(task.id);
71921
+ if (!output && task.output) output = task.output;
71922
+ if (output.length > MAX_OUTPUT_CHARS) {
71923
+ const half = Math.floor(MAX_OUTPUT_CHARS / 2);
71924
+ output = output.slice(0, half) + `
71925
+
71926
+ ... (${output.length - MAX_OUTPUT_CHARS} characters truncated) ...
71927
+
71928
+ ` + output.slice(-half);
71929
+ }
71930
+ const durationMs = task.endTime ? task.endTime - task.startTime : Date.now() - task.startTime;
71931
+ return {
71932
+ data: {
71933
+ task_id: task.id,
71934
+ status: task.status,
71935
+ output: output || "(no output yet)",
71936
+ exitCode: task.exitCode,
71937
+ durationMs,
71938
+ error: task.error,
71939
+ progress: task.progress
71940
+ }
71941
+ };
71942
+ },
71943
+ mapToolResultToToolResultBlockParam(result, toolUseId) {
71944
+ const parts = [];
71945
+ parts.push(`Task: ${result.task_id}`);
71946
+ parts.push(`Status: ${result.status}`);
71947
+ if (result.durationMs != null) {
71948
+ const sec = (result.durationMs / 1e3).toFixed(1);
71949
+ parts.push(`Duration: ${sec}s`);
71950
+ }
71951
+ if (result.exitCode != null) {
71952
+ parts.push(`Exit code: ${result.exitCode}`);
71953
+ }
71954
+ if (result.progress?.tokenCount) {
71955
+ parts.push(`Tokens: ${result.progress.tokenCount}`);
71956
+ }
71957
+ if (result.progress?.lastActivity) {
71958
+ parts.push(`Last activity: ${result.progress.lastActivity}`);
71959
+ }
71960
+ if (result.error) {
71961
+ parts.push(`Error: ${result.error}`);
71962
+ }
71963
+ parts.push("");
71964
+ parts.push(result.output);
71965
+ return {
71966
+ type: "tool_result",
71967
+ tool_use_id: toolUseId,
71968
+ content: parts.join("\n"),
71969
+ is_error: result.status === "not_found"
71970
+ };
71971
+ }
71972
+ };
71973
+ TaskStopInputSchema = external_exports.strictObject({
71974
+ task_id: external_exports.string().describe("The ID of the background task to stop (e.g. 'bg-1', 'agent-2')")
71975
+ });
71976
+ TaskStopOutputSchema = external_exports.object({
71977
+ task_id: external_exports.string(),
71978
+ status: external_exports.string(),
71979
+ message: external_exports.string()
71980
+ });
71981
+ taskStopTool = {
71982
+ name: TASK_STOP_TOOL,
71983
+ searchHint: "stop kill a running background task",
71984
+ maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
71985
+ shouldDefer: true,
71986
+ async description() {
71987
+ return "Stop a running background task";
71988
+ },
71989
+ async prompt() {
71990
+ return TASK_STOP_PROMPT;
71991
+ },
71992
+ get inputSchema() {
71993
+ return TaskStopInputSchema;
71994
+ },
71995
+ get outputSchema() {
71996
+ return TaskStopOutputSchema;
71997
+ },
71998
+ userFacingName() {
71999
+ return "TaskStop";
72000
+ },
72001
+ isEnabled() {
72002
+ return true;
72003
+ },
72004
+ isConcurrencySafe() {
72005
+ return true;
72006
+ },
72007
+ isReadOnly() {
72008
+ return false;
72009
+ },
72010
+ toAutoClassifierInput(input) {
72011
+ return input.task_id;
72012
+ },
72013
+ getActivityDescription(input) {
72014
+ return `Stopping task ${input.task_id}`;
72015
+ },
72016
+ async validateInput(input) {
72017
+ if (!input.task_id || !input.task_id.trim()) {
72018
+ return { result: false, message: "task_id is required", errorCode: 1 };
72019
+ }
72020
+ return { result: true };
72021
+ },
72022
+ async checkPermissions(input) {
72023
+ return { behavior: "allow", updatedInput: input };
72024
+ },
72025
+ async call(input) {
72026
+ const task = getTask(input.task_id);
72027
+ if (!task) {
72028
+ return {
72029
+ data: {
72030
+ task_id: input.task_id,
72031
+ status: "not_found",
72032
+ message: `No background task found with ID "${input.task_id}".`
72033
+ }
72034
+ };
72035
+ }
72036
+ if (task.status !== "running") {
72037
+ return {
72038
+ data: {
72039
+ task_id: task.id,
72040
+ status: task.status,
72041
+ message: `Task ${task.id} is already ${task.status} \u2014 cannot stop.`
72042
+ }
72043
+ };
72044
+ }
72045
+ killTask(task.id);
72046
+ return {
72047
+ data: {
72048
+ task_id: task.id,
72049
+ status: "killed",
72050
+ message: `Task ${task.id} has been killed.`
72051
+ }
72052
+ };
72053
+ },
72054
+ mapToolResultToToolResultBlockParam(result, toolUseId) {
72055
+ return {
72056
+ type: "tool_result",
72057
+ tool_use_id: toolUseId,
72058
+ content: result.message,
72059
+ is_error: result.status === "not_found"
72060
+ };
72061
+ }
72062
+ };
72063
+ TASK_OUTPUT_PROMPT = `Check the status and output of a background task.
72064
+
72065
+ Use this tool when you have previously started a bash command or agent with run_in_background:true
72066
+ and need to check whether it has completed, and retrieve its output.
72067
+
72068
+ The task_id is returned when you start a background task (e.g., "bg-1" for bash, "agent-1" for agents).
72069
+
72070
+ The tool returns:
72071
+ - status: "running", "completed", "failed", or "killed"
72072
+ - output: The stdout/stderr from the task (truncated to 30000 chars)
72073
+ - exitCode: The exit code (for bash tasks)
72074
+ - durationMs: How long the task has been running or ran
72075
+ - progress: For agents, includes tokenCount and lastActivity`;
72076
+ TASK_STOP_PROMPT = `Stop a running background task.
72077
+
72078
+ Use this tool to kill a background bash command or agent that is still running.
72079
+ The task will be marked as "killed" and its process terminated.
72080
+
72081
+ The task_id is returned when you start a background task (e.g., "bg-1" for bash, "agent-1" for agents).`;
72082
+ }
72083
+ });
72084
+
71068
72085
  // src/tools/builtin/ask-user.ts
71069
72086
  var OptionSchema, QuestionSchema, AskUserInputSchema, AskUserOutputSchema, askUserQuestionTool, ASK_USER_PROMPT;
71070
72087
  var init_ask_user = __esm({
@@ -71471,17 +72488,17 @@ LSP servers must be configured for the file type.`;
71471
72488
  });
71472
72489
 
71473
72490
  // src/tools/builtin/plan-mode.ts
71474
- import { readFileSync as readFileSync11, existsSync as existsSync13 } from "fs";
71475
- import { join as join7 } from "path";
72491
+ import { readFileSync as readFileSync13, existsSync as existsSync16 } from "fs";
72492
+ import { join as join10 } from "path";
71476
72493
  function getPlanFilePath(agentId) {
71477
72494
  const dir = getPlansDir();
71478
72495
  const name = agentId ? `plan-${agentId}.md` : "plan.md";
71479
- return join7(dir, name);
72496
+ return join10(dir, name);
71480
72497
  }
71481
72498
  function readPlanFile(agentId) {
71482
72499
  const path = getPlanFilePath(agentId);
71483
- if (!existsSync13(path)) return null;
71484
- return readFileSync11(path, "utf-8");
72500
+ if (!existsSync16(path)) return null;
72501
+ return readFileSync13(path, "utf-8");
71485
72502
  }
71486
72503
  var _hasExitedPlanMode, EnterPlanInputSchema, EnterPlanOutputSchema, enterPlanModeTool, ExitPlanInputSchema, ExitPlanOutputSchema, exitPlanModeTool, ENTER_PLAN_PROMPT, EXIT_PLAN_PROMPT;
71487
72504
  var init_plan_mode = __esm({
@@ -71709,8 +72726,43 @@ function getEnabledTools() {
71709
72726
  }
71710
72727
  return tools;
71711
72728
  }
71712
- function getDeferredToolInfos() {
71713
- return [...deferredTools.values()];
72729
+ function setDeferredToolSchemas(schemas) {
72730
+ for (const s of schemas) {
72731
+ deferredToolSchemas.set(s.name, s);
72732
+ }
72733
+ }
72734
+ function getAllDeferredToolSchemas() {
72735
+ return [...deferredToolSchemas.values()];
72736
+ }
72737
+ function searchDeferredToolSchemas(query, maxResults = 5) {
72738
+ const queryLower = query.toLowerCase();
72739
+ const queryTerms = queryLower.split(/\s+/);
72740
+ if (queryLower.startsWith("select:")) {
72741
+ const names = query.slice(7).split(",").map((n) => n.trim());
72742
+ return names.map((n) => deferredToolSchemas.get(n)).filter((s) => s !== void 0);
72743
+ }
72744
+ const requiredTerms = queryTerms.filter((t) => t.startsWith("+")).map((t) => t.slice(1));
72745
+ const searchTerms = queryTerms.filter((t) => !t.startsWith("+"));
72746
+ const results = [];
72747
+ for (const schema of deferredToolSchemas.values()) {
72748
+ const nameLower = schema.name.toLowerCase();
72749
+ const descLower = schema.description.toLowerCase();
72750
+ if (requiredTerms.length > 0 && !requiredTerms.every((rt) => nameLower.includes(rt))) {
72751
+ continue;
72752
+ }
72753
+ let score = 0;
72754
+ const allTerms = searchTerms.length > 0 ? searchTerms : requiredTerms;
72755
+ for (const term of allTerms) {
72756
+ if (nameLower === term) score += 10;
72757
+ else if (nameLower.includes(term)) score += 5;
72758
+ if (descLower.includes(term)) score += 3;
72759
+ }
72760
+ if (score > 0) {
72761
+ results.push({ schema, score });
72762
+ }
72763
+ }
72764
+ results.sort((a, b) => b.score - a.score);
72765
+ return results.slice(0, maxResults).map((r) => r.schema);
71714
72766
  }
71715
72767
  function searchTools(query, maxResults = 5) {
71716
72768
  const results = [];
@@ -71748,7 +72800,7 @@ function matchScore(name, hint, queryTerms) {
71748
72800
  }
71749
72801
  return score;
71750
72802
  }
71751
- var registeredTools, deferredTools, disabledTools, mcpTools;
72803
+ var registeredTools, deferredTools, disabledTools, mcpTools, deferredToolSchemas;
71752
72804
  var init_registry2 = __esm({
71753
72805
  "src/tools/registry.ts"() {
71754
72806
  "use strict";
@@ -71757,6 +72809,7 @@ var init_registry2 = __esm({
71757
72809
  deferredTools = /* @__PURE__ */ new Map();
71758
72810
  disabledTools = /* @__PURE__ */ new Set();
71759
72811
  mcpTools = /* @__PURE__ */ new Map();
72812
+ deferredToolSchemas = /* @__PURE__ */ new Map();
71760
72813
  }
71761
72814
  });
71762
72815
 
@@ -71804,6 +72857,24 @@ function simpleTool(opts) {
71804
72857
  mapToolResultToToolResultBlockParam: opts.mapResult
71805
72858
  };
71806
72859
  }
72860
+ function toNotebookSource(text) {
72861
+ const lines = text.split("\n");
72862
+ return lines.map(
72863
+ (l, i) => i < lines.length - 1 ? l + "\n" : l
72864
+ );
72865
+ }
72866
+ function createCell(cellType, source) {
72867
+ const base = {
72868
+ cell_type: cellType,
72869
+ metadata: {},
72870
+ source: toNotebookSource(source)
72871
+ };
72872
+ if (cellType === "code") {
72873
+ base.execution_count = null;
72874
+ base.outputs = [];
72875
+ }
72876
+ return base;
72877
+ }
71807
72878
  var toolSearchTool, cronCreateTool, cronDeleteTool, cronListTool, enterWorktreeTool, exitWorktreeTool, notebookEditTool, configTool, sendMessageTool;
71808
72879
  var init_misc = __esm({
71809
72880
  "src/tools/builtin/misc.ts"() {
@@ -71813,30 +72884,68 @@ var init_misc = __esm({
71813
72884
  init_constants();
71814
72885
  toolSearchTool = simpleTool({
71815
72886
  name: TOOL_SEARCH_TOOL,
71816
- hint: "find available tools by keyword",
72887
+ hint: "find available tools by keyword \u2014 fetches full schemas for deferred tools",
71817
72888
  inputSchema: external_exports.strictObject({
71818
- query: external_exports.string().describe("Query to find tools"),
71819
- max_results: external_exports.number().default(5).describe("Max results")
72889
+ query: external_exports.string().describe(
72890
+ 'Query to find deferred tools. Use "select:Read,Edit,Grep" for exact names, or keywords to search. Use "+slack send" to require "slack" in name.'
72891
+ ),
72892
+ max_results: external_exports.number().default(5).describe("Maximum number of results to return (default: 5)")
71820
72893
  }),
71821
72894
  readOnly: true,
71822
72895
  concurrent: true,
71823
72896
  defer: false,
71824
- prompt: "Search for available tools by keyword. Use to discover deferred tools.",
72897
+ prompt: `Fetches full schema definitions for deferred tools so they can be called.
72898
+
72899
+ Deferred tools appear by name in the "Deferred Tools" section of the system prompt. Until fetched, only the name is known \u2014 there is no parameter schema, so the tool cannot be invoked. This tool takes a query, matches it against the deferred tool list, and returns the matched tools' complete JSONSchema definitions inside a <functions> block. Once a tool's schema appears in that result, it is callable exactly like any tool defined at the top of the prompt.
72900
+
72901
+ Result format: each matched tool appears as one <function>{"description": "...", "name": "...", "parameters": {...}}</function> line inside the <functions> block \u2014 the same encoding as the tool list at the top of this prompt.
72902
+
72903
+ Query forms:
72904
+ - "select:Read,Edit,Grep" \u2014 fetch these exact tools by name
72905
+ - "notebook jupyter" \u2014 keyword search, up to max_results best matches
72906
+ - "+slack send" \u2014 require "slack" in the name, rank by remaining terms`,
71825
72907
  async call(input) {
71826
- const results = searchTools(input.query, input.max_results);
71827
- const deferred = getDeferredToolInfos();
72908
+ const matchedSchemas = searchDeferredToolSchemas(input.query, input.max_results);
72909
+ const registryResults = searchTools(input.query, input.max_results);
72910
+ const allDeferred = getAllDeferredToolSchemas();
71828
72911
  return {
71829
72912
  data: {
71830
- results,
71831
- deferredCount: deferred.length
72913
+ matchedSchemas,
72914
+ registryResults,
72915
+ totalDeferredCount: allDeferred.length
71832
72916
  }
71833
72917
  };
71834
72918
  },
71835
72919
  mapResult(result, id) {
71836
- const lines = result.results.map(
71837
- (r) => `${r.name}${r.deferred ? " (deferred)" : ""}: ${r.hint}`
71838
- );
71839
- return { type: "tool_result", tool_use_id: id, content: lines.join("\n") || "No tools found." };
72920
+ const sections = [];
72921
+ const schemas = result.matchedSchemas;
72922
+ if (schemas && schemas.length > 0) {
72923
+ const functionDefs = schemas.map(
72924
+ (s) => `<function>{"description": ${JSON.stringify(s.description)}, "name": ${JSON.stringify(s.name)}, "parameters": ${JSON.stringify(s.inputSchema)}}</function>`
72925
+ ).join("\n");
72926
+ sections.push(`<functions>
72927
+ ${functionDefs}
72928
+ </functions>`);
72929
+ }
72930
+ const registryResults = result.registryResults;
72931
+ if (registryResults && registryResults.length > 0) {
72932
+ const extraHits = registryResults.filter(
72933
+ (r) => !schemas?.some((s) => s.name === r.name)
72934
+ );
72935
+ if (extraHits.length > 0) {
72936
+ const lines = extraHits.map(
72937
+ (r) => `${r.name}${r.deferred ? " (deferred)" : ""}: ${r.hint}`
72938
+ );
72939
+ sections.push(lines.join("\n"));
72940
+ }
72941
+ }
72942
+ if (sections.length === 0) {
72943
+ return { type: "tool_result", tool_use_id: id, content: "No tools found matching the query." };
72944
+ }
72945
+ const footer = `
72946
+
72947
+ ${result.totalDeferredCount} deferred tool(s) available. Use ToolSearch to fetch schemas for any listed tool.`;
72948
+ return { type: "tool_result", tool_use_id: id, content: sections.join("\n\n") + footer };
71840
72949
  }
71841
72950
  });
71842
72951
  cronCreateTool = simpleTool({
@@ -71924,34 +73033,134 @@ var init_misc = __esm({
71924
73033
  });
71925
73034
  notebookEditTool = simpleTool({
71926
73035
  name: NOTEBOOK_EDIT_TOOL,
71927
- hint: "edit Jupyter notebook cells",
73036
+ hint: "edit Jupyter notebook cells \u2014 insert, replace, delete, move, change type",
71928
73037
  inputSchema: external_exports.strictObject({
71929
73038
  notebook_path: external_exports.string().describe("Path to the .ipynb file"),
71930
- cell_index: external_exports.number().describe("Cell index to edit"),
71931
- new_source: external_exports.string().describe("New cell source code")
73039
+ command: external_exports.enum(["insert_cell", "replace_cell", "delete_cell", "move_cell", "change_cell_type"]).describe("Operation to perform"),
73040
+ cell_index: external_exports.number().optional().describe("Cell index to operate on (0-based). For insert_cell, position to insert at (omit to append)."),
73041
+ cell_type: external_exports.enum(["code", "markdown", "raw"]).optional().describe("Cell type \u2014 required for insert_cell and change_cell_type"),
73042
+ new_source: external_exports.string().optional().describe("New cell content \u2014 required for insert_cell and replace_cell"),
73043
+ target_index: external_exports.number().optional().describe("Destination index for move_cell")
71932
73044
  }),
71933
73045
  readOnly: false,
71934
73046
  concurrent: false,
71935
- defer: true,
71936
- prompt: "Edit a specific cell in a Jupyter notebook (.ipynb file).",
73047
+ defer: false,
73048
+ prompt: `Edit Jupyter notebook (.ipynb) cells. Supports 5 commands:
73049
+
73050
+ - insert_cell: Insert a new cell at cell_index (or end if omitted). Requires cell_type and new_source.
73051
+ - replace_cell: Replace the source of the cell at cell_index with new_source. Requires cell_index and new_source.
73052
+ - delete_cell: Delete the cell at cell_index. Requires cell_index.
73053
+ - move_cell: Move cell from cell_index to target_index. Requires cell_index and target_index.
73054
+ - change_cell_type: Change the type of cell at cell_index to cell_type. Requires cell_index and cell_type.
73055
+
73056
+ All metadata, outputs, and kernel info are preserved. Cell indices are 0-based.`,
71937
73057
  async call(input) {
71938
- const { readFileSync: readFileSync14, writeFileSync: writeFileSync10 } = await import("fs");
73058
+ const { readFileSync: readFileSync15, writeFileSync: writeFileSync10 } = await import("fs");
73059
+ const { resolve: resolve8 } = await import("path");
73060
+ const notebookPath = resolve8(input.notebook_path);
71939
73061
  try {
71940
- const content = JSON.parse(readFileSync14(input.notebook_path, "utf-8"));
71941
- if (content.cells && content.cells[input.cell_index]) {
71942
- content.cells[input.cell_index].source = input.new_source.split("\n").map(
71943
- (l, i, a) => i < a.length - 1 ? l + "\n" : l
71944
- );
71945
- writeFileSync10(input.notebook_path, JSON.stringify(content, null, 1), "utf-8");
71946
- return { data: { success: true, cellIndex: input.cell_index } };
73062
+ const raw = readFileSync15(notebookPath, "utf-8");
73063
+ const notebook = JSON.parse(raw);
73064
+ if (!notebook.cells || !Array.isArray(notebook.cells)) {
73065
+ return { data: { success: false, error: "Invalid notebook: no cells array found" } };
73066
+ }
73067
+ const cells = notebook.cells;
73068
+ const totalCells = cells.length;
73069
+ switch (input.command) {
73070
+ case "insert_cell": {
73071
+ if (!input.cell_type) return { data: { success: false, error: "insert_cell requires cell_type" } };
73072
+ const source = input.new_source ?? "";
73073
+ const newCell = createCell(input.cell_type, source);
73074
+ const idx = input.cell_index ?? totalCells;
73075
+ if (idx < 0 || idx > totalCells) {
73076
+ return { data: { success: false, error: `cell_index ${idx} out of range (0-${totalCells})` } };
73077
+ }
73078
+ cells.splice(idx, 0, newCell);
73079
+ notebook.cells = cells;
73080
+ writeFileSync10(notebookPath, JSON.stringify(notebook, null, 1), "utf-8");
73081
+ return { data: { success: true, message: `Inserted ${input.cell_type} cell at index ${idx} (notebook now has ${cells.length} cells)` } };
73082
+ }
73083
+ case "replace_cell": {
73084
+ if (input.cell_index == null) return { data: { success: false, error: "replace_cell requires cell_index" } };
73085
+ if (input.new_source == null) return { data: { success: false, error: "replace_cell requires new_source" } };
73086
+ if (input.cell_index < 0 || input.cell_index >= totalCells) {
73087
+ return { data: { success: false, error: `cell_index ${input.cell_index} out of range (0-${totalCells - 1})` } };
73088
+ }
73089
+ const cell = cells[input.cell_index];
73090
+ cell.source = toNotebookSource(input.new_source);
73091
+ if (cell.cell_type === "code") {
73092
+ cell.outputs = [];
73093
+ cell.execution_count = null;
73094
+ }
73095
+ writeFileSync10(notebookPath, JSON.stringify(notebook, null, 1), "utf-8");
73096
+ return { data: { success: true, message: `Replaced source of cell ${input.cell_index} (${cell.cell_type})` } };
73097
+ }
73098
+ case "delete_cell": {
73099
+ if (input.cell_index == null) return { data: { success: false, error: "delete_cell requires cell_index" } };
73100
+ if (input.cell_index < 0 || input.cell_index >= totalCells) {
73101
+ return { data: { success: false, error: `cell_index ${input.cell_index} out of range (0-${totalCells - 1})` } };
73102
+ }
73103
+ const deletedType = cells[input.cell_index].cell_type;
73104
+ cells.splice(input.cell_index, 1);
73105
+ notebook.cells = cells;
73106
+ writeFileSync10(notebookPath, JSON.stringify(notebook, null, 1), "utf-8");
73107
+ return { data: { success: true, message: `Deleted ${deletedType} cell at index ${input.cell_index} (notebook now has ${cells.length} cells)` } };
73108
+ }
73109
+ case "move_cell": {
73110
+ if (input.cell_index == null) return { data: { success: false, error: "move_cell requires cell_index" } };
73111
+ if (input.target_index == null) return { data: { success: false, error: "move_cell requires target_index" } };
73112
+ if (input.cell_index < 0 || input.cell_index >= totalCells) {
73113
+ return { data: { success: false, error: `cell_index ${input.cell_index} out of range (0-${totalCells - 1})` } };
73114
+ }
73115
+ if (input.target_index < 0 || input.target_index >= totalCells) {
73116
+ return { data: { success: false, error: `target_index ${input.target_index} out of range (0-${totalCells - 1})` } };
73117
+ }
73118
+ if (input.cell_index === input.target_index) {
73119
+ return { data: { success: true, message: `Cell ${input.cell_index} is already at target position` } };
73120
+ }
73121
+ const [movedCell] = cells.splice(input.cell_index, 1);
73122
+ cells.splice(input.target_index, 0, movedCell);
73123
+ notebook.cells = cells;
73124
+ writeFileSync10(notebookPath, JSON.stringify(notebook, null, 1), "utf-8");
73125
+ return { data: { success: true, message: `Moved cell from index ${input.cell_index} to index ${input.target_index}` } };
73126
+ }
73127
+ case "change_cell_type": {
73128
+ if (input.cell_index == null) return { data: { success: false, error: "change_cell_type requires cell_index" } };
73129
+ if (!input.cell_type) return { data: { success: false, error: "change_cell_type requires cell_type" } };
73130
+ if (input.cell_index < 0 || input.cell_index >= totalCells) {
73131
+ return { data: { success: false, error: `cell_index ${input.cell_index} out of range (0-${totalCells - 1})` } };
73132
+ }
73133
+ const cell = cells[input.cell_index];
73134
+ const oldType = cell.cell_type;
73135
+ if (oldType === input.cell_type) {
73136
+ return { data: { success: true, message: `Cell ${input.cell_index} is already type ${input.cell_type}` } };
73137
+ }
73138
+ cell.cell_type = input.cell_type;
73139
+ if (input.cell_type === "code") {
73140
+ if (!("execution_count" in cell)) cell.execution_count = null;
73141
+ if (!("outputs" in cell)) cell.outputs = [];
73142
+ }
73143
+ if (oldType === "code" && input.cell_type !== "code") {
73144
+ delete cell.execution_count;
73145
+ delete cell.outputs;
73146
+ }
73147
+ writeFileSync10(notebookPath, JSON.stringify(notebook, null, 1), "utf-8");
73148
+ return { data: { success: true, message: `Changed cell ${input.cell_index} from ${oldType} to ${input.cell_type}` } };
73149
+ }
73150
+ default:
73151
+ return { data: { success: false, error: `Unknown command: ${input.command}` } };
71947
73152
  }
71948
- return { data: { success: false, error: "Cell index out of range" } };
71949
73153
  } catch (e) {
71950
73154
  return { data: { success: false, error: String(e) } };
71951
73155
  }
71952
73156
  },
71953
73157
  mapResult(result, id) {
71954
- return { type: "tool_result", tool_use_id: id, content: result.success ? `Edited cell ${result.cellIndex}` : `Error: ${result.error}` };
73158
+ return {
73159
+ type: "tool_result",
73160
+ tool_use_id: id,
73161
+ content: result.success ? result.message : `Error: ${result.error}`,
73162
+ ...result.success ? {} : { is_error: true }
73163
+ };
71955
73164
  }
71956
73165
  });
71957
73166
  configTool = simpleTool({
@@ -78249,9 +79458,9 @@ var init_protocol = __esm({
78249
79458
  });
78250
79459
  this.setRequestHandler(ListTasksRequestSchema, async (request, extra) => {
78251
79460
  try {
78252
- const { tasks, nextCursor } = await this._taskStore.listTasks(request.params?.cursor, extra.sessionId);
79461
+ const { tasks: tasks2, nextCursor } = await this._taskStore.listTasks(request.params?.cursor, extra.sessionId);
78253
79462
  return {
78254
- tasks,
79463
+ tasks: tasks2,
78255
79464
  nextCursor,
78256
79465
  _meta: {}
78257
79466
  };
@@ -88942,6 +90151,13 @@ async function connectMcpServers(configs, batchSize = DEFAULT_BATCH_SIZE) {
88942
90151
  }
88943
90152
  return results;
88944
90153
  }
90154
+ function getServerClient(name) {
90155
+ const server = connectedServers.get(name);
90156
+ return server?.client ?? null;
90157
+ }
90158
+ function getConnectedServerNames() {
90159
+ return [...connectedServers.keys()];
90160
+ }
88945
90161
  function wrapMcpTool(serverName, mcpTool, client) {
88946
90162
  const toolName = `mcp__${serverName}__${mcpTool.name}`;
88947
90163
  return {
@@ -89027,6 +90243,239 @@ var init_client4 = __esm({
89027
90243
  }
89028
90244
  });
89029
90245
 
90246
+ // src/tools/builtin/mcp-resources.ts
90247
+ var ListMcpResourcesInputSchema, ListMcpResourcesOutputSchema, listMcpResourcesTool, LIST_MCP_RESOURCES_PROMPT, ReadMcpResourceInputSchema, ReadMcpResourceOutputSchema, readMcpResourceTool, READ_MCP_RESOURCE_PROMPT;
90248
+ var init_mcp_resources = __esm({
90249
+ "src/tools/builtin/mcp-resources.ts"() {
90250
+ "use strict";
90251
+ init_zod();
90252
+ init_constants();
90253
+ init_client4();
90254
+ ListMcpResourcesInputSchema = external_exports.strictObject({});
90255
+ ListMcpResourcesOutputSchema = external_exports.object({
90256
+ resources: external_exports.array(external_exports.object({
90257
+ server: external_exports.string(),
90258
+ uri: external_exports.string(),
90259
+ name: external_exports.string(),
90260
+ description: external_exports.string().optional(),
90261
+ mimeType: external_exports.string().optional()
90262
+ })),
90263
+ totalCount: external_exports.number()
90264
+ });
90265
+ listMcpResourcesTool = {
90266
+ name: LIST_MCP_RESOURCES_TOOL,
90267
+ searchHint: "list MCP server resources",
90268
+ maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
90269
+ shouldDefer: true,
90270
+ async description() {
90271
+ return "List all resources available from connected MCP servers.";
90272
+ },
90273
+ async prompt() {
90274
+ return LIST_MCP_RESOURCES_PROMPT;
90275
+ },
90276
+ get inputSchema() {
90277
+ return ListMcpResourcesInputSchema;
90278
+ },
90279
+ get outputSchema() {
90280
+ return ListMcpResourcesOutputSchema;
90281
+ },
90282
+ userFacingName() {
90283
+ return "List MCP Resources";
90284
+ },
90285
+ isEnabled() {
90286
+ return true;
90287
+ },
90288
+ isConcurrencySafe() {
90289
+ return true;
90290
+ },
90291
+ isReadOnly() {
90292
+ return true;
90293
+ },
90294
+ toAutoClassifierInput() {
90295
+ return "list mcp resources";
90296
+ },
90297
+ async validateInput() {
90298
+ return { result: true };
90299
+ },
90300
+ async checkPermissions() {
90301
+ return { behavior: "allow" };
90302
+ },
90303
+ async call(_input, _context) {
90304
+ const serverNames = getConnectedServerNames();
90305
+ const resources = [];
90306
+ for (const serverName of serverNames) {
90307
+ const client = getServerClient(serverName);
90308
+ if (!client) continue;
90309
+ try {
90310
+ const result = await client.listResources();
90311
+ if (result.resources && Array.isArray(result.resources)) {
90312
+ for (const res of result.resources) {
90313
+ resources.push({
90314
+ server: serverName,
90315
+ uri: res.uri,
90316
+ name: res.name,
90317
+ description: res.description,
90318
+ mimeType: res.mimeType
90319
+ });
90320
+ }
90321
+ }
90322
+ } catch {
90323
+ }
90324
+ }
90325
+ return {
90326
+ data: {
90327
+ resources,
90328
+ totalCount: resources.length
90329
+ }
90330
+ };
90331
+ },
90332
+ mapToolResultToToolResultBlockParam(result, toolUseId) {
90333
+ if (result.resources.length === 0) {
90334
+ return {
90335
+ type: "tool_result",
90336
+ tool_use_id: toolUseId,
90337
+ content: "No resources found from any connected MCP server."
90338
+ };
90339
+ }
90340
+ const lines = result.resources.map((r) => {
90341
+ let line = `[${r.server}] ${r.uri} \u2014 ${r.name}`;
90342
+ if (r.description) line += ` (${r.description})`;
90343
+ if (r.mimeType) line += ` [${r.mimeType}]`;
90344
+ return line;
90345
+ });
90346
+ return {
90347
+ type: "tool_result",
90348
+ tool_use_id: toolUseId,
90349
+ content: `Found ${result.totalCount} resource(s):
90350
+
90351
+ ${lines.join("\n")}`
90352
+ };
90353
+ }
90354
+ };
90355
+ LIST_MCP_RESOURCES_PROMPT = `List all resources available from connected MCP servers.
90356
+ - Returns resources with server name, URI, name, description, and MIME type
90357
+ - No input required \u2014 queries all connected servers
90358
+ - Use ReadMcpResourceTool to read a specific resource by server name and URI`;
90359
+ ReadMcpResourceInputSchema = external_exports.strictObject({
90360
+ server_name: external_exports.string().describe("The name of the MCP server that owns the resource"),
90361
+ uri: external_exports.string().describe("The URI of the resource to read")
90362
+ });
90363
+ ReadMcpResourceOutputSchema = external_exports.object({
90364
+ contents: external_exports.array(external_exports.object({
90365
+ uri: external_exports.string(),
90366
+ mimeType: external_exports.string().optional(),
90367
+ text: external_exports.string().optional(),
90368
+ blob: external_exports.string().optional()
90369
+ }))
90370
+ });
90371
+ readMcpResourceTool = {
90372
+ name: READ_MCP_RESOURCE_TOOL,
90373
+ searchHint: "read MCP server resource by URI",
90374
+ maxResultSizeChars: DEFAULT_MAX_RESULT_SIZE_CHARS,
90375
+ shouldDefer: true,
90376
+ async description(input) {
90377
+ return input?.uri ? `Read MCP resource: ${input.uri}` : "Read a resource from a connected MCP server.";
90378
+ },
90379
+ async prompt() {
90380
+ return READ_MCP_RESOURCE_PROMPT;
90381
+ },
90382
+ get inputSchema() {
90383
+ return ReadMcpResourceInputSchema;
90384
+ },
90385
+ get outputSchema() {
90386
+ return ReadMcpResourceOutputSchema;
90387
+ },
90388
+ userFacingName() {
90389
+ return "Read MCP Resource";
90390
+ },
90391
+ isEnabled() {
90392
+ return true;
90393
+ },
90394
+ isConcurrencySafe() {
90395
+ return true;
90396
+ },
90397
+ isReadOnly() {
90398
+ return true;
90399
+ },
90400
+ toAutoClassifierInput(input) {
90401
+ return `${input.server_name} ${input.uri}`;
90402
+ },
90403
+ async validateInput(input) {
90404
+ if (!input.server_name?.trim()) {
90405
+ return { result: false, message: "server_name is required", errorCode: 1 };
90406
+ }
90407
+ if (!input.uri?.trim()) {
90408
+ return { result: false, message: "uri is required", errorCode: 2 };
90409
+ }
90410
+ return { result: true };
90411
+ },
90412
+ async checkPermissions() {
90413
+ return { behavior: "allow" };
90414
+ },
90415
+ async call(input, _context) {
90416
+ const client = getServerClient(input.server_name);
90417
+ if (!client) {
90418
+ return {
90419
+ data: {
90420
+ contents: [{
90421
+ uri: input.uri,
90422
+ text: `Error: MCP server "${input.server_name}" is not connected. Use ListMcpResourcesTool to see available servers.`
90423
+ }]
90424
+ }
90425
+ };
90426
+ }
90427
+ try {
90428
+ const result = await client.readResource({ uri: input.uri });
90429
+ const contents = (result.contents ?? []).map((c) => ({
90430
+ uri: c.uri ?? input.uri,
90431
+ mimeType: c.mimeType,
90432
+ text: c.text,
90433
+ blob: c.blob
90434
+ }));
90435
+ return { data: { contents } };
90436
+ } catch (error2) {
90437
+ return {
90438
+ data: {
90439
+ contents: [{
90440
+ uri: input.uri,
90441
+ text: `Error reading resource: ${error2 instanceof Error ? error2.message : String(error2)}`
90442
+ }]
90443
+ }
90444
+ };
90445
+ }
90446
+ },
90447
+ mapToolResultToToolResultBlockParam(result, toolUseId) {
90448
+ if (result.contents.length === 0) {
90449
+ return {
90450
+ type: "tool_result",
90451
+ tool_use_id: toolUseId,
90452
+ content: "Resource returned no content."
90453
+ };
90454
+ }
90455
+ const parts = [];
90456
+ for (const c of result.contents) {
90457
+ if (c.text) {
90458
+ parts.push(c.text);
90459
+ } else if (c.blob) {
90460
+ parts.push(`[base64 blob, ${c.blob.length} chars]${c.mimeType ? ` (${c.mimeType})` : ""}`);
90461
+ } else {
90462
+ parts.push(`[empty content for ${c.uri}]`);
90463
+ }
90464
+ }
90465
+ return {
90466
+ type: "tool_result",
90467
+ tool_use_id: toolUseId,
90468
+ content: parts.join("\n\n")
90469
+ };
90470
+ }
90471
+ };
90472
+ READ_MCP_RESOURCE_PROMPT = `Read a resource from a connected MCP server.
90473
+ - Requires server_name (the MCP server name) and uri (the resource URI)
90474
+ - Use ListMcpResourcesTool first to discover available resources
90475
+ - Returns text content or base64-encoded binary data`;
90476
+ }
90477
+ });
90478
+
89030
90479
  // src/mcp/handlers.ts
89031
90480
  function mcpToolsToHandlers() {
89032
90481
  const mcpTools2 = getMcpTools();
@@ -89115,51 +90564,61 @@ var init_init = __esm({
89115
90564
  });
89116
90565
 
89117
90566
  // src/core/team.ts
89118
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync14, mkdirSync as mkdirSync7 } from "fs";
89119
- import { join as join8 } from "path";
89120
90567
  function createTeam(name, description) {
89121
- const team = {
90568
+ const now = (/* @__PURE__ */ new Date()).toISOString();
90569
+ dbRun(
90570
+ `INSERT OR IGNORE INTO teams (name, description, task_list_id, created_at) VALUES (?, ?, ?, ?)`,
90571
+ [name, description ?? null, name, now]
90572
+ );
90573
+ return {
89122
90574
  name,
89123
90575
  description,
89124
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
90576
+ createdAt: now,
89125
90577
  members: [],
89126
90578
  taskListId: name
89127
90579
  };
89128
- const teamsDir = getTeamsDir();
89129
- writeFileSync8(join8(teamsDir, `${name}.json`), JSON.stringify(team, null, 2), "utf-8");
89130
- const taskDir = join8(getTasksDir(), name);
89131
- if (!existsSync14(taskDir)) mkdirSync7(taskDir, { recursive: true });
89132
- return team;
89133
90580
  }
89134
90581
  function getTeam(name) {
89135
- const path = join8(getTeamsDir(), `${name}.json`);
89136
- if (!existsSync14(path)) return null;
89137
- try {
89138
- return JSON.parse(readFileSync12(path, "utf-8"));
89139
- } catch {
89140
- return null;
89141
- }
90582
+ const row = dbGet(`SELECT * FROM teams WHERE name = ?`, [name]);
90583
+ if (!row) return null;
90584
+ const members = dbAll(
90585
+ `SELECT * FROM team_members WHERE team_name = ?`,
90586
+ [name]
90587
+ );
90588
+ return {
90589
+ name: row.name,
90590
+ description: row.description ?? void 0,
90591
+ createdAt: row.created_at ?? "",
90592
+ taskListId: row.task_list_id ?? row.name,
90593
+ members: members.map((m) => ({
90594
+ name: m.agent_name,
90595
+ agentId: m.agent_name,
90596
+ role: m.role ?? void 0,
90597
+ status: m.status ?? "idle",
90598
+ currentTask: m.current_task ?? void 0
90599
+ }))
90600
+ };
89142
90601
  }
89143
90602
  function addTeamMember(teamName, member) {
89144
- const team = getTeam(teamName);
89145
- if (!team) return;
89146
- team.members = team.members.filter((m) => m.name !== member.name);
89147
- team.members.push(member);
89148
- writeFileSync8(join8(getTeamsDir(), `${teamName}.json`), JSON.stringify(team, null, 2), "utf-8");
90603
+ dbRun(
90604
+ `DELETE FROM team_members WHERE team_name = ? AND agent_name = ?`,
90605
+ [teamName, member.name]
90606
+ );
90607
+ dbRun(
90608
+ `INSERT INTO team_members (team_name, agent_name, role, status, current_task) VALUES (?, ?, ?, ?, ?)`,
90609
+ [teamName, member.name, member.role ?? null, member.status, member.currentTask ?? null]
90610
+ );
89149
90611
  }
89150
90612
  function updateMemberStatus(teamName, memberName, status) {
89151
- const team = getTeam(teamName);
89152
- if (!team) return;
89153
- const member = team.members.find((m) => m.name === memberName);
89154
- if (member) {
89155
- member.status = status;
89156
- writeFileSync8(join8(getTeamsDir(), `${teamName}.json`), JSON.stringify(team, null, 2), "utf-8");
89157
- }
90613
+ dbRun(
90614
+ `UPDATE team_members SET status = ? WHERE team_name = ? AND agent_name = ?`,
90615
+ [status, teamName, memberName]
90616
+ );
89158
90617
  }
89159
90618
  var init_team = __esm({
89160
90619
  "src/core/team.ts"() {
89161
90620
  "use strict";
89162
- init_paths();
90621
+ init_db();
89163
90622
  }
89164
90623
  });
89165
90624
 
@@ -90163,6 +91622,7 @@ __export(app_exports, {
90163
91622
  launchInkApp: () => launchInkApp,
90164
91623
  runHeadless: () => runHeadless
90165
91624
  });
91625
+ import { execSync as execSync4 } from "child_process";
90166
91626
  function useSpinner(active) {
90167
91627
  const [i, setI] = (0, import_react22.useState)(0);
90168
91628
  (0, import_react22.useEffect)(() => {
@@ -90185,6 +91645,8 @@ function createToolHandlers(mcpHandlers) {
90185
91645
  taskGetTool,
90186
91646
  taskListTool,
90187
91647
  taskUpdateTool,
91648
+ taskOutputTool,
91649
+ taskStopTool,
90188
91650
  askUserQuestionTool,
90189
91651
  webSearchTool,
90190
91652
  webFetchTool,
@@ -90199,49 +91661,75 @@ function createToolHandlers(mcpHandlers) {
90199
91661
  exitWorktreeTool,
90200
91662
  notebookEditTool,
90201
91663
  configTool,
90202
- sendMessageTool
91664
+ sendMessageTool,
91665
+ listMcpResourcesTool,
91666
+ readMcpResourceTool,
91667
+ skillTool
90203
91668
  ];
90204
- const permCtx = createDefaultPermissionContext();
91669
+ const permCtx = createDefaultPermissionContext(getSettings());
90205
91670
  const appState = { toolPermissionContext: permCtx, verbose: false };
90206
- const builtinHandlers = tools.map((tool) => ({
90207
- name: tool.name,
90208
- description: TOOL_DESCRIPTIONS[tool.name] ?? `Tool: ${tool.name}`,
90209
- inputSchema: TOOL_JSON_SCHEMAS[tool.name] ?? { type: "object", properties: {} },
90210
- isReadOnly: tool.isReadOnly(),
90211
- isConcurrencySafe: tool.isConcurrencySafe(),
90212
- call: async (input, ctx) => {
90213
- const summary = toolSummary(tool.name, input);
90214
- const t0 = performance.now();
90215
- try {
90216
- const toolCtx = {
90217
- abortController: new AbortController(),
90218
- getAppState: () => appState,
90219
- setAppState: (fn) => Object.assign(appState, fn(appState)),
90220
- options: { mainLoopModel: "sonnet", thinkingConfig: { type: "disabled" }, isNonInteractiveSession: false, tools: [], agentDefinitions: { activeAgents: [] } }
90221
- };
90222
- const result = await tool.call(input, toolCtx);
90223
- const block2 = tool.mapToolResultToToolResultBlockParam(result.data, ctx.toolUseId ?? "");
90224
- const dur = performance.now() - t0;
91671
+ const immediate = [];
91672
+ const all = [];
91673
+ const deferredInfo = [];
91674
+ const deferredSchemas = /* @__PURE__ */ new Map();
91675
+ for (const tool of tools) {
91676
+ const handler = {
91677
+ name: tool.name,
91678
+ description: TOOL_DESCRIPTIONS[tool.name] ?? `Tool: ${tool.name}`,
91679
+ inputSchema: TOOL_JSON_SCHEMAS[tool.name] ?? { type: "object", properties: {} },
91680
+ isReadOnly: tool.isReadOnly(),
91681
+ isConcurrencySafe: tool.isConcurrencySafe(),
91682
+ call: async (input, ctx) => {
91683
+ const summary = toolSummary(tool.name, input);
91684
+ const t0 = performance.now();
90225
91685
  try {
90226
- dbRun("INSERT INTO audit_log (tool_name, input_summary, result_summary, duration_ms, was_allowed) VALUES (?, ?, ?, ?, 1)", [tool.name, summary, block2.content.slice(0, 200), dur]);
90227
- } catch {
90228
- }
90229
- return block2.is_error ? { data: block2.content, error: block2.content, isError: true } : { data: block2.content };
90230
- } catch (err) {
90231
- const dur = performance.now() - t0;
90232
- const errMsg = err instanceof Error ? err.message : String(err);
90233
- try {
90234
- dbRun("INSERT INTO audit_log (tool_name, input_summary, result_summary, duration_ms, was_allowed) VALUES (?, ?, ?, ?, 1)", [tool.name, summary, errMsg.slice(0, 200), dur]);
90235
- } catch {
91686
+ const toolCtx = {
91687
+ abortController: new AbortController(),
91688
+ getAppState: () => appState,
91689
+ setAppState: (fn) => Object.assign(appState, fn(appState)),
91690
+ options: { mainLoopModel: "sonnet", thinkingConfig: { type: "disabled" }, isNonInteractiveSession: false, tools: [], agentDefinitions: { activeAgents: [] } }
91691
+ };
91692
+ const result = await tool.call(input, toolCtx);
91693
+ const block2 = tool.mapToolResultToToolResultBlockParam(result.data, ctx.toolUseId ?? "");
91694
+ const dur = performance.now() - t0;
91695
+ try {
91696
+ dbRun("INSERT INTO audit_log (tool_name, input_summary, result_summary, duration_ms, was_allowed) VALUES (?, ?, ?, ?, 1)", [tool.name, summary, block2.content.slice(0, 200), dur]);
91697
+ } catch {
91698
+ }
91699
+ return block2.is_error ? { data: block2.content, error: block2.content, isError: true } : { data: block2.content };
91700
+ } catch (err) {
91701
+ const dur = performance.now() - t0;
91702
+ const errMsg = err instanceof Error ? err.message : String(err);
91703
+ try {
91704
+ dbRun("INSERT INTO audit_log (tool_name, input_summary, result_summary, duration_ms, was_allowed) VALUES (?, ?, ?, ?, 1)", [tool.name, summary, errMsg.slice(0, 200), dur]);
91705
+ } catch {
91706
+ }
91707
+ return { error: errMsg, isError: true };
90236
91708
  }
90237
- return { error: errMsg, isError: true };
90238
91709
  }
91710
+ };
91711
+ all.push(handler);
91712
+ if (tool.shouldDefer) {
91713
+ deferredInfo.push({
91714
+ name: tool.name,
91715
+ description: TOOL_DESCRIPTIONS[tool.name] ?? tool.searchHint
91716
+ });
91717
+ deferredSchemas.set(tool.name, {
91718
+ name: tool.name,
91719
+ description: TOOL_DESCRIPTIONS[tool.name] ?? tool.searchHint,
91720
+ inputSchema: TOOL_JSON_SCHEMAS[tool.name] ?? { type: "object", properties: {} }
91721
+ });
91722
+ } else {
91723
+ immediate.push(handler);
90239
91724
  }
90240
- }));
91725
+ }
90241
91726
  if (mcpHandlers && mcpHandlers.length > 0) {
90242
- return [...builtinHandlers, ...mcpHandlers];
91727
+ for (const h of mcpHandlers) {
91728
+ immediate.push(h);
91729
+ all.push(h);
91730
+ }
90243
91731
  }
90244
- return builtinHandlers;
91732
+ return { immediate, all, deferredInfo, deferredSchemas };
90245
91733
  }
90246
91734
  function shortPath(p) {
90247
91735
  if (!p) return "";
@@ -90304,11 +91792,21 @@ function toolSummary(name, input) {
90304
91792
  case "ExitWorktree":
90305
91793
  return String(input.action ?? "");
90306
91794
  case "NotebookEdit":
90307
- return shortPath(String(input.notebook_path ?? ""));
91795
+ return `${input.command ?? ""} ${shortPath(String(input.notebook_path ?? ""))}`.trim();
90308
91796
  case "Config":
90309
91797
  return String(input.setting ?? "");
90310
91798
  case "SendMessage":
90311
91799
  return `to:${input.to ?? ""}`;
91800
+ case "TaskOutput":
91801
+ return String(input.task_id ?? "");
91802
+ case "TaskStop":
91803
+ return String(input.task_id ?? "");
91804
+ case "ListMcpResourcesTool":
91805
+ return "";
91806
+ case "ReadMcpResourceTool":
91807
+ return `${input.server_name ?? ""}:${input.uri ?? ""}`.slice(0, 60);
91808
+ case "Skill":
91809
+ return String(input.skill ?? "").slice(0, 50);
90312
91810
  default:
90313
91811
  return "";
90314
91812
  }
@@ -90317,6 +91815,83 @@ function SpinnerDot() {
90317
91815
  const f = useSpinner(true);
90318
91816
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "cyan", children: f });
90319
91817
  }
91818
+ function BackgroundTaskItem({ task }) {
91819
+ const spinner = useSpinner(task.status === "running");
91820
+ const icon = task.status === "running" ? spinner : task.status === "completed" ? "\u2713" : "\u2717";
91821
+ const color = task.status === "running" ? "yellow" : task.status === "completed" ? "green" : "red";
91822
+ const now = Date.now();
91823
+ const durationMs = task.endTime ? task.endTime - task.startTime : now - task.startTime;
91824
+ const durSec = Math.round(durationMs / 1e3);
91825
+ const durStr = durSec < 60 ? `${durSec}s` : `${Math.floor(durSec / 60)}m ${durSec % 60}s`;
91826
+ const desc = task.description.length > 40 ? task.description.slice(0, 37) + "..." : task.description;
91827
+ let detail = `${task.status === "running" ? "running" : task.status}`;
91828
+ detail += ` \xB7 ${durStr}`;
91829
+ if (task.exitCode !== void 0 && task.exitCode !== null && task.status !== "running") {
91830
+ detail += ` \xB7 exit ${task.exitCode}`;
91831
+ }
91832
+ if (task.progress?.tokenCount) {
91833
+ detail += ` \xB7 ${fmtTok(task.progress.tokenCount)} tokens`;
91834
+ }
91835
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { children: [
91836
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { color, children: [
91837
+ icon,
91838
+ " "
91839
+ ] }),
91840
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { bold: true, children: task.id }),
91841
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
91842
+ ": ",
91843
+ desc,
91844
+ " "
91845
+ ] }),
91846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { dimColor: true, children: [
91847
+ "(",
91848
+ detail,
91849
+ ")"
91850
+ ] })
91851
+ ] });
91852
+ }
91853
+ function BackgroundTaskList({ tasks: tasks2 }) {
91854
+ if (tasks2.length === 0) return null;
91855
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { flexDirection: "column", children: tasks2.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BackgroundTaskItem, { task: t }, t.id)) });
91856
+ }
91857
+ function useCustomStatusLine(config2, context) {
91858
+ const [output, setOutput] = (0, import_react22.useState)(null);
91859
+ const lastRunRef = (0, import_react22.useRef)(0);
91860
+ (0, import_react22.useEffect)(() => {
91861
+ if (!config2 || config2.type !== "command") {
91862
+ setOutput(null);
91863
+ return;
91864
+ }
91865
+ const runCommand = () => {
91866
+ try {
91867
+ const stdinData = JSON.stringify({
91868
+ model: context.model,
91869
+ cost: context.cost,
91870
+ tokens: context.tokens,
91871
+ cwd: process.cwd(),
91872
+ version: VERSION
91873
+ });
91874
+ const result = execSync4(config2.command, {
91875
+ timeout: 3e3,
91876
+ input: stdinData,
91877
+ encoding: "utf-8",
91878
+ stdio: ["pipe", "pipe", "pipe"]
91879
+ });
91880
+ const trimmed = result.trim();
91881
+ if (trimmed) {
91882
+ setOutput(config2.padding ? trimmed.padEnd(config2.padding) : trimmed);
91883
+ }
91884
+ } catch {
91885
+ setOutput(null);
91886
+ }
91887
+ lastRunRef.current = Date.now();
91888
+ };
91889
+ runCommand();
91890
+ const interval = setInterval(runCommand, 2e3);
91891
+ return () => clearInterval(interval);
91892
+ }, [config2?.command, config2?.padding, context.model, context.cost, context.tokens]);
91893
+ return output;
91894
+ }
90320
91895
  function ToolItem({ tool }) {
90321
91896
  const f = useSpinner(tool.status === "running");
90322
91897
  const icon = tool.status === "running" ? f : "\u25CF";
@@ -90427,19 +92002,24 @@ function MessageView({ msg }) {
90427
92002
  ] }) })
90428
92003
  ] });
90429
92004
  }
90430
- function StatusBar({ model, mode, cost, tokens, agentName, teamName, classification }) {
92005
+ function StatusBar({ model, mode, cost, tokens, agentName, teamName, classification, bgTaskCount, customStatusLine }) {
92006
+ if (customStatusLine) {
92007
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: customStatusLine }) });
92008
+ }
90431
92009
  const teamLabel = agentName && teamName ? ` \xB7 ${agentName}@${teamName}` : "";
90432
92010
  const intentLabel = classification && classification.intent !== "general" ? ` \xB7 [${classification.intent}]` : "";
92011
+ const bgLabel = bgTaskCount && bgTaskCount > 0 ? ` \xB7 ${bgTaskCount} background task${bgTaskCount > 1 ? "s" : ""}` : "";
90433
92012
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { dimColor: true, children: [
90434
92013
  model,
90435
- " \xB7 ",
92014
+ " \\u00B7 ",
90436
92015
  mode,
90437
- " \xB7 $",
92016
+ " \\u00B7 $",
90438
92017
  cost.toFixed(4),
90439
- " \xB7 ",
92018
+ " \\u00B7 ",
90440
92019
  fmtTok(tokens),
90441
92020
  teamLabel,
90442
- intentLabel
92021
+ intentLabel,
92022
+ bgLabel
90443
92023
  ] }) });
90444
92024
  }
90445
92025
  function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedSession, mcpToolHandlers, agentId, agentName, teamName }) {
@@ -90464,7 +92044,21 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90464
92044
  const _rows = stdout?.rows ?? 24;
90465
92045
  const [slashSelected, setSlashSelected] = (0, import_react22.useState)(0);
90466
92046
  const [lastClassification, setLastClassification] = (0, import_react22.useState)(null);
92047
+ const [bgTasks, setBgTasks] = (0, import_react22.useState)([]);
90467
92048
  const appSettings = getSettings();
92049
+ const statusLineConfig = appSettings.statusLine;
92050
+ const customStatusLine = useCustomStatusLine(statusLineConfig, { model, cost, tokens });
92051
+ (0, import_react22.useEffect)(() => {
92052
+ const poll = () => {
92053
+ const running = getRunningTasks();
92054
+ const recent = getRecentlyCompletedTasks(5e3);
92055
+ const all = [...running, ...recent];
92056
+ setBgTasks(all);
92057
+ };
92058
+ poll();
92059
+ const interval = setInterval(poll, 1e3);
92060
+ return () => clearInterval(interval);
92061
+ }, []);
90468
92062
  const thinkingConfig = (() => {
90469
92063
  if (appSettings.thinking?.enabled) {
90470
92064
  return appSettings.thinking.budgetTokens ? { type: "enabled", budget_tokens: appSettings.thinking.budgetTokens } : { type: "enabled" };
@@ -90581,6 +92175,42 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90581
92175
  ]);
90582
92176
  return;
90583
92177
  }
92178
+ if (r.action === "compact") {
92179
+ if (history.length === 0) {
92180
+ setMsgs((p) => [...p, { id: `s${Date.now()}`, role: "system", content: "Nothing to compact \u2014 conversation is empty.", timestamp: Date.now() }]);
92181
+ return;
92182
+ }
92183
+ setMsgs((p) => [...p, { id: `s${Date.now()}`, role: "system", content: "Compacting conversation...", timestamp: Date.now() }]);
92184
+ try {
92185
+ const client = getApiClient();
92186
+ const customInstructions = r.data;
92187
+ const summaryPrompt = customInstructions ? `Summarize the conversation so far in 2-3 concise sentences, focusing on: ${customInstructions}. Include key decisions, files changed, and current state.` : "Summarize the conversation so far in 2-3 concise sentences. Include key decisions, files changed, and current state of the work.";
92188
+ const summaryResponse = await client.createMessage({
92189
+ model,
92190
+ messages: [
92191
+ ...history,
92192
+ { role: "user", content: summaryPrompt }
92193
+ ],
92194
+ maxTokens: 1024
92195
+ });
92196
+ const summaryText = summaryResponse.content.filter((b) => b.type === "text").map((b) => b.text).join("\n").trim() || "Conversation compacted.";
92197
+ const summarySystemMsg = {
92198
+ id: `compact${Date.now()}`,
92199
+ role: "system",
92200
+ content: `[Compacted conversation summary]
92201
+ ${summaryText}`,
92202
+ timestamp: Date.now()
92203
+ };
92204
+ setMsgs([summarySystemMsg]);
92205
+ setHistory([
92206
+ { role: "user", content: "Here is a summary of our conversation so far:\n" + summaryText },
92207
+ { role: "assistant", content: "Understood. I have the context from our previous conversation. How can I help you continue?" }
92208
+ ]);
92209
+ } catch (e) {
92210
+ setMsgs((p) => [...p, { id: `e${Date.now()}`, role: "system", content: `Failed to compact: ${e instanceof Error ? e.message : String(e)}`, timestamp: Date.now() }]);
92211
+ }
92212
+ return;
92213
+ }
90584
92214
  if (r.output) setMsgs((p) => [...p, { id: `s${Date.now()}`, role: "system", content: r.output, timestamp: Date.now() }]);
90585
92215
  if (r.action === "exit") exit();
90586
92216
  if (r.action === "clear") {
@@ -90610,48 +92240,22 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90610
92240
  }
90611
92241
  const t0 = performance.now();
90612
92242
  try {
90613
- const allToolHandlers = createToolHandlers(mcpToolHandlers);
90614
- const permCtx = createDefaultPermissionContext();
92243
+ const toolSplit = createToolHandlers(mcpToolHandlers);
92244
+ const permCtx = createDefaultPermissionContext(getSettings());
90615
92245
  const toolStartTimes = /* @__PURE__ */ new Map();
92246
+ setDeferredToolSchemas([...toolSplit.deferredSchemas.values()]);
90616
92247
  const classification = classifyPrompt(text);
90617
92248
  setLastClassification(classification);
90618
- const builtinToolNames = /* @__PURE__ */ new Set([
90619
- "Bash",
90620
- "Read",
90621
- "Edit",
90622
- "Write",
90623
- "Glob",
90624
- "Grep",
90625
- "Agent",
90626
- "TaskCreate",
90627
- "TaskGet",
90628
- "TaskList",
90629
- "TaskUpdate",
90630
- "AskUserQuestion",
90631
- "WebSearch",
90632
- "WebFetch",
90633
- "LSP",
90634
- "EnterPlanMode",
90635
- "ExitPlanMode",
90636
- "ToolSearch",
90637
- "CronCreate",
90638
- "CronDelete",
90639
- "CronList",
90640
- "EnterWorktree",
90641
- "ExitWorktree",
90642
- "NotebookEdit",
90643
- "Config",
90644
- "SendMessage"
90645
- ]);
90646
- const toolHandlers = filterToolsByClassification(allToolHandlers, classification, builtinToolNames);
92249
+ const allBuiltinToolNames = new Set(toolSplit.all.map((h) => h.name));
92250
+ const toolHandlers = filterToolsByClassification(toolSplit.all, classification, allBuiltinToolNames);
90647
92251
  try {
90648
92252
  dbRun(
90649
92253
  "INSERT INTO audit_log (tool_name, input_summary, result_summary, duration_ms, was_allowed) VALUES (?, ?, ?, ?, 1)",
90650
- ["__classifier", text.slice(0, 200), `${classification.intent}: ${classification.reason} (${toolHandlers.length}/${allToolHandlers.length} tools)`, 0]
92254
+ ["__classifier", text.slice(0, 200), `${classification.intent}: ${classification.reason} (${toolHandlers.length}/${toolSplit.all.length} tools, ${toolSplit.deferredInfo.length} deferred)`, 0]
90651
92255
  );
90652
92256
  } catch {
90653
92257
  }
90654
- const builtinTools = [
92258
+ const immediateBuiltinTools = [
90655
92259
  bashTool,
90656
92260
  readTool,
90657
92261
  editTool,
@@ -90659,28 +92263,11 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90659
92263
  globTool,
90660
92264
  grepTool,
90661
92265
  agentTool,
90662
- taskCreateTool,
90663
- taskGetTool,
90664
- taskListTool,
90665
- taskUpdateTool,
90666
- askUserQuestionTool,
90667
- webSearchTool,
90668
- webFetchTool,
90669
- lspTool,
90670
- enterPlanModeTool,
90671
- exitPlanModeTool,
90672
92266
  toolSearchTool,
90673
- cronCreateTool,
90674
- cronDeleteTool,
90675
- cronListTool,
90676
- enterWorktreeTool,
90677
- exitWorktreeTool,
90678
- notebookEditTool,
90679
- configTool,
90680
- sendMessageTool
90681
- ];
92267
+ skillTool
92268
+ ].filter((t) => !t.shouldDefer);
90682
92269
  const toolPrompts = await Promise.all(
90683
- builtinTools.map(async (t) => ({ name: t.name, prompt: await t.prompt() }))
92270
+ immediateBuiltinTools.map(async (t) => ({ name: t.name, prompt: await t.prompt() }))
90684
92271
  );
90685
92272
  const turnAbort = new AbortController();
90686
92273
  abortRef.current = turnAbort;
@@ -90693,7 +92280,8 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90693
92280
  projectDir: process.cwd(),
90694
92281
  model,
90695
92282
  permissionMode: mode,
90696
- tools: toolPrompts
92283
+ tools: toolPrompts,
92284
+ deferredTools: toolSplit.deferredInfo
90697
92285
  }),
90698
92286
  tools: toolHandlers,
90699
92287
  model,
@@ -90709,7 +92297,7 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90709
92297
  }
90710
92298
  } catch {
90711
92299
  }
90712
- if (dangerouslySkipPermissions) {
92300
+ if (dangerouslySkipPermissions || permCtx.mode === "bypassPermissions") {
90713
92301
  return { behavior: "allow" };
90714
92302
  }
90715
92303
  const ruleResult = checkToolPermission(toolName, toolInput, permCtx);
@@ -90956,6 +92544,7 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90956
92544
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: "]lways" })
90957
92545
  ] })
90958
92546
  ] }),
92547
+ bgTasks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BackgroundTaskList, { tasks: bgTasks }),
90959
92548
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", children: [
90960
92549
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: sep }),
90961
92550
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { children: [
@@ -90987,7 +92576,7 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90987
92576
  ] })
90988
92577
  ] }, cmd.name)) }),
90989
92578
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: sep }),
90990
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusBar, { model, mode, cost, tokens, agentName: agentName ?? agentId, teamName, classification: lastClassification })
92579
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusBar, { model, mode, cost, tokens, agentName: agentName ?? agentId, teamName, classification: lastClassification, bgTaskCount: bgTasks.filter((t) => t.status === "running").length, customStatusLine })
90991
92580
  ] })
90992
92581
  ] })
90993
92582
  ] });
@@ -91110,6 +92699,7 @@ var init_app = __esm({
91110
92699
  init_agent_loop();
91111
92700
  init_permissions();
91112
92701
  init_db();
92702
+ init_background_tasks();
91113
92703
  init_session();
91114
92704
  init_system_prompt();
91115
92705
  init_bash();
@@ -91120,17 +92710,21 @@ var init_app = __esm({
91120
92710
  init_grep();
91121
92711
  init_agent();
91122
92712
  init_tasks();
92713
+ init_task_output();
91123
92714
  init_ask_user();
91124
92715
  init_web_search();
91125
92716
  init_web_fetch();
91126
92717
  init_lsp();
91127
92718
  init_plan_mode();
91128
92719
  init_misc();
92720
+ init_mcp_resources();
91129
92721
  init_registry();
91130
92722
  init_init();
91131
92723
  init_client4();
91132
92724
  init_team();
91133
92725
  init_classifier();
92726
+ init_skill();
92727
+ init_registry2();
91134
92728
  import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
91135
92729
  CONN = "\u23BF";
91136
92730
  PROMPT = "\u276F";
@@ -91366,10 +92960,13 @@ var init_app = __esm({
91366
92960
  type: "object",
91367
92961
  properties: {
91368
92962
  notebook_path: { type: "string", description: "Path to the .ipynb file" },
91369
- cell_index: { type: "number", description: "Cell index to edit" },
91370
- new_source: { type: "string", description: "New cell source code" }
92963
+ command: { type: "string", enum: ["insert_cell", "replace_cell", "delete_cell", "move_cell", "change_cell_type"], description: "Operation to perform on the notebook" },
92964
+ cell_index: { type: "number", description: "Cell index to operate on (0-based). For insert_cell, position to insert at (omit to append)." },
92965
+ cell_type: { type: "string", enum: ["code", "markdown", "raw"], description: "Cell type \u2014 required for insert_cell and change_cell_type" },
92966
+ new_source: { type: "string", description: "New cell content \u2014 required for insert_cell and replace_cell" },
92967
+ target_index: { type: "number", description: "Destination index for move_cell" }
91371
92968
  },
91372
- required: ["notebook_path", "cell_index", "new_source"]
92969
+ required: ["notebook_path", "command"]
91373
92970
  },
91374
92971
  Config: {
91375
92972
  type: "object",
@@ -91386,6 +92983,40 @@ var init_app = __esm({
91386
92983
  message: { type: "string", description: "Message content" }
91387
92984
  },
91388
92985
  required: ["to", "message"]
92986
+ },
92987
+ TaskOutput: {
92988
+ type: "object",
92989
+ properties: {
92990
+ task_id: { type: "string", description: "The ID of the background task to check (e.g. 'bg-1', 'agent-2')" }
92991
+ },
92992
+ required: ["task_id"]
92993
+ },
92994
+ TaskStop: {
92995
+ type: "object",
92996
+ properties: {
92997
+ task_id: { type: "string", description: "The ID of the background task to stop (e.g. 'bg-1', 'agent-2')" }
92998
+ },
92999
+ required: ["task_id"]
93000
+ },
93001
+ ListMcpResourcesTool: {
93002
+ type: "object",
93003
+ properties: {}
93004
+ },
93005
+ ReadMcpResourceTool: {
93006
+ type: "object",
93007
+ properties: {
93008
+ server_name: { type: "string", description: "The name of the MCP server that owns the resource" },
93009
+ uri: { type: "string", description: "The URI of the resource to read" }
93010
+ },
93011
+ required: ["server_name", "uri"]
93012
+ },
93013
+ Skill: {
93014
+ type: "object",
93015
+ properties: {
93016
+ skill: { type: "string", description: "The skill name to invoke (e.g. 'commit', 'review-pr')" },
93017
+ args: { type: "string", description: "Optional arguments to pass to the skill prompt" }
93018
+ },
93019
+ required: ["skill"]
91389
93020
  }
91390
93021
  };
91391
93022
  TOOL_DESCRIPTIONS = {
@@ -91412,9 +93043,14 @@ var init_app = __esm({
91412
93043
  CronList: "List all active scheduled cron jobs.",
91413
93044
  EnterWorktree: "Create an isolated git worktree and switch into it.",
91414
93045
  ExitWorktree: "Exit a worktree session, keeping or removing the worktree.",
91415
- NotebookEdit: "Edit a specific cell in a Jupyter notebook (.ipynb file).",
93046
+ NotebookEdit: "Edit Jupyter notebook cells \u2014 insert, replace, delete, move cells, or change cell type. Preserves all metadata and outputs.",
91416
93047
  Config: "Get or set a configuration setting.",
91417
- SendMessage: "Send a direct message to another agent or to the user."
93048
+ SendMessage: "Send a direct message to another agent or to the user.",
93049
+ TaskOutput: "Check the status and output of a background task (bash or agent).",
93050
+ TaskStop: "Stop a running background task by its ID.",
93051
+ ListMcpResourcesTool: "List all resources available from connected MCP servers.",
93052
+ ReadMcpResourceTool: "Read a resource from a connected MCP server by server name and URI.",
93053
+ Skill: "Execute a skill within the main conversation. Skills are user-defined prompts in .coders/skills/ or .claude/skills/ directories."
91418
93054
  };
91419
93055
  }
91420
93056
  });
@@ -92233,16 +93869,16 @@ __export(marketplace_exports, {
92233
93869
  removeMarketplace: () => removeMarketplace,
92234
93870
  uninstallPlugin: () => uninstallPlugin
92235
93871
  });
92236
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync15, mkdirSync as mkdirSync8, cpSync, rmSync } from "fs";
92237
- import { join as join9, resolve as resolve7 } from "path";
92238
- import { execSync as execSync3 } from "child_process";
93872
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, existsSync as existsSync17, mkdirSync as mkdirSync8, cpSync, rmSync } from "fs";
93873
+ import { join as join11, resolve as resolve7 } from "path";
93874
+ import { execSync as execSync5 } from "child_process";
92239
93875
  import { tmpdir } from "os";
92240
93876
  import { randomBytes } from "crypto";
92241
93877
  function getMarketplaces() {
92242
93878
  const path = getMarketplacesConfigPath();
92243
- if (!existsSync15(path)) return [];
93879
+ if (!existsSync17(path)) return [];
92244
93880
  try {
92245
- return JSON.parse(readFileSync13(path, "utf-8"));
93881
+ return JSON.parse(readFileSync14(path, "utf-8"));
92246
93882
  } catch {
92247
93883
  return [];
92248
93884
  }
@@ -92262,8 +93898,8 @@ function removeMarketplace(name) {
92262
93898
  }
92263
93899
  function saveMarketplaces(marketplaces) {
92264
93900
  const path = getMarketplacesConfigPath();
92265
- const dir = join9(path, "..");
92266
- if (!existsSync15(dir)) mkdirSync8(dir, { recursive: true });
93901
+ const dir = join11(path, "..");
93902
+ if (!existsSync17(dir)) mkdirSync8(dir, { recursive: true });
92267
93903
  writeFileSync9(path, JSON.stringify(marketplaces, null, 2) + "\n", "utf-8");
92268
93904
  }
92269
93905
  async function installFromMarketplace(pluginName, marketplaceName) {
@@ -92287,7 +93923,7 @@ async function installPluginFromMarketplace(name, marketplace) {
92287
93923
  return installFromGit(gitUrl, pluginsDir);
92288
93924
  }
92289
93925
  case "directory": {
92290
- const sourcePath = join9(marketplace.source, name);
93926
+ const sourcePath = join11(marketplace.source, name);
92291
93927
  return installFromDirectory(sourcePath, pluginsDir);
92292
93928
  }
92293
93929
  case "url":
@@ -92306,10 +93942,10 @@ function installFromSource(source, pluginsDir) {
92306
93942
  }
92307
93943
  function installFromGit(url2, pluginsDir) {
92308
93944
  const dir = pluginsDir ?? getPluginsDir();
92309
- const tempDir = join9(tmpdir(), `coders-plugin-${randomBytes(8).toString("hex")}`);
93945
+ const tempDir = join11(tmpdir(), `coders-plugin-${randomBytes(8).toString("hex")}`);
92310
93946
  try {
92311
93947
  try {
92312
- execSync3(`git clone --depth 1 ${escapeShellArg(url2)} ${escapeShellArg(tempDir)}`, {
93948
+ execSync5(`git clone --depth 1 ${escapeShellArg(url2)} ${escapeShellArg(tempDir)}`, {
92313
93949
  encoding: "utf-8",
92314
93950
  stdio: "pipe",
92315
93951
  timeout: 6e4
@@ -92320,21 +93956,21 @@ function installFromGit(url2, pluginsDir) {
92320
93956
  error: `Failed to clone "${url2}": ${err instanceof Error ? err.message : String(err)}`
92321
93957
  };
92322
93958
  }
92323
- const manifestPath = join9(tempDir, "manifest.json");
92324
- if (!existsSync15(manifestPath)) {
93959
+ const manifestPath = join11(tempDir, "manifest.json");
93960
+ if (!existsSync17(manifestPath)) {
92325
93961
  return { success: false, error: `No manifest.json found in cloned repository "${url2}"` };
92326
93962
  }
92327
93963
  const validated = validateManifest(manifestPath);
92328
93964
  if (!validated.success) return validated;
92329
93965
  const pluginName = validated.pluginName;
92330
- const targetDir = join9(dir, pluginName);
92331
- if (existsSync15(targetDir)) {
93966
+ const targetDir = join11(dir, pluginName);
93967
+ if (existsSync17(targetDir)) {
92332
93968
  return { success: false, error: `Plugin "${pluginName}" is already installed` };
92333
93969
  }
92334
93970
  mkdirSync8(targetDir, { recursive: true });
92335
93971
  cpSync(tempDir, targetDir, {
92336
93972
  recursive: true,
92337
- filter: (src) => !src.includes(join9(tempDir, ".git"))
93973
+ filter: (src) => !src.includes(join11(tempDir, ".git"))
92338
93974
  });
92339
93975
  return {
92340
93976
  success: true,
@@ -92352,18 +93988,18 @@ function installFromGit(url2, pluginsDir) {
92352
93988
  function installFromDirectory(sourcePath, pluginsDir) {
92353
93989
  const dir = pluginsDir ?? getPluginsDir();
92354
93990
  const resolvedSource = resolve7(sourcePath);
92355
- if (!existsSync15(resolvedSource)) {
93991
+ if (!existsSync17(resolvedSource)) {
92356
93992
  return { success: false, error: `Source directory does not exist: "${resolvedSource}"` };
92357
93993
  }
92358
- const manifestPath = join9(resolvedSource, "manifest.json");
92359
- if (!existsSync15(manifestPath)) {
93994
+ const manifestPath = join11(resolvedSource, "manifest.json");
93995
+ if (!existsSync17(manifestPath)) {
92360
93996
  return { success: false, error: `No manifest.json found in "${resolvedSource}"` };
92361
93997
  }
92362
93998
  const validated = validateManifest(manifestPath);
92363
93999
  if (!validated.success) return validated;
92364
94000
  const pluginName = validated.pluginName;
92365
- const targetDir = join9(dir, pluginName);
92366
- if (existsSync15(targetDir)) {
94001
+ const targetDir = join11(dir, pluginName);
94002
+ if (existsSync17(targetDir)) {
92367
94003
  return { success: false, error: `Plugin "${pluginName}" is already installed` };
92368
94004
  }
92369
94005
  mkdirSync8(targetDir, { recursive: true });
@@ -92377,8 +94013,8 @@ function installFromDirectory(sourcePath, pluginsDir) {
92377
94013
  }
92378
94014
  function uninstallPlugin(name, pluginsDir) {
92379
94015
  const dir = pluginsDir ?? getPluginsDir();
92380
- const targetDir = join9(dir, name);
92381
- if (!existsSync15(targetDir)) return false;
94016
+ const targetDir = join11(dir, name);
94017
+ if (!existsSync17(targetDir)) return false;
92382
94018
  rmSync(targetDir, { recursive: true, force: true });
92383
94019
  return true;
92384
94020
  }
@@ -92387,7 +94023,7 @@ function isGitUrl(source) {
92387
94023
  }
92388
94024
  function validateManifest(manifestPath) {
92389
94025
  try {
92390
- const raw = JSON.parse(readFileSync13(manifestPath, "utf-8"));
94026
+ const raw = JSON.parse(readFileSync14(manifestPath, "utf-8"));
92391
94027
  const result = PluginManifestSchema.safeParse(raw);
92392
94028
  if (!result.success) {
92393
94029
  const issues = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
@@ -92421,8 +94057,8 @@ var main_exports = {};
92421
94057
  __export(main_exports, {
92422
94058
  main: () => main
92423
94059
  });
92424
- import { execSync as execSync4 } from "child_process";
92425
- import { existsSync as existsSync16 } from "fs";
94060
+ import { execSync as execSync6 } from "child_process";
94061
+ import { existsSync as existsSync18 } from "fs";
92426
94062
  function helpConfig() {
92427
94063
  return {
92428
94064
  sortSubcommands: true,
@@ -92856,15 +94492,15 @@ ${installed.length} plugin(s) installed`);
92856
94492
  }
92857
94493
  });
92858
94494
  plugin.command("install <plugin>").alias("i").description("Install a plugin from a marketplace or local path").option("-s, --scope <scope>", "Installation scope", "user").option("-m, --marketplace <name>", "Marketplace to install from").action(async (source, options2) => {
92859
- const { resolve: resolve8, join: join10 } = await import("path");
94495
+ const { resolve: resolve8, join: join12 } = await import("path");
92860
94496
  const { existsSync: pathExists } = await import("fs");
92861
94497
  const resolvedPath = resolve8(source);
92862
- const isLocalDir = pathExists(resolvedPath) && pathExists(join10(resolvedPath, "manifest.json"));
94498
+ const isLocalDir = pathExists(resolvedPath) && pathExists(join12(resolvedPath, "manifest.json"));
92863
94499
  if (isLocalDir) {
92864
94500
  try {
92865
- const { readFileSync: readFileSync14, cpSync: cpSync2, mkdirSync: mkdirSync9 } = await import("fs");
94501
+ const { readFileSync: readFileSync15, cpSync: cpSync2, mkdirSync: mkdirSync9 } = await import("fs");
92866
94502
  const { PluginManifestSchema: PluginManifestSchema2 } = await Promise.resolve().then(() => (init_manifest(), manifest_exports));
92867
- const manifestRaw = JSON.parse(readFileSync14(join10(resolvedPath, "manifest.json"), "utf-8"));
94503
+ const manifestRaw = JSON.parse(readFileSync15(join12(resolvedPath, "manifest.json"), "utf-8"));
92868
94504
  const result = PluginManifestSchema2.safeParse(manifestRaw);
92869
94505
  if (!result.success) {
92870
94506
  console.error("Error: Invalid plugin manifest:");
@@ -92875,7 +94511,7 @@ ${installed.length} plugin(s) installed`);
92875
94511
  }
92876
94512
  const pluginName = result.data.name;
92877
94513
  const pluginsDir = getPluginsDir();
92878
- const targetDir = join10(pluginsDir, pluginName);
94514
+ const targetDir = join12(pluginsDir, pluginName);
92879
94515
  if (pathExists(targetDir)) {
92880
94516
  console.error(`Error: Plugin "${pluginName}" is already installed.`);
92881
94517
  console.error("Uninstall it first: coders plugin uninstall " + pluginName);
@@ -92922,13 +94558,13 @@ ${installed.length} plugin(s) installed`);
92922
94558
  });
92923
94559
  plugin.command("validate <path>").description("Validate a plugin manifest").option("--json", "Output validation result as JSON").action(async (manifestPath, options2) => {
92924
94560
  try {
92925
- const { existsSync: pathExists, readFileSync: readFileSync14 } = await import("fs");
92926
- const { resolve: resolve8, join: join10 } = await import("path");
94561
+ const { existsSync: pathExists, readFileSync: readFileSync15 } = await import("fs");
94562
+ const { resolve: resolve8, join: join12 } = await import("path");
92927
94563
  const { PluginManifestSchema: PluginManifestSchema2 } = await Promise.resolve().then(() => (init_manifest(), manifest_exports));
92928
94564
  const resolvedPath = resolve8(manifestPath);
92929
94565
  let filePath;
92930
- if (pathExists(join10(resolvedPath, "manifest.json"))) {
92931
- filePath = join10(resolvedPath, "manifest.json");
94566
+ if (pathExists(join12(resolvedPath, "manifest.json"))) {
94567
+ filePath = join12(resolvedPath, "manifest.json");
92932
94568
  } else if (pathExists(resolvedPath)) {
92933
94569
  filePath = resolvedPath;
92934
94570
  } else {
@@ -92938,7 +94574,7 @@ ${installed.length} plugin(s) installed`);
92938
94574
  }
92939
94575
  let raw;
92940
94576
  try {
92941
- raw = JSON.parse(readFileSync14(filePath, "utf-8"));
94577
+ raw = JSON.parse(readFileSync15(filePath, "utf-8"));
92942
94578
  } catch {
92943
94579
  console.error(`Error: Could not parse JSON from ${filePath}`);
92944
94580
  process.exit(1);
@@ -93035,8 +94671,8 @@ ${installed.length} plugin(s) installed`);
93035
94671
  try {
93036
94672
  const userSettingsPath = getUserSettingsPath();
93037
94673
  const projectSettingsPath = getProjectSettingsPath(process.cwd());
93038
- const userExists = existsSync16(userSettingsPath);
93039
- const projectExists = existsSync16(projectSettingsPath);
94674
+ const userExists = existsSync18(userSettingsPath);
94675
+ const projectExists = existsSync18(projectSettingsPath);
93040
94676
  const parts = [];
93041
94677
  if (userExists) parts.push("user");
93042
94678
  if (projectExists) parts.push("project");
@@ -93067,7 +94703,7 @@ ${installed.length} plugin(s) installed`);
93067
94703
  issues++;
93068
94704
  }
93069
94705
  try {
93070
- const gitVersion = execSync4("git --version", { encoding: "utf-8", timeout: 5e3 }).trim();
94706
+ const gitVersion = execSync6("git --version", { encoding: "utf-8", timeout: 5e3 }).trim();
93071
94707
  console.log(` ${ok} Git: ${gitVersion.replace("git version ", "")}`);
93072
94708
  } catch {
93073
94709
  console.log(` ${fail} Git: not found in PATH`);
@@ -93075,7 +94711,7 @@ ${installed.length} plugin(s) installed`);
93075
94711
  }
93076
94712
  try {
93077
94713
  const pluginsDir = getPluginsDir();
93078
- const dirExists = existsSync16(pluginsDir);
94714
+ const dirExists = existsSync18(pluginsDir);
93079
94715
  if (dirExists) {
93080
94716
  const discovered = discoverPlugins();
93081
94717
  if (discovered.length > 0) {
@@ -93094,7 +94730,7 @@ ${installed.length} plugin(s) installed`);
93094
94730
  issues++;
93095
94731
  }
93096
94732
  try {
93097
- const rgVersion = execSync4("rg --version", { encoding: "utf-8", timeout: 5e3 }).split("\n")[0].trim();
94733
+ const rgVersion = execSync6("rg --version", { encoding: "utf-8", timeout: 5e3 }).split("\n")[0].trim();
93098
94734
  console.log(` ${ok} Ripgrep: ${rgVersion.replace("ripgrep ", "")}`);
93099
94735
  } catch {
93100
94736
  console.log(` ${fail} Ripgrep: not found in PATH (needed for Grep tool)`);
@@ -93113,7 +94749,7 @@ ${installed.length} plugin(s) installed`);
93113
94749
  console.log("Checking for updates...\n");
93114
94750
  let latestVersion;
93115
94751
  try {
93116
- latestVersion = execSync4(`npm view ${PACKAGE_NAME} version`, {
94752
+ latestVersion = execSync6(`npm view ${PACKAGE_NAME} version`, {
93117
94753
  encoding: "utf-8",
93118
94754
  timeout: 15e3,
93119
94755
  stdio: ["pipe", "pipe", "pipe"]
@@ -93157,7 +94793,7 @@ ${installed.length} plugin(s) installed`);
93157
94793
  }
93158
94794
  let installCmd;
93159
94795
  try {
93160
- execSync4("bun --version", { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] });
94796
+ execSync6("bun --version", { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] });
93161
94797
  installCmd = `bun install -g ${PACKAGE_NAME}@latest`;
93162
94798
  } catch {
93163
94799
  installCmd = `npm install -g ${PACKAGE_NAME}@latest`;
@@ -93165,7 +94801,7 @@ ${installed.length} plugin(s) installed`);
93165
94801
  console.log(`Running: ${installCmd}
93166
94802
  `);
93167
94803
  try {
93168
- execSync4(installCmd, { encoding: "utf-8", timeout: 12e4, stdio: "inherit" });
94804
+ execSync6(installCmd, { encoding: "utf-8", timeout: 12e4, stdio: "inherit" });
93169
94805
  console.log(`
93170
94806
  Successfully updated to v${latestVersion}.`);
93171
94807
  } catch (err) {
@@ -93433,7 +95069,7 @@ var VERSION, BUILD_TIME, PACKAGE_NAME, ISSUES_URL, startupTimestamps, originalCw
93433
95069
  var init_index = __esm({
93434
95070
  "src/cli/index.ts"() {
93435
95071
  VERSION = "0.1.2";
93436
- BUILD_TIME = "2026-03-20T18:42:45.981Z";
95072
+ BUILD_TIME = "2026-03-21T05:56:28.429Z";
93437
95073
  PACKAGE_NAME = "@hasna/coders";
93438
95074
  ISSUES_URL = "https://github.com/hasnaxyz/open-coders/issues";
93439
95075
  startupTimestamps = {};