cliskill 1.0.4 → 1.0.6

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.
@@ -90,6 +90,18 @@ var modelsSchema = z.object({
90
90
  fallbackModel: z.string().default("glm-5.1"),
91
91
  maxContextTokens: z.number().default(5e5)
92
92
  });
93
+ var mcpServerSchema = z.object({
94
+ /** Unique name for this MCP server */
95
+ name: z.string().min(1),
96
+ /** Command to start the MCP server process */
97
+ command: z.string().min(1),
98
+ /** Arguments passed to the command */
99
+ args: z.array(z.string()).default([]),
100
+ /** Environment variables for the server process */
101
+ env: z.record(z.string()).optional(),
102
+ /** Transport type (currently only stdio supported) */
103
+ transport: z.enum(["stdio"]).default("stdio")
104
+ });
93
105
  var appConfigSchema = z.object({
94
106
  /** Default provider name to use */
95
107
  defaultProvider: z.string().optional(),
@@ -136,7 +148,9 @@ var appConfigSchema = z.object({
136
148
  maxHistoryAge: z.number().default(60),
137
149
  maxMemorySize: z.number().default(50),
138
150
  idleThreshold: z.number().default(3e5)
139
- }).default({})
151
+ }).default({}),
152
+ /** MCP servers — external tool providers via Model Context Protocol */
153
+ mcpServers: z.array(mcpServerSchema).default([])
140
154
  });
141
155
 
142
156
  // src/config/constants.ts
@@ -417,7 +431,7 @@ var BaseAdapter = class {
417
431
  if (attempt >= 5) throw err;
418
432
  const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 1e4);
419
433
  log.warn(`Network error on ${url}: ${err.message}, retrying in ${delay}ms (attempt ${attempt})`);
420
- await new Promise((resolve9) => setTimeout(resolve9, delay));
434
+ await new Promise((resolve10) => setTimeout(resolve10, delay));
421
435
  continue;
422
436
  }
423
437
  if (response.ok) return response;
@@ -436,7 +450,7 @@ var BaseAdapter = class {
436
450
  return response;
437
451
  }
438
452
  log.warn(`Retrying ${url} in ${Math.round(action.delay)}ms (attempt ${attempt})`);
439
- await new Promise((resolve9) => setTimeout(resolve9, action.delay));
453
+ await new Promise((resolve10) => setTimeout(resolve10, action.delay));
440
454
  }
441
455
  }
442
456
  };
@@ -584,7 +598,16 @@ var GenericCompatAdapter = class extends BaseAdapter {
584
598
  let buffer = "";
585
599
  try {
586
600
  while (true) {
587
- const { done, value } = await reader.read();
601
+ let done;
602
+ let value;
603
+ try {
604
+ const result = await reader.read();
605
+ done = result.done;
606
+ value = result.value;
607
+ } catch (err) {
608
+ if (isAbortOrNetworkError(err)) break;
609
+ throw err;
610
+ }
588
611
  if (done) break;
589
612
  buffer += decoder.decode(value, { stream: true });
590
613
  const lines = buffer.split("\n");
@@ -649,6 +672,11 @@ var GenericCompatAdapter = class extends BaseAdapter {
649
672
  }
650
673
  }
651
674
  };
675
+ function isAbortOrNetworkError(err) {
676
+ const name = err.name ?? "";
677
+ const msg = err.message ?? "";
678
+ return name === "AbortError" || msg.includes("abort") || msg.includes("Abort") || msg.includes("timeout") || msg.includes("Timeout") || msg.includes("fetch failed") || msg.includes("ECONNRESET") || msg.includes("network");
679
+ }
652
680
 
653
681
  // src/connect/glm-adapter.ts
654
682
  import { randomUUID as randomUUID2 } from "crypto";
@@ -808,7 +836,16 @@ var GLMAdapter = class extends BaseAdapter {
808
836
  let buffer = "";
809
837
  try {
810
838
  while (true) {
811
- const { done, value } = await reader.read();
839
+ let done;
840
+ let value;
841
+ try {
842
+ const result = await reader.read();
843
+ done = result.done;
844
+ value = result.value;
845
+ } catch (err) {
846
+ if (isAbortOrNetworkError2(err)) break;
847
+ throw err;
848
+ }
812
849
  if (done) break;
813
850
  buffer += decoder.decode(value, { stream: true });
814
851
  const lines = buffer.split("\n");
@@ -880,6 +917,11 @@ var GLMAdapter = class extends BaseAdapter {
880
917
  }
881
918
  }
882
919
  };
920
+ function isAbortOrNetworkError2(err) {
921
+ const name = err.name ?? "";
922
+ const msg = err.message ?? "";
923
+ return name === "AbortError" || msg.includes("abort") || msg.includes("Abort") || msg.includes("timeout") || msg.includes("Timeout") || msg.includes("fetch failed") || msg.includes("ECONNRESET") || msg.includes("network");
924
+ }
883
925
 
884
926
  // src/connect/registry.ts
885
927
  var AdapterRegistry = class {
@@ -921,6 +963,30 @@ var AdapterRegistry = class {
921
963
  }
922
964
  };
923
965
 
966
+ // src/infra/errors.ts
967
+ var AppError = class extends Error {
968
+ constructor(message, code, recoverable = true) {
969
+ super(message);
970
+ this.code = code;
971
+ this.recoverable = recoverable;
972
+ this.name = "AppError";
973
+ }
974
+ code;
975
+ recoverable;
976
+ };
977
+ var FallbackTriggeredError = class extends AppError {
978
+ constructor(message, originalError, fromModel, fallbackModel) {
979
+ super(message, "FALLBACK_TRIGGERED", true);
980
+ this.originalError = originalError;
981
+ this.fromModel = fromModel;
982
+ this.fallbackModel = fallbackModel;
983
+ this.name = "FallbackTriggeredError";
984
+ }
985
+ originalError;
986
+ fromModel;
987
+ fallbackModel;
988
+ };
989
+
924
990
  // src/connect/model-router.ts
925
991
  var TASK_KEYWORDS = {
926
992
  reasoning: ["analyze", "reason", "think", "explain", "compare", "evaluate", "decide", "logic", "math", "prove"],
@@ -945,6 +1011,20 @@ var DEFAULT_PREFERENCES = {
945
1011
  analysis: "balanced",
946
1012
  default: "balanced"
947
1013
  };
1014
+ var FALLBACK_TRIGGER_PATTERNS = ["529", "503", "Overloaded", "overloaded", "capacity", "rate_limit"];
1015
+ var FALLBACK_TRIGGER_CODES = /* @__PURE__ */ new Set([529, 503, 502]);
1016
+ function isFallbackTriggeredError(err) {
1017
+ if (err instanceof FallbackTriggeredError) return true;
1018
+ if (!(err instanceof Error)) return false;
1019
+ const message = err.message;
1020
+ for (const pattern of FALLBACK_TRIGGER_PATTERNS) {
1021
+ if (message.includes(pattern)) return true;
1022
+ }
1023
+ if ("statusCode" in err && typeof err.statusCode === "number") {
1024
+ return FALLBACK_TRIGGER_CODES.has(err.statusCode);
1025
+ }
1026
+ return false;
1027
+ }
948
1028
  var ModelRouter = class {
949
1029
  registry;
950
1030
  modelAccess;
@@ -954,7 +1034,8 @@ var ModelRouter = class {
954
1034
  this.modelAccess = modelAccess;
955
1035
  this.config = {
956
1036
  defaultModel: config?.defaultModel ?? modelAccess.getEffectiveModel(),
957
- preferences: { ...DEFAULT_PREFERENCES, ...config?.preferences }
1037
+ preferences: { ...DEFAULT_PREFERENCES, ...config?.preferences },
1038
+ fallbackChain: config?.fallbackChain
958
1039
  };
959
1040
  }
960
1041
  /** Resolve a model name or alias to a concrete adapter */
@@ -1012,6 +1093,116 @@ var ModelRouter = class {
1012
1093
  getEffectiveModel(requestedModel) {
1013
1094
  return this.modelAccess.getEffectiveModel(requestedModel);
1014
1095
  }
1096
+ /**
1097
+ * Resolve with fallback: if primary adapter fails (529/503/overloaded),
1098
+ * try the fallback model. Returns both adapters for retry logic.
1099
+ */
1100
+ resolveWithFallback(primaryModel) {
1101
+ const primary = this.resolve(primaryModel);
1102
+ const fallbackModel = this.config.fallbackModel;
1103
+ const fallback = fallbackModel && fallbackModel !== primaryModel ? this.resolve(fallbackModel) : this.findAlternativeAdapter(primary);
1104
+ return { primary, fallback };
1105
+ }
1106
+ /**
1107
+ * Build the full fallback chain for a given primary model.
1108
+ * Returns adapters in priority order: [primary, ...fallbackChain].
1109
+ * Skips duplicates and null entries.
1110
+ */
1111
+ resolveFallbackChain(primaryModel) {
1112
+ const primary = this.resolve(primaryModel);
1113
+ const chain = [];
1114
+ const seen = /* @__PURE__ */ new Set();
1115
+ if (primary) {
1116
+ chain.push(primary);
1117
+ seen.add(primary.config.model);
1118
+ }
1119
+ const fallbackModels = this.config.fallbackChain ?? [];
1120
+ if (this.config.fallbackModel && !fallbackModels.includes(this.config.fallbackModel)) {
1121
+ fallbackModels.unshift(this.config.fallbackModel);
1122
+ }
1123
+ for (const modelOrAlias of fallbackModels) {
1124
+ const adapter = this.resolve(modelOrAlias);
1125
+ if (adapter && !seen.has(adapter.config.model)) {
1126
+ chain.push(adapter);
1127
+ seen.add(adapter.config.model);
1128
+ }
1129
+ }
1130
+ for (const adapter of this.registry.getAll()) {
1131
+ if (!seen.has(adapter.config.model)) {
1132
+ chain.push(adapter);
1133
+ seen.add(adapter.config.model);
1134
+ }
1135
+ }
1136
+ return chain;
1137
+ }
1138
+ /**
1139
+ * Stream with automatic fallback on overloaded errors.
1140
+ * Tries each adapter in the fallback chain until one succeeds.
1141
+ * Yields a mix of the primary response or fallback response events.
1142
+ */
1143
+ async *streamWithFallback(request, primaryModel) {
1144
+ const chain = this.resolveFallbackChain(primaryModel);
1145
+ const errors = [];
1146
+ for (const adapter of chain) {
1147
+ try {
1148
+ yield* adapter.stream(request);
1149
+ return;
1150
+ } catch (err) {
1151
+ const error = err;
1152
+ errors.push(error);
1153
+ if (!isFallbackTriggeredError(error)) {
1154
+ throw error;
1155
+ }
1156
+ continue;
1157
+ }
1158
+ }
1159
+ const lastError = errors[errors.length - 1];
1160
+ if (chain.length > 1) {
1161
+ throw new FallbackTriggeredError(
1162
+ `All ${chain.length} models in fallback chain failed. Last error: ${lastError?.message ?? "unknown"}`,
1163
+ lastError ?? new Error("unknown"),
1164
+ chain[0]?.config.model ?? "unknown",
1165
+ chain[chain.length - 1]?.config.model ?? "unknown"
1166
+ );
1167
+ }
1168
+ throw lastError ?? new Error("No adapters available");
1169
+ }
1170
+ /**
1171
+ * Complete with automatic fallback on overloaded errors.
1172
+ * Tries each adapter in the fallback chain until one succeeds.
1173
+ */
1174
+ async completeWithFallback(request, primaryModel) {
1175
+ const chain = this.resolveFallbackChain(primaryModel);
1176
+ const errors = [];
1177
+ for (const adapter of chain) {
1178
+ try {
1179
+ const result = await adapter.complete(request);
1180
+ return { result, usedModel: adapter.config.model };
1181
+ } catch (err) {
1182
+ const error = err;
1183
+ errors.push(error);
1184
+ if (!isFallbackTriggeredError(error)) {
1185
+ throw error;
1186
+ }
1187
+ continue;
1188
+ }
1189
+ }
1190
+ const lastError = errors[errors.length - 1];
1191
+ if (chain.length > 1) {
1192
+ throw new FallbackTriggeredError(
1193
+ `All ${chain.length} models in fallback chain failed. Last error: ${lastError?.message ?? "unknown"}`,
1194
+ lastError ?? new Error("unknown"),
1195
+ chain[0]?.config.model ?? "unknown",
1196
+ chain[chain.length - 1]?.config.model ?? "unknown"
1197
+ );
1198
+ }
1199
+ throw lastError ?? new Error("No adapters available");
1200
+ }
1201
+ /** Find an alternative adapter different from the excluded one */
1202
+ findAlternativeAdapter(exclude) {
1203
+ const adapters = this.registry.getAll();
1204
+ return adapters.find((a) => a !== exclude) ?? null;
1205
+ }
1015
1206
  findAdapterForModel(modelName) {
1016
1207
  const adapters = this.registry.getAll();
1017
1208
  return adapters.find((a) => a.config.model === modelName) ?? adapters.find((a) => a.config.model.includes(modelName)) ?? null;
@@ -1096,16 +1287,24 @@ var ModelAccessManager = class {
1096
1287
  // src/tools/registry.ts
1097
1288
  var ToolRegistry = class {
1098
1289
  tools = /* @__PURE__ */ new Map();
1099
- /** Register a tool */
1290
+ aliasMap = /* @__PURE__ */ new Map();
1291
+ /** Register a tool (with optional aliases) */
1100
1292
  register(tool) {
1101
1293
  if (this.tools.has(tool.name)) {
1102
1294
  throw new Error(`Tool "${tool.name}" is already registered`);
1103
1295
  }
1104
1296
  this.tools.set(tool.name, tool);
1297
+ if (tool.aliases) {
1298
+ for (const alias of tool.aliases) {
1299
+ if (!this.aliasMap.has(alias)) {
1300
+ this.aliasMap.set(alias, tool);
1301
+ }
1302
+ }
1303
+ }
1105
1304
  }
1106
- /** Get a tool by name */
1305
+ /** Get a tool by name or alias */
1107
1306
  get(name) {
1108
- return this.tools.get(name);
1307
+ return this.tools.get(name) ?? this.aliasMap.get(name);
1109
1308
  }
1110
1309
  /** Get all registered tools */
1111
1310
  getAll() {
@@ -1115,17 +1314,33 @@ var ToolRegistry = class {
1115
1314
  getToolDefinitions() {
1116
1315
  return this.getAll().map((t) => t.toToolDefinition());
1117
1316
  }
1118
- /** Check if a tool exists */
1317
+ /** Check if a tool exists (by name or alias) */
1119
1318
  has(name) {
1120
- return this.tools.has(name);
1319
+ return this.tools.has(name) || this.aliasMap.has(name);
1320
+ }
1321
+ /** Search tools by keyword in name, description, or searchHint */
1322
+ search(keyword) {
1323
+ const lower = keyword.toLowerCase();
1324
+ return this.getAll().filter(
1325
+ (t) => t.name.toLowerCase().includes(lower) || t.description.toLowerCase().includes(lower) || (t.searchHint?.toLowerCase().includes(lower) ?? false)
1326
+ );
1121
1327
  }
1122
- /** Unregister a tool */
1328
+ /** Unregister a tool (and its aliases) */
1123
1329
  unregister(name) {
1124
- this.tools.delete(name);
1330
+ const tool = this.tools.get(name);
1331
+ if (tool) {
1332
+ this.tools.delete(name);
1333
+ if (tool.aliases) {
1334
+ for (const alias of tool.aliases) {
1335
+ this.aliasMap.delete(alias);
1336
+ }
1337
+ }
1338
+ }
1125
1339
  }
1126
- /** Clear all tools */
1340
+ /** Clear all tools and aliases */
1127
1341
  clear() {
1128
1342
  this.tools.clear();
1343
+ this.aliasMap.clear();
1129
1344
  }
1130
1345
  };
1131
1346
 
@@ -1308,7 +1523,7 @@ var BashTool = class extends BaseTool {
1308
1523
  }
1309
1524
  const timeoutMs = input.timeout ?? 3e4;
1310
1525
  const command = process.platform === "win32" ? `chcp 65001 >nul 2>&1 && ${input.command}` : input.command;
1311
- return new Promise((resolve9) => {
1526
+ return new Promise((resolve10) => {
1312
1527
  const child = exec(
1313
1528
  command,
1314
1529
  {
@@ -1324,12 +1539,12 @@ var BashTool = class extends BaseTool {
1324
1539
  if (error) {
1325
1540
  result += (result ? "\n" : "") + `Exit code: ${error.code ?? "unknown"}`;
1326
1541
  }
1327
- resolve9(result || "(no output)");
1542
+ resolve10(result || "(no output)");
1328
1543
  }
1329
1544
  );
1330
1545
  context.abortSignal.addEventListener("abort", () => {
1331
1546
  child.kill("SIGTERM");
1332
- resolve9("Error: Command was aborted by user");
1547
+ resolve10("Error: Command was aborted by user");
1333
1548
  }, { once: true });
1334
1549
  });
1335
1550
  }
@@ -1567,10 +1782,10 @@ var GrepTool = class extends BaseTool {
1567
1782
  }
1568
1783
  }
1569
1784
  async isRgAvailable() {
1570
- return new Promise((resolve9) => {
1785
+ return new Promise((resolve10) => {
1571
1786
  const cmd = process.platform === "win32" ? "rg.exe" : "rg";
1572
1787
  execFile(cmd, ["--version"], (err) => {
1573
- resolve9(!err);
1788
+ resolve10(!err);
1574
1789
  });
1575
1790
  });
1576
1791
  }
@@ -1596,23 +1811,23 @@ var GrepTool = class extends BaseTool {
1596
1811
  args.push("--max-count", String(input.max_results));
1597
1812
  args.push(input.pattern, searchPath);
1598
1813
  const cmd = process.platform === "win32" ? "rg.exe" : "rg";
1599
- return new Promise((resolve9) => {
1814
+ return new Promise((resolve10) => {
1600
1815
  execFile(cmd, args, { maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
1601
1816
  if (error && !stdout) {
1602
1817
  if (error.code === "1") {
1603
- resolve9("No matches found.");
1818
+ resolve10("No matches found.");
1604
1819
  }
1605
- resolve9(`Error: ${stderr || error.message}`);
1820
+ resolve10(`Error: ${stderr || error.message}`);
1606
1821
  return;
1607
1822
  }
1608
1823
  const output = stdout.trim();
1609
1824
  if (!output) {
1610
- resolve9("No matches found.");
1825
+ resolve10("No matches found.");
1611
1826
  return;
1612
1827
  }
1613
1828
  const lines = output.split("\n");
1614
1829
  const maxLines = lines.slice(0, input.max_results);
1615
- resolve9(maxLines.join("\n"));
1830
+ resolve10(maxLines.join("\n"));
1616
1831
  });
1617
1832
  });
1618
1833
  }
@@ -2584,6 +2799,7 @@ var LspTool = class extends BaseTool {
2584
2799
 
2585
2800
  // src/tools/builtins/agent-tool.ts
2586
2801
  import { z as z15 } from "zod";
2802
+ import { randomUUID as randomUUID3 } from "crypto";
2587
2803
 
2588
2804
  // src/services/streaming-tool-executor.ts
2589
2805
  var StreamingToolExecutor = class {
@@ -2592,6 +2808,9 @@ var StreamingToolExecutor = class {
2592
2808
  bashErrorDescription = "";
2593
2809
  siblingAbortController = new AbortController();
2594
2810
  discarded = false;
2811
+ // P0.2: Progress events buffer
2812
+ pendingProgress = [];
2813
+ progressAvailableResolve = null;
2595
2814
  // Backward-compatible fields
2596
2815
  toolRegistry;
2597
2816
  executorConfig;
@@ -2623,10 +2842,19 @@ var StreamingToolExecutor = class {
2623
2842
  this.discarded = true;
2624
2843
  this.siblingAbortController.abort("discarded");
2625
2844
  }
2845
+ /** P0.2: Emit a progress event for a running tool */
2846
+ onToolProgress(toolName, toolId, message) {
2847
+ this.pendingProgress.push({ toolName, toolId, message });
2848
+ if (this.progressAvailableResolve) {
2849
+ this.progressAvailableResolve();
2850
+ this.progressAvailableResolve = null;
2851
+ }
2852
+ }
2626
2853
  /** Add a tool to the execution queue. Starts immediately if conditions allow. */
2627
2854
  addTool(toolCall) {
2628
2855
  const tool = this.toolRegistry.get(toolCall.name);
2629
- const isConcurrencySafe = tool ? tool.concurrencySafe : false;
2856
+ const safeFlag = tool ? tool.concurrencySafe : false;
2857
+ const isConcurrencySafe = typeof safeFlag === "function" ? safeFlag(toolCall.input) : safeFlag;
2630
2858
  this.tools.push({
2631
2859
  id: toolCall.id,
2632
2860
  name: toolCall.name,
@@ -2648,20 +2876,40 @@ var StreamingToolExecutor = class {
2648
2876
  }
2649
2877
  }
2650
2878
  }
2651
- /** Wait for all remaining tools and yield results as they complete */
2879
+ /** Drain buffered progress events */
2880
+ *drainProgress() {
2881
+ while (this.pendingProgress.length > 0) {
2882
+ yield this.pendingProgress.shift();
2883
+ }
2884
+ }
2885
+ /** Wait for all remaining tools and yield results + progress events as they arrive */
2652
2886
  async *getRemainingResults() {
2653
2887
  while (this.hasUnfinishedTools()) {
2654
2888
  await this.processQueue();
2889
+ for (const progress of this.drainProgress()) {
2890
+ yield { type: "progress", progress };
2891
+ }
2655
2892
  for (const result of this.getCompletedResults()) {
2656
2893
  yield result;
2657
2894
  }
2658
2895
  if (this.hasExecutingTools() && !this.hasCompletedResults()) {
2659
2896
  const executingPromises = this.tools.filter((t) => t.status === "executing" && t.promise).map((t) => t.promise);
2897
+ const progressPromise = new Promise((resolve10) => {
2898
+ this.progressAvailableResolve = resolve10;
2899
+ });
2660
2900
  if (executingPromises.length > 0) {
2661
- await Promise.race(executingPromises);
2901
+ await Promise.race([...executingPromises, progressPromise]);
2902
+ } else {
2903
+ await progressPromise;
2904
+ }
2905
+ for (const progress of this.drainProgress()) {
2906
+ yield { type: "progress", progress };
2662
2907
  }
2663
2908
  }
2664
2909
  }
2910
+ for (const progress of this.drainProgress()) {
2911
+ yield { type: "progress", progress };
2912
+ }
2665
2913
  for (const result of this.getCompletedResults()) {
2666
2914
  yield result;
2667
2915
  }
@@ -2720,6 +2968,8 @@ var StreamingToolExecutor = class {
2720
2968
  this.bashErrorDescription = "";
2721
2969
  this.siblingAbortController = new AbortController();
2722
2970
  this.discarded = false;
2971
+ this.pendingProgress = [];
2972
+ this.progressAvailableResolve = null;
2723
2973
  }
2724
2974
  canExecuteTool(isConcurrencySafe) {
2725
2975
  if (this.discarded) return false;
@@ -2750,12 +3000,29 @@ var StreamingToolExecutor = class {
2750
3000
  return;
2751
3001
  }
2752
3002
  const startTime = performance.now();
3003
+ const externalSignal = context?.abortSignal ?? this.toolContext?.abortSignal;
3004
+ const linkedController = new AbortController();
3005
+ const onDiscard = () => linkedController.abort("discarded");
3006
+ this.siblingAbortController.signal.addEventListener("abort", onDiscard);
3007
+ if (externalSignal) {
3008
+ externalSignal.addEventListener("abort", onDiscard);
3009
+ if (externalSignal.aborted) linkedController.abort("external");
3010
+ }
3011
+ const progressCallback = (message) => {
3012
+ this.onToolProgress(tool.name, tool.id, message);
3013
+ };
2753
3014
  try {
2754
3015
  if (this.executeFn && this.autoMode && this.onPermissionRequest) {
3016
+ const baseCtx = context ?? this.toolContext;
3017
+ const ctxWithProgress = {
3018
+ ...baseCtx,
3019
+ abortSignal: linkedController.signal,
3020
+ onProgress: progressCallback
3021
+ };
2755
3022
  const result = await this.executeFn(
2756
3023
  { id: tool.id, name: tool.name, input: tool.input },
2757
3024
  this.toolRegistry,
2758
- context ?? this.toolContext,
3025
+ ctxWithProgress,
2759
3026
  this.autoMode,
2760
3027
  this.onPermissionRequest
2761
3028
  );
@@ -2767,9 +3034,10 @@ var StreamingToolExecutor = class {
2767
3034
  tool.status = "completed";
2768
3035
  return;
2769
3036
  }
2770
- const ctx = context ?? { cwd: process.cwd(), abortSignal: new AbortController().signal, checkPermission: async () => true };
3037
+ const ctx = context ?? { cwd: process.cwd(), abortSignal: linkedController.signal, checkPermission: async () => true };
3038
+ const execCtx = { ...ctx, abortSignal: linkedController.signal, onProgress: progressCallback };
2771
3039
  const validatedInput = registeredTool.inputSchema.parse(tool.input);
2772
- const rawOutput = await registeredTool.execute(validatedInput, ctx);
3040
+ const rawOutput = await registeredTool.execute(validatedInput, execCtx);
2773
3041
  const output = rawOutput.length > 5e4 ? rawOutput.slice(0, 5e4) + "\n\n... [truncated]" : rawOutput;
2774
3042
  tool.result = {
2775
3043
  toolResult: {
@@ -2814,6 +3082,11 @@ var StreamingToolExecutor = class {
2814
3082
  isError: true
2815
3083
  }
2816
3084
  };
3085
+ } finally {
3086
+ this.siblingAbortController.signal.removeEventListener("abort", onDiscard);
3087
+ if (externalSignal) {
3088
+ externalSignal.removeEventListener("abort", onDiscard);
3089
+ }
2817
3090
  }
2818
3091
  tool.status = "completed";
2819
3092
  void this.processQueue(context);
@@ -2913,8 +3186,20 @@ function countLatinChars(text) {
2913
3186
  var DEFAULT_CONFIG = {
2914
3187
  maxTokens: 128e3,
2915
3188
  compactionThreshold: 0.8,
2916
- keepRecentMessages: 4
3189
+ keepRecentMessages: 6
2917
3190
  };
3191
+ var AUTOCOMPACT_BUFFER_TOKENS = 13e3;
3192
+ var MAX_CONSECUTIVE_FAILURES = 3;
3193
+ function calculateTokenWarningState(tokenUsage, maxTokens) {
3194
+ const usagePercent = tokenUsage / maxTokens;
3195
+ return {
3196
+ usagePercent,
3197
+ isNormal: usagePercent <= 0.8,
3198
+ isWarning: usagePercent > 0.8 && usagePercent <= 0.9,
3199
+ isError: usagePercent > 0.9 && usagePercent <= 0.95,
3200
+ shouldAutoCompact: usagePercent > 0.95
3201
+ };
3202
+ }
2918
3203
  var SUMMARIZATION_PROMPT = `Summarize the following conversation history concisely, preserving:
2919
3204
  - Key decisions and their rationale
2920
3205
  - Important code changes made
@@ -2927,13 +3212,18 @@ Conversation to summarize:
2927
3212
  var ContextCompactor = class {
2928
3213
  config;
2929
3214
  adapter;
3215
+ consecutiveFailures = 0;
2930
3216
  constructor(config, adapter) {
2931
3217
  this.config = { ...DEFAULT_CONFIG, ...config };
2932
3218
  this.adapter = adapter ?? null;
2933
3219
  }
2934
3220
  shouldCompact(messages) {
3221
+ if (this.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
3222
+ return false;
3223
+ }
2935
3224
  const totalTokens = this.estimateMessagesTokens(messages);
2936
- const threshold = this.config.compactionThreshold * this.config.maxTokens;
3225
+ const effectiveMax = this.config.maxTokens - AUTOCOMPACT_BUFFER_TOKENS;
3226
+ const threshold = this.config.compactionThreshold * effectiveMax;
2937
3227
  return totalTokens > threshold;
2938
3228
  }
2939
3229
  async compact(messages, systemPrompt) {
@@ -2960,11 +3250,22 @@ var ContextCompactor = class {
2960
3250
  };
2961
3251
  }
2962
3252
  let summary;
2963
- if (this.adapter) {
2964
- summary = await this.generateSummary(oldMessages);
2965
- } else {
3253
+ let succeeded = false;
3254
+ try {
3255
+ if (this.adapter) {
3256
+ summary = await this.generateSummary(oldMessages);
3257
+ succeeded = true;
3258
+ } else {
3259
+ summary = this.truncateMessages(oldMessages);
3260
+ succeeded = true;
3261
+ }
3262
+ } catch {
3263
+ this.consecutiveFailures++;
2966
3264
  summary = this.truncateMessages(oldMessages);
2967
3265
  }
3266
+ if (succeeded) {
3267
+ this.consecutiveFailures = 0;
3268
+ }
2968
3269
  const summaryMessage = {
2969
3270
  role: "user",
2970
3271
  content: `[Previous conversation summary]
@@ -3018,30 +3319,96 @@ ${summary}`
3018
3319
 
3019
3320
  // src/services/cost-tracker.ts
3020
3321
  var DEFAULT_PRICES = {
3322
+ // OpenAI
3021
3323
  "gpt-4o": { input: 2.5, output: 10, cacheRead: 1.25, cacheWrite: 2.5 },
3022
3324
  "gpt-4o-mini": { input: 0.15, output: 0.6, cacheRead: 0.075, cacheWrite: 0.15 },
3023
3325
  "gpt-4-turbo": { input: 10, output: 30, cacheRead: 5, cacheWrite: 10 },
3024
3326
  "gpt-3.5-turbo": { input: 0.5, output: 1.5, cacheRead: 0.25, cacheWrite: 0.5 },
3327
+ "o1": { input: 15, output: 60, cacheRead: 7.5, cacheWrite: 15 },
3328
+ "o1-mini": { input: 3, output: 12, cacheRead: 1.5, cacheWrite: 3 },
3329
+ "o3-mini": { input: 1.1, output: 4.4, cacheRead: 0.55, cacheWrite: 1.1 },
3330
+ // Anthropic
3025
3331
  "claude-3.5-sonnet": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
3026
3332
  "claude-3-opus": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
3027
3333
  "claude-3-haiku": { input: 0.25, output: 1.25, cacheRead: 0.03, cacheWrite: 0.3 },
3334
+ "claude-3.5-haiku": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
3335
+ // DeepSeek
3028
3336
  "deepseek-chat": { input: 0.14, output: 0.28, cacheRead: 0.014, cacheWrite: 0.14 },
3029
- "deepseek-reasoner": { input: 0.55, output: 2.19, cacheRead: 0.14, cacheWrite: 0.55 }
3337
+ "deepseek-reasoner": { input: 0.55, output: 2.19, cacheRead: 0.14, cacheWrite: 0.55 },
3338
+ // Google
3339
+ "gemini-2.0-flash": { input: 0.1, output: 0.4, cacheRead: 0.025, cacheWrite: 0.1 },
3340
+ "gemini-1.5-pro": { input: 1.25, output: 5, cacheRead: 0.3125, cacheWrite: 1.25 },
3341
+ "gemini-1.5-flash": { input: 0.075, output: 0.3, cacheRead: 0.01875, cacheWrite: 0.075 },
3342
+ // GLM
3343
+ "glm-4": { input: 1, output: 3, cacheRead: 0.5, cacheWrite: 1 },
3344
+ "glm-4.5": { input: 1.5, output: 4.5, cacheRead: 0.75, cacheWrite: 1.5 },
3345
+ "glm-4.7": { input: 2, output: 6, cacheRead: 1, cacheWrite: 2 },
3346
+ "glm-5": { input: 2.5, output: 8, cacheRead: 1.25, cacheWrite: 2.5 },
3347
+ "glm-5.1": { input: 3, output: 10, cacheRead: 1.5, cacheWrite: 3 },
3348
+ "glm-5-turbo": { input: 0.5, output: 2, cacheRead: 0.25, cacheWrite: 0.5 },
3349
+ // Qwen
3350
+ "qwen-2.5-72b": { input: 0.4, output: 1.2, cacheRead: 0.2, cacheWrite: 0.4 },
3351
+ // Llama
3352
+ "llama-3.1-405b": { input: 3, output: 9, cacheRead: 1.5, cacheWrite: 3 }
3030
3353
  };
3354
+ var MODEL_PATTERNS2 = [
3355
+ { pattern: /gpt-4o-mini/i, baseModel: "gpt-4o-mini" },
3356
+ { pattern: /gpt-4o/i, baseModel: "gpt-4o" },
3357
+ { pattern: /gpt-4-turbo/i, baseModel: "gpt-4-turbo" },
3358
+ { pattern: /gpt-3\.5/i, baseModel: "gpt-3.5-turbo" },
3359
+ { pattern: /o1-mini/i, baseModel: "o1-mini" },
3360
+ { pattern: /o3-mini/i, baseModel: "o3-mini" },
3361
+ { pattern: /o1/i, baseModel: "o1" },
3362
+ { pattern: /claude-3\.5-sonnet/i, baseModel: "claude-3.5-sonnet" },
3363
+ { pattern: /claude-3\.5-haiku/i, baseModel: "claude-3.5-haiku" },
3364
+ { pattern: /claude-3-opus/i, baseModel: "claude-3-opus" },
3365
+ { pattern: /claude-3-haiku/i, baseModel: "claude-3-haiku" },
3366
+ { pattern: /deepseek-reasoner/i, baseModel: "deepseek-reasoner" },
3367
+ { pattern: /deepseek-chat/i, baseModel: "deepseek-chat" },
3368
+ { pattern: /deepseek/i, baseModel: "deepseek-chat" },
3369
+ { pattern: /gemini-2\.0-flash/i, baseModel: "gemini-2.0-flash" },
3370
+ { pattern: /gemini-1\.5-pro/i, baseModel: "gemini-1.5-pro" },
3371
+ { pattern: /gemini-1\.5-flash/i, baseModel: "gemini-1.5-flash" },
3372
+ { pattern: /gemini/i, baseModel: "gemini-2.0-flash" },
3373
+ { pattern: /glm-5-turbo/i, baseModel: "glm-5-turbo" },
3374
+ { pattern: /glm-5\.1/i, baseModel: "glm-5.1" },
3375
+ { pattern: /glm-5/i, baseModel: "glm-5" },
3376
+ { pattern: /glm-4\.7/i, baseModel: "glm-4.7" },
3377
+ { pattern: /glm-4\.5/i, baseModel: "glm-4.5" },
3378
+ { pattern: /glm-4/i, baseModel: "glm-4" },
3379
+ { pattern: /glm/i, baseModel: "glm-5.1" },
3380
+ { pattern: /qwen.*72b/i, baseModel: "qwen-2.5-72b" },
3381
+ { pattern: /qwen/i, baseModel: "qwen-2.5-72b" },
3382
+ { pattern: /llama.*405b/i, baseModel: "llama-3.1-405b" },
3383
+ { pattern: /llama/i, baseModel: "llama-3.1-405b" }
3384
+ ];
3031
3385
  var CostTracker = class {
3032
3386
  entries = [];
3033
3387
  config;
3034
3388
  sessionStart;
3389
+ lastBudgetLevel = "normal";
3035
3390
  constructor(config) {
3036
3391
  this.config = {
3037
3392
  prices: { ...DEFAULT_PRICES, ...config?.prices },
3038
- budgetLimit: config?.budgetLimit
3393
+ budgetLimit: config?.budgetLimit,
3394
+ warningThreshold: config?.warningThreshold ?? 0.75,
3395
+ criticalThreshold: config?.criticalThreshold ?? 0.9
3039
3396
  };
3040
3397
  this.sessionStart = Date.now();
3041
3398
  }
3399
+ /** Find prices for a model — exact match first, then fuzzy pattern matching */
3400
+ findPrices(model) {
3401
+ if (this.config.prices[model]) return this.config.prices[model];
3402
+ for (const { pattern, baseModel } of MODEL_PATTERNS2) {
3403
+ if (pattern.test(model)) {
3404
+ return this.config.prices[baseModel];
3405
+ }
3406
+ }
3407
+ return void 0;
3408
+ }
3042
3409
  /** Record token usage for a model */
3043
3410
  recordUsage(model, usage) {
3044
- const prices = this.config.prices[model];
3411
+ const prices = this.findPrices(model);
3045
3412
  let cost = 0;
3046
3413
  if (prices) {
3047
3414
  const inputCost = usage.inputTokens / 1e6 * prices.input;
@@ -3128,6 +3495,66 @@ var CostTracker = class {
3128
3495
  percent
3129
3496
  };
3130
3497
  }
3498
+ /**
3499
+ * Get detailed budget status with warning levels.
3500
+ * Returns current budget level and whether it changed since last check.
3501
+ */
3502
+ getBudgetStatus() {
3503
+ const used = this.getSessionTotal();
3504
+ const limit = this.config.budgetLimit;
3505
+ if (!limit) {
3506
+ return { level: "normal", used, percent: 0 };
3507
+ }
3508
+ const percent = used / limit * 100;
3509
+ const ratio = used / limit;
3510
+ let level = "normal";
3511
+ if (ratio >= 1) level = "exceeded";
3512
+ else if (ratio >= (this.config.criticalThreshold ?? 0.9)) level = "critical";
3513
+ else if (ratio >= (this.config.warningThreshold ?? 0.75)) level = "warning";
3514
+ return {
3515
+ level,
3516
+ used,
3517
+ limit,
3518
+ percent,
3519
+ remainingBudget: limit - used
3520
+ };
3521
+ }
3522
+ /**
3523
+ * Check if budget level changed since last call.
3524
+ * Returns the new status if it escalated, null if unchanged.
3525
+ * Use this to emit budget warnings to the user.
3526
+ */
3527
+ checkBudgetWarning() {
3528
+ const status = this.getBudgetStatus();
3529
+ const levels = ["normal", "warning", "critical", "exceeded"];
3530
+ const currentIdx = levels.indexOf(status.level);
3531
+ const lastIdx = levels.indexOf(this.lastBudgetLevel);
3532
+ if (currentIdx > lastIdx) {
3533
+ this.lastBudgetLevel = status.level;
3534
+ return status;
3535
+ }
3536
+ this.lastBudgetLevel = status.level;
3537
+ return null;
3538
+ }
3539
+ /** Get cache efficiency metrics */
3540
+ getCacheMetrics() {
3541
+ let totalCacheReads = 0;
3542
+ let totalCacheWrites = 0;
3543
+ let savings = 0;
3544
+ for (const entry of this.entries) {
3545
+ totalCacheReads += entry.cacheReadTokens;
3546
+ totalCacheWrites += entry.cacheCreationTokens;
3547
+ const prices = this.findPrices(entry.model);
3548
+ if (prices) {
3549
+ const fullInputCost = entry.inputTokens / 1e6 * prices.input;
3550
+ const actualInputCost = (entry.inputTokens - entry.cacheReadTokens) / 1e6 * prices.input + entry.cacheReadTokens / 1e6 * prices.cacheRead;
3551
+ savings += fullInputCost - actualInputCost;
3552
+ }
3553
+ }
3554
+ const totalInputTokens = this.entries.reduce((sum, e) => sum + e.inputTokens, 0);
3555
+ const cacheHitRate = totalInputTokens > 0 ? totalCacheReads / totalInputTokens : 0;
3556
+ return { totalCacheReads, totalCacheWrites, cacheHitRate, savings };
3557
+ }
3131
3558
  /** Get session duration in ms */
3132
3559
  getSessionDuration() {
3133
3560
  return Date.now() - this.sessionStart;
@@ -3136,6 +3563,7 @@ var CostTracker = class {
3136
3563
  reset() {
3137
3564
  this.entries = [];
3138
3565
  this.sessionStart = Date.now();
3566
+ this.lastBudgetLevel = "normal";
3139
3567
  }
3140
3568
  };
3141
3569
 
@@ -3246,6 +3674,41 @@ function extractToolCalls(content) {
3246
3674
  );
3247
3675
  }
3248
3676
 
3677
+ // src/infra/abort-controller.ts
3678
+ import { setMaxListeners } from "events";
3679
+ var DEFAULT_MAX_LISTENERS = 50;
3680
+ function createAbortController(maxListeners = DEFAULT_MAX_LISTENERS) {
3681
+ const controller = new AbortController();
3682
+ setMaxListeners(maxListeners, controller.signal);
3683
+ return controller;
3684
+ }
3685
+ function createChildAbortController(parent, maxListeners) {
3686
+ const child = createAbortController(maxListeners);
3687
+ const parentSignal = parent instanceof AbortController ? parent.signal : parent.signal;
3688
+ if (parentSignal.aborted) {
3689
+ child.abort(parentSignal.reason);
3690
+ return child;
3691
+ }
3692
+ const weakChild = new WeakRef(child);
3693
+ const weakParentRef = new WeakRef(parent);
3694
+ const handler = () => {
3695
+ const p = weakParentRef.deref();
3696
+ const signal = p instanceof AbortController ? p.signal : p.signal;
3697
+ weakChild.deref()?.abort(signal.reason);
3698
+ };
3699
+ parentSignal.addEventListener("abort", handler, { once: true });
3700
+ child.signal.addEventListener(
3701
+ "abort",
3702
+ () => {
3703
+ const p = weakParentRef.deref();
3704
+ const signal = p instanceof AbortController ? p.signal : p.signal;
3705
+ signal.removeEventListener("abort", handler);
3706
+ },
3707
+ { once: true }
3708
+ );
3709
+ return child;
3710
+ }
3711
+
3249
3712
  // src/core/query-engine.ts
3250
3713
  var DEFAULT_CONFIG2 = {
3251
3714
  maxTurns: 50,
@@ -3259,7 +3722,7 @@ var QueryEngine = class {
3259
3722
  executor;
3260
3723
  compactor;
3261
3724
  costTracker;
3262
- abortController = new AbortController();
3725
+ abortController = createAbortController();
3263
3726
  constructor(config, adapter, tools) {
3264
3727
  this.config = { ...DEFAULT_CONFIG2, ...config };
3265
3728
  this.adapter = adapter;
@@ -3278,6 +3741,7 @@ var QueryEngine = class {
3278
3741
  let totalInputTokens = 0;
3279
3742
  let totalOutputTokens = 0;
3280
3743
  let wasCompacted = false;
3744
+ const runAbortController = this.config.signal ? createChildAbortController({ signal: this.config.signal }) : this.abortController;
3281
3745
  messages.push({
3282
3746
  role: "user",
3283
3747
  content: [{ type: "text", text: prompt }]
@@ -3285,11 +3749,11 @@ var QueryEngine = class {
3285
3749
  const toolDefs = this.tools.getToolDefinitions();
3286
3750
  const toolContext = {
3287
3751
  cwd: process.cwd(),
3288
- abortSignal: this.abortController.signal,
3752
+ abortSignal: runAbortController.signal,
3289
3753
  checkPermission: this.config.checkPermission ?? (async () => true)
3290
3754
  };
3291
3755
  let turn = 0;
3292
- while (turn < this.config.maxTurns && !this.abortController.signal.aborted) {
3756
+ while (turn < this.config.maxTurns && !runAbortController.signal.aborted) {
3293
3757
  turn++;
3294
3758
  yield { type: "turn_start", turn };
3295
3759
  try {
@@ -3326,11 +3790,11 @@ ${compactionResult.summary}` }]
3326
3790
  systemPrompt: this.config.systemPrompt,
3327
3791
  maxTokens: Math.max(this.adapter.config.maxTokens, 16384),
3328
3792
  stream: true,
3329
- signal: this.config.signal
3793
+ signal: runAbortController.signal
3330
3794
  };
3331
3795
  const { assistantContent, stopReason, usage, events } = await parseStreamToBlocks(
3332
3796
  this.adapter.stream(request),
3333
- this.abortController.signal
3797
+ runAbortController.signal
3334
3798
  );
3335
3799
  for (const evt of events) {
3336
3800
  if (evt.type === "text_delta") {
@@ -3357,11 +3821,12 @@ ${compactionResult.summary}` }]
3357
3821
  if (toolCalls.length > 0) {
3358
3822
  const executorRequests = toolCalls.map((tc) => {
3359
3823
  const tool = this.tools.get(tc.name);
3824
+ const safeFlag = tool?.concurrencySafe ?? false;
3360
3825
  return {
3361
3826
  id: tc.id,
3362
3827
  name: tc.name,
3363
3828
  input: tc.input,
3364
- concurrencySafe: tool?.concurrencySafe ?? false
3829
+ concurrencySafe: typeof safeFlag === "function" ? safeFlag(tc.input) : safeFlag
3365
3830
  };
3366
3831
  });
3367
3832
  for (const tc of toolCalls) {
@@ -3418,6 +3883,13 @@ function formatContentBlocks(blocks) {
3418
3883
  }).join("\n");
3419
3884
  }
3420
3885
 
3886
+ // src/infra/agent-context.ts
3887
+ import { AsyncLocalStorage } from "async_hooks";
3888
+ var agentContextStorage = new AsyncLocalStorage();
3889
+ function runWithAgentContext(ctx, fn) {
3890
+ return agentContextStorage.run(ctx, fn);
3891
+ }
3892
+
3421
3893
  // src/tools/builtins/agent-tool.ts
3422
3894
  var agentInputSchema = z15.object({
3423
3895
  task: z15.string().describe("Description of the task for the sub-agent"),
@@ -3492,10 +3964,12 @@ var AgentTool = class extends BaseTool {
3492
3964
  let finalText = "";
3493
3965
  let turnsUsed = 0;
3494
3966
  try {
3495
- for await (const event of engine.run(input.task)) {
3496
- if (subAgentController.signal.aborted) {
3497
- break;
3498
- }
3967
+ const eventStream = runWithAgentContext(
3968
+ { agentId: randomUUID3(), agentType: "subagent", agentName: "agent" },
3969
+ () => engine.run(input.task)
3970
+ );
3971
+ for await (const event of eventStream) {
3972
+ if (subAgentController.signal.aborted) break;
3499
3973
  switch (event.type) {
3500
3974
  case "text_delta":
3501
3975
  finalText += event.text;
@@ -3559,12 +4033,12 @@ import { execFile as execFile2 } from "child_process";
3559
4033
  import { readFile as readFile5, unlink } from "fs/promises";
3560
4034
  import { tmpdir } from "os";
3561
4035
  import { join as join5 } from "path";
3562
- import { randomUUID as randomUUID3 } from "crypto";
4036
+ import { randomUUID as randomUUID4 } from "crypto";
3563
4037
  function getPlatform() {
3564
4038
  return process.platform;
3565
4039
  }
3566
4040
  function execCommand(cmd, args) {
3567
- return new Promise((resolve9, reject) => {
4041
+ return new Promise((resolve10, reject) => {
3568
4042
  execFile2(cmd, args, { timeout: 15e3, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
3569
4043
  if (err) {
3570
4044
  reject(new Error(`Command "${cmd} ${args.join(" ")}" failed: ${err.message}`));
@@ -3574,12 +4048,12 @@ function execCommand(cmd, args) {
3574
4048
  reject(new Error(`Command stderr: ${stderr}`));
3575
4049
  return;
3576
4050
  }
3577
- resolve9(stdout.trim());
4051
+ resolve10(stdout.trim());
3578
4052
  });
3579
4053
  });
3580
4054
  }
3581
4055
  function execPowerShell(script) {
3582
- return new Promise((resolve9, reject) => {
4056
+ return new Promise((resolve10, reject) => {
3583
4057
  execFile2(
3584
4058
  "powershell.exe",
3585
4059
  ["-NoProfile", "-NonInteractive", "-Command", script],
@@ -3593,13 +4067,13 @@ function execPowerShell(script) {
3593
4067
  reject(new Error(`PowerShell stderr: ${stderr}`));
3594
4068
  return;
3595
4069
  }
3596
- resolve9(stdout.trim());
4070
+ resolve10(stdout.trim());
3597
4071
  }
3598
4072
  );
3599
4073
  });
3600
4074
  }
3601
4075
  async function tmpFile(ext) {
3602
- return join5(tmpdir(), `cliskill-capture-${randomUUID3()}.${ext}`);
4076
+ return join5(tmpdir(), `cliskill-capture-${randomUUID4()}.${ext}`);
3603
4077
  }
3604
4078
  async function readFileAsBase64(path) {
3605
4079
  const buf = await readFile5(path);
@@ -3743,7 +4217,7 @@ function getPlatform2() {
3743
4217
  return process.platform;
3744
4218
  }
3745
4219
  function execPowerShell2(script) {
3746
- return new Promise((resolve9, reject) => {
4220
+ return new Promise((resolve10, reject) => {
3747
4221
  execFile3(
3748
4222
  "powershell.exe",
3749
4223
  ["-NoProfile", "-NonInteractive", "-Command", script],
@@ -3757,13 +4231,13 @@ function execPowerShell2(script) {
3757
4231
  reject(new Error(`PowerShell stderr: ${stderr}`));
3758
4232
  return;
3759
4233
  }
3760
- resolve9(stdout.trim());
4234
+ resolve10(stdout.trim());
3761
4235
  }
3762
4236
  );
3763
4237
  });
3764
4238
  }
3765
4239
  function execCommand2(cmd, args) {
3766
- return new Promise((resolve9, reject) => {
4240
+ return new Promise((resolve10, reject) => {
3767
4241
  execFile3(cmd, args, { timeout: 1e4 }, (err, stdout, stderr) => {
3768
4242
  if (err) {
3769
4243
  reject(new Error(`Command "${cmd} ${args.join(" ")}" failed: ${err.message}`));
@@ -3773,7 +4247,7 @@ function execCommand2(cmd, args) {
3773
4247
  reject(new Error(`Command stderr: ${stderr}`));
3774
4248
  return;
3775
4249
  }
3776
- resolve9(stdout.trim());
4250
+ resolve10(stdout.trim());
3777
4251
  });
3778
4252
  });
3779
4253
  }
@@ -4327,7 +4801,7 @@ var ComputerUseTool = class extends BaseTool {
4327
4801
  import { z as z17 } from "zod";
4328
4802
 
4329
4803
  // src/remote/session-manager.ts
4330
- import { randomUUID as randomUUID4 } from "crypto";
4804
+ import { randomUUID as randomUUID5 } from "crypto";
4331
4805
  import { readFile as readFile6 } from "fs/promises";
4332
4806
  var DEFAULT_PORT = 22;
4333
4807
  var CONNECTION_TIMEOUT = 3e4;
@@ -4343,7 +4817,7 @@ var RemoteSessionManager = class {
4343
4817
  }
4344
4818
  async connect(config) {
4345
4819
  const ssh2 = await this.loadSsh2();
4346
- const id = randomUUID4();
4820
+ const id = randomUUID5();
4347
4821
  const session = {
4348
4822
  id,
4349
4823
  config: { ...config, port: config.port || DEFAULT_PORT },
@@ -4366,7 +4840,7 @@ var RemoteSessionManager = class {
4366
4840
  } else if (config.password) {
4367
4841
  connectConfig.password = config.password;
4368
4842
  }
4369
- await new Promise((resolve9, reject) => {
4843
+ await new Promise((resolve10, reject) => {
4370
4844
  const timeout = setTimeout(() => {
4371
4845
  reject(new Error(`Connection timeout to ${config.host}:${config.port}`));
4372
4846
  }, CONNECTION_TIMEOUT);
@@ -4375,7 +4849,7 @@ var RemoteSessionManager = class {
4375
4849
  session.status = "connected";
4376
4850
  session.connectedAt = /* @__PURE__ */ new Date();
4377
4851
  log.info(`Remote session ${id} connected to ${config.host}`);
4378
- resolve9();
4852
+ resolve10();
4379
4853
  });
4380
4854
  client.on("error", (err) => {
4381
4855
  clearTimeout(timeout);
@@ -4406,7 +4880,7 @@ var RemoteSessionManager = class {
4406
4880
  throw new Error(`Session ${sessionId} is not connected (status: ${pooled.session.status})`);
4407
4881
  }
4408
4882
  }
4409
- return new Promise((resolve9, reject) => {
4883
+ return new Promise((resolve10, reject) => {
4410
4884
  pooled.client.exec(command, (err, stream) => {
4411
4885
  if (err) {
4412
4886
  reject(err);
@@ -4419,7 +4893,7 @@ var RemoteSessionManager = class {
4419
4893
  stream.stderr.on("data", (data) => stderrChunks.push(data));
4420
4894
  stream.on("close", (code) => {
4421
4895
  exitCode = code ?? 0;
4422
- resolve9({
4896
+ resolve10({
4423
4897
  stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
4424
4898
  stderr: Buffer.concat(stderrChunks).toString("utf-8"),
4425
4899
  exitCode
@@ -4431,20 +4905,20 @@ var RemoteSessionManager = class {
4431
4905
  async downloadFile(sessionId, remotePath, localPath) {
4432
4906
  const pooled = this.getSessionOrThrow(sessionId);
4433
4907
  const sftp = await this.getSftp(pooled);
4434
- return new Promise((resolve9, reject) => {
4908
+ return new Promise((resolve10, reject) => {
4435
4909
  sftp.fastGet(remotePath, localPath, (err) => {
4436
4910
  if (err) reject(err);
4437
- else resolve9();
4911
+ else resolve10();
4438
4912
  });
4439
4913
  });
4440
4914
  }
4441
4915
  async uploadFile(sessionId, localPath, remotePath) {
4442
4916
  const pooled = this.getSessionOrThrow(sessionId);
4443
4917
  const sftp = await this.getSftp(pooled);
4444
- return new Promise((resolve9, reject) => {
4918
+ return new Promise((resolve10, reject) => {
4445
4919
  sftp.fastPut(localPath, remotePath, (err) => {
4446
4920
  if (err) reject(err);
4447
- else resolve9();
4921
+ else resolve10();
4448
4922
  });
4449
4923
  });
4450
4924
  }
@@ -4479,12 +4953,12 @@ var RemoteSessionManager = class {
4479
4953
  }
4480
4954
  async getSftp(pooled) {
4481
4955
  if (pooled.sftp) return pooled.sftp;
4482
- return new Promise((resolve9, reject) => {
4956
+ return new Promise((resolve10, reject) => {
4483
4957
  pooled.client.sftp((err, sftp) => {
4484
4958
  if (err) reject(err);
4485
4959
  else {
4486
4960
  pooled.sftp = sftp;
4487
- resolve9(sftp);
4961
+ resolve10(sftp);
4488
4962
  }
4489
4963
  });
4490
4964
  });
@@ -4501,7 +4975,7 @@ var RemoteSessionManager = class {
4501
4975
  return true;
4502
4976
  } catch {
4503
4977
  if (attempt < MAX_RECONNECT_ATTEMPTS) {
4504
- await new Promise((resolve9) => setTimeout(resolve9, RECONNECT_DELAY_MS * attempt));
4978
+ await new Promise((resolve10) => setTimeout(resolve10, RECONNECT_DELAY_MS * attempt));
4505
4979
  }
4506
4980
  }
4507
4981
  }
@@ -5140,6 +5614,196 @@ var MemoryTool = class extends BaseTool {
5140
5614
  }
5141
5615
  };
5142
5616
 
5617
+ // src/tools/builtins/tool-search-tool.ts
5618
+ import { z as z20 } from "zod";
5619
+ var inputSchema = z20.object({
5620
+ query: z20.string().describe("Search query \u2014 matches against tool name, aliases, description, and searchHint"),
5621
+ limit: z20.number().min(1).max(20).optional().describe("Maximum results to return (default: 10)")
5622
+ });
5623
+ var ToolSearchTool = class extends BaseTool {
5624
+ name = "tool_search";
5625
+ aliases = ["search-tools", "find-tool"];
5626
+ description = "Search available tools by keyword. Returns matching tools with name, description, and usage hints.";
5627
+ searchHint = "discover find lookup explore tools capabilities";
5628
+ inputSchema = inputSchema;
5629
+ riskLevel = "readonly";
5630
+ concurrencySafe = true;
5631
+ readOnly = true;
5632
+ getRegistry;
5633
+ constructor(registry) {
5634
+ super();
5635
+ this.getRegistry = registry;
5636
+ }
5637
+ async execute(input, _context) {
5638
+ const registry = this.getRegistry();
5639
+ const results = registry.search(input.query);
5640
+ const limit = input.limit ?? 10;
5641
+ const limited = results.slice(0, limit);
5642
+ if (limited.length === 0) {
5643
+ return `No tools found matching "${input.query}". Use 'list' to see all available tools.`;
5644
+ }
5645
+ const lines = limited.map((tool) => {
5646
+ const aliasStr = tool.aliases?.length ? ` (aliases: ${tool.aliases.join(", ")})` : "";
5647
+ const hintStr = tool.searchHint ? ` [${tool.searchHint}]` : "";
5648
+ return `\u2022 ${tool.name}${aliasStr}: ${tool.description}${hintStr}`;
5649
+ });
5650
+ return `Found ${results.length} tool(s) matching "${input.query}":
5651
+ ${lines.join("\n")}`;
5652
+ }
5653
+ };
5654
+
5655
+ // src/tools/builtins/background-bash-tool.ts
5656
+ import { z as z21 } from "zod";
5657
+ var bashSchema = z21.object({
5658
+ command: z21.string().describe("Shell command to run in background"),
5659
+ cwd: z21.string().optional().describe("Working directory")
5660
+ });
5661
+ var BackgroundBashTool = class extends BaseTool {
5662
+ name = "background_bash";
5663
+ aliases = ["bg-bash", "async-bash"];
5664
+ description = "Run a shell command in the background. Returns a task ID for checking output later.";
5665
+ searchHint = "background async shell command run execute";
5666
+ inputSchema = bashSchema;
5667
+ riskLevel = "destructive";
5668
+ concurrencySafe = true;
5669
+ readOnly = false;
5670
+ getManager;
5671
+ constructor(manager) {
5672
+ super();
5673
+ this.getManager = manager;
5674
+ }
5675
+ async execute(input, _context) {
5676
+ const manager = this.getManager();
5677
+ const task = manager.startTask(input.command, [], input.cwd);
5678
+ return [
5679
+ `Background task started:`,
5680
+ ` Task ID: ${task.id}`,
5681
+ ` Command: ${input.command}`,
5682
+ ` Status: ${task.status}`,
5683
+ "",
5684
+ `Use task_output with task_id "${task.id}" to check progress.`,
5685
+ `Use task_stop with task_id "${task.id}" to cancel.`
5686
+ ].join("\n");
5687
+ }
5688
+ };
5689
+
5690
+ // src/tools/builtins/task-list-tool.ts
5691
+ import { z as z22 } from "zod";
5692
+ var listSchema = z22.object({
5693
+ status: z22.enum(["pending", "running", "completed", "failed", "cancelled"]).optional().describe("Filter by task status")
5694
+ });
5695
+ var TaskListTool = class extends BaseTool {
5696
+ name = "task_list";
5697
+ aliases = ["list-tasks", "tasks"];
5698
+ description = "List background tasks and their statuses.";
5699
+ searchHint = "list background tasks status";
5700
+ inputSchema = listSchema;
5701
+ riskLevel = "readonly";
5702
+ concurrencySafe = true;
5703
+ readOnly = true;
5704
+ getManager;
5705
+ constructor(manager) {
5706
+ super();
5707
+ this.getManager = manager;
5708
+ }
5709
+ async execute(input, _context) {
5710
+ const manager = this.getManager();
5711
+ const tasks = manager.listTasks(input.status);
5712
+ if (tasks.length === 0) {
5713
+ return "No tasks found.";
5714
+ }
5715
+ const lines = tasks.map((t) => {
5716
+ const age = t.completedAt ? `${Math.round((t.completedAt - t.createdAt) / 1e3)}s` : `${Math.round((Date.now() - t.createdAt) / 1e3)}s`;
5717
+ return `\u2022 ${t.id.slice(0, 8)}\u2026 [${t.status}] ${t.command} ${t.args.join(" ")} (${age})`;
5718
+ });
5719
+ return `Tasks (${tasks.length}):
5720
+ ${lines.join("\n")}`;
5721
+ }
5722
+ };
5723
+
5724
+ // src/tools/builtins/task-output-tool.ts
5725
+ import { z as z23 } from "zod";
5726
+ var outputSchema = z23.object({
5727
+ task_id: z23.string().describe("ID of the background task")
5728
+ });
5729
+ var TaskOutputTool = class extends BaseTool {
5730
+ name = "task_output";
5731
+ aliases = ["get-task-output"];
5732
+ description = "Get the output of a background task. Returns current status and accumulated output.";
5733
+ searchHint = "background task result output status";
5734
+ inputSchema = outputSchema;
5735
+ riskLevel = "readonly";
5736
+ concurrencySafe = true;
5737
+ readOnly = true;
5738
+ getManager;
5739
+ constructor(manager) {
5740
+ super();
5741
+ this.getManager = manager;
5742
+ }
5743
+ async execute(input, _context) {
5744
+ const manager = this.getManager();
5745
+ const task = manager.getTask(input.task_id);
5746
+ if (!task) {
5747
+ return `Task not found: ${input.task_id}. Use task_list to see available tasks.`;
5748
+ }
5749
+ const lines = [
5750
+ `Task: ${task.id}`,
5751
+ `Command: ${task.command} ${task.args.join(" ")}`,
5752
+ `Status: ${task.status}`,
5753
+ `Exit Code: ${task.exitCode ?? "N/A"}`,
5754
+ `Created: ${new Date(task.createdAt).toISOString()}`
5755
+ ];
5756
+ if (task.progress > 0) {
5757
+ lines.push(`Progress: ${task.progress}%`);
5758
+ }
5759
+ if (task.completedAt) {
5760
+ lines.push(`Completed: ${new Date(task.completedAt).toISOString()}`);
5761
+ }
5762
+ if (task.error) {
5763
+ lines.push("", "--- Error ---", task.error);
5764
+ }
5765
+ if (task.result) {
5766
+ lines.push("", "--- Result ---", task.result);
5767
+ }
5768
+ if (task.output) {
5769
+ lines.push("", "--- Output ---", task.output);
5770
+ }
5771
+ if (!task.output && !task.result && !task.error) {
5772
+ lines.push("", "(no output yet)");
5773
+ }
5774
+ return lines.join("\n");
5775
+ }
5776
+ };
5777
+
5778
+ // src/tools/builtins/task-stop-tool.ts
5779
+ import { z as z24 } from "zod";
5780
+ var stopSchema = z24.object({
5781
+ task_id: z24.string().describe("ID of the background task to stop")
5782
+ });
5783
+ var TaskStopTool = class extends BaseTool {
5784
+ name = "task_stop";
5785
+ aliases = ["stop-task", "cancel-task"];
5786
+ description = "Stop a running background task. Sends SIGTERM to the process.";
5787
+ searchHint = "stop cancel kill background task";
5788
+ inputSchema = stopSchema;
5789
+ riskLevel = "destructive";
5790
+ concurrencySafe = false;
5791
+ readOnly = false;
5792
+ getManager;
5793
+ constructor(manager) {
5794
+ super();
5795
+ this.getManager = manager;
5796
+ }
5797
+ async execute(input, _context) {
5798
+ const manager = this.getManager();
5799
+ const stopped = manager.stopTask(input.task_id);
5800
+ if (stopped) {
5801
+ return `Task ${input.task_id} stopped successfully.`;
5802
+ }
5803
+ return `Failed to stop task ${input.task_id}. Task may not exist or not be running.`;
5804
+ }
5805
+ };
5806
+
5143
5807
  // src/services/auto-mode.ts
5144
5808
  var DEFAULT_READONLY_TOOLS = [
5145
5809
  "file_read",
@@ -5387,7 +6051,7 @@ var ContextWindowManager = class {
5387
6051
  import { mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
5388
6052
  import { join as join8 } from "path";
5389
6053
  import { tmpdir as tmpdir2 } from "os";
5390
- import { randomUUID as randomUUID5 } from "crypto";
6054
+ import { randomUUID as randomUUID6 } from "crypto";
5391
6055
  var MAX_RESULT_SIZE_CHARS = 5e4;
5392
6056
  var MAX_RESULTS_PER_MESSAGE_CHARS = 2e5;
5393
6057
  var PREVIEW_LENGTH = 500;
@@ -5395,7 +6059,7 @@ var TOOL_RESULTS_SUBDIR = "cliskill-tool-results";
5395
6059
  var sessionDir = null;
5396
6060
  async function getSessionDir() {
5397
6061
  if (!sessionDir) {
5398
- sessionDir = join8(tmpdir2(), TOOL_RESULTS_SUBDIR, randomUUID5());
6062
+ sessionDir = join8(tmpdir2(), TOOL_RESULTS_SUBDIR, randomUUID6());
5399
6063
  await mkdir3(sessionDir, { recursive: true });
5400
6064
  }
5401
6065
  return sessionDir;
@@ -5415,7 +6079,7 @@ async function persistLargeResult(content, toolName) {
5415
6079
  return { persisted: false, content };
5416
6080
  }
5417
6081
  const dir = await getSessionDir();
5418
- const fileName = `${toolName}-${Date.now()}-${randomUUID5().slice(0, 8)}.txt`;
6082
+ const fileName = `${toolName}-${Date.now()}-${randomUUID6().slice(0, 8)}.txt`;
5419
6083
  const filePath = join8(dir, fileName);
5420
6084
  await writeFile4(filePath, content, "utf-8");
5421
6085
  const preview = content.slice(0, PREVIEW_LENGTH);
@@ -5463,6 +6127,249 @@ async function applyResultBudget(results) {
5463
6127
  return output;
5464
6128
  }
5465
6129
 
6130
+ // src/tasks/manager.ts
6131
+ import { randomUUID as randomUUID7 } from "crypto";
6132
+ import { spawn } from "child_process";
6133
+ import { EventEmitter } from "events";
6134
+ var MAX_COMPLETED_TASKS = 50;
6135
+ var TaskManager = class extends EventEmitter {
6136
+ tasks = /* @__PURE__ */ new Map();
6137
+ completedOrder = [];
6138
+ /** Create a generic task entry (for agent/shell tasks via executor) */
6139
+ createTask(options) {
6140
+ const id = `task_${randomUUID7()}`;
6141
+ const task = {
6142
+ id,
6143
+ name: options.name,
6144
+ description: options.description,
6145
+ type: options.type,
6146
+ status: "pending",
6147
+ progress: 0,
6148
+ result: null,
6149
+ error: null,
6150
+ output: "",
6151
+ exitCode: null,
6152
+ createdAt: Date.now(),
6153
+ completedAt: null,
6154
+ command: options.name,
6155
+ args: []
6156
+ };
6157
+ this.tasks.set(id, task);
6158
+ return id;
6159
+ }
6160
+ /** Start a shell task with a spawned child process */
6161
+ startTask(command, args = [], cwd2) {
6162
+ const id = `task_${randomUUID7()}`;
6163
+ const task = {
6164
+ id,
6165
+ name: command,
6166
+ description: args.join(" "),
6167
+ type: "shell",
6168
+ status: "running",
6169
+ progress: 0,
6170
+ result: null,
6171
+ error: null,
6172
+ output: "",
6173
+ exitCode: null,
6174
+ createdAt: Date.now(),
6175
+ completedAt: null,
6176
+ command,
6177
+ args
6178
+ };
6179
+ const child = spawn(command, args, {
6180
+ cwd: cwd2 ?? process.cwd(),
6181
+ shell: true,
6182
+ stdio: ["ignore", "pipe", "pipe"]
6183
+ });
6184
+ task.process = child;
6185
+ child.stdout?.on("data", (data) => {
6186
+ task.output += data.toString();
6187
+ });
6188
+ child.stderr?.on("data", (data) => {
6189
+ task.output += data.toString();
6190
+ });
6191
+ child.on("close", (code) => {
6192
+ task.status = code === 0 ? "completed" : "failed";
6193
+ task.exitCode = code;
6194
+ task.completedAt = Date.now();
6195
+ task.process = void 0;
6196
+ this.completedOrder.push(id);
6197
+ this.evictOldTasks();
6198
+ this.emit("onStatusChange", id, task.status);
6199
+ });
6200
+ child.on("error", (err) => {
6201
+ task.status = "failed";
6202
+ task.output += `
6203
+ Error: ${err.message}`;
6204
+ task.exitCode = -1;
6205
+ task.completedAt = Date.now();
6206
+ task.process = void 0;
6207
+ this.emit("onStatusChange", id, "failed");
6208
+ });
6209
+ this.tasks.set(id, task);
6210
+ this.emit("onStatusChange", id, "running");
6211
+ return task;
6212
+ }
6213
+ getTask(id) {
6214
+ return this.tasks.get(id);
6215
+ }
6216
+ stopTask(id) {
6217
+ const task = this.tasks.get(id);
6218
+ if (!task || task.status !== "running" || !task.process) return false;
6219
+ task.process.kill("SIGTERM");
6220
+ task.status = "cancelled";
6221
+ task.completedAt = Date.now();
6222
+ task.process = void 0;
6223
+ this.emit("onStatusChange", id, "cancelled");
6224
+ return true;
6225
+ }
6226
+ /** Update task status (for agent/shell tasks via executor) */
6227
+ updateStatus(id, status) {
6228
+ const task = this.tasks.get(id);
6229
+ if (!task) return;
6230
+ task.status = status;
6231
+ if (status === "completed" || status === "failed" || status === "cancelled") {
6232
+ task.completedAt = Date.now();
6233
+ }
6234
+ this.emit("onStatusChange", id, status);
6235
+ }
6236
+ /** Update task progress percentage (for agent tasks) */
6237
+ updateProgress(id, progress, message) {
6238
+ const task = this.tasks.get(id);
6239
+ if (!task) return;
6240
+ task.progress = Math.max(0, Math.min(100, progress));
6241
+ this.emit("onProgress", id, task.progress, message);
6242
+ }
6243
+ /** Cancel a task (for agent/shell tasks via executor) */
6244
+ cancelTask(id) {
6245
+ const task = this.tasks.get(id);
6246
+ if (!task) return;
6247
+ if (task.process) {
6248
+ task.process.kill("SIGTERM");
6249
+ task.process = void 0;
6250
+ }
6251
+ task.status = "cancelled";
6252
+ task.completedAt = Date.now();
6253
+ this.emit("onStatusChange", id, "cancelled");
6254
+ }
6255
+ /** Set error result on a task */
6256
+ setError(id, error) {
6257
+ const task = this.tasks.get(id);
6258
+ if (!task) return;
6259
+ task.error = error;
6260
+ task.status = "failed";
6261
+ task.completedAt = Date.now();
6262
+ this.emit("onStatusChange", id, "failed");
6263
+ this.emit("onError", id, error);
6264
+ }
6265
+ /** Set success result on a task */
6266
+ setResult(id, result) {
6267
+ const task = this.tasks.get(id);
6268
+ if (!task) return;
6269
+ task.result = result;
6270
+ task.status = "completed";
6271
+ task.completedAt = Date.now();
6272
+ this.completedOrder.push(id);
6273
+ this.evictOldTasks();
6274
+ this.emit("onStatusChange", id, "completed");
6275
+ this.emit("onComplete", id, result);
6276
+ }
6277
+ /** Remove a completed/failed/cancelled task */
6278
+ removeTask(id) {
6279
+ const task = this.tasks.get(id);
6280
+ if (!task) return;
6281
+ if (task.status === "running") {
6282
+ throw new Error("Cannot remove running task");
6283
+ }
6284
+ this.tasks.delete(id);
6285
+ const idx = this.completedOrder.indexOf(id);
6286
+ if (idx >= 0) this.completedOrder.splice(idx, 1);
6287
+ }
6288
+ /** Clear all completed/failed/cancelled tasks */
6289
+ clearCompleted() {
6290
+ for (const [id, task] of this.tasks) {
6291
+ if (task.status !== "running" && task.status !== "pending") {
6292
+ this.tasks.delete(id);
6293
+ }
6294
+ }
6295
+ this.completedOrder = this.completedOrder.filter((id) => this.tasks.has(id));
6296
+ }
6297
+ /** Count of active (pending + running) tasks */
6298
+ getActiveCount() {
6299
+ let count = 0;
6300
+ for (const task of this.tasks.values()) {
6301
+ if (task.status === "pending" || task.status === "running") count++;
6302
+ }
6303
+ return count;
6304
+ }
6305
+ listTasks(filter) {
6306
+ const all = Array.from(this.tasks.values());
6307
+ const status = typeof filter === "string" ? filter : filter?.status;
6308
+ if (status) return all.filter((t) => t.status === status);
6309
+ return all;
6310
+ }
6311
+ /** Type-safe event listener */
6312
+ on(event, listener) {
6313
+ return super.on(event, listener);
6314
+ }
6315
+ evictOldTasks() {
6316
+ while (this.completedOrder.length > MAX_COMPLETED_TASKS) {
6317
+ const oldestId = this.completedOrder.shift();
6318
+ if (oldestId) this.tasks.delete(oldestId);
6319
+ }
6320
+ }
6321
+ };
6322
+
6323
+ // src/memory/project-scanner.ts
6324
+ import { readFile as readFile8, stat as stat3 } from "fs/promises";
6325
+ import { join as join9, resolve as resolve8, dirname as dirname2 } from "path";
6326
+ import { existsSync as existsSync4 } from "fs";
6327
+ var MEMORY_FILENAMES = [".cliskill.md", "CLAUDE.md", ".claude.md"];
6328
+ async function scanProjectMemory(cwd2) {
6329
+ const results = [];
6330
+ const visitedDirs = /* @__PURE__ */ new Set();
6331
+ let currentDir = resolve8(cwd2);
6332
+ for (let i = 0; i < 20; i++) {
6333
+ if (visitedDirs.has(currentDir)) break;
6334
+ visitedDirs.add(currentDir);
6335
+ for (const filename of MEMORY_FILENAMES) {
6336
+ const filePath = join9(currentDir, filename);
6337
+ if (existsSync4(filePath)) {
6338
+ try {
6339
+ const content = await readFile8(filePath, "utf-8");
6340
+ const fileStat = await stat3(filePath);
6341
+ results.push({
6342
+ content: content.trim(),
6343
+ filePath,
6344
+ modifiedAt: fileStat.mtimeMs
6345
+ });
6346
+ } catch {
6347
+ }
6348
+ }
6349
+ }
6350
+ const parent = dirname2(currentDir);
6351
+ if (parent === currentDir) break;
6352
+ currentDir = parent;
6353
+ }
6354
+ return results;
6355
+ }
6356
+ async function loadProjectMemoryPrompt(cwd2) {
6357
+ const memories = await scanProjectMemory(cwd2);
6358
+ if (memories.length === 0) return "";
6359
+ const sections = memories.map((m) => {
6360
+ const relativePath = m.filePath.startsWith(cwd2) ? m.filePath.slice(cwd2.length + 1) : m.filePath;
6361
+ return `### ${relativePath}
6362
+ ${m.content}`;
6363
+ });
6364
+ return [
6365
+ "<project-memory>",
6366
+ "The following project-level instructions were found in memory files:",
6367
+ "",
6368
+ ...sections,
6369
+ "</project-memory>"
6370
+ ].join("\n");
6371
+ }
6372
+
5466
6373
  // src/core/loop.ts
5467
6374
  var MAX_TURNS = 50;
5468
6375
  var MAX_CONSECUTIVE_TOOL_ERRORS = 3;
@@ -5470,11 +6377,11 @@ var MAX_TOOL_OUTPUT_CHARS = 5e4;
5470
6377
  var MIN_OUTPUT_TOKENS = 16384;
5471
6378
  var MAX_OUTPUT_TOKENS_RECOVERY_LIMIT = 3;
5472
6379
  var ESCALATED_MAX_TOKENS = 65536;
5473
- function sanitizeToolOutput(output) {
6380
+ function sanitizeToolOutput(output, maxChars = MAX_TOOL_OUTPUT_CHARS) {
5474
6381
  const sanitized = output.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
5475
- if (sanitized.length <= MAX_TOOL_OUTPUT_CHARS) return sanitized;
5476
- const truncated = sanitized.slice(0, MAX_TOOL_OUTPUT_CHARS);
5477
- const omitted = sanitized.length - MAX_TOOL_OUTPUT_CHARS;
6382
+ if (sanitized.length <= maxChars) return sanitized;
6383
+ const truncated = sanitized.slice(0, maxChars);
6384
+ const omitted = sanitized.length - maxChars;
5478
6385
  return `${truncated}
5479
6386
 
5480
6387
  ... [truncated ${omitted} characters]`;
@@ -5524,7 +6431,8 @@ ${result.summary}` }]
5524
6431
  content: [{ type: "text", text: userMessage }]
5525
6432
  });
5526
6433
  const toolDefs = toolRegistry.getToolDefinitions();
5527
- const effectiveSystemPrompt = systemPrompt + fastMode.getSystemPromptModifier();
6434
+ const projectMemory = await loadProjectMemoryPrompt(cwd2);
6435
+ const effectiveSystemPrompt = systemPrompt + fastMode.getSystemPromptModifier() + (projectMemory ? "\n\n" + projectMemory : "");
5528
6436
  const toolContext = {
5529
6437
  cwd: cwd2,
5530
6438
  abortSignal,
@@ -5540,7 +6448,7 @@ ${result.summary}` }]
5540
6448
  state.turnCount++;
5541
6449
  const delay = fastMode.getInterTurnDelay();
5542
6450
  if (delay > 0) {
5543
- await new Promise((resolve9) => setTimeout(resolve9, delay));
6451
+ await new Promise((resolve10) => setTimeout(resolve10, delay));
5544
6452
  }
5545
6453
  if (state.messages.length > 10) {
5546
6454
  const compactionInput = state.messages.map((m) => ({
@@ -5683,19 +6591,39 @@ ${result.summary}` }]
5683
6591
  for (const toolCall of toolCalls) {
5684
6592
  executor.addTool(toolCall);
5685
6593
  }
5686
- for await (const result of executor.getRemainingResults()) {
5687
- toolResults.push(result.toolResult);
5688
- yield result.event;
5689
- if (result.toolResult.isError) hadError = true;
6594
+ for await (const execEvent of executor.getRemainingResults()) {
6595
+ if ("toolResult" in execEvent) {
6596
+ toolResults.push(execEvent.toolResult);
6597
+ yield execEvent.event;
6598
+ if (execEvent.toolResult.isError) hadError = true;
6599
+ } else {
6600
+ const p = execEvent.progress;
6601
+ yield { type: "tool_progress", toolName: p.toolName, toolId: p.toolId, message: p.message };
6602
+ }
5690
6603
  }
5691
6604
  } else {
6605
+ const progressBuffer = [];
6606
+ const singleToolContext = {
6607
+ ...toolContext,
6608
+ onProgress: (message) => {
6609
+ progressBuffer.push({
6610
+ type: "tool_progress",
6611
+ toolName: toolCalls[0].name,
6612
+ toolId: toolCalls[0].id,
6613
+ message
6614
+ });
6615
+ }
6616
+ };
5692
6617
  const result = await executeToolCall(
5693
6618
  toolCalls[0],
5694
6619
  toolRegistry,
5695
- toolContext,
6620
+ singleToolContext,
5696
6621
  autoMode,
5697
6622
  onPermissionRequest
5698
6623
  );
6624
+ for (const progressEvent of progressBuffer) {
6625
+ yield progressEvent;
6626
+ }
5699
6627
  toolResults.push(result.toolResult);
5700
6628
  yield result.event;
5701
6629
  if (result.toolResult.isError) hadError = true;
@@ -5734,12 +6662,20 @@ ${result.summary}` }]
5734
6662
  }
5735
6663
  }
5736
6664
  } catch (err) {
5737
- const errMsg = err.message ?? String(err);
5738
- if (errMsg.includes("529") || errMsg.includes("503") || errMsg.includes("Overloaded")) {
6665
+ const lastAssistantContent = state.messages.length > 0 && state.messages[state.messages.length - 1].role === "assistant" ? state.messages[state.messages.length - 1].content : [];
6666
+ const pendingToolCalls = extractToolCalls(lastAssistantContent);
6667
+ const pendingResults = createMissingToolResults(
6668
+ pendingToolCalls,
6669
+ `Request failed: ${err.message}`
6670
+ );
6671
+ if (pendingResults.length > 0) {
6672
+ state.messages.push({ role: "user", content: pendingResults });
6673
+ }
6674
+ if (isFallbackTriggeredError(err)) {
5739
6675
  yield {
5740
6676
  type: "error",
5741
6677
  error: new Error(
5742
- "Model is currently overloaded (529/503). The adapter will automatically retry. If this persists, try a different model."
6678
+ "Model is currently overloaded (529/503). Fallback chain will be attempted on next request. If this persists, configure fallbackChain in config or try a different model."
5743
6679
  )
5744
6680
  };
5745
6681
  } else {
@@ -5795,15 +6731,13 @@ function summarizeOldToolResults(messages, keepRecent) {
5795
6731
  }
5796
6732
  return compressed;
5797
6733
  }
5798
- var TOKEN_BUDGET_WARNING_THRESHOLD = 0.8;
5799
- var TOKEN_BUDGET_CRITICAL_THRESHOLD = 0.95;
5800
6734
  function checkTokenBudget(inputTokens, outputTokens, maxContextTokens) {
5801
6735
  const totalTokens = inputTokens + outputTokens;
5802
- const usagePercent = totalTokens / maxContextTokens;
6736
+ const state = calculateTokenWarningState(totalTokens, maxContextTokens);
5803
6737
  return {
5804
- usagePercent,
5805
- shouldNudge: usagePercent > TOKEN_BUDGET_WARNING_THRESHOLD && usagePercent <= TOKEN_BUDGET_CRITICAL_THRESHOLD,
5806
- shouldBlock: usagePercent > TOKEN_BUDGET_CRITICAL_THRESHOLD
6738
+ usagePercent: state.usagePercent,
6739
+ shouldNudge: state.isWarning || state.isError,
6740
+ shouldBlock: state.shouldAutoCompact
5807
6741
  };
5808
6742
  }
5809
6743
  function runStopHooks(assistantContent, consecutiveErrors, turnCount) {
@@ -5869,7 +6803,8 @@ async function executeToolCall(toolCall, toolRegistry, toolContext, autoMode, on
5869
6803
  try {
5870
6804
  const validatedInput = tool.inputSchema.parse(toolCall.input);
5871
6805
  const rawResult = await tool.execute(validatedInput, toolContext);
5872
- const result = sanitizeToolOutput(rawResult);
6806
+ const maxChars = tool.maxResultSizeChars ?? MAX_TOOL_OUTPUT_CHARS;
6807
+ const result = sanitizeToolOutput(rawResult, maxChars);
5873
6808
  return {
5874
6809
  toolResult: {
5875
6810
  type: "tool_result",
@@ -5934,6 +6869,12 @@ function createDefaultToolRegistry(modelRouter) {
5934
6869
  registry.register(new RemoteTool());
5935
6870
  registry.register(new WorktreeTool());
5936
6871
  registry.register(new MemoryTool());
6872
+ registry.register(new ToolSearchTool(() => registry));
6873
+ const taskManager = new TaskManager();
6874
+ registry.register(new BackgroundBashTool(() => taskManager));
6875
+ registry.register(new TaskListTool(() => taskManager));
6876
+ registry.register(new TaskOutputTool(() => taskManager));
6877
+ registry.register(new TaskStopTool(() => taskManager));
5937
6878
  if (modelRouter) {
5938
6879
  const agentTool = new AgentTool(
5939
6880
  () => modelRouter,
@@ -5943,12 +6884,20 @@ function createDefaultToolRegistry(modelRouter) {
5943
6884
  }
5944
6885
  return registry;
5945
6886
  }
6887
+ function createMissingToolResults(toolCalls, errorMessage) {
6888
+ return toolCalls.map((tc) => ({
6889
+ type: "tool_result",
6890
+ toolUseId: tc.id,
6891
+ content: `Error: ${errorMessage}`,
6892
+ isError: true
6893
+ }));
6894
+ }
5946
6895
 
5947
6896
  // src/ui/ink-app.tsx
5948
6897
  import { useState, useEffect, useCallback, useRef, useMemo, memo } from "react";
5949
6898
  import { Box, Text, render, useInput, useApp, useStdout } from "ink";
5950
6899
  import { readdirSync } from "fs";
5951
- import { join as join9, basename as basename2, dirname as dirname2 } from "path";
6900
+ import { join as join10, basename as basename2, dirname as dirname3 } from "path";
5952
6901
  import { exec as exec2, execSync as execSync2 } from "child_process";
5953
6902
  import { jsx, jsxs } from "react/jsx-runtime";
5954
6903
  var C = {
@@ -6354,7 +7303,7 @@ var InputBar = memo(function InputBar2({ input, active }) {
6354
7303
  active && /* @__PURE__ */ jsx(Text, { color: C.primary, children: "\u258E" })
6355
7304
  ] });
6356
7305
  });
6357
- function InkApp({ model, toolCount, onSubmit }) {
7306
+ function InkApp({ model, toolCount, onSubmit, onCancel }) {
6358
7307
  const { exit } = useApp();
6359
7308
  const { stdout } = useStdout();
6360
7309
  const terminalHeight = stdout?.rows ?? 24;
@@ -6521,16 +7470,16 @@ function InkApp({ model, toolCount, onSubmit }) {
6521
7470
  const selected = currentFiles[selectedIdx];
6522
7471
  if (selected) {
6523
7472
  if (selected.isDir) {
6524
- setFileCwd(join9(fileCwd, selected.name));
7473
+ setFileCwd(join10(fileCwd, selected.name));
6525
7474
  setSelectedIdx(0);
6526
7475
  } else {
6527
- openInEditor(join9(fileCwd, selected.name));
7476
+ openInEditor(join10(fileCwd, selected.name));
6528
7477
  }
6529
7478
  }
6530
7479
  return;
6531
7480
  }
6532
7481
  if (key.backspace || key.delete) {
6533
- const parent = dirname2(fileCwd);
7482
+ const parent = dirname3(fileCwd);
6534
7483
  if (parent !== fileCwd) {
6535
7484
  setFileCwd(parent);
6536
7485
  setSelectedIdx(0);
@@ -6544,6 +7493,10 @@ function InkApp({ model, toolCount, onSubmit }) {
6544
7493
  return;
6545
7494
  }
6546
7495
  if (key.escape) {
7496
+ if (loading && onCancel) {
7497
+ onCancel();
7498
+ return;
7499
+ }
6547
7500
  exit();
6548
7501
  return;
6549
7502
  }
@@ -6665,8 +7618,8 @@ function MessageList({ messages }) {
6665
7618
 
6666
7619
  // src/ui/repl.ts
6667
7620
  import { mkdir as mkdir4, appendFile } from "fs/promises";
6668
- import { join as join10 } from "path";
6669
- import { randomUUID as randomUUID6 } from "crypto";
7621
+ import { join as join12 } from "path";
7622
+ import { randomUUID as randomUUID8 } from "crypto";
6670
7623
 
6671
7624
  // src/prompts/system-prompt.ts
6672
7625
  import { hostname } from "os";
@@ -6811,15 +7764,424 @@ function buildSystemPrompt() {
6811
7764
  ].join("\n\n");
6812
7765
  }
6813
7766
 
7767
+ // src/mcp/client.ts
7768
+ import { spawn as spawn2 } from "child_process";
7769
+ import { createInterface } from "readline";
7770
+ var REQUEST_TIMEOUT = 3e4;
7771
+ var MCPClient = class {
7772
+ config;
7773
+ process = null;
7774
+ rl = null;
7775
+ connected = false;
7776
+ nextId = 1;
7777
+ pendingRequests = /* @__PURE__ */ new Map();
7778
+ buffer = "";
7779
+ constructor(config) {
7780
+ this.config = config;
7781
+ }
7782
+ async connect() {
7783
+ if (this.connected) return;
7784
+ const env = {
7785
+ ...process.env,
7786
+ ...this.config.env
7787
+ };
7788
+ this.process = spawn2(this.config.command, this.config.args, {
7789
+ stdio: ["pipe", "pipe", "pipe"],
7790
+ env
7791
+ });
7792
+ this.process.on("error", (err) => {
7793
+ this.cleanup();
7794
+ throw err;
7795
+ });
7796
+ this.process.on("exit", () => {
7797
+ this.cleanup();
7798
+ });
7799
+ if (!this.process.stdout) {
7800
+ throw new Error("MCP server stdout is not available");
7801
+ }
7802
+ this.rl = createInterface({ input: this.process.stdout });
7803
+ this.rl.on("line", (line) => {
7804
+ this.handleLine(line);
7805
+ });
7806
+ if (this.process.stderr) {
7807
+ this.process.stderr.on("data", () => {
7808
+ });
7809
+ }
7810
+ await this.sendRequest("initialize", {
7811
+ protocolVersion: "2024-11-05",
7812
+ capabilities: {},
7813
+ clientInfo: { name: "cliskill", version: "1.0.0" }
7814
+ });
7815
+ this.sendNotification("notifications/initialized", {});
7816
+ this.connected = true;
7817
+ }
7818
+ async disconnect() {
7819
+ if (!this.process) return;
7820
+ for (const [, pending] of this.pendingRequests) {
7821
+ clearTimeout(pending.timer);
7822
+ pending.reject(new Error("Connection closed"));
7823
+ }
7824
+ this.pendingRequests.clear();
7825
+ this.process.kill("SIGTERM");
7826
+ this.cleanup();
7827
+ }
7828
+ async listTools() {
7829
+ const response = await this.sendRequest("tools/list", {});
7830
+ const result = response.result;
7831
+ return result?.tools ?? [];
7832
+ }
7833
+ async callTool(name, args) {
7834
+ const response = await this.sendRequest("tools/call", { name, arguments: args });
7835
+ return response.result;
7836
+ }
7837
+ async listResources() {
7838
+ const response = await this.sendRequest("resources/list", {});
7839
+ const result = response.result;
7840
+ return result?.resources ?? [];
7841
+ }
7842
+ async readResource(uri) {
7843
+ const response = await this.sendRequest("resources/read", { uri });
7844
+ return response.result;
7845
+ }
7846
+ async listPrompts() {
7847
+ const response = await this.sendRequest("prompts/list", {});
7848
+ const result = response.result;
7849
+ return result?.prompts ?? [];
7850
+ }
7851
+ isConnected() {
7852
+ return this.connected;
7853
+ }
7854
+ sendRequest(method, params) {
7855
+ return new Promise((resolve10, reject) => {
7856
+ if (!this.process?.stdin) {
7857
+ reject(new Error("MCP server not connected"));
7858
+ return;
7859
+ }
7860
+ const id = this.nextId++;
7861
+ const request = { jsonrpc: "2.0", id, method, params };
7862
+ const timer = setTimeout(() => {
7863
+ this.pendingRequests.delete(id);
7864
+ reject(new Error(`Request timeout: ${method} (id=${id})`));
7865
+ }, REQUEST_TIMEOUT);
7866
+ this.pendingRequests.set(id, { resolve: resolve10, reject, timer });
7867
+ const data = JSON.stringify(request) + "\n";
7868
+ this.process.stdin.write(data, (err) => {
7869
+ if (err) {
7870
+ clearTimeout(timer);
7871
+ this.pendingRequests.delete(id);
7872
+ reject(err);
7873
+ }
7874
+ });
7875
+ });
7876
+ }
7877
+ sendNotification(method, params) {
7878
+ if (!this.process?.stdin) return;
7879
+ const notification = { jsonrpc: "2.0", method, params };
7880
+ const data = JSON.stringify(notification) + "\n";
7881
+ this.process.stdin.write(data);
7882
+ }
7883
+ handleLine(line) {
7884
+ this.buffer += line;
7885
+ let response;
7886
+ try {
7887
+ response = JSON.parse(this.buffer);
7888
+ } catch {
7889
+ return;
7890
+ } finally {
7891
+ this.buffer = "";
7892
+ }
7893
+ if (response.id !== void 0 && response.id !== null) {
7894
+ const pending = this.pendingRequests.get(response.id);
7895
+ if (pending) {
7896
+ clearTimeout(pending.timer);
7897
+ this.pendingRequests.delete(response.id);
7898
+ pending.resolve(response);
7899
+ }
7900
+ }
7901
+ }
7902
+ cleanup() {
7903
+ this.connected = false;
7904
+ this.rl?.close();
7905
+ this.rl = null;
7906
+ this.process = null;
7907
+ this.buffer = "";
7908
+ }
7909
+ };
7910
+
7911
+ // src/mcp/manager.ts
7912
+ var MCPConnectionManager = class {
7913
+ clients = /* @__PURE__ */ new Map();
7914
+ async addServer(config) {
7915
+ const client = new MCPClient(config);
7916
+ await client.connect();
7917
+ this.clients.set(config.name, client);
7918
+ }
7919
+ async removeServer(name) {
7920
+ const client = this.clients.get(name);
7921
+ if (client) {
7922
+ await client.disconnect();
7923
+ this.clients.delete(name);
7924
+ }
7925
+ }
7926
+ getConnectedServers() {
7927
+ return Array.from(this.clients.entries()).filter(([, client]) => client.isConnected()).map(([name]) => name);
7928
+ }
7929
+ async getAllTools() {
7930
+ const allTools = [];
7931
+ for (const [, client] of this.clients) {
7932
+ if (!client.isConnected()) continue;
7933
+ try {
7934
+ const tools = await client.listTools();
7935
+ allTools.push(...tools);
7936
+ } catch {
7937
+ }
7938
+ }
7939
+ return allTools;
7940
+ }
7941
+ async callTool(serverName, toolName, args) {
7942
+ const client = this.clients.get(serverName);
7943
+ if (!client) {
7944
+ throw new Error(`MCP server "${serverName}" not found`);
7945
+ }
7946
+ if (!client.isConnected()) {
7947
+ throw new Error(`MCP server "${serverName}" is not connected`);
7948
+ }
7949
+ return client.callTool(toolName, args);
7950
+ }
7951
+ async getToolsForServer(serverName) {
7952
+ const client = this.clients.get(serverName);
7953
+ if (!client || !client.isConnected()) {
7954
+ return [];
7955
+ }
7956
+ return client.listTools();
7957
+ }
7958
+ async disconnectAll() {
7959
+ for (const [, client] of this.clients) {
7960
+ try {
7961
+ await client.disconnect();
7962
+ } catch {
7963
+ }
7964
+ }
7965
+ this.clients.clear();
7966
+ }
7967
+ };
7968
+
7969
+ // src/mcp/mcp-tool-adapter.ts
7970
+ import { z as z25 } from "zod";
7971
+ var MCPToolAdapter = class {
7972
+ name;
7973
+ description;
7974
+ inputSchema;
7975
+ riskLevel = "safe";
7976
+ concurrencySafe = true;
7977
+ readOnly = true;
7978
+ serverName;
7979
+ mcpManager;
7980
+ rawInputSchema;
7981
+ constructor(serverName, toolName, description, inputSchema2, mcpManager) {
7982
+ this.name = `mcp_${serverName}_${toolName}`;
7983
+ this.description = description;
7984
+ this.serverName = serverName;
7985
+ this.mcpManager = mcpManager;
7986
+ this.rawInputSchema = inputSchema2;
7987
+ this.inputSchema = this.buildZodSchema(inputSchema2);
7988
+ }
7989
+ async execute(input, context) {
7990
+ const allowed = await context.checkPermission(this.name, JSON.stringify(input));
7991
+ if (!allowed) {
7992
+ return `Error: Permission denied for MCP tool: ${this.name}`;
7993
+ }
7994
+ try {
7995
+ const result = await this.mcpManager.callTool(this.serverName, this.getOriginalName(), input);
7996
+ return typeof result === "string" ? result : JSON.stringify(result, null, 2);
7997
+ } catch (err) {
7998
+ return `Error calling MCP tool ${this.name}: ${err.message}`;
7999
+ }
8000
+ }
8001
+ toToolDefinition() {
8002
+ return {
8003
+ name: this.name,
8004
+ description: this.description,
8005
+ inputSchema: this.rawInputSchema
8006
+ };
8007
+ }
8008
+ getOriginalName() {
8009
+ const parts = this.name.split("_");
8010
+ return parts.slice(2).join("_");
8011
+ }
8012
+ getServerName() {
8013
+ return this.serverName;
8014
+ }
8015
+ buildZodSchema(jsonSchema) {
8016
+ const properties = jsonSchema.properties ?? {};
8017
+ const required = new Set(jsonSchema.required ?? []);
8018
+ const shape = {};
8019
+ for (const [key, propSchema] of Object.entries(properties)) {
8020
+ const zodType = this.jsonSchemaPropertyToZod(propSchema);
8021
+ shape[key] = required.has(key) ? zodType : zodType.optional();
8022
+ }
8023
+ return z25.object(shape);
8024
+ }
8025
+ jsonSchemaPropertyToZod(prop) {
8026
+ const type = prop.type;
8027
+ switch (type) {
8028
+ case "string":
8029
+ if (prop.enum) return z25.enum(prop.enum);
8030
+ return z25.string();
8031
+ case "number":
8032
+ case "integer":
8033
+ return z25.number();
8034
+ case "boolean":
8035
+ return z25.boolean();
8036
+ case "array":
8037
+ if (prop.items && typeof prop.items === "object") {
8038
+ return z25.array(this.jsonSchemaPropertyToZod(prop.items));
8039
+ }
8040
+ return z25.array(z25.unknown());
8041
+ case "object":
8042
+ if (prop.properties && typeof prop.properties === "object") {
8043
+ return this.buildZodSchema(prop);
8044
+ }
8045
+ return z25.record(z25.unknown());
8046
+ default:
8047
+ return z25.unknown();
8048
+ }
8049
+ }
8050
+ };
8051
+ async function registerMCPTools(mcpManager, registerFn) {
8052
+ const registered = [];
8053
+ for (const serverName of mcpManager.getConnectedServers()) {
8054
+ try {
8055
+ const tools = await mcpManager.getToolsForServer(serverName);
8056
+ for (const tool of tools) {
8057
+ const adapter = new MCPToolAdapter(
8058
+ serverName,
8059
+ tool.name,
8060
+ tool.description ?? `MCP tool: ${tool.name}`,
8061
+ tool.inputSchema,
8062
+ mcpManager
8063
+ );
8064
+ registerFn(adapter);
8065
+ registered.push(adapter.name);
8066
+ }
8067
+ } catch {
8068
+ }
8069
+ }
8070
+ return registered;
8071
+ }
8072
+
8073
+ // src/services/session-recovery.ts
8074
+ import { readFile as readFile9, readdir as readdir5 } from "fs/promises";
8075
+ import { join as join11 } from "path";
8076
+ import { existsSync as existsSync5 } from "fs";
8077
+ async function recoverSession(filePath) {
8078
+ const raw = await readFile9(filePath, "utf-8");
8079
+ const lines = raw.split("\n").filter((line) => line.trim().length > 0);
8080
+ const entries = [];
8081
+ for (const line of lines) {
8082
+ try {
8083
+ const entry = JSON.parse(line);
8084
+ if (entry.type && entry.content !== void 0) {
8085
+ entries.push(entry);
8086
+ }
8087
+ } catch {
8088
+ }
8089
+ }
8090
+ const messages = reconstructMessages(entries);
8091
+ const firstEntry = entries[0];
8092
+ const timestamp = firstEntry?.timestamp ?? 0;
8093
+ return {
8094
+ sessionId: filePath,
8095
+ filePath,
8096
+ timestamp,
8097
+ messages,
8098
+ entryCount: entries.length
8099
+ };
8100
+ }
8101
+ async function listSessions(limit = 20) {
8102
+ const dir = getSessionsDir();
8103
+ if (!existsSync5(dir)) return [];
8104
+ const files = await readdir5(dir);
8105
+ const sessionFiles = files.filter((f) => f.startsWith("session-") && f.endsWith(".jsonl"));
8106
+ const sessions = [];
8107
+ for (const fileName of sessionFiles) {
8108
+ const filePath = join11(dir, fileName);
8109
+ try {
8110
+ const raw = await readFile9(filePath, "utf-8");
8111
+ const lines = raw.split("\n").filter((line) => line.trim().length > 0);
8112
+ let lastActivity = 0;
8113
+ let entryCount = 0;
8114
+ for (const line of lines) {
8115
+ try {
8116
+ const entry = JSON.parse(line);
8117
+ if (entry.timestamp && entry.timestamp > lastActivity) {
8118
+ lastActivity = entry.timestamp;
8119
+ }
8120
+ entryCount++;
8121
+ } catch {
8122
+ }
8123
+ }
8124
+ const dateStr = fileName.replace("session-", "").replace(".jsonl", "");
8125
+ const fileTimestamp = Date.parse(dateStr) || lastActivity;
8126
+ sessions.push({
8127
+ fileName,
8128
+ filePath,
8129
+ timestamp: fileTimestamp,
8130
+ entryCount,
8131
+ lastActivity
8132
+ });
8133
+ } catch {
8134
+ }
8135
+ }
8136
+ sessions.sort((a, b) => b.lastActivity - a.lastActivity);
8137
+ return sessions.slice(0, limit);
8138
+ }
8139
+ async function findLatestSession() {
8140
+ const sessions = await listSessions(1);
8141
+ return sessions[0] ?? null;
8142
+ }
8143
+ function reconstructMessages(entries) {
8144
+ const messages = [];
8145
+ let i = 0;
8146
+ while (i < entries.length) {
8147
+ const entry = entries[i];
8148
+ if (entry.type === "user") {
8149
+ const content = [{ type: "text", text: entry.content }];
8150
+ messages.push({ role: "user", content });
8151
+ i++;
8152
+ } else if (entry.type === "assistant") {
8153
+ const content = [{ type: "text", text: entry.content }];
8154
+ messages.push({ role: "assistant", content });
8155
+ i++;
8156
+ } else if (entry.type === "tool_use") {
8157
+ const lastMsg = messages[messages.length - 1];
8158
+ if (lastMsg && lastMsg.role === "assistant") {
8159
+ }
8160
+ i++;
8161
+ } else if (entry.type === "tool_result") {
8162
+ const content = [{
8163
+ type: "tool_result",
8164
+ toolUseId: `recovered-${i}`,
8165
+ content: entry.content
8166
+ }];
8167
+ messages.push({ role: "user", content });
8168
+ i++;
8169
+ } else {
8170
+ i++;
8171
+ }
8172
+ }
8173
+ return messages;
8174
+ }
8175
+
6814
8176
  // src/ui/repl.ts
6815
8177
  var SessionSaver = class {
6816
8178
  filePath;
6817
8179
  sessionId;
6818
8180
  constructor() {
6819
- this.sessionId = randomUUID6();
8181
+ this.sessionId = randomUUID8();
6820
8182
  const dir = getSessionsDir();
6821
8183
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
6822
- this.filePath = join10(dir, `session-${timestamp}.jsonl`);
8184
+ this.filePath = join12(dir, `session-${timestamp}.jsonl`);
6823
8185
  }
6824
8186
  async init() {
6825
8187
  const dir = getSessionsDir();
@@ -6857,6 +8219,36 @@ function buildModelRouter(adapterRegistry, config) {
6857
8219
  defaultModel: config.models.defaultModel
6858
8220
  });
6859
8221
  }
8222
+ async function connectMcpServers(config, toolRegistry) {
8223
+ if (config.mcpServers.length === 0) return null;
8224
+ const mcpManager = new MCPConnectionManager();
8225
+ let connectedCount = 0;
8226
+ for (const serverConfig of config.mcpServers) {
8227
+ try {
8228
+ await mcpManager.addServer({
8229
+ name: serverConfig.name,
8230
+ command: serverConfig.command,
8231
+ args: serverConfig.args,
8232
+ env: serverConfig.env,
8233
+ transport: serverConfig.transport
8234
+ });
8235
+ connectedCount++;
8236
+ } catch (err) {
8237
+ console.error(`MCP server "${serverConfig.name}" connection failed: ${err.message}`);
8238
+ }
8239
+ }
8240
+ if (connectedCount === 0) return mcpManager;
8241
+ const registeredTools = await registerMCPTools(mcpManager, (tool) => {
8242
+ try {
8243
+ toolRegistry.register(tool);
8244
+ } catch {
8245
+ }
8246
+ });
8247
+ if (registeredTools.length > 0) {
8248
+ console.log(` MCP: ${connectedCount} server(s) connected, ${registeredTools.length} tool(s) loaded`);
8249
+ }
8250
+ return mcpManager;
8251
+ }
6860
8252
  async function runTuiRepl(config) {
6861
8253
  const adapterRegistry = new AdapterRegistry();
6862
8254
  for (const provider of config.providers) {
@@ -6869,12 +8261,15 @@ async function runTuiRepl(config) {
6869
8261
  const adapter = config.defaultProvider ? adapterRegistry.get(config.defaultProvider) : adapterRegistry.getAll()[0];
6870
8262
  const modelRouter = buildModelRouter(adapterRegistry, config);
6871
8263
  const toolRegistry = createDefaultToolRegistry(modelRouter);
8264
+ const mcpManager = await connectMcpServers(config, toolRegistry);
6872
8265
  ensureDataDir();
6873
8266
  const sessionSaver = new SessionSaver();
6874
8267
  await sessionSaver.init();
6875
8268
  let sessionMessages = [];
8269
+ let activeAbortController = new AbortController();
6876
8270
  const onSubmit = async (input, onEvent) => {
6877
8271
  if (!adapter) return;
8272
+ activeAbortController = new AbortController();
6878
8273
  await sessionSaver.append({
6879
8274
  type: "user",
6880
8275
  content: input,
@@ -6887,7 +8282,7 @@ async function runTuiRepl(config) {
6887
8282
  config,
6888
8283
  systemPrompt: config.systemPrompt ?? buildSystemPrompt(),
6889
8284
  cwd: process.cwd(),
6890
- abortSignal: new AbortController().signal,
8285
+ abortSignal: activeAbortController.signal,
6891
8286
  onPermissionRequest: async () => true,
6892
8287
  sessionHistory: sessionMessages
6893
8288
  })) {
@@ -6931,7 +8326,8 @@ async function runTuiRepl(config) {
6931
8326
  renderInkApp({
6932
8327
  model: adapter?.config.model ?? "N/A",
6933
8328
  toolCount: toolRegistry.getAll().length,
6934
- onSubmit
8329
+ onSubmit,
8330
+ onCancel: () => activeAbortController.abort()
6935
8331
  });
6936
8332
  }
6937
8333
  async function runBasicRepl(config) {
@@ -6963,13 +8359,14 @@ async function runBasicRepl(config) {
6963
8359
  `);
6964
8360
  }
6965
8361
  const toolRegistry = createDefaultToolRegistry(modelRouter);
8362
+ await connectMcpServers(config, toolRegistry);
6966
8363
  const abortController = new AbortController();
6967
8364
  ensureDataDir();
6968
8365
  const sessionSaver = new SessionSaver();
6969
8366
  await sessionSaver.init();
6970
8367
  let sessionMessages = [];
6971
- const { createInterface } = await import("readline");
6972
- const rl = createInterface({
8368
+ const { createInterface: createInterface2 } = await import("readline");
8369
+ const rl = createInterface2({
6973
8370
  input: process.stdin,
6974
8371
  output: process.stdout,
6975
8372
  prompt: "> "
@@ -6991,6 +8388,42 @@ async function runBasicRepl(config) {
6991
8388
  rl.close();
6992
8389
  return;
6993
8390
  }
8391
+ if (input === "/resume") {
8392
+ try {
8393
+ const latest = await findLatestSession();
8394
+ if (!latest) {
8395
+ console.log(" No previous sessions found.");
8396
+ rl.prompt();
8397
+ return;
8398
+ }
8399
+ const recovered = await recoverSession(latest.filePath);
8400
+ sessionMessages = recovered.messages;
8401
+ console.log(` Resumed session from ${new Date(recovered.timestamp).toISOString()} (${recovered.entryCount} entries, ${recovered.messages.length} messages)`);
8402
+ } catch (err) {
8403
+ console.error(` Failed to resume session: ${err.message}`);
8404
+ }
8405
+ rl.prompt();
8406
+ return;
8407
+ }
8408
+ if (input === "/sessions") {
8409
+ try {
8410
+ const sessions = await listSessions(10);
8411
+ if (sessions.length === 0) {
8412
+ console.log(" No sessions found.");
8413
+ } else {
8414
+ console.log(" Recent sessions:");
8415
+ for (const s of sessions) {
8416
+ const date = new Date(s.lastActivity).toLocaleString();
8417
+ console.log(` ${s.fileName} \u2014 ${s.entryCount} entries, last activity: ${date}`);
8418
+ }
8419
+ console.log(" Use /resume to restore the most recent session.");
8420
+ }
8421
+ } catch (err) {
8422
+ console.error(` Failed to list sessions: ${err.message}`);
8423
+ }
8424
+ rl.prompt();
8425
+ return;
8426
+ }
6994
8427
  if (!adapter) {
6995
8428
  console.log("[No provider configured]");
6996
8429
  rl.prompt();
@@ -7011,10 +8444,10 @@ async function runBasicRepl(config) {
7011
8444
  cwd: process.cwd(),
7012
8445
  abortSignal: abortController.signal,
7013
8446
  onPermissionRequest: async (operation, details) => {
7014
- return new Promise((resolve9) => {
8447
+ return new Promise((resolve10) => {
7015
8448
  const detailStr = details ? ` (${details})` : "";
7016
8449
  rl.question(` Allow ${operation}${detailStr}? [y/N]: `, (answer) => {
7017
- resolve9(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
8450
+ resolve10(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
7018
8451
  });
7019
8452
  });
7020
8453
  },
@@ -7499,9 +8932,9 @@ function colorizeAnsi(text, ansiName) {
7499
8932
  }
7500
8933
 
7501
8934
  // src/services/cron/task-store.ts
7502
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
7503
- import { join as join11, dirname as dirname3 } from "path";
7504
- import { randomUUID as randomUUID7 } from "crypto";
8935
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
8936
+ import { join as join13, dirname as dirname4 } from "path";
8937
+ import { randomUUID as randomUUID9 } from "crypto";
7505
8938
  var DEFAULT_STORE_DIR = getDataDir();
7506
8939
  var DEFAULT_STORE_FILE = "scheduled_tasks.json";
7507
8940
  var TaskStore2 = class {
@@ -7510,10 +8943,10 @@ var TaskStore2 = class {
7510
8943
  loaded = false;
7511
8944
  constructor(storeDir) {
7512
8945
  const dir = storeDir ?? DEFAULT_STORE_DIR;
7513
- this.filePath = join11(dir, DEFAULT_STORE_FILE);
8946
+ this.filePath = join13(dir, DEFAULT_STORE_FILE);
7514
8947
  }
7515
8948
  load() {
7516
- if (!existsSync4(this.filePath)) {
8949
+ if (!existsSync6(this.filePath)) {
7517
8950
  this.tasks.clear();
7518
8951
  this.loaded = true;
7519
8952
  return;
@@ -7531,8 +8964,8 @@ var TaskStore2 = class {
7531
8964
  this.loaded = true;
7532
8965
  }
7533
8966
  save() {
7534
- const dir = dirname3(this.filePath);
7535
- if (!existsSync4(dir)) {
8967
+ const dir = dirname4(this.filePath);
8968
+ if (!existsSync6(dir)) {
7536
8969
  mkdirSync2(dir, { recursive: true });
7537
8970
  }
7538
8971
  const data = Array.from(this.tasks.values());
@@ -7546,7 +8979,7 @@ var TaskStore2 = class {
7546
8979
  addTask(expression, prompt, type = "recurring", nextRun = null) {
7547
8980
  this.ensureLoaded();
7548
8981
  const task = {
7549
- id: randomUUID7(),
8982
+ id: randomUUID9(),
7550
8983
  expression,
7551
8984
  prompt,
7552
8985
  type,
@@ -7841,14 +9274,14 @@ var CronScheduler = class {
7841
9274
  }
7842
9275
  }
7843
9276
  delay(ms) {
7844
- return new Promise((resolve9) => setTimeout(resolve9, ms));
9277
+ return new Promise((resolve10) => setTimeout(resolve10, ms));
7845
9278
  }
7846
9279
  };
7847
9280
 
7848
9281
  // src/services/doctor.ts
7849
9282
  import { execSync as execSync3 } from "child_process";
7850
- import { readFileSync as readFileSync4, existsSync as existsSync5, statSync as statSync2 } from "fs";
7851
- import { join as join12 } from "path";
9283
+ import { readFileSync as readFileSync4, existsSync as existsSync7, statSync as statSync2 } from "fs";
9284
+ import { join as join14 } from "path";
7852
9285
  var TOOLS = [
7853
9286
  { name: "git", command: "git", versionFlag: "--version", optional: false },
7854
9287
  { name: "ripgrep (rg)", command: "rg", versionFlag: "--version", optional: true },
@@ -7875,8 +9308,8 @@ var DoctorDiagnostic = class {
7875
9308
  checkInstallation() {
7876
9309
  const execPath = process.execPath;
7877
9310
  const isNpmGlobal = execPath.includes("npm") || execPath.includes("npx");
7878
- const isLocal = existsSync5(join12(process.cwd(), "node_modules", "cliskill"));
7879
- const isPackageManager = existsSync5(join12(process.cwd(), "package.json"));
9311
+ const isLocal = existsSync7(join14(process.cwd(), "node_modules", "cliskill"));
9312
+ const isPackageManager = existsSync7(join14(process.cwd(), "package.json"));
7880
9313
  let installType = "unknown";
7881
9314
  if (isNpmGlobal) installType = "npm-global";
7882
9315
  else if (isLocal) installType = "local";
@@ -7988,7 +9421,7 @@ var DoctorDiagnostic = class {
7988
9421
  checkConfig() {
7989
9422
  const configPaths = getConfigSearchPaths();
7990
9423
  for (const configPath of configPaths) {
7991
- if (existsSync5(configPath)) {
9424
+ if (existsSync7(configPath)) {
7992
9425
  try {
7993
9426
  const raw = readFileSync4(configPath, "utf-8");
7994
9427
  JSON.parse(raw);
@@ -8019,7 +9452,7 @@ var DoctorDiagnostic = class {
8019
9452
  const configPaths = getConfigSearchPaths();
8020
9453
  let baseUrl = "";
8021
9454
  for (const configPath of configPaths) {
8022
- if (existsSync5(configPath)) {
9455
+ if (existsSync7(configPath)) {
8023
9456
  try {
8024
9457
  const raw = readFileSync4(configPath, "utf-8");
8025
9458
  const config = JSON.parse(raw);
@@ -8066,7 +9499,7 @@ var DoctorDiagnostic = class {
8066
9499
  checkDiskSpace() {
8067
9500
  const memoryDir = getDataDir();
8068
9501
  const historyFile = getDiskHistoryPath();
8069
- if (!existsSync5(memoryDir)) {
9502
+ if (!existsSync7(memoryDir)) {
8070
9503
  return {
8071
9504
  name: "Disk Space",
8072
9505
  status: "ok",
@@ -8075,7 +9508,7 @@ var DoctorDiagnostic = class {
8075
9508
  };
8076
9509
  }
8077
9510
  try {
8078
- const historyStats = existsSync5(historyFile) ? statSync2(historyFile) : null;
9511
+ const historyStats = existsSync7(historyFile) ? statSync2(historyFile) : null;
8079
9512
  const historySize = historyStats ? historyStats.size : 0;
8080
9513
  return {
8081
9514
  name: "Disk Space",
@@ -8143,9 +9576,9 @@ var DoctorDiagnostic = class {
8143
9576
  };
8144
9577
 
8145
9578
  // src/services/conversation-recovery.ts
8146
- import { readFile as readFile8, readdir as readdir5, stat as stat3 } from "fs/promises";
8147
- import { existsSync as existsSync6 } from "fs";
8148
- import { join as join13, basename as basename3 } from "path";
9579
+ import { readFile as readFile10, readdir as readdir6, stat as stat4 } from "fs/promises";
9580
+ import { existsSync as existsSync8 } from "fs";
9581
+ import { join as join15, basename as basename3 } from "path";
8149
9582
  var DEFAULT_RECOVERY_OPTIONS = {
8150
9583
  sessionId: "",
8151
9584
  repairMode: false,
@@ -8159,7 +9592,7 @@ var ConversationRecovery = class {
8159
9592
  const opts = { ...DEFAULT_RECOVERY_OPTIONS, ...options };
8160
9593
  const warnings = [];
8161
9594
  const repairs = [];
8162
- if (!existsSync6(filePath)) {
9595
+ if (!existsSync8(filePath)) {
8163
9596
  return {
8164
9597
  success: false,
8165
9598
  messages: [],
@@ -8174,7 +9607,7 @@ var ConversationRecovery = class {
8174
9607
  }
8175
9608
  };
8176
9609
  }
8177
- const rawContent = await readFile8(filePath, "utf-8");
9610
+ const rawContent = await readFile10(filePath, "utf-8");
8178
9611
  const rawMessages = this.parseJsonl(rawContent);
8179
9612
  const totalMessages = rawMessages.length;
8180
9613
  const validated = this.validateMessages(rawMessages, opts.repairMode);
@@ -8216,7 +9649,7 @@ var ConversationRecovery = class {
8216
9649
  };
8217
9650
  }
8218
9651
  async findLatestSession(historyDir) {
8219
- if (!existsSync6(historyDir)) return null;
9652
+ if (!existsSync8(historyDir)) return null;
8220
9653
  const sessions = await this.listSessions(historyDir);
8221
9654
  if (sessions.length === 0) return null;
8222
9655
  sessions.sort(
@@ -8225,7 +9658,7 @@ var ConversationRecovery = class {
8225
9658
  return sessions[0].filePath;
8226
9659
  }
8227
9660
  async findSessionById(historyDir, sessionId) {
8228
- if (!existsSync6(historyDir)) return null;
9661
+ if (!existsSync8(historyDir)) return null;
8229
9662
  const sessions = await this.listSessions(historyDir);
8230
9663
  const match = sessions.find(
8231
9664
  (s) => s.id === sessionId || s.id.startsWith(sessionId) || basename3(s.filePath).includes(sessionId)
@@ -8235,12 +9668,12 @@ var ConversationRecovery = class {
8235
9668
  async validate(filePath) {
8236
9669
  const errors = [];
8237
9670
  const warnings = [];
8238
- if (!existsSync6(filePath)) {
9671
+ if (!existsSync8(filePath)) {
8239
9672
  return { valid: false, errors: ["File not found"], warnings: [] };
8240
9673
  }
8241
9674
  let rawContent;
8242
9675
  try {
8243
- rawContent = await readFile8(filePath, "utf-8");
9676
+ rawContent = await readFile10(filePath, "utf-8");
8244
9677
  } catch {
8245
9678
  return { valid: false, errors: ["Cannot read file"], warnings: [] };
8246
9679
  }
@@ -8275,15 +9708,15 @@ var ConversationRecovery = class {
8275
9708
  };
8276
9709
  }
8277
9710
  async listSessions(historyDir) {
8278
- if (!existsSync6(historyDir)) return [];
8279
- const entries = await readdir5(historyDir);
9711
+ if (!existsSync8(historyDir)) return [];
9712
+ const entries = await readdir6(historyDir);
8280
9713
  const sessions = [];
8281
9714
  for (const entry of entries) {
8282
- const filePath = join13(historyDir, entry);
8283
- const fileStat = await stat3(filePath);
9715
+ const filePath = join15(historyDir, entry);
9716
+ const fileStat = await stat4(filePath);
8284
9717
  if (fileStat.isDirectory()) {
8285
- const sessionFile = join13(filePath, "conversation.jsonl");
8286
- if (existsSync6(sessionFile)) {
9718
+ const sessionFile = join15(filePath, "conversation.jsonl");
9719
+ if (existsSync8(sessionFile)) {
8287
9720
  const info2 = await this.buildSessionInfo(
8288
9721
  sessionFile,
8289
9722
  fileStat.mtimeMs
@@ -8300,7 +9733,7 @@ var ConversationRecovery = class {
8300
9733
  }
8301
9734
  async buildSessionInfo(filePath, mtimeMs) {
8302
9735
  try {
8303
- const content = await readFile8(filePath, "utf-8");
9736
+ const content = await readFile10(filePath, "utf-8");
8304
9737
  const lines = content.split("\n").filter(Boolean);
8305
9738
  const messages = [];
8306
9739
  for (const line of lines) {
@@ -8494,7 +9927,7 @@ var ConversationRecovery = class {
8494
9927
  // src/services/deep-links.ts
8495
9928
  import { execFile as execFile4 } from "child_process";
8496
9929
  import { platform as platform2 } from "os";
8497
- import { resolve as resolve8, normalize, sep } from "path";
9930
+ import { resolve as resolve9, normalize, sep } from "path";
8498
9931
  import { promisify } from "util";
8499
9932
  var execFileAsync = promisify(execFile4);
8500
9933
  var ALLOWED_ACTIONS = /* @__PURE__ */ new Set(["open", "run", "config"]);
@@ -8775,10 +10208,10 @@ var DeepLinkHandler = class {
8775
10208
  }
8776
10209
  async checkMacOS() {
8777
10210
  try {
8778
- const { stat: stat4 } = await import("fs/promises");
10211
+ const { stat: stat5 } = await import("fs/promises");
8779
10212
  const { getPlistPath } = await import("./paths-OODUHG6V.js");
8780
10213
  const plistPath = getPlistPath();
8781
- await stat4(plistPath);
10214
+ await stat5(plistPath);
8782
10215
  return true;
8783
10216
  } catch {
8784
10217
  return false;
@@ -8795,10 +10228,10 @@ NoDisplay=true
8795
10228
  `;
8796
10229
  const { writeFile: writeFile5, mkdir: mkdir5 } = await import("fs/promises");
8797
10230
  const { homedir } = await import("os");
8798
- const { join: join14 } = await import("path");
8799
- const dir = join14(homedir(), ".local", "share", "applications");
10231
+ const { join: join16 } = await import("path");
10232
+ const dir = join16(homedir(), ".local", "share", "applications");
8800
10233
  await mkdir5(dir, { recursive: true });
8801
- await writeFile5(join14(dir, "cliskill.desktop"), desktopContent, "utf-8");
10234
+ await writeFile5(join16(dir, "cliskill.desktop"), desktopContent, "utf-8");
8802
10235
  try {
8803
10236
  await execFileAsync("update-desktop-database", [dir]);
8804
10237
  } catch {
@@ -8815,8 +10248,8 @@ NoDisplay=true
8815
10248
  async unregisterLinux() {
8816
10249
  const { unlink: unlink4 } = await import("fs/promises");
8817
10250
  const { homedir } = await import("os");
8818
- const { join: join14 } = await import("path");
8819
- const desktopPath = join14(
10251
+ const { join: join16 } = await import("path");
10252
+ const desktopPath = join16(
8820
10253
  homedir(),
8821
10254
  ".local",
8822
10255
  "share",
@@ -8830,17 +10263,17 @@ NoDisplay=true
8830
10263
  }
8831
10264
  async checkLinux() {
8832
10265
  try {
8833
- const { stat: stat4 } = await import("fs/promises");
10266
+ const { stat: stat5 } = await import("fs/promises");
8834
10267
  const { homedir } = await import("os");
8835
- const { join: join14 } = await import("path");
8836
- const desktopPath = join14(
10268
+ const { join: join16 } = await import("path");
10269
+ const desktopPath = join16(
8837
10270
  homedir(),
8838
10271
  ".local",
8839
10272
  "share",
8840
10273
  "applications",
8841
10274
  "cliskill.desktop"
8842
10275
  );
8843
- await stat4(desktopPath);
10276
+ await stat5(desktopPath);
8844
10277
  return true;
8845
10278
  } catch {
8846
10279
  return false;
@@ -9754,18 +11187,18 @@ function WizardLayout() {
9754
11187
  // src/ui/wizard/index.tsx
9755
11188
  import { jsx as jsx16 } from "react/jsx-runtime";
9756
11189
  function renderWizard() {
9757
- return new Promise((resolve9) => {
11190
+ return new Promise((resolve10) => {
9758
11191
  const { unmount } = render2(
9759
11192
  /* @__PURE__ */ jsx16(
9760
11193
  WizardProvider,
9761
11194
  {
9762
11195
  onComplete: (state) => {
9763
11196
  unmount();
9764
- resolve9(state);
11197
+ resolve10(state);
9765
11198
  },
9766
11199
  onCancel: () => {
9767
11200
  unmount();
9768
- resolve9(null);
11201
+ resolve10(null);
9769
11202
  },
9770
11203
  children: /* @__PURE__ */ jsx16(WizardLayout, {})
9771
11204
  }
@@ -10030,6 +11463,7 @@ export {
10030
11463
  QueryEngine,
10031
11464
  AgentTool,
10032
11465
  EnhancedMemoryStore,
11466
+ TaskManager,
10033
11467
  runAgentLoop,
10034
11468
  createDefaultToolRegistry,
10035
11469
  StatusBar,
@@ -10038,6 +11472,8 @@ export {
10038
11472
  renderInkApp,
10039
11473
  formatAnsiMessage,
10040
11474
  MessageList,
11475
+ MCPClient,
11476
+ MCPConnectionManager,
10041
11477
  runCli
10042
11478
  };
10043
- //# sourceMappingURL=chunk-UJMUL64T.js.map
11479
+ //# sourceMappingURL=chunk-S4ZZPUPF.js.map