@hasna/coders 0.1.8 → 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}
@@ -62587,29 +63089,40 @@ function renderBlockquote(token) {
62587
63089
  }
62588
63090
  function renderTable(token, maxWidth) {
62589
63091
  const lines = [];
62590
- const colWidths = token.header.map((h) => h.text.length);
63092
+ const stripMd = (s) => s.replace(/\*\*(.+?)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
63093
+ const colWidths = token.header.map((h) => stripMd(h.text).length);
62591
63094
  for (const row of token.rows) {
62592
63095
  for (let i = 0; i < row.length; i++) {
62593
- colWidths[i] = Math.max(colWidths[i] ?? 0, row[i].text.length);
63096
+ colWidths[i] = Math.max(colWidths[i] ?? 0, stripMd(row[i].text).length);
62594
63097
  }
62595
63098
  }
62596
- const totalWidth = colWidths.reduce((a, b) => a + b, 0) + colWidths.length * 3 + 1;
62597
- if (totalWidth > maxWidth) {
62598
- const scale = maxWidth / totalWidth;
63099
+ const overhead = colWidths.length * 3 + 1;
63100
+ const maxTable = maxWidth - 4;
63101
+ const totalWidth = colWidths.reduce((a, b) => a + b, 0) + overhead;
63102
+ if (totalWidth > maxTable) {
63103
+ const available = maxTable - overhead;
63104
+ const total = colWidths.reduce((a, b) => a + b, 0);
62599
63105
  for (let i = 0; i < colWidths.length; i++) {
62600
- colWidths[i] = Math.max(3, Math.floor(colWidths[i] * scale));
63106
+ colWidths[i] = Math.max(4, Math.floor(colWidths[i] / total * available));
62601
63107
  }
62602
63108
  }
62603
- const headerLine = token.header.map((h, i) => pad(h.text, colWidths[i])).join(` ${FG_GRAY}\u2502${FG_DEFAULT} `);
63109
+ const headerLine = token.header.map((h, i) => padRendered(renderInline(h.text), stripMd(h.text), colWidths[i])).join(` ${FG_GRAY}\u2502${FG_DEFAULT} `);
62604
63110
  lines.push(`${BOLD}${headerLine}${BOLD_OFF}`);
62605
63111
  const sepLine = colWidths.map((w) => "\u2500".repeat(w)).join(`\u2500\u253C\u2500`);
62606
63112
  lines.push(`${FG_GRAY}${sepLine}${FG_DEFAULT}`);
62607
63113
  for (const row of token.rows) {
62608
- const rowLine = row.map((cell, i) => pad(cell.text, colWidths[i])).join(` ${FG_GRAY}\u2502${FG_DEFAULT} `);
63114
+ const rowLine = row.map((cell, i) => padRendered(renderInline(cell.text), stripMd(cell.text), colWidths[i])).join(` ${FG_GRAY}\u2502${FG_DEFAULT} `);
62609
63115
  lines.push(rowLine);
62610
63116
  }
62611
63117
  return lines;
62612
63118
  }
63119
+ function padRendered(rendered, plain, width) {
63120
+ if (plain.length >= width) {
63121
+ const truncPlain = plain.slice(0, width - 1) + "\u2026";
63122
+ return renderInline(truncPlain);
63123
+ }
63124
+ return rendered + " ".repeat(width - plain.length);
63125
+ }
62613
63126
  function renderInline(text) {
62614
63127
  return renderInlineWithRestore(text, "");
62615
63128
  }
@@ -62641,10 +63154,6 @@ function highlightSyntax(line, lang) {
62641
63154
  result = result.replace(/\b(\d+\.?\d*)\b/g, `${FG_YELLOW}$1${FG_DEFAULT}`);
62642
63155
  return result;
62643
63156
  }
62644
- function pad(text, width) {
62645
- if (text.length >= width) return text.slice(0, width);
62646
- return text + " ".repeat(width - text.length);
62647
- }
62648
63157
  var ESC2, RESET, BOLD, BOLD_OFF, DIM, DIM_OFF, ITALIC, ITALIC_OFF, UNDERLINE, UNDERLINE_OFF, STRIKETHROUGH, STRIKE_OFF, FG_CYAN, FG_YELLOW, FG_GREEN, FG_BLUE, FG_MAGENTA, FG_GRAY, FG_DEFAULT, BG_GRAY, BG_DEFAULT;
62649
63158
  var init_markdown = __esm({
62650
63159
  "src/ui/components/markdown.tsx"() {
@@ -63016,8 +63525,8 @@ var init_permissions = __esm({
63016
63525
  });
63017
63526
 
63018
63527
  // src/memory/files.ts
63019
- import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
63020
- import { join as join6 } from "path";
63528
+ import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
63529
+ import { join as join8 } from "path";
63021
63530
  function getInstructionsContent(projectDir) {
63022
63531
  const cached2 = _cache.get(projectDir);
63023
63532
  if (cached2 !== void 0) return cached2 || null;
@@ -63027,7 +63536,7 @@ function getInstructionsContent(projectDir) {
63027
63536
  return null;
63028
63537
  }
63029
63538
  try {
63030
- let content = readFileSync7(filePath, "utf-8");
63539
+ let content = readFileSync8(filePath, "utf-8");
63031
63540
  content = stripHtmlComments(content);
63032
63541
  _cache.set(projectDir, content);
63033
63542
  return content;
@@ -63038,11 +63547,11 @@ function getInstructionsContent(projectDir) {
63038
63547
  }
63039
63548
  function getGlobalInstructions() {
63040
63549
  const configDir = getConfigDir();
63041
- const primary = join6(configDir, "CODERS.md");
63042
- const path = existsSync8(primary) ? primary : null;
63550
+ const primary = join8(configDir, "CODERS.md");
63551
+ const path = existsSync10(primary) ? primary : null;
63043
63552
  if (!path) return null;
63044
63553
  try {
63045
- return stripHtmlComments(readFileSync7(path, "utf-8"));
63554
+ return stripHtmlComments(readFileSync8(path, "utf-8"));
63046
63555
  } catch {
63047
63556
  return null;
63048
63557
  }
@@ -63057,13 +63566,13 @@ ${global2}`);
63057
63566
  if (project) parts.push(`# Project Instructions
63058
63567
 
63059
63568
  ${project}`);
63060
- const rulesDir = join6(projectDir, ".coders", "rules");
63061
- if (existsSync8(rulesDir)) {
63569
+ const rulesDir = join8(projectDir, ".coders", "rules");
63570
+ if (existsSync10(rulesDir)) {
63062
63571
  try {
63063
- const { readdirSync: readdirSync2 } = __require("fs");
63064
- for (const file of readdirSync2(rulesDir)) {
63572
+ const { readdirSync: readdirSync4 } = __require("fs");
63573
+ for (const file of readdirSync4(rulesDir)) {
63065
63574
  if (!file.endsWith(".md")) continue;
63066
- const content = readFileSync7(join6(rulesDir, file), "utf-8");
63575
+ const content = readFileSync8(join8(rulesDir, file), "utf-8");
63067
63576
  parts.push(`# Rule: ${file}
63068
63577
 
63069
63578
  ${stripHtmlComments(content)}`);
@@ -63085,6 +63594,257 @@ var init_files = __esm({
63085
63594
  }
63086
63595
  });
63087
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
+
63088
63848
  // src/core/system-prompt.ts
63089
63849
  function buildSystemPrompt(ctx) {
63090
63850
  const sections = [];
@@ -63113,6 +63873,28 @@ ${t.prompt}`).join("\n\n");
63113
63873
  ${toolSection}`);
63114
63874
  }
63115
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
+ }
63116
63898
  const ctxParts = [];
63117
63899
  ctxParts.push(`Working directory: ${ctx.projectDir}`);
63118
63900
  ctxParts.push(`Model: ${ctx.model}`);
@@ -63140,6 +63922,7 @@ var init_system_prompt = __esm({
63140
63922
  "src/core/system-prompt.ts"() {
63141
63923
  "use strict";
63142
63924
  init_files();
63925
+ init_skill();
63143
63926
  CORE_INSTRUCTIONS = `You are Coders, an open-source interactive CLI agent built by Hasna for software engineering.
63144
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.
63145
63928
 
@@ -63183,44 +63966,6 @@ You are an interactive agent that helps users with software engineering tasks. U
63183
63966
  }
63184
63967
  });
63185
63968
 
63186
- // src/core/constants.ts
63187
- 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;
63188
- var init_constants = __esm({
63189
- "src/core/constants.ts"() {
63190
- "use strict";
63191
- BASH_TOOL = "Bash";
63192
- READ_TOOL = "Read";
63193
- EDIT_TOOL = "Edit";
63194
- WRITE_TOOL = "Write";
63195
- GLOB_TOOL = "Glob";
63196
- GREP_TOOL = "Grep";
63197
- AGENT_TOOL = "Agent";
63198
- WEB_FETCH_TOOL = "WebFetch";
63199
- WEB_SEARCH_TOOL = "WebSearch";
63200
- NOTEBOOK_EDIT_TOOL = "NotebookEdit";
63201
- LSP_TOOL = "LSP";
63202
- TASK_CREATE_TOOL = "TaskCreate";
63203
- TASK_GET_TOOL = "TaskGet";
63204
- TASK_LIST_TOOL = "TaskList";
63205
- TASK_UPDATE_TOOL = "TaskUpdate";
63206
- ENTER_PLAN_MODE_TOOL = "EnterPlanMode";
63207
- EXIT_PLAN_MODE_TOOL = "ExitPlanMode";
63208
- ASK_USER_QUESTION_TOOL = "AskUserQuestion";
63209
- CRON_CREATE_TOOL = "CronCreate";
63210
- CRON_DELETE_TOOL = "CronDelete";
63211
- CRON_LIST_TOOL = "CronList";
63212
- ENTER_WORKTREE_TOOL = "EnterWorktree";
63213
- EXIT_WORKTREE_TOOL = "ExitWorktree";
63214
- TOOL_SEARCH_TOOL = "ToolSearch";
63215
- SEND_MESSAGE_TOOL = "SendMessage";
63216
- CONFIG_TOOL = "Config";
63217
- DEFAULT_BASH_TIMEOUT_MS = 12e4;
63218
- MAX_BASH_TIMEOUT_MS = 6e5;
63219
- DEFAULT_READ_LINE_LIMIT = 2e3;
63220
- DEFAULT_MAX_RESULT_SIZE_CHARS = 1e5;
63221
- }
63222
- });
63223
-
63224
63969
  // src/tools/builtin/bash.ts
63225
63970
  import { spawn } from "child_process";
63226
63971
  function isDangerousCommand(command) {
@@ -63335,13 +64080,14 @@ function truncateOutput(output, maxChars = DEFAULT_MAX_RESULT_SIZE_CHARS) {
63335
64080
 
63336
64081
  ` + output.slice(-half);
63337
64082
  }
63338
- 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;
63339
64084
  var init_bash = __esm({
63340
64085
  "src/tools/builtin/bash.ts"() {
63341
64086
  "use strict";
63342
64087
  init_zod();
63343
64088
  init_constants();
63344
64089
  init_db();
64090
+ init_background_tasks();
63345
64091
  DANGEROUS_PATTERNS = [
63346
64092
  { pattern: /\brm\s+-rf\s+\/\s*$/, reason: "Recursive delete of root filesystem" },
63347
64093
  { pattern: /\brm\s+-rf\s+~\//, reason: "Recursive delete of home directory" },
@@ -63534,7 +64280,6 @@ var init_bash = __esm({
63534
64280
  "version"
63535
64281
  ]);
63536
64282
  backgroundTasks = /* @__PURE__ */ new Map();
63537
- nextBgId = 1;
63538
64283
  bashTool = {
63539
64284
  name: BASH_TOOL,
63540
64285
  searchHint: "execute shell commands in the terminal",
@@ -63605,7 +64350,8 @@ var init_bash = __esm({
63605
64350
  const timeoutMs = timeout ?? getDefaultTimeout();
63606
64351
  const startTime = performance.now();
63607
64352
  if (run_in_background) {
63608
- const taskId = `bg-${nextBgId++}`;
64353
+ const bgTask = createTask("bash", command);
64354
+ const taskId = bgTask.id;
63609
64355
  const child = spawn(command, [], {
63610
64356
  shell: getShell(),
63611
64357
  cwd: process.cwd(),
@@ -63614,10 +64360,14 @@ var init_bash = __esm({
63614
64360
  });
63615
64361
  let output = "";
63616
64362
  child.stdout?.on("data", (data) => {
63617
- output += data.toString();
64363
+ const chunk = data.toString();
64364
+ output += chunk;
64365
+ writeTaskOutput(taskId, chunk);
63618
64366
  });
63619
64367
  child.stderr?.on("data", (data) => {
63620
- output += data.toString();
64368
+ const chunk = data.toString();
64369
+ output += chunk;
64370
+ writeTaskOutput(taskId, chunk);
63621
64371
  });
63622
64372
  const task = { process: child, output: "", done: false, exitCode: null };
63623
64373
  backgroundTasks.set(taskId, task);
@@ -63625,10 +64375,16 @@ var init_bash = __esm({
63625
64375
  task.output = output;
63626
64376
  task.done = true;
63627
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);
63628
64384
  });
63629
64385
  return {
63630
64386
  data: {
63631
- stdout: `Background task started with ID: ${taskId}`,
64387
+ stdout: `Background task started with ID: ${taskId}. Use TaskOutput to check its status.`,
63632
64388
  stderr: "",
63633
64389
  exitCode: null,
63634
64390
  interrupted: false,
@@ -63741,7 +64497,7 @@ Instructions:
63741
64497
  });
63742
64498
 
63743
64499
  // src/tools/builtin/read.ts
63744
- import { readFileSync as readFileSync8, statSync, existsSync as existsSync9 } from "fs";
64500
+ import { readFileSync as readFileSync10, statSync as statSync2, existsSync as existsSync12 } from "fs";
63745
64501
  import { extname, isAbsolute, resolve as resolve2 } from "path";
63746
64502
  function hasFileBeenRead(filePath) {
63747
64503
  return readFiles.has(resolve2(filePath));
@@ -63750,7 +64506,7 @@ function markFileAsRead(filePath) {
63750
64506
  readFiles.add(resolve2(filePath));
63751
64507
  }
63752
64508
  function readTextFile(filePath, input) {
63753
- const rawContent = readFileSync8(filePath, "utf-8");
64509
+ const rawContent = readFileSync10(filePath, "utf-8");
63754
64510
  const allLines = rawContent.split("\n");
63755
64511
  const totalLines = allLines.length;
63756
64512
  const startLine = input.offset ?? 1;
@@ -63773,7 +64529,7 @@ function readTextFile(filePath, input) {
63773
64529
  };
63774
64530
  }
63775
64531
  function readImageFile(filePath, input) {
63776
- const stat = statSync(filePath);
64532
+ const stat = statSync2(filePath);
63777
64533
  const ext = extname(filePath).toLowerCase();
63778
64534
  const content = `[Image file: ${filePath} (${ext}, ${formatBytes(stat.size)})]`;
63779
64535
  return {
@@ -63787,7 +64543,7 @@ function readImageFile(filePath, input) {
63787
64543
  };
63788
64544
  }
63789
64545
  function readPdfFile(filePath, input) {
63790
- const stat = statSync(filePath);
64546
+ const stat = statSync2(filePath);
63791
64547
  const pages = input.pages ?? "1-5";
63792
64548
  const content = `[PDF file: ${filePath} (${formatBytes(stat.size)}, requested pages: ${pages}). PDF reading requires pdf-parse library \u2014 install to enable.]`;
63793
64549
  return {
@@ -63802,7 +64558,7 @@ function readPdfFile(filePath, input) {
63802
64558
  }
63803
64559
  function readNotebookFile(filePath, input) {
63804
64560
  try {
63805
- const rawContent = readFileSync8(filePath, "utf-8");
64561
+ const rawContent = readFileSync10(filePath, "utf-8");
63806
64562
  const notebook = JSON.parse(rawContent);
63807
64563
  if (!notebook.cells || !Array.isArray(notebook.cells)) {
63808
64564
  return {
@@ -63922,11 +64678,11 @@ var init_read = __esm({
63922
64678
  return { result: false, message: "file_path is required", errorCode: 1 };
63923
64679
  }
63924
64680
  const resolved = resolvePath(input.file_path);
63925
- if (!existsSync9(resolved)) {
64681
+ if (!existsSync12(resolved)) {
63926
64682
  return { result: false, message: `File does not exist: ${input.file_path}`, errorCode: 2 };
63927
64683
  }
63928
64684
  try {
63929
- const stat = statSync(resolved);
64685
+ const stat = statSync2(resolved);
63930
64686
  if (stat.isDirectory()) {
63931
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 };
63932
64688
  }
@@ -63979,7 +64735,7 @@ Usage:
63979
64735
  });
63980
64736
 
63981
64737
  // src/tools/builtin/edit.ts
63982
- 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";
63983
64739
  import { resolve as resolve3, isAbsolute as isAbsolute2 } from "path";
63984
64740
  import { randomUUID as randomUUID2 } from "crypto";
63985
64741
  function resolvePath2(filePath) {
@@ -64119,7 +64875,7 @@ var init_edit = __esm({
64119
64875
  return { result: false, message: "old_string and new_string must be different", errorCode: 2 };
64120
64876
  }
64121
64877
  const resolved = resolvePath2(input.file_path);
64122
- if (!existsSync10(resolved)) {
64878
+ if (!existsSync13(resolved)) {
64123
64879
  return { result: false, message: `File does not exist: ${input.file_path}`, errorCode: 3 };
64124
64880
  }
64125
64881
  if (!hasFileBeenRead(resolved)) {
@@ -64129,7 +64885,7 @@ var init_edit = __esm({
64129
64885
  errorCode: 4
64130
64886
  };
64131
64887
  }
64132
- const content = readFileSync9(resolved, "utf-8");
64888
+ const content = readFileSync11(resolved, "utf-8");
64133
64889
  const count = countOccurrences(content, input.old_string);
64134
64890
  if (count === 0) {
64135
64891
  return {
@@ -64160,7 +64916,7 @@ var init_edit = __esm({
64160
64916
  const resolved = resolvePath2(input.file_path);
64161
64917
  const MAX_FILE_SIZE = 50 * 1024 * 1024;
64162
64918
  try {
64163
- const fileSize = statSync2(resolved).size;
64919
+ const fileSize = statSync3(resolved).size;
64164
64920
  if (fileSize > MAX_FILE_SIZE) {
64165
64921
  return {
64166
64922
  data: {
@@ -64175,7 +64931,7 @@ var init_edit = __esm({
64175
64931
  }
64176
64932
  } catch {
64177
64933
  }
64178
- const originalContent = readFileSync9(resolved, "utf-8");
64934
+ const originalContent = readFileSync11(resolved, "utf-8");
64179
64935
  let newContent;
64180
64936
  let replacements;
64181
64937
  if (input.replace_all) {
@@ -64206,7 +64962,7 @@ var init_edit = __esm({
64206
64962
  );
64207
64963
  } catch {
64208
64964
  }
64209
- writeFileSync5(resolved, newContent, "utf-8");
64965
+ writeFileSync6(resolved, newContent, "utf-8");
64210
64966
  markFileAsRead(resolved);
64211
64967
  const gitDiff = generateUnifiedDiff(originalContent, newContent, input.file_path);
64212
64968
  return {
@@ -64244,7 +65000,7 @@ Usage:
64244
65000
  });
64245
65001
 
64246
65002
  // src/tools/builtin/write.ts
64247
- 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";
64248
65004
  import { dirname as dirname3, resolve as resolve4, isAbsolute as isAbsolute3 } from "path";
64249
65005
  import { randomUUID as randomUUID3 } from "crypto";
64250
65006
  function resolvePath3(filePath) {
@@ -64317,14 +65073,14 @@ var init_write = __esm({
64317
65073
  return { data: { filePath: "", bytesWritten: 0, created: false } };
64318
65074
  }
64319
65075
  const resolved = resolvePath3(input.file_path);
64320
- const created = !existsSync11(resolved);
65076
+ const created = !existsSync14(resolved);
64321
65077
  const dir = dirname3(resolved);
64322
- if (!existsSync11(dir)) {
64323
- mkdirSync5(dir, { recursive: true });
65078
+ if (!existsSync14(dir)) {
65079
+ mkdirSync6(dir, { recursive: true });
64324
65080
  }
64325
65081
  if (!created) {
64326
65082
  try {
64327
- const originalContent = readFileSync10(resolved, "utf-8");
65083
+ const originalContent = readFileSync12(resolved, "utf-8");
64328
65084
  const cpId = randomUUID3().slice(0, 8);
64329
65085
  dbRun(
64330
65086
  "INSERT INTO checkpoints (id, session_id, file_path, original_content, edit_operation) VALUES (?, ?, ?, ?, ?)",
@@ -64333,7 +65089,7 @@ var init_write = __esm({
64333
65089
  } catch {
64334
65090
  }
64335
65091
  }
64336
- writeFileSync6(resolved, input.content, "utf-8");
65092
+ writeFileSync7(resolved, input.content, "utf-8");
64337
65093
  markFileAsRead(resolved);
64338
65094
  return {
64339
65095
  data: {
@@ -65026,7 +65782,7 @@ var require_fill_range = __commonJS({
65026
65782
  }
65027
65783
  return options2.stringify === true;
65028
65784
  };
65029
- var pad2 = (input, maxLength, toNumber) => {
65785
+ var pad = (input, maxLength, toNumber) => {
65030
65786
  if (maxLength > 0) {
65031
65787
  let dash = input[0] === "-" ? "-" : "";
65032
65788
  if (dash) input = input.slice(1);
@@ -65128,7 +65884,7 @@ var require_fill_range = __commonJS({
65128
65884
  if (options2.toRegex === true && step > 1) {
65129
65885
  push(a);
65130
65886
  } else {
65131
- range.push(pad2(format(a, index), maxLen, toNumber));
65887
+ range.push(pad(format(a, index), maxLen, toNumber));
65132
65888
  }
65133
65889
  a = descending ? a - step : a + step;
65134
65890
  index++;
@@ -67798,18 +68554,18 @@ var require_tasks = __commonJS({
67798
68554
  return patterns.map((pattern) => utils.pattern.removeDuplicateSlashes(pattern));
67799
68555
  }
67800
68556
  function convertPatternsToTasks(positive, negative, dynamic) {
67801
- const tasks = [];
68557
+ const tasks2 = [];
67802
68558
  const patternsOutsideCurrentDirectory = utils.pattern.getPatternsOutsideCurrentDirectory(positive);
67803
68559
  const patternsInsideCurrentDirectory = utils.pattern.getPatternsInsideCurrentDirectory(positive);
67804
68560
  const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory);
67805
68561
  const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory);
67806
- tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic));
68562
+ tasks2.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic));
67807
68563
  if ("." in insideCurrentDirectoryGroup) {
67808
- tasks.push(convertPatternGroupToTask(".", patternsInsideCurrentDirectory, negative, dynamic));
68564
+ tasks2.push(convertPatternGroupToTask(".", patternsInsideCurrentDirectory, negative, dynamic));
67809
68565
  } else {
67810
- tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic));
68566
+ tasks2.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic));
67811
68567
  }
67812
- return tasks;
68568
+ return tasks2;
67813
68569
  }
67814
68570
  exports.convertPatternsToTasks = convertPatternsToTasks;
67815
68571
  function getPositivePatterns(patterns) {
@@ -67987,11 +68743,11 @@ var require_out = __commonJS({
67987
68743
  async.read(path, getSettings2(optionsOrSettingsOrCallback), callback);
67988
68744
  }
67989
68745
  exports.stat = stat;
67990
- function statSync4(path, optionsOrSettings) {
68746
+ function statSync5(path, optionsOrSettings) {
67991
68747
  const settings = getSettings2(optionsOrSettings);
67992
68748
  return sync.read(path, settings);
67993
68749
  }
67994
- exports.statSync = statSync4;
68750
+ exports.statSync = statSync5;
67995
68751
  function getSettings2(settingsOrOptions = {}) {
67996
68752
  if (settingsOrOptions instanceof settings_1.default) {
67997
68753
  return settingsOrOptions;
@@ -68016,14 +68772,14 @@ var require_run_parallel = __commonJS({
68016
68772
  "node_modules/run-parallel/index.js"(exports, module) {
68017
68773
  module.exports = runParallel;
68018
68774
  var queueMicrotask2 = require_queue_microtask();
68019
- function runParallel(tasks, cb) {
68775
+ function runParallel(tasks2, cb) {
68020
68776
  let results, pending, keys;
68021
68777
  let isSync = true;
68022
- if (Array.isArray(tasks)) {
68778
+ if (Array.isArray(tasks2)) {
68023
68779
  results = [];
68024
- pending = tasks.length;
68780
+ pending = tasks2.length;
68025
68781
  } else {
68026
- keys = Object.keys(tasks);
68782
+ keys = Object.keys(tasks2);
68027
68783
  results = {};
68028
68784
  pending = keys.length;
68029
68785
  }
@@ -68045,12 +68801,12 @@ var require_run_parallel = __commonJS({
68045
68801
  done(null);
68046
68802
  } else if (keys) {
68047
68803
  keys.forEach(function(key) {
68048
- tasks[key](function(err, result) {
68804
+ tasks2[key](function(err, result) {
68049
68805
  each(key, err, result);
68050
68806
  });
68051
68807
  });
68052
68808
  } else {
68053
- tasks.forEach(function(task, i) {
68809
+ tasks2.forEach(function(task, i) {
68054
68810
  task(function(err, result) {
68055
68811
  each(i, err, result);
68056
68812
  });
@@ -68167,8 +68923,8 @@ var require_async2 = __commonJS({
68167
68923
  callSuccessCallback(callback, entries);
68168
68924
  return;
68169
68925
  }
68170
- const tasks = entries.map((entry) => makeRplTaskEntry(entry, settings));
68171
- rpl(tasks, (rplError, rplEntries) => {
68926
+ const tasks2 = entries.map((entry) => makeRplTaskEntry(entry, settings));
68927
+ rpl(tasks2, (rplError, rplEntries) => {
68172
68928
  if (rplError !== null) {
68173
68929
  callFailureCallback(callback, rplError);
68174
68930
  return;
@@ -68204,7 +68960,7 @@ var require_async2 = __commonJS({
68204
68960
  callFailureCallback(callback, readdirError);
68205
68961
  return;
68206
68962
  }
68207
- const tasks = names.map((name) => {
68963
+ const tasks2 = names.map((name) => {
68208
68964
  const path = common.joinPathSegments(directory, name, settings.pathSegmentSeparator);
68209
68965
  return (done) => {
68210
68966
  fsStat.stat(path, settings.fsStatSettings, (error2, stats) => {
@@ -68224,7 +68980,7 @@ var require_async2 = __commonJS({
68224
68980
  });
68225
68981
  };
68226
68982
  });
68227
- rpl(tasks, (rplError, entries) => {
68983
+ rpl(tasks2, (rplError, entries) => {
68228
68984
  if (rplError !== null) {
68229
68985
  callFailureCallback(callback, rplError);
68230
68986
  return;
@@ -68489,12 +69245,12 @@ var require_queue = __commonJS({
68489
69245
  }
68490
69246
  function getQueue() {
68491
69247
  var current = queueHead;
68492
- var tasks = [];
69248
+ var tasks2 = [];
68493
69249
  while (current) {
68494
- tasks.push(current.value);
69250
+ tasks2.push(current.value);
68495
69251
  current = current.next;
68496
69252
  }
68497
- return tasks;
69253
+ return tasks2;
68498
69254
  }
68499
69255
  function resume() {
68500
69256
  if (!self2.paused) return;
@@ -69860,9 +70616,9 @@ var require_out4 = __commonJS({
69860
70616
  function getWorks(source, _Provider, options2) {
69861
70617
  const patterns = [].concat(source);
69862
70618
  const settings = new settings_1.default(options2);
69863
- const tasks = taskManager.generate(patterns, settings);
70619
+ const tasks2 = taskManager.generate(patterns, settings);
69864
70620
  const provider = new _Provider(settings);
69865
- return tasks.map(provider.read, provider);
70621
+ return tasks2.map(provider.read, provider);
69866
70622
  }
69867
70623
  function assertPatternsInput(input) {
69868
70624
  const source = [].concat(input);
@@ -69876,7 +70632,7 @@ var require_out4 = __commonJS({
69876
70632
  });
69877
70633
 
69878
70634
  // src/tools/builtin/glob.ts
69879
- import { statSync as statSync3, existsSync as existsSync12 } from "fs";
70635
+ import { statSync as statSync4, existsSync as existsSync15 } from "fs";
69880
70636
  import { resolve as resolve5, isAbsolute as isAbsolute4 } from "path";
69881
70637
  function resolvePath4(p) {
69882
70638
  if (isAbsolute4(p)) return p;
@@ -69942,7 +70698,7 @@ var init_glob = __esm({
69942
70698
  },
69943
70699
  async call(input, context) {
69944
70700
  const cwd2 = input.path ? resolvePath4(input.path) : process.cwd();
69945
- if (input.path && !existsSync12(cwd2)) {
70701
+ if (input.path && !existsSync15(cwd2)) {
69946
70702
  return {
69947
70703
  data: {
69948
70704
  files: [],
@@ -69973,7 +70729,7 @@ var init_glob = __esm({
69973
70729
  });
69974
70730
  const withStats = allFiles.map((f) => {
69975
70731
  try {
69976
- const stat = statSync3(f);
70732
+ const stat = statSync4(f);
69977
70733
  return { path: f, mtime: stat.mtimeMs };
69978
70734
  } catch {
69979
70735
  return { path: f, mtime: 0 };
@@ -70275,7 +71031,7 @@ function filterToolsForAgentType(tools, typeDef) {
70275
71031
  const excluded = new Set(typeDef.excludedTools ?? []);
70276
71032
  return tools.filter((t) => allowed.has(t.name) && !excluded.has(t.name));
70277
71033
  }
70278
- function buildSubAgentSystemPrompt(agentType, typeDef) {
71034
+ function buildSubAgentSystemPrompt(_agentType, typeDef) {
70279
71035
  return `You are a ${typeDef.description}
70280
71036
 
70281
71037
  You are a sub-agent spawned to handle a specific task. Complete the task thoroughly and return your findings/results.
@@ -70294,7 +71050,7 @@ function extractText(message) {
70294
71050
  }
70295
71051
  return "(no text content)";
70296
71052
  }
70297
- function runAgentInBackground(agentId, record2, input, tools, systemPrompt, model, parentContext) {
71053
+ function runAgentInBackground(agentId, bgTaskId, record2, input, tools, systemPrompt, model, parentContext) {
70298
71054
  const abortController = new AbortController();
70299
71055
  runAgentLoop(
70300
71056
  [{ role: "user", content: input.prompt }],
@@ -70307,22 +71063,40 @@ function runAgentInBackground(agentId, record2, input, tools, systemPrompt, mode
70307
71063
  signal: abortController.signal,
70308
71064
  permissionContext: parentContext.getAppState().toolPermissionContext,
70309
71065
  agentId,
70310
- maxTurns: 25
71066
+ maxTurns: 25,
71067
+ onTextDelta: (_text) => {
71068
+ updateTask(bgTaskId, {
71069
+ progress: {
71070
+ lastActivity: (/* @__PURE__ */ new Date()).toISOString()
71071
+ }
71072
+ });
71073
+ }
70311
71074
  }
70312
71075
  ).then((result) => {
70313
71076
  const lastAssistant = [...result.messages].reverse().find((m) => m.role === "assistant");
71077
+ const resultText = extractText(lastAssistant);
70314
71078
  record2.status = "completed";
70315
- 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);
70316
71088
  }).catch((error2) => {
71089
+ const errorMsg = error2 instanceof Error ? error2.message : String(error2);
70317
71090
  record2.status = "failed";
70318
- record2.error = error2 instanceof Error ? error2.message : String(error2);
71091
+ record2.error = errorMsg;
71092
+ failTask(bgTaskId, errorMsg);
70319
71093
  });
70320
71094
  }
70321
71095
  function truncate3(str, maxLen) {
70322
71096
  if (str.length <= maxLen) return str;
70323
71097
  return str.slice(0, maxLen - 100) + "\n\n... (truncated)";
70324
71098
  }
70325
- var BUILTIN_AGENT_TYPES, AgentInputSchema, AgentOutputSchema, nextAgentId, runningAgents, agentTool, AGENT_PROMPT;
71099
+ var BUILTIN_AGENT_TYPES, AgentInputSchema, AgentOutputSchema, nextAgentId2, runningAgents, agentTool, AGENT_PROMPT;
70326
71100
  var init_agent = __esm({
70327
71101
  "src/tools/builtin/agent.ts"() {
70328
71102
  "use strict";
@@ -70330,6 +71104,7 @@ var init_agent = __esm({
70330
71104
  init_constants();
70331
71105
  init_agent_loop();
70332
71106
  init_client();
71107
+ init_background_tasks();
70333
71108
  BUILTIN_AGENT_TYPES = {
70334
71109
  "general-purpose": {
70335
71110
  name: "general-purpose",
@@ -70372,7 +71147,7 @@ var init_agent = __esm({
70372
71147
  usage: external_exports.object({ inputTokens: external_exports.number(), outputTokens: external_exports.number() }),
70373
71148
  backgrounded: external_exports.boolean().optional()
70374
71149
  });
70375
- nextAgentId = 1;
71150
+ nextAgentId2 = 1;
70376
71151
  runningAgents = /* @__PURE__ */ new Map();
70377
71152
  agentTool = {
70378
71153
  name: AGENT_TOOL,
@@ -70420,7 +71195,7 @@ var init_agent = __esm({
70420
71195
  return { behavior: "allow", updatedInput: input };
70421
71196
  },
70422
71197
  async call(input, context) {
70423
- const agentId = `agent-${nextAgentId++}`;
71198
+ const agentId = `agent-${nextAgentId2++}`;
70424
71199
  const agentType = input.subagent_type ?? "general-purpose";
70425
71200
  const typeDef = BUILTIN_AGENT_TYPES[agentType] ?? BUILTIN_AGENT_TYPES["general-purpose"];
70426
71201
  const model = input.model ?? "sonnet";
@@ -70441,17 +71216,18 @@ var init_agent = __esm({
70441
71216
  inputSchema: { type: "object", properties: {} },
70442
71217
  isReadOnly: t.isReadOnly(),
70443
71218
  isConcurrencySafe: t.isConcurrencySafe(),
70444
- call: async (toolInput, toolCtx) => {
71219
+ call: async (toolInput, _toolCtx) => {
70445
71220
  const result = await t.call(toolInput, context);
70446
71221
  return { data: result.data };
70447
71222
  }
70448
71223
  }));
70449
71224
  const systemPrompt = buildSubAgentSystemPrompt(agentType, typeDef);
70450
71225
  if (input.run_in_background) {
70451
- 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);
70452
71228
  return {
70453
71229
  data: {
70454
- 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.`,
70455
71231
  agentId,
70456
71232
  agentType,
70457
71233
  model,
@@ -70487,7 +71263,10 @@ var init_agent = __esm({
70487
71263
  agentType,
70488
71264
  model,
70489
71265
  totalTurns: loopResult.totalTurns,
70490
- usage: loopResult.usage
71266
+ usage: {
71267
+ inputTokens: loopResult.usage.totalInputTokens,
71268
+ outputTokens: loopResult.usage.totalOutputTokens
71269
+ }
70491
71270
  }
70492
71271
  };
70493
71272
  } catch (error2) {
@@ -70638,11 +71417,11 @@ var init_todos = __esm({
70638
71417
  });
70639
71418
  return result.map(mapFromHasna);
70640
71419
  }
70641
- let tasks = [...this.fallbackStore.values()];
71420
+ let tasks2 = [...this.fallbackStore.values()];
70642
71421
  if (filter?.status) {
70643
- tasks = tasks.filter((t) => t.status === filter.status);
71422
+ tasks2 = tasks2.filter((t) => t.status === filter.status);
70644
71423
  }
70645
- return tasks;
71424
+ return tasks2;
70646
71425
  }
70647
71426
  async updateTask(id, params) {
70648
71427
  if (this.hasnaTodos) {
@@ -70715,23 +71494,23 @@ var init_todos = __esm({
70715
71494
  await this.hasnaTodos.sync({ taskListId: this.taskListId, direction: "push" });
70716
71495
  return;
70717
71496
  }
70718
- const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync9, existsSync: existsSync17 } = await import("fs");
70719
- const { join: join10 } = await import("path");
70720
- if (!existsSync17(dir)) mkdirSync9(dir, { recursive: true });
70721
- const tasks = await this.listTasks();
70722
- for (const task of tasks) {
70723
- 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`);
70724
71503
  writeFileSync10(filePath, JSON.stringify(task, null, 2), "utf-8");
70725
71504
  }
70726
71505
  }
70727
71506
  // ── Stats ──────────────────────────────────────────────────────
70728
71507
  async getStats() {
70729
- const tasks = await this.listTasks();
71508
+ const tasks2 = await this.listTasks();
70730
71509
  return {
70731
- total: tasks.length,
70732
- pending: tasks.filter((t) => t.status === "pending").length,
70733
- inProgress: tasks.filter((t) => t.status === "in_progress").length,
70734
- 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
70735
71514
  };
70736
71515
  }
70737
71516
  };
@@ -70915,8 +71694,8 @@ var init_tasks = __esm({
70915
71694
  },
70916
71695
  async call() {
70917
71696
  const todos = getTodosIntegration();
70918
- const tasks = await todos.listTasks();
70919
- return { data: { tasks } };
71697
+ const tasks2 = await todos.listTasks();
71698
+ return { data: { tasks: tasks2 } };
70920
71699
  },
70921
71700
  mapToolResultToToolResultBlockParam(result, toolUseId) {
70922
71701
  if (result.tasks.length === 0) {
@@ -71058,6 +71837,251 @@ var init_tasks = __esm({
71058
71837
  }
71059
71838
  });
71060
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
+
71061
72085
  // src/tools/builtin/ask-user.ts
71062
72086
  var OptionSchema, QuestionSchema, AskUserInputSchema, AskUserOutputSchema, askUserQuestionTool, ASK_USER_PROMPT;
71063
72087
  var init_ask_user = __esm({
@@ -71464,17 +72488,17 @@ LSP servers must be configured for the file type.`;
71464
72488
  });
71465
72489
 
71466
72490
  // src/tools/builtin/plan-mode.ts
71467
- import { readFileSync as readFileSync11, existsSync as existsSync13 } from "fs";
71468
- import { join as join7 } from "path";
72491
+ import { readFileSync as readFileSync13, existsSync as existsSync16 } from "fs";
72492
+ import { join as join10 } from "path";
71469
72493
  function getPlanFilePath(agentId) {
71470
72494
  const dir = getPlansDir();
71471
72495
  const name = agentId ? `plan-${agentId}.md` : "plan.md";
71472
- return join7(dir, name);
72496
+ return join10(dir, name);
71473
72497
  }
71474
72498
  function readPlanFile(agentId) {
71475
72499
  const path = getPlanFilePath(agentId);
71476
- if (!existsSync13(path)) return null;
71477
- return readFileSync11(path, "utf-8");
72500
+ if (!existsSync16(path)) return null;
72501
+ return readFileSync13(path, "utf-8");
71478
72502
  }
71479
72503
  var _hasExitedPlanMode, EnterPlanInputSchema, EnterPlanOutputSchema, enterPlanModeTool, ExitPlanInputSchema, ExitPlanOutputSchema, exitPlanModeTool, ENTER_PLAN_PROMPT, EXIT_PLAN_PROMPT;
71480
72504
  var init_plan_mode = __esm({
@@ -71702,8 +72726,43 @@ function getEnabledTools() {
71702
72726
  }
71703
72727
  return tools;
71704
72728
  }
71705
- function getDeferredToolInfos() {
71706
- 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);
71707
72766
  }
71708
72767
  function searchTools(query, maxResults = 5) {
71709
72768
  const results = [];
@@ -71741,7 +72800,7 @@ function matchScore(name, hint, queryTerms) {
71741
72800
  }
71742
72801
  return score;
71743
72802
  }
71744
- var registeredTools, deferredTools, disabledTools, mcpTools;
72803
+ var registeredTools, deferredTools, disabledTools, mcpTools, deferredToolSchemas;
71745
72804
  var init_registry2 = __esm({
71746
72805
  "src/tools/registry.ts"() {
71747
72806
  "use strict";
@@ -71750,6 +72809,7 @@ var init_registry2 = __esm({
71750
72809
  deferredTools = /* @__PURE__ */ new Map();
71751
72810
  disabledTools = /* @__PURE__ */ new Set();
71752
72811
  mcpTools = /* @__PURE__ */ new Map();
72812
+ deferredToolSchemas = /* @__PURE__ */ new Map();
71753
72813
  }
71754
72814
  });
71755
72815
 
@@ -71797,6 +72857,24 @@ function simpleTool(opts) {
71797
72857
  mapToolResultToToolResultBlockParam: opts.mapResult
71798
72858
  };
71799
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
+ }
71800
72878
  var toolSearchTool, cronCreateTool, cronDeleteTool, cronListTool, enterWorktreeTool, exitWorktreeTool, notebookEditTool, configTool, sendMessageTool;
71801
72879
  var init_misc = __esm({
71802
72880
  "src/tools/builtin/misc.ts"() {
@@ -71806,30 +72884,68 @@ var init_misc = __esm({
71806
72884
  init_constants();
71807
72885
  toolSearchTool = simpleTool({
71808
72886
  name: TOOL_SEARCH_TOOL,
71809
- hint: "find available tools by keyword",
72887
+ hint: "find available tools by keyword \u2014 fetches full schemas for deferred tools",
71810
72888
  inputSchema: external_exports.strictObject({
71811
- query: external_exports.string().describe("Query to find tools"),
71812
- 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)")
71813
72893
  }),
71814
72894
  readOnly: true,
71815
72895
  concurrent: true,
71816
72896
  defer: false,
71817
- 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`,
71818
72907
  async call(input) {
71819
- const results = searchTools(input.query, input.max_results);
71820
- 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();
71821
72911
  return {
71822
72912
  data: {
71823
- results,
71824
- deferredCount: deferred.length
72913
+ matchedSchemas,
72914
+ registryResults,
72915
+ totalDeferredCount: allDeferred.length
71825
72916
  }
71826
72917
  };
71827
72918
  },
71828
72919
  mapResult(result, id) {
71829
- const lines = result.results.map(
71830
- (r) => `${r.name}${r.deferred ? " (deferred)" : ""}: ${r.hint}`
71831
- );
71832
- 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 };
71833
72949
  }
71834
72950
  });
71835
72951
  cronCreateTool = simpleTool({
@@ -71917,34 +73033,134 @@ var init_misc = __esm({
71917
73033
  });
71918
73034
  notebookEditTool = simpleTool({
71919
73035
  name: NOTEBOOK_EDIT_TOOL,
71920
- hint: "edit Jupyter notebook cells",
73036
+ hint: "edit Jupyter notebook cells \u2014 insert, replace, delete, move, change type",
71921
73037
  inputSchema: external_exports.strictObject({
71922
73038
  notebook_path: external_exports.string().describe("Path to the .ipynb file"),
71923
- cell_index: external_exports.number().describe("Cell index to edit"),
71924
- 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")
71925
73044
  }),
71926
73045
  readOnly: false,
71927
73046
  concurrent: false,
71928
- defer: true,
71929
- 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.`,
71930
73057
  async call(input) {
71931
- 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);
71932
73061
  try {
71933
- const content = JSON.parse(readFileSync14(input.notebook_path, "utf-8"));
71934
- if (content.cells && content.cells[input.cell_index]) {
71935
- content.cells[input.cell_index].source = input.new_source.split("\n").map(
71936
- (l, i, a) => i < a.length - 1 ? l + "\n" : l
71937
- );
71938
- writeFileSync10(input.notebook_path, JSON.stringify(content, null, 1), "utf-8");
71939
- 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}` } };
71940
73152
  }
71941
- return { data: { success: false, error: "Cell index out of range" } };
71942
73153
  } catch (e) {
71943
73154
  return { data: { success: false, error: String(e) } };
71944
73155
  }
71945
73156
  },
71946
73157
  mapResult(result, id) {
71947
- 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
+ };
71948
73164
  }
71949
73165
  });
71950
73166
  configTool = simpleTool({
@@ -78242,9 +79458,9 @@ var init_protocol = __esm({
78242
79458
  });
78243
79459
  this.setRequestHandler(ListTasksRequestSchema, async (request, extra) => {
78244
79460
  try {
78245
- 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);
78246
79462
  return {
78247
- tasks,
79463
+ tasks: tasks2,
78248
79464
  nextCursor,
78249
79465
  _meta: {}
78250
79466
  };
@@ -88935,6 +90151,13 @@ async function connectMcpServers(configs, batchSize = DEFAULT_BATCH_SIZE) {
88935
90151
  }
88936
90152
  return results;
88937
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
+ }
88938
90161
  function wrapMcpTool(serverName, mcpTool, client) {
88939
90162
  const toolName = `mcp__${serverName}__${mcpTool.name}`;
88940
90163
  return {
@@ -89020,6 +90243,239 @@ var init_client4 = __esm({
89020
90243
  }
89021
90244
  });
89022
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
+
89023
90479
  // src/mcp/handlers.ts
89024
90480
  function mcpToolsToHandlers() {
89025
90481
  const mcpTools2 = getMcpTools();
@@ -89108,51 +90564,61 @@ var init_init = __esm({
89108
90564
  });
89109
90565
 
89110
90566
  // src/core/team.ts
89111
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync14, mkdirSync as mkdirSync7 } from "fs";
89112
- import { join as join8 } from "path";
89113
90567
  function createTeam(name, description) {
89114
- 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 {
89115
90574
  name,
89116
90575
  description,
89117
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
90576
+ createdAt: now,
89118
90577
  members: [],
89119
90578
  taskListId: name
89120
90579
  };
89121
- const teamsDir = getTeamsDir();
89122
- writeFileSync8(join8(teamsDir, `${name}.json`), JSON.stringify(team, null, 2), "utf-8");
89123
- const taskDir = join8(getTasksDir(), name);
89124
- if (!existsSync14(taskDir)) mkdirSync7(taskDir, { recursive: true });
89125
- return team;
89126
90580
  }
89127
90581
  function getTeam(name) {
89128
- const path = join8(getTeamsDir(), `${name}.json`);
89129
- if (!existsSync14(path)) return null;
89130
- try {
89131
- return JSON.parse(readFileSync12(path, "utf-8"));
89132
- } catch {
89133
- return null;
89134
- }
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
+ };
89135
90601
  }
89136
90602
  function addTeamMember(teamName, member) {
89137
- const team = getTeam(teamName);
89138
- if (!team) return;
89139
- team.members = team.members.filter((m) => m.name !== member.name);
89140
- team.members.push(member);
89141
- 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
+ );
89142
90611
  }
89143
90612
  function updateMemberStatus(teamName, memberName, status) {
89144
- const team = getTeam(teamName);
89145
- if (!team) return;
89146
- const member = team.members.find((m) => m.name === memberName);
89147
- if (member) {
89148
- member.status = status;
89149
- writeFileSync8(join8(getTeamsDir(), `${teamName}.json`), JSON.stringify(team, null, 2), "utf-8");
89150
- }
90613
+ dbRun(
90614
+ `UPDATE team_members SET status = ? WHERE team_name = ? AND agent_name = ?`,
90615
+ [status, teamName, memberName]
90616
+ );
89151
90617
  }
89152
90618
  var init_team = __esm({
89153
90619
  "src/core/team.ts"() {
89154
90620
  "use strict";
89155
- init_paths();
90621
+ init_db();
89156
90622
  }
89157
90623
  });
89158
90624
 
@@ -90156,6 +91622,7 @@ __export(app_exports, {
90156
91622
  launchInkApp: () => launchInkApp,
90157
91623
  runHeadless: () => runHeadless
90158
91624
  });
91625
+ import { execSync as execSync4 } from "child_process";
90159
91626
  function useSpinner(active) {
90160
91627
  const [i, setI] = (0, import_react22.useState)(0);
90161
91628
  (0, import_react22.useEffect)(() => {
@@ -90178,6 +91645,8 @@ function createToolHandlers(mcpHandlers) {
90178
91645
  taskGetTool,
90179
91646
  taskListTool,
90180
91647
  taskUpdateTool,
91648
+ taskOutputTool,
91649
+ taskStopTool,
90181
91650
  askUserQuestionTool,
90182
91651
  webSearchTool,
90183
91652
  webFetchTool,
@@ -90192,49 +91661,75 @@ function createToolHandlers(mcpHandlers) {
90192
91661
  exitWorktreeTool,
90193
91662
  notebookEditTool,
90194
91663
  configTool,
90195
- sendMessageTool
91664
+ sendMessageTool,
91665
+ listMcpResourcesTool,
91666
+ readMcpResourceTool,
91667
+ skillTool
90196
91668
  ];
90197
- const permCtx = createDefaultPermissionContext();
91669
+ const permCtx = createDefaultPermissionContext(getSettings());
90198
91670
  const appState = { toolPermissionContext: permCtx, verbose: false };
90199
- const builtinHandlers = tools.map((tool) => ({
90200
- name: tool.name,
90201
- description: TOOL_DESCRIPTIONS[tool.name] ?? `Tool: ${tool.name}`,
90202
- inputSchema: TOOL_JSON_SCHEMAS[tool.name] ?? { type: "object", properties: {} },
90203
- isReadOnly: tool.isReadOnly(),
90204
- isConcurrencySafe: tool.isConcurrencySafe(),
90205
- call: async (input, ctx) => {
90206
- const summary = toolSummary(tool.name, input);
90207
- const t0 = performance.now();
90208
- try {
90209
- const toolCtx = {
90210
- abortController: new AbortController(),
90211
- getAppState: () => appState,
90212
- setAppState: (fn) => Object.assign(appState, fn(appState)),
90213
- options: { mainLoopModel: "sonnet", thinkingConfig: { type: "disabled" }, isNonInteractiveSession: false, tools: [], agentDefinitions: { activeAgents: [] } }
90214
- };
90215
- const result = await tool.call(input, toolCtx);
90216
- const block2 = tool.mapToolResultToToolResultBlockParam(result.data, ctx.toolUseId ?? "");
90217
- const dur = performance.now() - t0;
90218
- try {
90219
- 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]);
90220
- } catch {
90221
- }
90222
- return block2.is_error ? { data: block2.content, error: block2.content, isError: true } : { data: block2.content };
90223
- } catch (err) {
90224
- const dur = performance.now() - t0;
90225
- const errMsg = err instanceof Error ? err.message : String(err);
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();
90226
91685
  try {
90227
- 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]);
90228
- } 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 };
90229
91708
  }
90230
- return { error: errMsg, isError: true };
90231
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);
90232
91724
  }
90233
- }));
91725
+ }
90234
91726
  if (mcpHandlers && mcpHandlers.length > 0) {
90235
- return [...builtinHandlers, ...mcpHandlers];
91727
+ for (const h of mcpHandlers) {
91728
+ immediate.push(h);
91729
+ all.push(h);
91730
+ }
90236
91731
  }
90237
- return builtinHandlers;
91732
+ return { immediate, all, deferredInfo, deferredSchemas };
90238
91733
  }
90239
91734
  function shortPath(p) {
90240
91735
  if (!p) return "";
@@ -90246,8 +91741,12 @@ function shortPath(p) {
90246
91741
  }
90247
91742
  function toolSummary(name, input) {
90248
91743
  switch (name) {
90249
- case "Bash":
90250
- return String(input.command ?? "").slice(0, 60);
91744
+ case "Bash": {
91745
+ let cmd = String(input.command ?? "");
91746
+ const cwd2 = process.cwd();
91747
+ if (cwd2 && cmd.includes(cwd2)) cmd = cmd.replaceAll(cwd2, ".");
91748
+ return cmd.slice(0, 60);
91749
+ }
90251
91750
  case "Read":
90252
91751
  return shortPath(String(input.file_path ?? ""));
90253
91752
  case "Edit":
@@ -90293,11 +91792,21 @@ function toolSummary(name, input) {
90293
91792
  case "ExitWorktree":
90294
91793
  return String(input.action ?? "");
90295
91794
  case "NotebookEdit":
90296
- return shortPath(String(input.notebook_path ?? ""));
91795
+ return `${input.command ?? ""} ${shortPath(String(input.notebook_path ?? ""))}`.trim();
90297
91796
  case "Config":
90298
91797
  return String(input.setting ?? "");
90299
91798
  case "SendMessage":
90300
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);
90301
91810
  default:
90302
91811
  return "";
90303
91812
  }
@@ -90306,20 +91815,97 @@ function SpinnerDot() {
90306
91815
  const f = useSpinner(true);
90307
91816
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "cyan", children: f });
90308
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
+ }
90309
91895
  function ToolItem({ tool }) {
90310
91896
  const f = useSpinner(tool.status === "running");
90311
- const icon = tool.status === "running" ? f : tool.status === "error" ? "\u25CF" : "\u25CF";
90312
- const color = tool.status === "running" ? "yellow" : tool.status === "error" ? "red" : "green";
90313
- const toolArgs = tool.summary ? `(${tool.summary.slice(0, 50)})` : "";
91897
+ const icon = tool.status === "running" ? f : "\u25CF";
91898
+ const color = tool.status === "running" ? "cyan" : tool.status === "error" ? "red" : "cyan";
91899
+ const toolArgs = tool.summary ? ` ${tool.summary.slice(0, 50)}` : "";
90314
91900
  const dur = tool.durationMs != null ? ` (${(tool.durationMs / 1e3).toFixed(1)}s)` : "";
90315
91901
  const resultLine = tool.result && tool.status === "done" ? formatToolResult(tool.name, tool.result)[0] ?? "" : "";
90316
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", children: [
91902
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", marginTop: 0, children: [
90317
91903
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { children: [
90318
91904
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { color, children: [
90319
91905
  icon,
90320
91906
  " "
90321
91907
  ] }),
90322
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { bold: true, children: tool.name }),
91908
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { bold: true, color, children: tool.name }),
90323
91909
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { dimColor: true, children: [
90324
91910
  toolArgs,
90325
91911
  dur
@@ -90331,7 +91917,7 @@ function ToolItem({ tool }) {
90331
91917
  CONN,
90332
91918
  " "
90333
91919
  ] }),
90334
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: resultLine.slice(0, 90) })
91920
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: resultLine.slice(0, 90) })
90335
91921
  ] }),
90336
91922
  tool.error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { children: [
90337
91923
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { dimColor: true, children: [
@@ -90416,19 +92002,24 @@ function MessageView({ msg }) {
90416
92002
  ] }) })
90417
92003
  ] });
90418
92004
  }
90419
- 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
+ }
90420
92009
  const teamLabel = agentName && teamName ? ` \xB7 ${agentName}@${teamName}` : "";
90421
92010
  const intentLabel = classification && classification.intent !== "general" ? ` \xB7 [${classification.intent}]` : "";
92011
+ const bgLabel = bgTaskCount && bgTaskCount > 0 ? ` \xB7 ${bgTaskCount} background task${bgTaskCount > 1 ? "s" : ""}` : "";
90422
92012
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { dimColor: true, children: [
90423
92013
  model,
90424
- " \xB7 ",
92014
+ " \\u00B7 ",
90425
92015
  mode,
90426
- " \xB7 $",
92016
+ " \\u00B7 $",
90427
92017
  cost.toFixed(4),
90428
- " \xB7 ",
92018
+ " \\u00B7 ",
90429
92019
  fmtTok(tokens),
90430
92020
  teamLabel,
90431
- intentLabel
92021
+ intentLabel,
92022
+ bgLabel
90432
92023
  ] }) });
90433
92024
  }
90434
92025
  function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedSession, mcpToolHandlers, agentId, agentName, teamName }) {
@@ -90453,7 +92044,21 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90453
92044
  const _rows = stdout?.rows ?? 24;
90454
92045
  const [slashSelected, setSlashSelected] = (0, import_react22.useState)(0);
90455
92046
  const [lastClassification, setLastClassification] = (0, import_react22.useState)(null);
92047
+ const [bgTasks, setBgTasks] = (0, import_react22.useState)([]);
90456
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
+ }, []);
90457
92062
  const thinkingConfig = (() => {
90458
92063
  if (appSettings.thinking?.enabled) {
90459
92064
  return appSettings.thinking.budgetTokens ? { type: "enabled", budget_tokens: appSettings.thinking.budgetTokens } : { type: "enabled" };
@@ -90570,6 +92175,42 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90570
92175
  ]);
90571
92176
  return;
90572
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
+ }
90573
92214
  if (r.output) setMsgs((p) => [...p, { id: `s${Date.now()}`, role: "system", content: r.output, timestamp: Date.now() }]);
90574
92215
  if (r.action === "exit") exit();
90575
92216
  if (r.action === "clear") {
@@ -90599,48 +92240,22 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90599
92240
  }
90600
92241
  const t0 = performance.now();
90601
92242
  try {
90602
- const allToolHandlers = createToolHandlers(mcpToolHandlers);
90603
- const permCtx = createDefaultPermissionContext();
92243
+ const toolSplit = createToolHandlers(mcpToolHandlers);
92244
+ const permCtx = createDefaultPermissionContext(getSettings());
90604
92245
  const toolStartTimes = /* @__PURE__ */ new Map();
92246
+ setDeferredToolSchemas([...toolSplit.deferredSchemas.values()]);
90605
92247
  const classification = classifyPrompt(text);
90606
92248
  setLastClassification(classification);
90607
- const builtinToolNames = /* @__PURE__ */ new Set([
90608
- "Bash",
90609
- "Read",
90610
- "Edit",
90611
- "Write",
90612
- "Glob",
90613
- "Grep",
90614
- "Agent",
90615
- "TaskCreate",
90616
- "TaskGet",
90617
- "TaskList",
90618
- "TaskUpdate",
90619
- "AskUserQuestion",
90620
- "WebSearch",
90621
- "WebFetch",
90622
- "LSP",
90623
- "EnterPlanMode",
90624
- "ExitPlanMode",
90625
- "ToolSearch",
90626
- "CronCreate",
90627
- "CronDelete",
90628
- "CronList",
90629
- "EnterWorktree",
90630
- "ExitWorktree",
90631
- "NotebookEdit",
90632
- "Config",
90633
- "SendMessage"
90634
- ]);
90635
- 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);
90636
92251
  try {
90637
92252
  dbRun(
90638
92253
  "INSERT INTO audit_log (tool_name, input_summary, result_summary, duration_ms, was_allowed) VALUES (?, ?, ?, ?, 1)",
90639
- ["__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]
90640
92255
  );
90641
92256
  } catch {
90642
92257
  }
90643
- const builtinTools = [
92258
+ const immediateBuiltinTools = [
90644
92259
  bashTool,
90645
92260
  readTool,
90646
92261
  editTool,
@@ -90648,28 +92263,11 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90648
92263
  globTool,
90649
92264
  grepTool,
90650
92265
  agentTool,
90651
- taskCreateTool,
90652
- taskGetTool,
90653
- taskListTool,
90654
- taskUpdateTool,
90655
- askUserQuestionTool,
90656
- webSearchTool,
90657
- webFetchTool,
90658
- lspTool,
90659
- enterPlanModeTool,
90660
- exitPlanModeTool,
90661
92266
  toolSearchTool,
90662
- cronCreateTool,
90663
- cronDeleteTool,
90664
- cronListTool,
90665
- enterWorktreeTool,
90666
- exitWorktreeTool,
90667
- notebookEditTool,
90668
- configTool,
90669
- sendMessageTool
90670
- ];
92267
+ skillTool
92268
+ ].filter((t) => !t.shouldDefer);
90671
92269
  const toolPrompts = await Promise.all(
90672
- builtinTools.map(async (t) => ({ name: t.name, prompt: await t.prompt() }))
92270
+ immediateBuiltinTools.map(async (t) => ({ name: t.name, prompt: await t.prompt() }))
90673
92271
  );
90674
92272
  const turnAbort = new AbortController();
90675
92273
  abortRef.current = turnAbort;
@@ -90682,7 +92280,8 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90682
92280
  projectDir: process.cwd(),
90683
92281
  model,
90684
92282
  permissionMode: mode,
90685
- tools: toolPrompts
92283
+ tools: toolPrompts,
92284
+ deferredTools: toolSplit.deferredInfo
90686
92285
  }),
90687
92286
  tools: toolHandlers,
90688
92287
  model,
@@ -90698,7 +92297,7 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90698
92297
  }
90699
92298
  } catch {
90700
92299
  }
90701
- if (dangerouslySkipPermissions) {
92300
+ if (dangerouslySkipPermissions || permCtx.mode === "bypassPermissions") {
90702
92301
  return { behavior: "allow" };
90703
92302
  }
90704
92303
  const ruleResult = checkToolPermission(toolName, toolInput, permCtx);
@@ -90896,11 +92495,12 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90896
92495
  else if (!key.ctrl && !key.meta && ch) setInput((p) => p + ch);
90897
92496
  });
90898
92497
  const cols = stdout?.columns ?? 80;
90899
- const sep = "\u2500".repeat(Math.min(cols, 120));
92498
+ const pad = 2;
92499
+ const sep = "\u2500".repeat(Math.min(cols - pad, 120));
90900
92500
  const hasRunningTools = activeTools.some((t) => t.status === "running");
90901
92501
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
90902
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Static, { items: msgs, children: (msg) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessageView, { msg }) }, msg.id) }),
90903
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", children: [
92502
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Static, { items: msgs, children: (msg) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessageView, { msg }) }, msg.id) }),
92503
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, children: [
90904
92504
  busy && activeTools.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { flexDirection: "column", children: activeTools.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToolItem, { tool: t }, t.id)) }),
90905
92505
  busy && thinkingText && !streaming && activeTools.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", children: [
90906
92506
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { children: [
@@ -90914,10 +92514,7 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90914
92514
  line.slice(0, 120)
90915
92515
  ] }) }, i))
90916
92516
  ] }),
90917
- busy && streaming && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { children: [
90918
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "green", children: "\u25CF " }),
90919
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: streaming.split("\n").filter((l) => l.trim()).slice(-3).join("\n").slice(-200) })
90920
- ] }),
92517
+ busy && streaming && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: streaming.split("\n").filter((l) => l.trim()).slice(-3).join("\n").slice(-200) }) }),
90921
92518
  busy && !streaming && !thinkingText && activeTools.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { children: [
90922
92519
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SpinnerDot, {}),
90923
92520
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: " Thinking..." })
@@ -90947,6 +92544,7 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90947
92544
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: "]lways" })
90948
92545
  ] })
90949
92546
  ] }),
92547
+ bgTasks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BackgroundTaskList, { tasks: bgTasks }),
90950
92548
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "column", children: [
90951
92549
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: sep }),
90952
92550
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { children: [
@@ -90978,7 +92576,7 @@ function App2({ model, mode, dangerouslySkipPermissions, initialPrompt, resumedS
90978
92576
  ] })
90979
92577
  ] }, cmd.name)) }),
90980
92578
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { dimColor: true, children: sep }),
90981
- /* @__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 })
90982
92580
  ] })
90983
92581
  ] })
90984
92582
  ] });
@@ -91101,6 +92699,7 @@ var init_app = __esm({
91101
92699
  init_agent_loop();
91102
92700
  init_permissions();
91103
92701
  init_db();
92702
+ init_background_tasks();
91104
92703
  init_session();
91105
92704
  init_system_prompt();
91106
92705
  init_bash();
@@ -91111,17 +92710,21 @@ var init_app = __esm({
91111
92710
  init_grep();
91112
92711
  init_agent();
91113
92712
  init_tasks();
92713
+ init_task_output();
91114
92714
  init_ask_user();
91115
92715
  init_web_search();
91116
92716
  init_web_fetch();
91117
92717
  init_lsp();
91118
92718
  init_plan_mode();
91119
92719
  init_misc();
92720
+ init_mcp_resources();
91120
92721
  init_registry();
91121
92722
  init_init();
91122
92723
  init_client4();
91123
92724
  init_team();
91124
92725
  init_classifier();
92726
+ init_skill();
92727
+ init_registry2();
91125
92728
  import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
91126
92729
  CONN = "\u23BF";
91127
92730
  PROMPT = "\u276F";
@@ -91357,10 +92960,13 @@ var init_app = __esm({
91357
92960
  type: "object",
91358
92961
  properties: {
91359
92962
  notebook_path: { type: "string", description: "Path to the .ipynb file" },
91360
- cell_index: { type: "number", description: "Cell index to edit" },
91361
- 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" }
91362
92968
  },
91363
- required: ["notebook_path", "cell_index", "new_source"]
92969
+ required: ["notebook_path", "command"]
91364
92970
  },
91365
92971
  Config: {
91366
92972
  type: "object",
@@ -91377,6 +92983,40 @@ var init_app = __esm({
91377
92983
  message: { type: "string", description: "Message content" }
91378
92984
  },
91379
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"]
91380
93020
  }
91381
93021
  };
91382
93022
  TOOL_DESCRIPTIONS = {
@@ -91403,9 +93043,14 @@ var init_app = __esm({
91403
93043
  CronList: "List all active scheduled cron jobs.",
91404
93044
  EnterWorktree: "Create an isolated git worktree and switch into it.",
91405
93045
  ExitWorktree: "Exit a worktree session, keeping or removing the worktree.",
91406
- 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.",
91407
93047
  Config: "Get or set a configuration setting.",
91408
- 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."
91409
93054
  };
91410
93055
  }
91411
93056
  });
@@ -92224,16 +93869,16 @@ __export(marketplace_exports, {
92224
93869
  removeMarketplace: () => removeMarketplace,
92225
93870
  uninstallPlugin: () => uninstallPlugin
92226
93871
  });
92227
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync15, mkdirSync as mkdirSync8, cpSync, rmSync } from "fs";
92228
- import { join as join9, resolve as resolve7 } from "path";
92229
- 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";
92230
93875
  import { tmpdir } from "os";
92231
93876
  import { randomBytes } from "crypto";
92232
93877
  function getMarketplaces() {
92233
93878
  const path = getMarketplacesConfigPath();
92234
- if (!existsSync15(path)) return [];
93879
+ if (!existsSync17(path)) return [];
92235
93880
  try {
92236
- return JSON.parse(readFileSync13(path, "utf-8"));
93881
+ return JSON.parse(readFileSync14(path, "utf-8"));
92237
93882
  } catch {
92238
93883
  return [];
92239
93884
  }
@@ -92253,8 +93898,8 @@ function removeMarketplace(name) {
92253
93898
  }
92254
93899
  function saveMarketplaces(marketplaces) {
92255
93900
  const path = getMarketplacesConfigPath();
92256
- const dir = join9(path, "..");
92257
- if (!existsSync15(dir)) mkdirSync8(dir, { recursive: true });
93901
+ const dir = join11(path, "..");
93902
+ if (!existsSync17(dir)) mkdirSync8(dir, { recursive: true });
92258
93903
  writeFileSync9(path, JSON.stringify(marketplaces, null, 2) + "\n", "utf-8");
92259
93904
  }
92260
93905
  async function installFromMarketplace(pluginName, marketplaceName) {
@@ -92278,7 +93923,7 @@ async function installPluginFromMarketplace(name, marketplace) {
92278
93923
  return installFromGit(gitUrl, pluginsDir);
92279
93924
  }
92280
93925
  case "directory": {
92281
- const sourcePath = join9(marketplace.source, name);
93926
+ const sourcePath = join11(marketplace.source, name);
92282
93927
  return installFromDirectory(sourcePath, pluginsDir);
92283
93928
  }
92284
93929
  case "url":
@@ -92297,10 +93942,10 @@ function installFromSource(source, pluginsDir) {
92297
93942
  }
92298
93943
  function installFromGit(url2, pluginsDir) {
92299
93944
  const dir = pluginsDir ?? getPluginsDir();
92300
- const tempDir = join9(tmpdir(), `coders-plugin-${randomBytes(8).toString("hex")}`);
93945
+ const tempDir = join11(tmpdir(), `coders-plugin-${randomBytes(8).toString("hex")}`);
92301
93946
  try {
92302
93947
  try {
92303
- execSync3(`git clone --depth 1 ${escapeShellArg(url2)} ${escapeShellArg(tempDir)}`, {
93948
+ execSync5(`git clone --depth 1 ${escapeShellArg(url2)} ${escapeShellArg(tempDir)}`, {
92304
93949
  encoding: "utf-8",
92305
93950
  stdio: "pipe",
92306
93951
  timeout: 6e4
@@ -92311,21 +93956,21 @@ function installFromGit(url2, pluginsDir) {
92311
93956
  error: `Failed to clone "${url2}": ${err instanceof Error ? err.message : String(err)}`
92312
93957
  };
92313
93958
  }
92314
- const manifestPath = join9(tempDir, "manifest.json");
92315
- if (!existsSync15(manifestPath)) {
93959
+ const manifestPath = join11(tempDir, "manifest.json");
93960
+ if (!existsSync17(manifestPath)) {
92316
93961
  return { success: false, error: `No manifest.json found in cloned repository "${url2}"` };
92317
93962
  }
92318
93963
  const validated = validateManifest(manifestPath);
92319
93964
  if (!validated.success) return validated;
92320
93965
  const pluginName = validated.pluginName;
92321
- const targetDir = join9(dir, pluginName);
92322
- if (existsSync15(targetDir)) {
93966
+ const targetDir = join11(dir, pluginName);
93967
+ if (existsSync17(targetDir)) {
92323
93968
  return { success: false, error: `Plugin "${pluginName}" is already installed` };
92324
93969
  }
92325
93970
  mkdirSync8(targetDir, { recursive: true });
92326
93971
  cpSync(tempDir, targetDir, {
92327
93972
  recursive: true,
92328
- filter: (src) => !src.includes(join9(tempDir, ".git"))
93973
+ filter: (src) => !src.includes(join11(tempDir, ".git"))
92329
93974
  });
92330
93975
  return {
92331
93976
  success: true,
@@ -92343,18 +93988,18 @@ function installFromGit(url2, pluginsDir) {
92343
93988
  function installFromDirectory(sourcePath, pluginsDir) {
92344
93989
  const dir = pluginsDir ?? getPluginsDir();
92345
93990
  const resolvedSource = resolve7(sourcePath);
92346
- if (!existsSync15(resolvedSource)) {
93991
+ if (!existsSync17(resolvedSource)) {
92347
93992
  return { success: false, error: `Source directory does not exist: "${resolvedSource}"` };
92348
93993
  }
92349
- const manifestPath = join9(resolvedSource, "manifest.json");
92350
- if (!existsSync15(manifestPath)) {
93994
+ const manifestPath = join11(resolvedSource, "manifest.json");
93995
+ if (!existsSync17(manifestPath)) {
92351
93996
  return { success: false, error: `No manifest.json found in "${resolvedSource}"` };
92352
93997
  }
92353
93998
  const validated = validateManifest(manifestPath);
92354
93999
  if (!validated.success) return validated;
92355
94000
  const pluginName = validated.pluginName;
92356
- const targetDir = join9(dir, pluginName);
92357
- if (existsSync15(targetDir)) {
94001
+ const targetDir = join11(dir, pluginName);
94002
+ if (existsSync17(targetDir)) {
92358
94003
  return { success: false, error: `Plugin "${pluginName}" is already installed` };
92359
94004
  }
92360
94005
  mkdirSync8(targetDir, { recursive: true });
@@ -92368,8 +94013,8 @@ function installFromDirectory(sourcePath, pluginsDir) {
92368
94013
  }
92369
94014
  function uninstallPlugin(name, pluginsDir) {
92370
94015
  const dir = pluginsDir ?? getPluginsDir();
92371
- const targetDir = join9(dir, name);
92372
- if (!existsSync15(targetDir)) return false;
94016
+ const targetDir = join11(dir, name);
94017
+ if (!existsSync17(targetDir)) return false;
92373
94018
  rmSync(targetDir, { recursive: true, force: true });
92374
94019
  return true;
92375
94020
  }
@@ -92378,7 +94023,7 @@ function isGitUrl(source) {
92378
94023
  }
92379
94024
  function validateManifest(manifestPath) {
92380
94025
  try {
92381
- const raw = JSON.parse(readFileSync13(manifestPath, "utf-8"));
94026
+ const raw = JSON.parse(readFileSync14(manifestPath, "utf-8"));
92382
94027
  const result = PluginManifestSchema.safeParse(raw);
92383
94028
  if (!result.success) {
92384
94029
  const issues = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
@@ -92412,8 +94057,8 @@ var main_exports = {};
92412
94057
  __export(main_exports, {
92413
94058
  main: () => main
92414
94059
  });
92415
- import { execSync as execSync4 } from "child_process";
92416
- import { existsSync as existsSync16 } from "fs";
94060
+ import { execSync as execSync6 } from "child_process";
94061
+ import { existsSync as existsSync18 } from "fs";
92417
94062
  function helpConfig() {
92418
94063
  return {
92419
94064
  sortSubcommands: true,
@@ -92847,15 +94492,15 @@ ${installed.length} plugin(s) installed`);
92847
94492
  }
92848
94493
  });
92849
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) => {
92850
- const { resolve: resolve8, join: join10 } = await import("path");
94495
+ const { resolve: resolve8, join: join12 } = await import("path");
92851
94496
  const { existsSync: pathExists } = await import("fs");
92852
94497
  const resolvedPath = resolve8(source);
92853
- const isLocalDir = pathExists(resolvedPath) && pathExists(join10(resolvedPath, "manifest.json"));
94498
+ const isLocalDir = pathExists(resolvedPath) && pathExists(join12(resolvedPath, "manifest.json"));
92854
94499
  if (isLocalDir) {
92855
94500
  try {
92856
- const { readFileSync: readFileSync14, cpSync: cpSync2, mkdirSync: mkdirSync9 } = await import("fs");
94501
+ const { readFileSync: readFileSync15, cpSync: cpSync2, mkdirSync: mkdirSync9 } = await import("fs");
92857
94502
  const { PluginManifestSchema: PluginManifestSchema2 } = await Promise.resolve().then(() => (init_manifest(), manifest_exports));
92858
- const manifestRaw = JSON.parse(readFileSync14(join10(resolvedPath, "manifest.json"), "utf-8"));
94503
+ const manifestRaw = JSON.parse(readFileSync15(join12(resolvedPath, "manifest.json"), "utf-8"));
92859
94504
  const result = PluginManifestSchema2.safeParse(manifestRaw);
92860
94505
  if (!result.success) {
92861
94506
  console.error("Error: Invalid plugin manifest:");
@@ -92866,7 +94511,7 @@ ${installed.length} plugin(s) installed`);
92866
94511
  }
92867
94512
  const pluginName = result.data.name;
92868
94513
  const pluginsDir = getPluginsDir();
92869
- const targetDir = join10(pluginsDir, pluginName);
94514
+ const targetDir = join12(pluginsDir, pluginName);
92870
94515
  if (pathExists(targetDir)) {
92871
94516
  console.error(`Error: Plugin "${pluginName}" is already installed.`);
92872
94517
  console.error("Uninstall it first: coders plugin uninstall " + pluginName);
@@ -92913,13 +94558,13 @@ ${installed.length} plugin(s) installed`);
92913
94558
  });
92914
94559
  plugin.command("validate <path>").description("Validate a plugin manifest").option("--json", "Output validation result as JSON").action(async (manifestPath, options2) => {
92915
94560
  try {
92916
- const { existsSync: pathExists, readFileSync: readFileSync14 } = await import("fs");
92917
- 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");
92918
94563
  const { PluginManifestSchema: PluginManifestSchema2 } = await Promise.resolve().then(() => (init_manifest(), manifest_exports));
92919
94564
  const resolvedPath = resolve8(manifestPath);
92920
94565
  let filePath;
92921
- if (pathExists(join10(resolvedPath, "manifest.json"))) {
92922
- filePath = join10(resolvedPath, "manifest.json");
94566
+ if (pathExists(join12(resolvedPath, "manifest.json"))) {
94567
+ filePath = join12(resolvedPath, "manifest.json");
92923
94568
  } else if (pathExists(resolvedPath)) {
92924
94569
  filePath = resolvedPath;
92925
94570
  } else {
@@ -92929,7 +94574,7 @@ ${installed.length} plugin(s) installed`);
92929
94574
  }
92930
94575
  let raw;
92931
94576
  try {
92932
- raw = JSON.parse(readFileSync14(filePath, "utf-8"));
94577
+ raw = JSON.parse(readFileSync15(filePath, "utf-8"));
92933
94578
  } catch {
92934
94579
  console.error(`Error: Could not parse JSON from ${filePath}`);
92935
94580
  process.exit(1);
@@ -93026,8 +94671,8 @@ ${installed.length} plugin(s) installed`);
93026
94671
  try {
93027
94672
  const userSettingsPath = getUserSettingsPath();
93028
94673
  const projectSettingsPath = getProjectSettingsPath(process.cwd());
93029
- const userExists = existsSync16(userSettingsPath);
93030
- const projectExists = existsSync16(projectSettingsPath);
94674
+ const userExists = existsSync18(userSettingsPath);
94675
+ const projectExists = existsSync18(projectSettingsPath);
93031
94676
  const parts = [];
93032
94677
  if (userExists) parts.push("user");
93033
94678
  if (projectExists) parts.push("project");
@@ -93058,7 +94703,7 @@ ${installed.length} plugin(s) installed`);
93058
94703
  issues++;
93059
94704
  }
93060
94705
  try {
93061
- const gitVersion = execSync4("git --version", { encoding: "utf-8", timeout: 5e3 }).trim();
94706
+ const gitVersion = execSync6("git --version", { encoding: "utf-8", timeout: 5e3 }).trim();
93062
94707
  console.log(` ${ok} Git: ${gitVersion.replace("git version ", "")}`);
93063
94708
  } catch {
93064
94709
  console.log(` ${fail} Git: not found in PATH`);
@@ -93066,7 +94711,7 @@ ${installed.length} plugin(s) installed`);
93066
94711
  }
93067
94712
  try {
93068
94713
  const pluginsDir = getPluginsDir();
93069
- const dirExists = existsSync16(pluginsDir);
94714
+ const dirExists = existsSync18(pluginsDir);
93070
94715
  if (dirExists) {
93071
94716
  const discovered = discoverPlugins();
93072
94717
  if (discovered.length > 0) {
@@ -93085,7 +94730,7 @@ ${installed.length} plugin(s) installed`);
93085
94730
  issues++;
93086
94731
  }
93087
94732
  try {
93088
- 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();
93089
94734
  console.log(` ${ok} Ripgrep: ${rgVersion.replace("ripgrep ", "")}`);
93090
94735
  } catch {
93091
94736
  console.log(` ${fail} Ripgrep: not found in PATH (needed for Grep tool)`);
@@ -93104,7 +94749,7 @@ ${installed.length} plugin(s) installed`);
93104
94749
  console.log("Checking for updates...\n");
93105
94750
  let latestVersion;
93106
94751
  try {
93107
- latestVersion = execSync4(`npm view ${PACKAGE_NAME} version`, {
94752
+ latestVersion = execSync6(`npm view ${PACKAGE_NAME} version`, {
93108
94753
  encoding: "utf-8",
93109
94754
  timeout: 15e3,
93110
94755
  stdio: ["pipe", "pipe", "pipe"]
@@ -93148,7 +94793,7 @@ ${installed.length} plugin(s) installed`);
93148
94793
  }
93149
94794
  let installCmd;
93150
94795
  try {
93151
- 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"] });
93152
94797
  installCmd = `bun install -g ${PACKAGE_NAME}@latest`;
93153
94798
  } catch {
93154
94799
  installCmd = `npm install -g ${PACKAGE_NAME}@latest`;
@@ -93156,7 +94801,7 @@ ${installed.length} plugin(s) installed`);
93156
94801
  console.log(`Running: ${installCmd}
93157
94802
  `);
93158
94803
  try {
93159
- execSync4(installCmd, { encoding: "utf-8", timeout: 12e4, stdio: "inherit" });
94804
+ execSync6(installCmd, { encoding: "utf-8", timeout: 12e4, stdio: "inherit" });
93160
94805
  console.log(`
93161
94806
  Successfully updated to v${latestVersion}.`);
93162
94807
  } catch (err) {
@@ -93424,7 +95069,7 @@ var VERSION, BUILD_TIME, PACKAGE_NAME, ISSUES_URL, startupTimestamps, originalCw
93424
95069
  var init_index = __esm({
93425
95070
  "src/cli/index.ts"() {
93426
95071
  VERSION = "0.1.2";
93427
- BUILD_TIME = "2026-03-20T18:36:45.761Z";
95072
+ BUILD_TIME = "2026-03-21T05:56:28.429Z";
93428
95073
  PACKAGE_NAME = "@hasna/coders";
93429
95074
  ISSUES_URL = "https://github.com/hasnaxyz/open-coders/issues";
93430
95075
  startupTimestamps = {};