@wrongstack/tools 0.264.0 → 0.265.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit.js +154 -11
- package/dist/audit.js.map +1 -1
- package/dist/bash.js +138 -2
- package/dist/bash.js.map +1 -1
- package/dist/batch-tool-use.js +1 -0
- package/dist/batch-tool-use.js.map +1 -1
- package/dist/builtin.d.ts +20 -1
- package/dist/builtin.js +661 -325
- package/dist/builtin.js.map +1 -1
- package/dist/circuit-breaker.d.ts +20 -0
- package/dist/circuit-breaker.js +40 -2
- package/dist/circuit-breaker.js.map +1 -1
- package/dist/codebase-index/index.d.ts +16 -0
- package/dist/codebase-index/index.js +59 -25
- package/dist/codebase-index/index.js.map +1 -1
- package/dist/codebase-index/worker.js +56 -25
- package/dist/codebase-index/worker.js.map +1 -1
- package/dist/diff.js +14 -7
- package/dist/diff.js.map +1 -1
- package/dist/document.js +14 -8
- package/dist/document.js.map +1 -1
- package/dist/edit.js +21 -15
- package/dist/edit.js.map +1 -1
- package/dist/exec.js +140 -3
- package/dist/exec.js.map +1 -1
- package/dist/fetch.js +1 -0
- package/dist/fetch.js.map +1 -1
- package/dist/format.js +153 -11
- package/dist/format.js.map +1 -1
- package/dist/git.js +1 -0
- package/dist/git.js.map +1 -1
- package/dist/glob.js +14 -7
- package/dist/glob.js.map +1 -1
- package/dist/grep.js +14 -7
- package/dist/grep.js.map +1 -1
- package/dist/index.d.ts +55 -3
- package/dist/index.js +819 -325
- package/dist/index.js.map +1 -1
- package/dist/install.js +153 -11
- package/dist/install.js.map +1 -1
- package/dist/json.js +1 -0
- package/dist/json.js.map +1 -1
- package/dist/lint.js +153 -11
- package/dist/lint.js.map +1 -1
- package/dist/logs.js +14 -7
- package/dist/logs.js.map +1 -1
- package/dist/memory.js +1 -0
- package/dist/memory.js.map +1 -1
- package/dist/mode.js +1 -0
- package/dist/mode.js.map +1 -1
- package/dist/outdated.js +16 -8
- package/dist/outdated.js.map +1 -1
- package/dist/pack.js +630 -324
- package/dist/pack.js.map +1 -1
- package/dist/patch.js +14 -7
- package/dist/patch.js.map +1 -1
- package/dist/process-registry.d.ts +56 -2
- package/dist/process-registry.js +138 -3
- package/dist/process-registry.js.map +1 -1
- package/dist/read.js +21 -16
- package/dist/read.js.map +1 -1
- package/dist/replace.js +14 -7
- package/dist/replace.js.map +1 -1
- package/dist/scaffold.js +14 -7
- package/dist/scaffold.js.map +1 -1
- package/dist/search.js +1 -0
- package/dist/search.js.map +1 -1
- package/dist/test.js +153 -11
- package/dist/test.js.map +1 -1
- package/dist/todo.js +1 -0
- package/dist/todo.js.map +1 -1
- package/dist/tool-help.js +1 -0
- package/dist/tool-help.js.map +1 -1
- package/dist/tool-icons.d.ts +20 -0
- package/dist/tool-icons.js +130 -0
- package/dist/tool-icons.js.map +1 -0
- package/dist/tool-search.js +1 -0
- package/dist/tool-search.js.map +1 -1
- package/dist/tool-use.js +1 -0
- package/dist/tool-use.js.map +1 -1
- package/dist/tree.js +14 -7
- package/dist/tree.js.map +1 -1
- package/dist/typecheck.js +153 -11
- package/dist/typecheck.js.map +1 -1
- package/dist/write.js +21 -15
- package/dist/write.js.map +1 -1
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs4 from 'node:fs/promises';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { resolve, sep, dirname, join } from 'node:path';
|
|
4
4
|
import * as Core from '@wrongstack/core';
|
|
5
|
-
import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, expectDefined, buildChildEnv, isPrivateIPv4, isPrivateIPv6, loadPlan, setPlanItemStatus, savePlan, loadTasks, saveTasks, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem,
|
|
5
|
+
import { atomicWrite, unifiedDiff, detectNewlineStyle, normalizeToLf, toStyle, compileGlob, expectDefined, buildChildEnv, isPrivateIPv4, isPrivateIPv6, loadPlan, setPlanItemStatus, savePlan, loadTasks, saveTasks, mutatePlan, clearPlan, getPlanTemplate, addPlanItem, deriveTodosFromPlanItem, removePlanItem, mutateTasks, formatTaskList, formatPlan, recordPackageAction, detectPackageEcosystem, computeTaskItemProgress, wstackGlobalRoot, resolveWstackPaths, truncate } from '@wrongstack/core';
|
|
6
6
|
import { toErrorMessage } from '@wrongstack/core/utils';
|
|
7
7
|
import { spawn, execFileSync, spawnSync } from 'node:child_process';
|
|
8
8
|
import * as os from 'node:os';
|
|
@@ -36,22 +36,29 @@ async function detectPackageManager(cwd) {
|
|
|
36
36
|
function resolvePath(input, ctx) {
|
|
37
37
|
return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.workingDir ?? ctx.cwd, input);
|
|
38
38
|
}
|
|
39
|
+
function allowedRoots(ctx) {
|
|
40
|
+
return [path.resolve(ctx.projectRoot), path.resolve(Core.wstackGlobalRoot())];
|
|
41
|
+
}
|
|
42
|
+
function isInsideAny(target, roots) {
|
|
43
|
+
return roots.some((root) => {
|
|
44
|
+
const rel = path.relative(root, target);
|
|
45
|
+
return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
39
48
|
function ensureInsideRoot(absPath, ctx) {
|
|
40
|
-
if (ctx.allowOutsideProjectRoot) return path.resolve(absPath);
|
|
41
|
-
const root = path.resolve(ctx.projectRoot);
|
|
42
49
|
const target = path.resolve(absPath);
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
return target;
|
|
50
|
+
if (ctx.allowOutsideProjectRoot) return target;
|
|
51
|
+
if (isInsideAny(target, allowedRoots(ctx))) return target;
|
|
52
|
+
throw new Error(`Path "${absPath}" is outside project root "${path.resolve(ctx.projectRoot)}"`);
|
|
48
53
|
}
|
|
49
54
|
function safeResolve(input, ctx) {
|
|
50
55
|
return ensureInsideRoot(resolvePath(input, ctx), ctx);
|
|
51
56
|
}
|
|
52
57
|
async function assertRealInsideRoot(absPath, ctx) {
|
|
53
58
|
if (ctx.allowOutsideProjectRoot) return;
|
|
54
|
-
const
|
|
59
|
+
const realRoots = await Promise.all(
|
|
60
|
+
allowedRoots(ctx).map((r) => fs4.realpath(r).catch(() => path.resolve(r)))
|
|
61
|
+
);
|
|
55
62
|
let probe = absPath;
|
|
56
63
|
for (; ; ) {
|
|
57
64
|
let real;
|
|
@@ -66,13 +73,10 @@ async function assertRealInsideRoot(absPath, ctx) {
|
|
|
66
73
|
}
|
|
67
74
|
throw err;
|
|
68
75
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
return;
|
|
76
|
+
if (isInsideAny(real, realRoots)) return;
|
|
77
|
+
throw new Error(
|
|
78
|
+
`Path "${absPath}" resolves through a symlink outside project root "${realRoots[0]}"`
|
|
79
|
+
);
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
82
|
async function safeResolveReal(input, ctx) {
|
|
@@ -173,6 +177,7 @@ var readTool = {
|
|
|
173
177
|
permission: "auto",
|
|
174
178
|
mutating: false,
|
|
175
179
|
capabilities: ["fs.read"],
|
|
180
|
+
icon: "file",
|
|
176
181
|
maxOutputBytes: 262144,
|
|
177
182
|
timeoutMs: 5e3,
|
|
178
183
|
inputSchema: {
|
|
@@ -245,6 +250,7 @@ var writeTool = {
|
|
|
245
250
|
mutating: true,
|
|
246
251
|
timeoutMs: 5e3,
|
|
247
252
|
capabilities: ["fs.write"],
|
|
253
|
+
icon: "file",
|
|
248
254
|
inputSchema: {
|
|
249
255
|
type: "object",
|
|
250
256
|
properties: {
|
|
@@ -308,6 +314,7 @@ var editTool = {
|
|
|
308
314
|
permission: "confirm",
|
|
309
315
|
mutating: true,
|
|
310
316
|
capabilities: ["fs.write"],
|
|
317
|
+
icon: "edit",
|
|
311
318
|
timeoutMs: 5e3,
|
|
312
319
|
inputSchema: {
|
|
313
320
|
type: "object",
|
|
@@ -475,6 +482,7 @@ var replaceTool = {
|
|
|
475
482
|
permission: "confirm",
|
|
476
483
|
mutating: true,
|
|
477
484
|
capabilities: ["fs.write"],
|
|
485
|
+
icon: "edit",
|
|
478
486
|
timeoutMs: 3e4,
|
|
479
487
|
inputSchema: {
|
|
480
488
|
type: "object",
|
|
@@ -676,6 +684,7 @@ var globTool = {
|
|
|
676
684
|
permission: "auto",
|
|
677
685
|
mutating: false,
|
|
678
686
|
capabilities: ["fs.read"],
|
|
687
|
+
icon: "folder",
|
|
679
688
|
maxOutputBytes: 65536,
|
|
680
689
|
timeoutMs: 5e3,
|
|
681
690
|
inputSchema: {
|
|
@@ -761,6 +770,7 @@ var grepTool = {
|
|
|
761
770
|
permission: "auto",
|
|
762
771
|
mutating: false,
|
|
763
772
|
capabilities: ["fs.read"],
|
|
773
|
+
icon: "search",
|
|
764
774
|
maxOutputBytes: 131072,
|
|
765
775
|
timeoutMs: 1e4,
|
|
766
776
|
inputSchema: {
|
|
@@ -1162,6 +1172,23 @@ var CircuitBreaker = class {
|
|
|
1162
1172
|
lastSlowAt = null;
|
|
1163
1173
|
/** Timestamp when the breaker was opened (for cooldown calculation). */
|
|
1164
1174
|
openedAt = null;
|
|
1175
|
+
/**
|
|
1176
|
+
* Master enable flag. When false the breaker is bypassed: `beforeCall`
|
|
1177
|
+
* always returns true and `afterCall` records nothing. The class itself
|
|
1178
|
+
* defaults to enabled (so the standalone unit tests exercise tripping); the
|
|
1179
|
+
* ProcessRegistry flips this off until the user opts in via `/settings`.
|
|
1180
|
+
*/
|
|
1181
|
+
enabled = true;
|
|
1182
|
+
/**
|
|
1183
|
+
* Fired (best-effort) when the breaker transitions into the `open` state.
|
|
1184
|
+
* The registry uses this to arm its auto kill/reset countdown.
|
|
1185
|
+
*/
|
|
1186
|
+
onTrip;
|
|
1187
|
+
/**
|
|
1188
|
+
* Fired (best-effort) when the breaker returns to `closed` after having been
|
|
1189
|
+
* open/half-open. The registry uses this to cancel a pending kill/reset.
|
|
1190
|
+
*/
|
|
1191
|
+
onReset;
|
|
1165
1192
|
constructor(config = {}) {
|
|
1166
1193
|
this.maxConsecutiveFailures = config.maxConsecutiveFailures ?? DEFAULT_MAX_CONSECUTIVE_FAILURES;
|
|
1167
1194
|
this.slowCallThresholdMs = config.slowCallThresholdMs ?? DEFAULT_SLOW_CALL_THRESHOLD_MS;
|
|
@@ -1170,12 +1197,22 @@ var CircuitBreaker = class {
|
|
|
1170
1197
|
this.maxCallsPerWindow = config.maxCallsPerWindow ?? DEFAULT_MAX_CALLS_PER_WINDOW;
|
|
1171
1198
|
this.cooldownMs = config.cooldownMs ?? DEFAULT_COOLDOWN_MS;
|
|
1172
1199
|
}
|
|
1200
|
+
/** Toggle the master enable. Disabling resets to a clean `closed` state. */
|
|
1201
|
+
setEnabled(enabled) {
|
|
1202
|
+
if (this.enabled === enabled) return;
|
|
1203
|
+
this.enabled = enabled;
|
|
1204
|
+
if (!enabled) this._reset();
|
|
1205
|
+
}
|
|
1206
|
+
get isEnabled() {
|
|
1207
|
+
return this.enabled;
|
|
1208
|
+
}
|
|
1173
1209
|
/**
|
|
1174
1210
|
* Returns true if the circuit allows a new call to proceed.
|
|
1175
1211
|
* When false, callers should abort the tool call and return a
|
|
1176
1212
|
* circuit-breaker error instead of spawning a process.
|
|
1177
1213
|
*/
|
|
1178
1214
|
get canProceed() {
|
|
1215
|
+
if (!this.enabled) return true;
|
|
1179
1216
|
this._checkStateTransition();
|
|
1180
1217
|
return this.state !== "open";
|
|
1181
1218
|
}
|
|
@@ -1211,7 +1248,7 @@ var CircuitBreaker = class {
|
|
|
1211
1248
|
* not affect breaker state.
|
|
1212
1249
|
*/
|
|
1213
1250
|
beforeCall(bypass = false) {
|
|
1214
|
-
if (bypass) return true;
|
|
1251
|
+
if (bypass || !this.enabled) return true;
|
|
1215
1252
|
this._checkStateTransition();
|
|
1216
1253
|
if (this.state === "open") return false;
|
|
1217
1254
|
return true;
|
|
@@ -1226,7 +1263,7 @@ var CircuitBreaker = class {
|
|
|
1226
1263
|
* Use for background/fire-and-forget processes.
|
|
1227
1264
|
*/
|
|
1228
1265
|
afterCall(durationMs, failed, bypass = false) {
|
|
1229
|
-
if (bypass) return;
|
|
1266
|
+
if (bypass || !this.enabled) return;
|
|
1230
1267
|
const now = Date.now();
|
|
1231
1268
|
if (this.state === "half-open") {
|
|
1232
1269
|
if (failed) {
|
|
@@ -1272,12 +1309,23 @@ var CircuitBreaker = class {
|
|
|
1272
1309
|
if (this.state === "open") return;
|
|
1273
1310
|
this.state = "open";
|
|
1274
1311
|
this.openedAt = Date.now();
|
|
1312
|
+
try {
|
|
1313
|
+
this.onTrip?.();
|
|
1314
|
+
} catch {
|
|
1315
|
+
}
|
|
1275
1316
|
}
|
|
1276
1317
|
_reset() {
|
|
1318
|
+
const wasRecovering = this.state !== "closed";
|
|
1277
1319
|
this.state = "closed";
|
|
1278
1320
|
this.consecutiveFailures = 0;
|
|
1279
1321
|
this.window = [];
|
|
1280
1322
|
this.openedAt = null;
|
|
1323
|
+
if (wasRecovering) {
|
|
1324
|
+
try {
|
|
1325
|
+
this.onReset?.();
|
|
1326
|
+
} catch {
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1281
1329
|
}
|
|
1282
1330
|
/** Transition from open → half-open when cooldown elapses. */
|
|
1283
1331
|
_checkStateTransition() {
|
|
@@ -1339,8 +1387,21 @@ function killWin32Tree(pid) {
|
|
|
1339
1387
|
var ProcessRegistryImpl = class {
|
|
1340
1388
|
processes = /* @__PURE__ */ new Map();
|
|
1341
1389
|
breaker;
|
|
1390
|
+
/**
|
|
1391
|
+
* Auto kill/reset config. When the breaker trips and `autoKillResetMs > 0`,
|
|
1392
|
+
* a countdown is armed; on expiry all tracked processes are killed and the
|
|
1393
|
+
* breaker is reset to closed (forced recovery). Zero means manual recovery
|
|
1394
|
+
* only (`/kill reset`).
|
|
1395
|
+
*/
|
|
1396
|
+
autoKillResetMs = 0;
|
|
1397
|
+
autoKillTimer = null;
|
|
1398
|
+
autoKillArmedAt = null;
|
|
1399
|
+
breakerCountdownListeners = [];
|
|
1342
1400
|
constructor(breakerConfig) {
|
|
1343
1401
|
this.breaker = new CircuitBreaker(breakerConfig);
|
|
1402
|
+
this.breaker.onTrip = () => this._armAutoKillReset();
|
|
1403
|
+
this.breaker.onReset = () => this._cancelAutoKillReset();
|
|
1404
|
+
this.breaker.setEnabled(false);
|
|
1344
1405
|
}
|
|
1345
1406
|
register(info) {
|
|
1346
1407
|
this.processes.set(info.pid, { ...info, killed: false, protected: info.protected ?? false });
|
|
@@ -1416,6 +1477,90 @@ var ProcessRegistryImpl = class {
|
|
|
1416
1477
|
forceBreakerReset() {
|
|
1417
1478
|
this.breaker.forceReset();
|
|
1418
1479
|
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Configure circuit-breaker protection at runtime. Called from `/settings`
|
|
1482
|
+
* (instant, all modes) and on TUI mount (applies persisted config).
|
|
1483
|
+
*
|
|
1484
|
+
* - `enabled` toggles whether the breaker gates `bash`/`exec`.
|
|
1485
|
+
* - `autoKillResetMs` arms the auto kill/reset countdown when the breaker
|
|
1486
|
+
* trips (0 = manual recovery only).
|
|
1487
|
+
*
|
|
1488
|
+
* Re-applies cleanly on every call: cancels a pending countdown when the
|
|
1489
|
+
* timeout is cleared or protection disabled, and re-arms if the breaker is
|
|
1490
|
+
* currently open under the new settings.
|
|
1491
|
+
*/
|
|
1492
|
+
setBreakerConfig(cfg) {
|
|
1493
|
+
if (cfg.enabled !== void 0) this.breaker.setEnabled(cfg.enabled);
|
|
1494
|
+
if (cfg.autoKillResetMs !== void 0) this.autoKillResetMs = Math.max(0, cfg.autoKillResetMs);
|
|
1495
|
+
if (this.autoKillResetMs <= 0) {
|
|
1496
|
+
this._cancelAutoKillReset();
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
if (this.breaker.isEnabled && this.breaker.snapshot().state === "open") {
|
|
1500
|
+
this._armAutoKillReset();
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Live countdown to the next auto kill/reset, or null when nothing is armed.
|
|
1505
|
+
* The TUI polls this on a 1s tick while armed so the statusline decrements.
|
|
1506
|
+
*/
|
|
1507
|
+
getBreakerCountdown() {
|
|
1508
|
+
if (this.autoKillArmedAt === null || this.autoKillResetMs <= 0) return null;
|
|
1509
|
+
const elapsed = Date.now() - this.autoKillArmedAt;
|
|
1510
|
+
return { remainingMs: Math.max(0, this.autoKillResetMs - elapsed), totalMs: this.autoKillResetMs };
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Subscribe to countdown arm/cancel events. Returns an unsubscribe function.
|
|
1514
|
+
* Use {@link getBreakerCountdown} for the live ticking value between events.
|
|
1515
|
+
*/
|
|
1516
|
+
onBreakerCountdownChange(listener) {
|
|
1517
|
+
this.breakerCountdownListeners.push(listener);
|
|
1518
|
+
return () => {
|
|
1519
|
+
this.breakerCountdownListeners = this.breakerCountdownListeners.filter((l) => l !== listener);
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
_emitBreakerCountdown() {
|
|
1523
|
+
const snap = this.getBreakerCountdown();
|
|
1524
|
+
for (const l of this.breakerCountdownListeners) {
|
|
1525
|
+
try {
|
|
1526
|
+
l(snap);
|
|
1527
|
+
} catch {
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Arm the auto kill/reset countdown. Idempotent: re-arming resets the window
|
|
1533
|
+
* (a fresh trip after a failed half-open probe restarts the clock). No-op
|
|
1534
|
+
* when protection is off or no timeout is configured.
|
|
1535
|
+
*/
|
|
1536
|
+
_armAutoKillReset() {
|
|
1537
|
+
if (this.autoKillResetMs <= 0 || !this.breaker.isEnabled) return;
|
|
1538
|
+
this._clearAutoKillTimer();
|
|
1539
|
+
this.autoKillArmedAt = Date.now();
|
|
1540
|
+
this.autoKillTimer = setTimeout(() => {
|
|
1541
|
+
this.autoKillTimer = null;
|
|
1542
|
+
this.autoKillArmedAt = null;
|
|
1543
|
+
this.killAll({ force: false });
|
|
1544
|
+
this.breaker.forceReset();
|
|
1545
|
+
this._emitBreakerCountdown();
|
|
1546
|
+
}, this.autoKillResetMs);
|
|
1547
|
+
this.autoKillTimer.unref?.();
|
|
1548
|
+
this._emitBreakerCountdown();
|
|
1549
|
+
}
|
|
1550
|
+
_cancelAutoKillReset() {
|
|
1551
|
+
const wasArmed = this.autoKillArmedAt !== null;
|
|
1552
|
+
this._clearAutoKillTimer();
|
|
1553
|
+
if (wasArmed) {
|
|
1554
|
+
this.autoKillArmedAt = null;
|
|
1555
|
+
this._emitBreakerCountdown();
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
_clearAutoKillTimer() {
|
|
1559
|
+
if (this.autoKillTimer !== null) {
|
|
1560
|
+
clearTimeout(this.autoKillTimer);
|
|
1561
|
+
this.autoKillTimer = null;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1419
1564
|
/** Kill a single process by PID.
|
|
1420
1565
|
*
|
|
1421
1566
|
* On POSIX: sends SIGTERM to the *process group* (-pid) so that
|
|
@@ -1540,6 +1685,7 @@ var bashTool = {
|
|
|
1540
1685
|
permission: "confirm",
|
|
1541
1686
|
mutating: true,
|
|
1542
1687
|
riskTier: "destructive",
|
|
1688
|
+
icon: "terminal",
|
|
1543
1689
|
// Trust rules match on the literal `command` string. Without subjectKey
|
|
1544
1690
|
// the policy heuristic would have done the same here, but declaring it
|
|
1545
1691
|
// explicitly removes the implicit cross-tool aliasing.
|
|
@@ -2002,6 +2148,7 @@ var execTool = {
|
|
|
2002
2148
|
riskTier: "standard",
|
|
2003
2149
|
timeoutMs: DEFAULT_TIMEOUT_MS2,
|
|
2004
2150
|
capabilities: ["shell.restricted"],
|
|
2151
|
+
icon: "terminal",
|
|
2005
2152
|
inputSchema: {
|
|
2006
2153
|
type: "object",
|
|
2007
2154
|
properties: {
|
|
@@ -2101,7 +2248,8 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId) {
|
|
|
2101
2248
|
const spool = createOutputSpool({ tool: `exec-${cmd}`, thresholdBytes: MAX_OUTPUT2 });
|
|
2102
2249
|
const resolved = resolveWin32Command(cmd);
|
|
2103
2250
|
const needsShell = isWin && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
2104
|
-
const
|
|
2251
|
+
const spawnCmd = needsShell ? cmd : resolved;
|
|
2252
|
+
const child = spawn(spawnCmd, args, {
|
|
2105
2253
|
cwd,
|
|
2106
2254
|
env: buildChildEnv(sessionId),
|
|
2107
2255
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -2288,6 +2436,7 @@ var fetchTool = {
|
|
|
2288
2436
|
permission: "confirm",
|
|
2289
2437
|
mutating: false,
|
|
2290
2438
|
capabilities: ["net.outbound"],
|
|
2439
|
+
icon: "web",
|
|
2291
2440
|
// Trust rules for fetch match on the literal URL — declare it explicitly
|
|
2292
2441
|
// so a user can trust `https://api.example.com/*` without accidentally
|
|
2293
2442
|
// matching that pattern on any other tool that happens to have a `url`
|
|
@@ -2438,6 +2587,7 @@ var searchTool = {
|
|
|
2438
2587
|
permission: "confirm",
|
|
2439
2588
|
mutating: false,
|
|
2440
2589
|
capabilities: ["net.outbound"],
|
|
2590
|
+
icon: "search",
|
|
2441
2591
|
timeoutMs: TIMEOUT_MS2,
|
|
2442
2592
|
inputSchema: {
|
|
2443
2593
|
type: "object",
|
|
@@ -2653,6 +2803,7 @@ var todoTool = {
|
|
|
2653
2803
|
// mutates only conversation state (ctx.todos), not external state — no confirmation needed
|
|
2654
2804
|
timeoutMs: 1e3,
|
|
2655
2805
|
capabilities: ["session.todo"],
|
|
2806
|
+
icon: "todo",
|
|
2656
2807
|
inputSchema: {
|
|
2657
2808
|
type: "object",
|
|
2658
2809
|
properties: {
|
|
@@ -2753,11 +2904,12 @@ var todoTool = {
|
|
|
2753
2904
|
var planTool = {
|
|
2754
2905
|
name: "plan",
|
|
2755
2906
|
category: "Session",
|
|
2756
|
-
description:
|
|
2757
|
-
usageHint: 'RECOMMENDED FOR COMPLEX, MULTI-PHASE WORK:\n\n- Start by creating a high-level plan with `action: "add"` or using templates (`template_use`).\n- Use `promote` to turn a plan item into actionable todos.\n- Use `taskify` to convert a plan item into a structured task (with type/priority/deps).\n- Keep plans at the "why and what" level, and todos at the "how and next step" level.\n- Common templates: "new-feature", "bug-fix", "refactor", "release", "security-audit".\n\nThis tool is excellent for maintaining long-term direction across many turns
|
|
2907
|
+
description: 'Manage a session-persistent strategic plan. The plan is written to disk and survives conversation resumptions within the same session, but is isolated to this session \u2014 other sessions have their own separate plans. Unlike todos (which are per-turn and lost on restart), a plan tracks high-level progress across multiple turns. Use this to outline big-picture work, then promote concrete items into the todo list when ready to execute. By default plans are isolated to this session; use `scope: "project"` to store the plan in a shared project-level file visible to all sessions.',
|
|
2908
|
+
usageHint: 'RECOMMENDED FOR COMPLEX, MULTI-PHASE WORK:\n\n- Start by creating a high-level plan with `action: "add"` or using templates (`template_use`).\n- Use `promote` to turn a plan item into actionable todos.\n- Use `taskify` to convert a plan item into a structured task (with type/priority/deps).\n- Keep plans at the "why and what" level, and todos at the "how and next step" level.\n- Common templates: "new-feature", "bug-fix", "refactor", "release", "security-audit".\n\nThis tool is excellent for maintaining long-term direction across many turns within a session. Plans survive resume but are not shared across separate sessions.\nUse `scope: "project"` to use a shared project-level plan file.',
|
|
2758
2909
|
permission: "confirm",
|
|
2759
2910
|
mutating: true,
|
|
2760
2911
|
capabilities: ["fs.write"],
|
|
2912
|
+
icon: "plan",
|
|
2761
2913
|
timeoutMs: 2e3,
|
|
2762
2914
|
inputSchema: {
|
|
2763
2915
|
type: "object",
|
|
@@ -2797,12 +2949,26 @@ var planTool = {
|
|
|
2797
2949
|
template: {
|
|
2798
2950
|
type: "string",
|
|
2799
2951
|
description: "Template identifier when using action=template_use. Common values: new-feature, bug-fix, refactor, release, security-audit."
|
|
2952
|
+
},
|
|
2953
|
+
scope: {
|
|
2954
|
+
type: "string",
|
|
2955
|
+
enum: ["session", "project"],
|
|
2956
|
+
description: 'Storage scope: "session" (default, isolated to this session) or "project" (shared across all sessions for this project).'
|
|
2800
2957
|
}
|
|
2801
2958
|
},
|
|
2802
2959
|
required: ["action"]
|
|
2803
2960
|
},
|
|
2804
2961
|
async execute(input, ctx) {
|
|
2805
|
-
const
|
|
2962
|
+
const sessionPlanPath = ctx.meta["plan.path"];
|
|
2963
|
+
let planPath;
|
|
2964
|
+
if (input.scope === "project") {
|
|
2965
|
+
if (typeof sessionPlanPath === "string") {
|
|
2966
|
+
const lastSep = Math.max(sessionPlanPath.lastIndexOf("/"), sessionPlanPath.lastIndexOf("\\"));
|
|
2967
|
+
planPath = lastSep >= 0 ? sessionPlanPath.slice(0, lastSep + 1) + "backlog.plan.json" : "backlog.plan.json";
|
|
2968
|
+
}
|
|
2969
|
+
} else {
|
|
2970
|
+
planPath = sessionPlanPath;
|
|
2971
|
+
}
|
|
2806
2972
|
if (typeof planPath !== "string" || !planPath) {
|
|
2807
2973
|
return {
|
|
2808
2974
|
ok: false,
|
|
@@ -2816,148 +2982,169 @@ var planTool = {
|
|
|
2816
2982
|
let early = null;
|
|
2817
2983
|
const taskifyMeta = { title: "", details: "" };
|
|
2818
2984
|
let didTaskify = false;
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
case "done": {
|
|
2834
|
-
if (!input.target) {
|
|
2835
|
-
early = mkResult(p, false, `${input.action} requires \`target\` (id|index|substring).`);
|
|
2836
|
-
return p;
|
|
2837
|
-
}
|
|
2838
|
-
const next = setPlanItemStatus(
|
|
2839
|
-
p,
|
|
2840
|
-
input.target,
|
|
2841
|
-
input.action === "start" ? "in_progress" : "done"
|
|
2842
|
-
);
|
|
2843
|
-
if (next === p) {
|
|
2844
|
-
early = mkResult(p, false, `No plan item matched "${input.target}".`);
|
|
2845
|
-
return p;
|
|
2846
|
-
}
|
|
2847
|
-
return next;
|
|
2848
|
-
}
|
|
2849
|
-
case "remove": {
|
|
2850
|
-
if (!input.target) {
|
|
2851
|
-
early = mkResult(p, false, "remove requires `target` (id|index|substring).");
|
|
2852
|
-
return p;
|
|
2853
|
-
}
|
|
2854
|
-
const next = removePlanItem(p, input.target);
|
|
2855
|
-
if (next === p) {
|
|
2856
|
-
early = mkResult(p, false, `No plan item matched "${input.target}".`);
|
|
2857
|
-
return p;
|
|
2858
|
-
}
|
|
2859
|
-
return next;
|
|
2860
|
-
}
|
|
2861
|
-
case "promote": {
|
|
2862
|
-
if (!input.target) {
|
|
2863
|
-
early = mkResult(p, false, `${input.action} requires \`target\` (id|index|substring).`);
|
|
2864
|
-
return p;
|
|
2865
|
-
}
|
|
2866
|
-
const derived = deriveTodosFromPlanItem(p, input.target, input.subtasks);
|
|
2867
|
-
if (!derived) {
|
|
2868
|
-
early = mkResult(p, false, `No plan item matched "${input.target}".`);
|
|
2869
|
-
return p;
|
|
2985
|
+
let plan;
|
|
2986
|
+
try {
|
|
2987
|
+
plan = await mutatePlan(planPath, sessionId, async (p) => {
|
|
2988
|
+
switch (input.action) {
|
|
2989
|
+
case "show":
|
|
2990
|
+
break;
|
|
2991
|
+
case "add": {
|
|
2992
|
+
const title = input.title?.trim();
|
|
2993
|
+
if (!title) {
|
|
2994
|
+
early = mkResult(p, false, "add requires `title`.");
|
|
2995
|
+
return p;
|
|
2996
|
+
}
|
|
2997
|
+
const { plan: updated } = addPlanItem(p, title, input.details?.trim() || void 0);
|
|
2998
|
+
return updated;
|
|
2870
2999
|
}
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
3000
|
+
case "start":
|
|
3001
|
+
case "done": {
|
|
3002
|
+
if (!input.target) {
|
|
3003
|
+
early = mkResult(p, false, `${input.action} requires \`target\` (id|index|substring).`);
|
|
3004
|
+
return p;
|
|
3005
|
+
}
|
|
3006
|
+
const next = setPlanItemStatus(
|
|
3007
|
+
p,
|
|
3008
|
+
input.target,
|
|
3009
|
+
input.action === "start" ? "in_progress" : "done"
|
|
3010
|
+
);
|
|
3011
|
+
if (next === p) {
|
|
3012
|
+
early = mkResult(p, false, `No plan item matched "${input.target}".`);
|
|
3013
|
+
return p;
|
|
3014
|
+
}
|
|
3015
|
+
return next;
|
|
2885
3016
|
}
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
3017
|
+
case "remove": {
|
|
3018
|
+
if (!input.target) {
|
|
3019
|
+
early = mkResult(p, false, "remove requires `target` (id|index|substring).");
|
|
3020
|
+
return p;
|
|
3021
|
+
}
|
|
3022
|
+
const next = removePlanItem(p, input.target);
|
|
3023
|
+
if (next === p) {
|
|
3024
|
+
early = mkResult(p, false, `No plan item matched "${input.target}".`);
|
|
3025
|
+
return p;
|
|
3026
|
+
}
|
|
3027
|
+
return next;
|
|
2890
3028
|
}
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
3029
|
+
case "promote": {
|
|
3030
|
+
if (!input.target) {
|
|
3031
|
+
early = mkResult(p, false, `${input.action} requires \`target\` (id|index|substring).`);
|
|
3032
|
+
return p;
|
|
3033
|
+
}
|
|
3034
|
+
const derived = deriveTodosFromPlanItem(p, input.target, input.subtasks);
|
|
3035
|
+
if (!derived) {
|
|
3036
|
+
early = mkResult(p, false, `No plan item matched "${input.target}".`);
|
|
3037
|
+
return p;
|
|
3038
|
+
}
|
|
3039
|
+
ctx.state.replaceTodos(derived.todos);
|
|
3040
|
+
early = mkResult(
|
|
3041
|
+
derived.plan,
|
|
3042
|
+
true,
|
|
3043
|
+
`${input.action} ok \u2014 ${derived.todos.length} todo(s) created.`,
|
|
3044
|
+
derived.todos
|
|
3045
|
+
);
|
|
3046
|
+
return derived.plan;
|
|
2894
3047
|
}
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
3048
|
+
case "template_use": {
|
|
3049
|
+
const templateName = input.template?.trim();
|
|
3050
|
+
if (!templateName) {
|
|
3051
|
+
early = mkResult(p, false, "template_use requires `template` name.");
|
|
3052
|
+
return p;
|
|
3053
|
+
}
|
|
3054
|
+
const template = getPlanTemplate(templateName);
|
|
3055
|
+
if (!template) {
|
|
3056
|
+
early = mkResult(p, false, `Unknown template "${templateName}".`);
|
|
3057
|
+
return p;
|
|
3058
|
+
}
|
|
3059
|
+
let updated = p;
|
|
3060
|
+
for (const item of template.items) {
|
|
3061
|
+
({ plan: updated } = addPlanItem(updated, item.title, item.details));
|
|
3062
|
+
}
|
|
3063
|
+
early = mkResult(
|
|
3064
|
+
updated,
|
|
3065
|
+
true,
|
|
3066
|
+
`Applied template "${template.name}" \u2014 ${template.items.length} items added.`
|
|
3067
|
+
);
|
|
3068
|
+
return updated;
|
|
2908
3069
|
}
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
if (itemIdx === -1) {
|
|
2916
|
-
const lower = input.target.toLowerCase();
|
|
2917
|
-
itemIdx = p.items.findIndex((it) => it.title.toLowerCase().includes(lower));
|
|
3070
|
+
case "clear":
|
|
3071
|
+
return clearPlan(p);
|
|
3072
|
+
case "taskify": {
|
|
3073
|
+
if (!input.target) {
|
|
3074
|
+
early = mkResult(p, false, "taskify requires `target` (plan item id|index|substring).");
|
|
3075
|
+
return p;
|
|
2918
3076
|
}
|
|
3077
|
+
let itemIdx = -1;
|
|
3078
|
+
const asNum = Number.parseInt(input.target, 10);
|
|
3079
|
+
if (!Number.isNaN(asNum) && asNum >= 1 && asNum <= p.items.length) {
|
|
3080
|
+
itemIdx = asNum - 1;
|
|
3081
|
+
} else {
|
|
3082
|
+
itemIdx = p.items.findIndex((it) => it.id === input.target);
|
|
3083
|
+
if (itemIdx === -1) {
|
|
3084
|
+
const lower = input.target.toLowerCase();
|
|
3085
|
+
itemIdx = p.items.findIndex((it) => it.title.toLowerCase().includes(lower));
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
if (itemIdx === -1 || !p.items[itemIdx]) {
|
|
3089
|
+
early = mkResult(p, false, `No plan item matched "${input.target}".`);
|
|
3090
|
+
return p;
|
|
3091
|
+
}
|
|
3092
|
+
const item = p.items[itemIdx];
|
|
3093
|
+
taskifyMeta.title = item.title;
|
|
3094
|
+
taskifyMeta.details = item.details ?? "";
|
|
3095
|
+
didTaskify = true;
|
|
3096
|
+
break;
|
|
2919
3097
|
}
|
|
2920
|
-
|
|
2921
|
-
early = mkResult(p, false, `
|
|
3098
|
+
default:
|
|
3099
|
+
early = mkResult(p, false, `Unknown action "${input.action}".`);
|
|
2922
3100
|
return p;
|
|
2923
|
-
}
|
|
2924
|
-
const item = p.items[itemIdx];
|
|
2925
|
-
taskifyMeta.title = item.title;
|
|
2926
|
-
taskifyMeta.details = item.details ?? "";
|
|
2927
|
-
didTaskify = true;
|
|
2928
|
-
break;
|
|
2929
3101
|
}
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
3102
|
+
return p;
|
|
3103
|
+
});
|
|
3104
|
+
} catch (err) {
|
|
3105
|
+
return {
|
|
3106
|
+
ok: false,
|
|
3107
|
+
message: `Plan change not saved \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
3108
|
+
plan: "",
|
|
3109
|
+
count: 0,
|
|
3110
|
+
open: 0
|
|
3111
|
+
};
|
|
3112
|
+
}
|
|
2936
3113
|
if (early) return early;
|
|
2937
3114
|
if (didTaskify) {
|
|
2938
|
-
const
|
|
2939
|
-
if (typeof
|
|
3115
|
+
const taskPathRaw = ctx.meta["task.path"];
|
|
3116
|
+
if (typeof taskPathRaw !== "string" || !taskPathRaw) {
|
|
2940
3117
|
return mkResult(plan, false, "Task storage path not configured \u2014 cannot taskify.");
|
|
2941
3118
|
}
|
|
2942
|
-
|
|
3119
|
+
let taskPath = taskPathRaw;
|
|
3120
|
+
if (input.scope === "project") {
|
|
3121
|
+
const lastSep = Math.max(taskPath.lastIndexOf("/"), taskPath.lastIndexOf("\\"));
|
|
3122
|
+
taskPath = lastSep >= 0 ? taskPath.slice(0, lastSep + 1) + "backlog.tasks.json" : "backlog.tasks.json";
|
|
3123
|
+
}
|
|
2943
3124
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
3125
|
+
try {
|
|
3126
|
+
const taskFile = await mutateTasks(taskPath, sessionId, (f) => {
|
|
3127
|
+
f.tasks.push({
|
|
3128
|
+
id: `task_${randomUUID()}`,
|
|
3129
|
+
title: taskifyMeta.title,
|
|
3130
|
+
description: taskifyMeta.details || void 0,
|
|
3131
|
+
type: "feature",
|
|
3132
|
+
priority: "medium",
|
|
3133
|
+
status: "pending",
|
|
3134
|
+
createdAt: now,
|
|
3135
|
+
updatedAt: now
|
|
3136
|
+
});
|
|
3137
|
+
return f;
|
|
3138
|
+
});
|
|
3139
|
+
return mkResult(
|
|
3140
|
+
plan,
|
|
3141
|
+
true,
|
|
3142
|
+
`taskify ok \u2014 added "${taskifyMeta.title}" to tasks.
|
|
2959
3143
|
${formatTaskList(taskFile.tasks)}`
|
|
2960
|
-
|
|
3144
|
+
);
|
|
3145
|
+
} catch (err) {
|
|
3146
|
+
return mkResult(plan, false, `taskify: task not saved \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
3147
|
+
}
|
|
2961
3148
|
}
|
|
2962
3149
|
return mkResult(plan, true, `Plan ${input.action} ok.`);
|
|
2963
3150
|
}
|
|
@@ -2982,6 +3169,7 @@ var gitTool = {
|
|
|
2982
3169
|
description: "Safe wrapper around common git operations. Supports status, log, diff, commit, branch, checkout, stash, push, pull, fetch, reset, worktree, etc. This is the preferred way to interact with git instead of using the raw `bash` or `exec` tools.",
|
|
2983
3170
|
usageHint: "ALWAYS prefer this tool over raw shell git commands.\n\nKey fields:\n- `command`: one of the supported subcommands (status, log, diff, commit, etc.)\n- Use `message` only for commit operations.\n- Use `files` array for operations that take paths (status, diff, add, etc.).\n- Non-mutating commands (status, log, diff, branch, fetch) are still permission:confirm for safety.\nNever pass raw git flags through `args` for dangerous operations \u2014 use the structured fields.",
|
|
2984
3171
|
permission: "confirm",
|
|
3172
|
+
icon: "git",
|
|
2985
3173
|
// Conservative: any of these may mutate. The non-mutating commands
|
|
2986
3174
|
// (status/log/diff/branch/fetch) are still gated on `permission: 'confirm'`
|
|
2987
3175
|
// and `MUTATING_SUBCOMMANDS` is consulted at runtime for per-call checks.
|
|
@@ -3240,6 +3428,7 @@ var patchTool = {
|
|
|
3240
3428
|
permission: "confirm",
|
|
3241
3429
|
mutating: true,
|
|
3242
3430
|
capabilities: ["fs.write"],
|
|
3431
|
+
icon: "edit",
|
|
3243
3432
|
timeoutMs: 3e4,
|
|
3244
3433
|
inputSchema: {
|
|
3245
3434
|
type: "object",
|
|
@@ -3353,6 +3542,7 @@ var jsonTool = {
|
|
|
3353
3542
|
mutating: false,
|
|
3354
3543
|
timeoutMs: 5e3,
|
|
3355
3544
|
capabilities: ["fs.read"],
|
|
3545
|
+
icon: "json",
|
|
3356
3546
|
inputSchema: {
|
|
3357
3547
|
type: "object",
|
|
3358
3548
|
properties: {
|
|
@@ -3475,6 +3665,7 @@ var diffTool = {
|
|
|
3475
3665
|
permission: "auto",
|
|
3476
3666
|
mutating: false,
|
|
3477
3667
|
capabilities: ["fs.read"],
|
|
3668
|
+
icon: "diff",
|
|
3478
3669
|
timeoutMs: 1e4,
|
|
3479
3670
|
inputSchema: {
|
|
3480
3671
|
type: "object",
|
|
@@ -3633,6 +3824,7 @@ var treeTool = {
|
|
|
3633
3824
|
permission: "auto",
|
|
3634
3825
|
mutating: false,
|
|
3635
3826
|
capabilities: ["fs.read"],
|
|
3827
|
+
icon: "tree",
|
|
3636
3828
|
timeoutMs: 15e3,
|
|
3637
3829
|
inputSchema: {
|
|
3638
3830
|
type: "object",
|
|
@@ -3799,8 +3991,9 @@ async function* spawnStream(opts) {
|
|
|
3799
3991
|
let pending2 = "";
|
|
3800
3992
|
let error;
|
|
3801
3993
|
const spool = createOutputSpool({ tool: opts.cmd, thresholdBytes: max });
|
|
3802
|
-
const
|
|
3803
|
-
const needsShell = isWin2 && (
|
|
3994
|
+
const resolved = resolveWin32Command(opts.cmd);
|
|
3995
|
+
const needsShell = isWin2 && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
3996
|
+
const cmd = needsShell ? opts.cmd : resolved;
|
|
3804
3997
|
const child = spawn(cmd, opts.args, {
|
|
3805
3998
|
cwd: opts.cwd,
|
|
3806
3999
|
env: buildChildEnv(),
|
|
@@ -3958,6 +4151,7 @@ var lintTool = {
|
|
|
3958
4151
|
mutating: false,
|
|
3959
4152
|
timeoutMs: 6e4,
|
|
3960
4153
|
capabilities: ["shell.restricted"],
|
|
4154
|
+
icon: "code",
|
|
3961
4155
|
inputSchema: {
|
|
3962
4156
|
type: "object",
|
|
3963
4157
|
properties: {
|
|
@@ -4052,6 +4246,7 @@ var formatTool = {
|
|
|
4052
4246
|
permission: "confirm",
|
|
4053
4247
|
mutating: true,
|
|
4054
4248
|
capabilities: ["fs.write", "shell.exec"],
|
|
4249
|
+
icon: "code",
|
|
4055
4250
|
timeoutMs: 6e4,
|
|
4056
4251
|
inputSchema: {
|
|
4057
4252
|
type: "object",
|
|
@@ -4153,6 +4348,7 @@ var typecheckTool = {
|
|
|
4153
4348
|
mutating: false,
|
|
4154
4349
|
timeoutMs: 12e4,
|
|
4155
4350
|
capabilities: ["shell.restricted"],
|
|
4351
|
+
icon: "code",
|
|
4156
4352
|
inputSchema: {
|
|
4157
4353
|
type: "object",
|
|
4158
4354
|
properties: {
|
|
@@ -4239,6 +4435,7 @@ var testTool = {
|
|
|
4239
4435
|
usageHint: "ESSENTIAL BEFORE CONSIDERING WORK DONE:\n\n- Use `files` or `grep` to run only relevant tests during development.\n- `coverage: true` is useful when working on critical paths.\nRun tests frequently. A clean test run is usually required before the task can be considered complete.",
|
|
4240
4436
|
permission: "confirm",
|
|
4241
4437
|
mutating: false,
|
|
4438
|
+
icon: "test",
|
|
4242
4439
|
timeoutMs: 12e4,
|
|
4243
4440
|
capabilities: ["shell.restricted"],
|
|
4244
4441
|
inputSchema: {
|
|
@@ -4396,6 +4593,7 @@ var installTool = {
|
|
|
4396
4593
|
permission: "confirm",
|
|
4397
4594
|
mutating: true,
|
|
4398
4595
|
riskTier: "standard",
|
|
4596
|
+
icon: "package",
|
|
4399
4597
|
timeoutMs: 12e4,
|
|
4400
4598
|
capabilities: ["package.install", "shell.restricted"],
|
|
4401
4599
|
inputSchema: {
|
|
@@ -4535,6 +4733,7 @@ var auditTool = {
|
|
|
4535
4733
|
permission: "confirm",
|
|
4536
4734
|
mutating: false,
|
|
4537
4735
|
capabilities: ["shell.restricted"],
|
|
4736
|
+
icon: "package",
|
|
4538
4737
|
timeoutMs: 6e4,
|
|
4539
4738
|
inputSchema: {
|
|
4540
4739
|
type: "object",
|
|
@@ -4630,6 +4829,7 @@ var outdatedTool = {
|
|
|
4630
4829
|
description: "Check for outdated dependencies in the project. Reports current, wanted (semver range), and latest versions available.",
|
|
4631
4830
|
usageHint: "MAINTENANCE & SECURITY TOOL:\n\n- Run periodically or before dependency-related work.\n- Helps surface packages that may need updates for security or features.\n- Hits the package registry over HTTP, so it is NOT purely local \u2014 flagged as mutating for the confirmation gate.\nUse the output to decide on upgrades. Prefer this over manual shell commands for dependency hygiene.",
|
|
4632
4831
|
permission: "confirm",
|
|
4832
|
+
icon: "package",
|
|
4633
4833
|
// Network side-effecting (registry HTTP). Pairs with `mutating: true`
|
|
4634
4834
|
// so the H7 invariant test (`no auto-permission tool declares
|
|
4635
4835
|
// mutating: true`) passes — a tool claiming `'auto'` must be purely
|
|
@@ -4681,7 +4881,8 @@ function runOutdated(manager, args, cwd, signal) {
|
|
|
4681
4881
|
const MAX = 1e5;
|
|
4682
4882
|
const resolved = resolveWin32Command(manager);
|
|
4683
4883
|
const needsShell = process.platform === "win32" && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
|
|
4684
|
-
const
|
|
4884
|
+
const spawnCmd = needsShell ? manager : resolved;
|
|
4885
|
+
const child = spawn(spawnCmd, args, { cwd, signal, env: buildChildEnv(), stdio: ["ignore", "pipe", "pipe"], windowsHide: true, ...needsShell ? { shell: true, windowsVerbatimArguments: true } : {} });
|
|
4685
4886
|
child.stdout?.on("data", (c) => {
|
|
4686
4887
|
if (stdout.length < MAX) stdout += c.toString();
|
|
4687
4888
|
});
|
|
@@ -4746,6 +4947,7 @@ var logsTool = {
|
|
|
4746
4947
|
mutating: false,
|
|
4747
4948
|
timeoutMs: 3e4,
|
|
4748
4949
|
capabilities: ["shell.restricted"],
|
|
4950
|
+
icon: "logs",
|
|
4749
4951
|
inputSchema: {
|
|
4750
4952
|
type: "object",
|
|
4751
4953
|
properties: {
|
|
@@ -4950,6 +5152,7 @@ var documentTool = {
|
|
|
4950
5152
|
mutating: false,
|
|
4951
5153
|
timeoutMs: 3e4,
|
|
4952
5154
|
capabilities: ["fs.read"],
|
|
5155
|
+
icon: "document",
|
|
4953
5156
|
inputSchema: {
|
|
4954
5157
|
type: "object",
|
|
4955
5158
|
properties: {
|
|
@@ -5185,6 +5388,7 @@ var scaffoldTool = {
|
|
|
5185
5388
|
permission: "confirm",
|
|
5186
5389
|
mutating: true,
|
|
5187
5390
|
capabilities: ["fs.write.outside-project", "fs.write"],
|
|
5391
|
+
icon: "scaffold",
|
|
5188
5392
|
timeoutMs: 3e4,
|
|
5189
5393
|
inputSchema: {
|
|
5190
5394
|
type: "object",
|
|
@@ -5280,6 +5484,7 @@ var toolSearchTool = {
|
|
|
5280
5484
|
mutating: false,
|
|
5281
5485
|
timeoutMs: 1e3,
|
|
5282
5486
|
capabilities: ["tool.meta"],
|
|
5487
|
+
icon: "meta",
|
|
5283
5488
|
inputSchema: {
|
|
5284
5489
|
type: "object",
|
|
5285
5490
|
properties: {
|
|
@@ -5359,6 +5564,7 @@ var toolUseTool = {
|
|
|
5359
5564
|
mutating: true,
|
|
5360
5565
|
timeoutMs: 6e4,
|
|
5361
5566
|
capabilities: ["tool.mutate.any"],
|
|
5567
|
+
icon: "meta",
|
|
5362
5568
|
inputSchema: {
|
|
5363
5569
|
type: "object",
|
|
5364
5570
|
properties: {
|
|
@@ -5429,6 +5635,7 @@ var batchToolUseTool = {
|
|
|
5429
5635
|
mutating: true,
|
|
5430
5636
|
timeoutMs: 12e4,
|
|
5431
5637
|
capabilities: ["tool.mutate.any"],
|
|
5638
|
+
icon: "meta",
|
|
5432
5639
|
inputSchema: {
|
|
5433
5640
|
type: "object",
|
|
5434
5641
|
properties: {
|
|
@@ -5534,6 +5741,7 @@ var toolHelpTool = {
|
|
|
5534
5741
|
mutating: false,
|
|
5535
5742
|
timeoutMs: 5e3,
|
|
5536
5743
|
capabilities: ["tool.meta"],
|
|
5744
|
+
icon: "meta",
|
|
5537
5745
|
inputSchema: {
|
|
5538
5746
|
type: "object",
|
|
5539
5747
|
properties: {
|
|
@@ -5658,6 +5866,7 @@ function rememberTool(memory) {
|
|
|
5658
5866
|
mutating: true,
|
|
5659
5867
|
timeoutMs: 2e3,
|
|
5660
5868
|
capabilities: ["memory.write"],
|
|
5869
|
+
icon: "settings",
|
|
5661
5870
|
inputSchema: {
|
|
5662
5871
|
type: "object",
|
|
5663
5872
|
properties: {
|
|
@@ -5832,6 +6041,7 @@ function createModeTool(modeStore) {
|
|
|
5832
6041
|
mutating: true,
|
|
5833
6042
|
timeoutMs: 5e3,
|
|
5834
6043
|
capabilities: ["session.mode"],
|
|
6044
|
+
icon: "settings",
|
|
5835
6045
|
inputSchema: {
|
|
5836
6046
|
type: "object",
|
|
5837
6047
|
properties: {
|
|
@@ -6588,39 +6798,57 @@ var IndexStore = class {
|
|
|
6588
6798
|
}
|
|
6589
6799
|
});
|
|
6590
6800
|
}
|
|
6801
|
+
/**
|
|
6802
|
+
* Bulk-insert refs for many source symbols in a single transaction.
|
|
6803
|
+
*
|
|
6804
|
+
* Unlike {@link insertRefs} this does NOT delete per source id — the caller
|
|
6805
|
+
* (the indexer) has already cleared stale refs for the file via
|
|
6806
|
+
* {@link deleteRefsForFile}, so the per-source DELETE would be redundant work
|
|
6807
|
+
* repeated once per symbol. One transaction for the whole file instead of one
|
|
6808
|
+
* per symbol turns an O(symbols) transaction count into O(1).
|
|
6809
|
+
*
|
|
6810
|
+
* Each ref's own {@link Ref.fromId} is used; pass an empty array to no-op.
|
|
6811
|
+
*/
|
|
6812
|
+
insertRefsBatch(refs) {
|
|
6813
|
+
if (refs.length === 0) return;
|
|
6814
|
+
this.runWithRetry(() => {
|
|
6815
|
+
const stmt = this.db.prepare(
|
|
6816
|
+
`INSERT INTO refs(from_id, to_name, to_id, call_type, line)
|
|
6817
|
+
VALUES (?, ?, ?, ?, ?)`
|
|
6818
|
+
);
|
|
6819
|
+
for (const ref of refs) {
|
|
6820
|
+
stmt.run(ref.fromId, ref.toName, ref.toId ?? null, ref.callType, ref.line);
|
|
6821
|
+
}
|
|
6822
|
+
});
|
|
6823
|
+
}
|
|
6591
6824
|
/**
|
|
6592
6825
|
* Delete all refs whose source symbols are in a given file.
|
|
6593
6826
|
* Used when re-indexing a file to clear stale refs.
|
|
6594
6827
|
*/
|
|
6595
6828
|
deleteRefsForFile(file) {
|
|
6596
6829
|
this.runWithRetry(() => {
|
|
6597
|
-
|
|
6598
|
-
"SELECT id FROM symbols WHERE file = ?"
|
|
6599
|
-
).
|
|
6600
|
-
if (!ids.length) return;
|
|
6601
|
-
const placeholders = ids.map(() => "?").join(",");
|
|
6602
|
-
this.db.prepare(`DELETE FROM refs WHERE from_id IN (${placeholders})`).run(...ids.map((r) => r.id));
|
|
6830
|
+
this.db.prepare(
|
|
6831
|
+
"DELETE FROM refs WHERE from_id IN (SELECT id FROM symbols WHERE file = ?)"
|
|
6832
|
+
).run(file);
|
|
6603
6833
|
});
|
|
6604
6834
|
}
|
|
6605
6835
|
/**
|
|
6606
6836
|
* Resolve `to_name` → `to_id` for all refs that have a name but no id.
|
|
6607
6837
|
* Call this after all symbols have been inserted to fill in cross-references.
|
|
6838
|
+
*
|
|
6839
|
+
* Single statement: the `to_name IN (SELECT name FROM symbols)` guard restricts
|
|
6840
|
+
* the UPDATE to refs that will actually resolve, so `.changes` counts only refs
|
|
6841
|
+
* that found a target — matching the previous per-row loop's return value.
|
|
6608
6842
|
*/
|
|
6609
6843
|
resolveRefs() {
|
|
6610
6844
|
return this.runWithRetry(() => {
|
|
6611
|
-
const
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
if (first) {
|
|
6619
|
-
this.db.prepare("UPDATE refs SET to_id = ? WHERE id = ?").run(first.id, row.id);
|
|
6620
|
-
resolved++;
|
|
6621
|
-
}
|
|
6622
|
-
}
|
|
6623
|
-
return resolved;
|
|
6845
|
+
const result = this.db.prepare(
|
|
6846
|
+
`UPDATE refs SET to_id = (
|
|
6847
|
+
SELECT id FROM symbols WHERE name = refs.to_name LIMIT 1
|
|
6848
|
+
) WHERE to_id IS NULL AND to_name IS NOT NULL
|
|
6849
|
+
AND to_name IN (SELECT name FROM symbols)`
|
|
6850
|
+
).run();
|
|
6851
|
+
return result.changes ?? 0;
|
|
6624
6852
|
});
|
|
6625
6853
|
}
|
|
6626
6854
|
/**
|
|
@@ -8095,13 +8323,26 @@ async function runIndexerWithStore(store, opts) {
|
|
|
8095
8323
|
symbolsIndexed += count;
|
|
8096
8324
|
langStats[lang] = (langStats[lang] ?? 0) + count;
|
|
8097
8325
|
if (parsed.refs && parsed.refs.length > 0) {
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
if (
|
|
8102
|
-
|
|
8103
|
-
|
|
8326
|
+
const refsByLine = /* @__PURE__ */ new Map();
|
|
8327
|
+
for (const r of parsed.refs) {
|
|
8328
|
+
let arr = refsByLine.get(r.line);
|
|
8329
|
+
if (!arr) {
|
|
8330
|
+
arr = [];
|
|
8331
|
+
refsByLine.set(r.line, arr);
|
|
8104
8332
|
}
|
|
8333
|
+
arr.push(r);
|
|
8334
|
+
}
|
|
8335
|
+
const batch = [];
|
|
8336
|
+
for (const sym of symbolsWithIds) {
|
|
8337
|
+
const symRefs = refsByLine.get(sym.line);
|
|
8338
|
+
if (symRefs) {
|
|
8339
|
+
for (const r of symRefs) {
|
|
8340
|
+
batch.push({ ...r, fromId: sym.id });
|
|
8341
|
+
}
|
|
8342
|
+
}
|
|
8343
|
+
}
|
|
8344
|
+
if (batch.length > 0) {
|
|
8345
|
+
store.insertRefsBatch(batch);
|
|
8105
8346
|
}
|
|
8106
8347
|
}
|
|
8107
8348
|
store.upsertFile({
|
|
@@ -8494,6 +8735,7 @@ async function codebaseIndexStats(args, opts = {}) {
|
|
|
8494
8735
|
var codebaseIndexTool = {
|
|
8495
8736
|
name: "codebase-index",
|
|
8496
8737
|
category: "Project",
|
|
8738
|
+
icon: "index",
|
|
8497
8739
|
description: "Build or incrementally update the project-wide symbol index. This powers fast codebase search and understanding. By default it only processes files that have changed since the last indexing run.",
|
|
8498
8740
|
usageHint: "IMPORTANT FOR LARGE CODEBASES:\n\n- First run (or after major changes): consider `force: true` for a clean rebuild.\n- Normal usage: call without arguments for fast incremental updates.\n- Use `langs` to restrict to specific languages if you only care about certain parts of the project.\nThis tool is relatively expensive \u2014 do not call it on every turn. Use it when the index is stale or before heavy codebase-search sessions.",
|
|
8499
8741
|
permission: "confirm",
|
|
@@ -8550,6 +8792,7 @@ var codebaseIndexTool = {
|
|
|
8550
8792
|
var codebaseSearchTool = {
|
|
8551
8793
|
name: "codebase-search",
|
|
8552
8794
|
category: "Project",
|
|
8795
|
+
icon: "index",
|
|
8553
8796
|
description: "Semantic/keyword search over the indexed codebase symbols (functions, classes, interfaces, etc.). Uses BM25 ranking. Much more powerful and structured than raw `grep` for finding code by name or concept.",
|
|
8554
8797
|
usageHint: "PREFERRED FOR CODE UNDERSTANDING:\n\n- Use when you need to find where something is defined or used by name.\n- `kind` filter is very useful (e.g. only functions or only interfaces).\n- Combine with `file` filter to scope to a specific directory or module.\nThis is generally better than `grep` when you are looking for symbols rather than arbitrary text patterns.",
|
|
8555
8798
|
permission: "auto",
|
|
@@ -8638,6 +8881,7 @@ var codebaseSearchTool = {
|
|
|
8638
8881
|
var codebaseStatsTool = {
|
|
8639
8882
|
name: "codebase-stats",
|
|
8640
8883
|
category: "Project",
|
|
8884
|
+
icon: "index",
|
|
8641
8885
|
description: "Return health and statistics about the current symbol index (total symbols, files, language/kind breakdown, size, last update). Useful to decide whether to re-index.",
|
|
8642
8886
|
usageHint: "CALL BEFORE HEAVY CODEBASE-SEARCH WORK:\n\n- Use to see if the index is up-to-date or needs a refresh.\n- No arguments required.\n- Helps avoid wasting tokens on searches against a stale index.\nLightweight and safe to call frequently.",
|
|
8643
8887
|
permission: "auto",
|
|
@@ -8698,6 +8942,7 @@ var setWorkingDirTool = {
|
|
|
8698
8942
|
permission: "confirm",
|
|
8699
8943
|
mutating: true,
|
|
8700
8944
|
capabilities: ["fs.read"],
|
|
8945
|
+
icon: "settings",
|
|
8701
8946
|
timeoutMs: 5e3,
|
|
8702
8947
|
inputSchema: {
|
|
8703
8948
|
type: "object",
|
|
@@ -8758,11 +9003,12 @@ function findTaskIndex(tasks, query2) {
|
|
|
8758
9003
|
var taskTool = {
|
|
8759
9004
|
name: "task",
|
|
8760
9005
|
category: "Session",
|
|
8761
|
-
description:
|
|
8762
|
-
usageHint: 'USE FOR STRUCTURED WORK:\n- `action: "replace"` \u2014 set the complete task list (tasks ordered by priority)\n- `action: "add"` \u2014 append a single task\n- `action: "status"` \u2014 update a task\'s status (e.g. pending\u2192in_progress, in_progress\u2192completed)\n- `action: "show"` \u2014 view current tasks without changing them\n- `action: "promote"` \u2014 convert a task into actionable todo items via `target` (id|index|substring)\n- `action: "planify"` \u2014 promote a task to a plan item (strategic level) via `target` (id|index|substring)\n\nTask fields:\n- `dependsOn`: list of task IDs this one waits for\n- `type`: "feature" | "bugfix" | "refactor" | "docs" | "test" | "chore"\n- `priority`: "critical" | "high" | "medium" | "low"\n- `assignee`: agent/subagent name (e.g. "bug-hunter", "refactor-planner")\n- `estimateHours`: rough time estimate',
|
|
9006
|
+
description: 'Manage session-persistent structured work items with dependencies, types, and priorities. Unlike `todo` (flat, tactical), `task` supports typed work (feature/bugfix/refactor/etc.), dependencies between items, priority ranking, and agent assignment. Tasks are written to disk and survive session resumes. By default they are isolated to this session; use `scope: "project"` to store tasks in a shared project-level file visible to all sessions.',
|
|
9007
|
+
usageHint: 'USE FOR STRUCTURED WORK:\n- `action: "replace"` \u2014 set the complete task list (tasks ordered by priority)\n- `action: "add"` \u2014 append a single task\n- `action: "status"` \u2014 update a task\'s status (e.g. pending\u2192in_progress, in_progress\u2192completed)\n- `action: "show"` \u2014 view current tasks without changing them\n- `action: "promote"` \u2014 convert a task into actionable todo items via `target` (id|index|substring)\n- `action: "planify"` \u2014 promote a task to a plan item (strategic level) via `target` (id|index|substring)\n\nTask fields:\n- `dependsOn`: list of task IDs this one waits for\n- `type`: "feature" | "bugfix" | "refactor" | "docs" | "test" | "chore"\n- `priority`: "critical" | "high" | "medium" | "low"\n- `assignee`: agent/subagent name (e.g. "bug-hunter", "refactor-planner")\n- `estimateHours`: rough time estimate\n- `scope`: "session" (default, isolated) or "project" (shared across sessions)',
|
|
8763
9008
|
permission: "confirm",
|
|
8764
9009
|
mutating: true,
|
|
8765
9010
|
capabilities: ["fs.write"],
|
|
9011
|
+
icon: "task",
|
|
8766
9012
|
timeoutMs: 2e3,
|
|
8767
9013
|
inputSchema: {
|
|
8768
9014
|
type: "object",
|
|
@@ -8828,12 +9074,26 @@ var taskTool = {
|
|
|
8828
9074
|
type: "array",
|
|
8829
9075
|
items: { type: "string" },
|
|
8830
9076
|
description: "Optional subtask titles for action=promote. Each becomes a pending todo."
|
|
9077
|
+
},
|
|
9078
|
+
scope: {
|
|
9079
|
+
type: "string",
|
|
9080
|
+
enum: ["session", "project"],
|
|
9081
|
+
description: 'Storage scope: "session" (default, isolated to this session) or "project" (shared across all sessions for this project).'
|
|
8831
9082
|
}
|
|
8832
9083
|
},
|
|
8833
9084
|
required: ["action"]
|
|
8834
9085
|
},
|
|
8835
9086
|
async execute(input, ctx) {
|
|
8836
|
-
const
|
|
9087
|
+
const sessionTaskPath = ctx.meta["task.path"];
|
|
9088
|
+
let taskPath;
|
|
9089
|
+
if (input.scope === "project") {
|
|
9090
|
+
if (typeof sessionTaskPath === "string") {
|
|
9091
|
+
const lastSep = Math.max(sessionTaskPath.lastIndexOf("/"), sessionTaskPath.lastIndexOf("\\"));
|
|
9092
|
+
taskPath = lastSep >= 0 ? sessionTaskPath.slice(0, lastSep + 1) + "backlog.tasks.json" : "backlog.tasks.json";
|
|
9093
|
+
}
|
|
9094
|
+
} else {
|
|
9095
|
+
taskPath = sessionTaskPath;
|
|
9096
|
+
}
|
|
8837
9097
|
if (typeof taskPath !== "string" || !taskPath) {
|
|
8838
9098
|
return { ok: false, message: "Task storage path not configured.", count: 0, completed: 0, inProgress: 0 };
|
|
8839
9099
|
}
|
|
@@ -8843,23 +9103,66 @@ var taskTool = {
|
|
|
8843
9103
|
const planifyMeta = { title: "", details: "" };
|
|
8844
9104
|
let didPlanify = false;
|
|
8845
9105
|
let todosToReplace = null;
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
9106
|
+
let file;
|
|
9107
|
+
try {
|
|
9108
|
+
file = await mutateTasks(taskPath, sessionId, async (f) => {
|
|
9109
|
+
switch (input.action) {
|
|
9110
|
+
case "show":
|
|
9111
|
+
break;
|
|
9112
|
+
case "replace": {
|
|
9113
|
+
if (!Array.isArray(input.tasks)) {
|
|
9114
|
+
early = { ok: false, message: "action=replace requires `tasks` array.", count: 0, completed: 0, inProgress: 0 };
|
|
9115
|
+
return f;
|
|
9116
|
+
}
|
|
9117
|
+
const newIds = new Set(input.tasks.map((t) => t.id));
|
|
9118
|
+
if (newIds.size !== input.tasks.length) {
|
|
9119
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9120
|
+
const dupes = [...new Set(input.tasks.map((t) => t.id).filter((id) => seen.has(id) ? true : (seen.add(id), false)))];
|
|
9121
|
+
early = {
|
|
9122
|
+
ok: false,
|
|
9123
|
+
message: `action=replace has duplicate task IDs: ${dupes.join(", ")}. Each task id must be unique.`,
|
|
9124
|
+
count: 0,
|
|
9125
|
+
completed: 0,
|
|
9126
|
+
inProgress: 0
|
|
9127
|
+
};
|
|
9128
|
+
return f;
|
|
9129
|
+
}
|
|
9130
|
+
for (const t of input.tasks) {
|
|
9131
|
+
if (t.dependsOn && t.dependsOn.length > 0) {
|
|
9132
|
+
const missing = t.dependsOn.filter((d) => !newIds.has(d));
|
|
9133
|
+
if (missing.length > 0) {
|
|
9134
|
+
early = {
|
|
9135
|
+
ok: false,
|
|
9136
|
+
message: `dependsOn validation failed: task "${t.id}" references unknown IDs: ${missing.join(", ")}`,
|
|
9137
|
+
count: 0,
|
|
9138
|
+
completed: 0,
|
|
9139
|
+
inProgress: 0
|
|
9140
|
+
};
|
|
9141
|
+
return f;
|
|
9142
|
+
}
|
|
9143
|
+
}
|
|
9144
|
+
}
|
|
9145
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9146
|
+
f.tasks = input.tasks.map((t) => ({
|
|
9147
|
+
...t,
|
|
9148
|
+
createdAt: t.createdAt || now,
|
|
9149
|
+
updatedAt: now
|
|
9150
|
+
}));
|
|
9151
|
+
break;
|
|
8854
9152
|
}
|
|
8855
|
-
|
|
8856
|
-
|
|
9153
|
+
case "add": {
|
|
9154
|
+
const t = input.task;
|
|
9155
|
+
if (!t || !t.title) {
|
|
9156
|
+
early = { ok: false, message: "action=add requires `task` with at least `title`.", count: 0, completed: 0, inProgress: 0 };
|
|
9157
|
+
return f;
|
|
9158
|
+
}
|
|
8857
9159
|
if (t.dependsOn && t.dependsOn.length > 0) {
|
|
8858
|
-
const
|
|
9160
|
+
const existingIds = new Set(f.tasks.map((e) => e.id));
|
|
9161
|
+
const missing = t.dependsOn.filter((d) => !existingIds.has(d));
|
|
8859
9162
|
if (missing.length > 0) {
|
|
8860
9163
|
early = {
|
|
8861
9164
|
ok: false,
|
|
8862
|
-
message: `dependsOn validation failed: task
|
|
9165
|
+
message: `dependsOn validation failed: unknown task IDs: ${missing.join(", ")}`,
|
|
8863
9166
|
count: 0,
|
|
8864
9167
|
completed: 0,
|
|
8865
9168
|
inProgress: 0
|
|
@@ -8867,165 +9170,170 @@ var taskTool = {
|
|
|
8867
9170
|
return f;
|
|
8868
9171
|
}
|
|
8869
9172
|
}
|
|
9173
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9174
|
+
const newTask = {
|
|
9175
|
+
id: `task_${Date.now()}_${randomUUID().slice(0, 8)}`,
|
|
9176
|
+
title: t.title,
|
|
9177
|
+
description: t.description,
|
|
9178
|
+
type: t.type || "feature",
|
|
9179
|
+
priority: t.priority || "medium",
|
|
9180
|
+
status: t.status || "pending",
|
|
9181
|
+
dependsOn: t.dependsOn,
|
|
9182
|
+
assignee: t.assignee,
|
|
9183
|
+
estimateHours: t.estimateHours,
|
|
9184
|
+
tags: t.tags,
|
|
9185
|
+
createdAt: now,
|
|
9186
|
+
updatedAt: now
|
|
9187
|
+
};
|
|
9188
|
+
f.tasks.push(newTask);
|
|
9189
|
+
break;
|
|
8870
9190
|
}
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
createdAt: t.createdAt || now,
|
|
8875
|
-
updatedAt: now
|
|
8876
|
-
}));
|
|
8877
|
-
break;
|
|
8878
|
-
}
|
|
8879
|
-
case "add": {
|
|
8880
|
-
const t = input.task;
|
|
8881
|
-
if (!t || !t.title) {
|
|
8882
|
-
early = { ok: false, message: "action=add requires `task` with at least `title`.", count: 0, completed: 0, inProgress: 0 };
|
|
8883
|
-
return f;
|
|
8884
|
-
}
|
|
8885
|
-
if (t.dependsOn && t.dependsOn.length > 0) {
|
|
8886
|
-
const existingIds = new Set(f.tasks.map((e) => e.id));
|
|
8887
|
-
const missing = t.dependsOn.filter((d) => !existingIds.has(d));
|
|
8888
|
-
if (missing.length > 0) {
|
|
8889
|
-
early = {
|
|
8890
|
-
ok: false,
|
|
8891
|
-
message: `dependsOn validation failed: unknown task IDs: ${missing.join(", ")}`,
|
|
8892
|
-
count: 0,
|
|
8893
|
-
completed: 0,
|
|
8894
|
-
inProgress: 0
|
|
8895
|
-
};
|
|
9191
|
+
case "status": {
|
|
9192
|
+
if (!input.id || !input.status) {
|
|
9193
|
+
early = { ok: false, message: "action=status requires `id` and `status`.", count: 0, completed: 0, inProgress: 0 };
|
|
8896
9194
|
return f;
|
|
8897
9195
|
}
|
|
9196
|
+
const task = f.tasks.find((t) => t.id === input.id);
|
|
9197
|
+
if (!task) {
|
|
9198
|
+
early = { ok: false, message: `Task "${input.id}" not found.`, count: 0, completed: 0, inProgress: 0 };
|
|
9199
|
+
return f;
|
|
9200
|
+
}
|
|
9201
|
+
task.status = input.status;
|
|
9202
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9203
|
+
break;
|
|
8898
9204
|
}
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
}
|
|
8922
|
-
const task = f.tasks.find((t) => t.id === input.id);
|
|
8923
|
-
if (!task) {
|
|
8924
|
-
early = { ok: false, message: `Task "${input.id}" not found.`, count: 0, completed: 0, inProgress: 0 };
|
|
8925
|
-
return f;
|
|
8926
|
-
}
|
|
8927
|
-
task.status = input.status;
|
|
8928
|
-
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8929
|
-
break;
|
|
8930
|
-
}
|
|
8931
|
-
case "promote": {
|
|
8932
|
-
const target = input.target?.trim();
|
|
8933
|
-
if (!target) {
|
|
8934
|
-
early = { ok: false, message: "action=promote requires `target` (task id, index, or title substring).", count: 0, completed: 0, inProgress: 0 };
|
|
8935
|
-
return f;
|
|
8936
|
-
}
|
|
8937
|
-
const idx = findTaskIndex(f.tasks, target);
|
|
8938
|
-
if (idx === -1) {
|
|
8939
|
-
early = { ok: false, message: `No task matched "${target}".`, count: 0, completed: 0, inProgress: 0 };
|
|
8940
|
-
return f;
|
|
8941
|
-
}
|
|
8942
|
-
const match = f.tasks[idx];
|
|
8943
|
-
if (!match) {
|
|
8944
|
-
early = { ok: false, message: `No task matched "${target}".`, count: 0, completed: 0, inProgress: 0 };
|
|
8945
|
-
return f;
|
|
8946
|
-
}
|
|
8947
|
-
if (match.status !== "completed" && match.status !== "failed") {
|
|
8948
|
-
match.status = "in_progress";
|
|
8949
|
-
match.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8950
|
-
}
|
|
8951
|
-
const todos = [];
|
|
8952
|
-
const ts2 = Date.now();
|
|
8953
|
-
todos.push({
|
|
8954
|
-
id: `todo_${ts2}_task`,
|
|
8955
|
-
content: match.title,
|
|
8956
|
-
status: "in_progress",
|
|
8957
|
-
activeForm: match.title,
|
|
8958
|
-
promotedFromTask: match.id
|
|
8959
|
-
});
|
|
8960
|
-
if (match.description) {
|
|
9205
|
+
case "promote": {
|
|
9206
|
+
const target = input.target?.trim();
|
|
9207
|
+
if (!target) {
|
|
9208
|
+
early = { ok: false, message: "action=promote requires `target` (task id, index, or title substring).", count: 0, completed: 0, inProgress: 0 };
|
|
9209
|
+
return f;
|
|
9210
|
+
}
|
|
9211
|
+
const idx = findTaskIndex(f.tasks, target);
|
|
9212
|
+
if (idx === -1) {
|
|
9213
|
+
early = { ok: false, message: `No task matched "${target}".`, count: 0, completed: 0, inProgress: 0 };
|
|
9214
|
+
return f;
|
|
9215
|
+
}
|
|
9216
|
+
const match = f.tasks[idx];
|
|
9217
|
+
if (!match) {
|
|
9218
|
+
early = { ok: false, message: `No task matched "${target}".`, count: 0, completed: 0, inProgress: 0 };
|
|
9219
|
+
return f;
|
|
9220
|
+
}
|
|
9221
|
+
if (match.status !== "completed" && match.status !== "failed") {
|
|
9222
|
+
match.status = "in_progress";
|
|
9223
|
+
match.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9224
|
+
}
|
|
9225
|
+
const todos = [];
|
|
9226
|
+
const ts2 = Date.now();
|
|
8961
9227
|
todos.push({
|
|
8962
|
-
id: `todo_${ts2}
|
|
8963
|
-
content: match.
|
|
8964
|
-
status: "
|
|
9228
|
+
id: `todo_${ts2}_task`,
|
|
9229
|
+
content: match.title,
|
|
9230
|
+
status: "in_progress",
|
|
9231
|
+
activeForm: match.title,
|
|
8965
9232
|
promotedFromTask: match.id
|
|
8966
9233
|
});
|
|
8967
|
-
|
|
8968
|
-
if (input.subtasks && input.subtasks.length > 0) {
|
|
8969
|
-
for (const st of input.subtasks) {
|
|
9234
|
+
if (match.description) {
|
|
8970
9235
|
todos.push({
|
|
8971
9236
|
id: `todo_${ts2}_${randomUUID().slice(0, 6)}`,
|
|
8972
|
-
content:
|
|
9237
|
+
content: match.description.slice(0, 200),
|
|
8973
9238
|
status: "pending",
|
|
8974
9239
|
promotedFromTask: match.id
|
|
8975
9240
|
});
|
|
8976
9241
|
}
|
|
9242
|
+
if (input.subtasks && input.subtasks.length > 0) {
|
|
9243
|
+
for (const st of input.subtasks) {
|
|
9244
|
+
todos.push({
|
|
9245
|
+
id: `todo_${ts2}_${randomUUID().slice(0, 6)}`,
|
|
9246
|
+
content: st,
|
|
9247
|
+
status: "pending",
|
|
9248
|
+
promotedFromTask: match.id
|
|
9249
|
+
});
|
|
9250
|
+
}
|
|
9251
|
+
}
|
|
9252
|
+
todosToReplace = todos;
|
|
9253
|
+
promoteMeta.count = todos.length;
|
|
9254
|
+
promoteMeta.title = match.title;
|
|
9255
|
+
break;
|
|
8977
9256
|
}
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
9257
|
+
case "planify": {
|
|
9258
|
+
const target = input.target?.trim();
|
|
9259
|
+
if (!target) {
|
|
9260
|
+
early = { ok: false, message: "action=planify requires `target` (task id, index, or title substring).", count: 0, completed: 0, inProgress: 0 };
|
|
9261
|
+
return f;
|
|
9262
|
+
}
|
|
9263
|
+
const idx = findTaskIndex(f.tasks, target);
|
|
9264
|
+
if (idx === -1) {
|
|
9265
|
+
early = { ok: false, message: `No task matched "${target}".`, count: 0, completed: 0, inProgress: 0 };
|
|
9266
|
+
return f;
|
|
9267
|
+
}
|
|
9268
|
+
const match = f.tasks[idx];
|
|
9269
|
+
if (!match) {
|
|
9270
|
+
early = { ok: false, message: `No task matched "${target}".`, count: 0, completed: 0, inProgress: 0 };
|
|
9271
|
+
return f;
|
|
9272
|
+
}
|
|
9273
|
+
planifyMeta.title = match.title;
|
|
9274
|
+
planifyMeta.details = match.description ?? "";
|
|
9275
|
+
didPlanify = true;
|
|
9276
|
+
break;
|
|
8993
9277
|
}
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
early = { ok: false, message: `No task matched "${target}".`, count: 0, completed: 0, inProgress: 0 };
|
|
9278
|
+
default:
|
|
9279
|
+
early = { ok: false, message: `Unknown action "${input.action}". Use replace | add | status | show | promote | planify.`, count: 0, completed: 0, inProgress: 0 };
|
|
8997
9280
|
return f;
|
|
8998
|
-
}
|
|
8999
|
-
planifyMeta.title = match.title;
|
|
9000
|
-
planifyMeta.details = match.description ?? "";
|
|
9001
|
-
didPlanify = true;
|
|
9002
|
-
break;
|
|
9003
9281
|
}
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9282
|
+
return f;
|
|
9283
|
+
});
|
|
9284
|
+
} catch (err) {
|
|
9285
|
+
return {
|
|
9286
|
+
ok: false,
|
|
9287
|
+
message: `Task change not saved \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
9288
|
+
count: 0,
|
|
9289
|
+
completed: 0,
|
|
9290
|
+
inProgress: 0
|
|
9291
|
+
};
|
|
9292
|
+
}
|
|
9010
9293
|
if (todosToReplace) ctx.state.replaceTodos(todosToReplace);
|
|
9011
9294
|
if (early) return early;
|
|
9012
9295
|
if (didPlanify) {
|
|
9013
9296
|
const { title, details } = planifyMeta;
|
|
9014
|
-
const
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9297
|
+
const planPathRaw = ctx.meta["plan.path"];
|
|
9298
|
+
const prog = computeTaskItemProgress(file.tasks);
|
|
9299
|
+
if (typeof planPathRaw === "string" && planPathRaw) {
|
|
9300
|
+
let planPath = planPathRaw;
|
|
9301
|
+
if (input.scope === "project") {
|
|
9302
|
+
const lastSep = Math.max(planPath.lastIndexOf("/"), planPath.lastIndexOf("\\"));
|
|
9303
|
+
planPath = lastSep >= 0 ? planPath.slice(0, lastSep + 1) + "backlog.plan.json" : "backlog.plan.json";
|
|
9304
|
+
}
|
|
9305
|
+
let formatted = "";
|
|
9306
|
+
try {
|
|
9307
|
+
await mutatePlan(planPath, sessionId, (pf) => {
|
|
9308
|
+
const { plan: updated } = addPlanItem(pf, title, details || void 0);
|
|
9309
|
+
formatted = formatPlan(updated);
|
|
9310
|
+
return updated;
|
|
9311
|
+
});
|
|
9312
|
+
} catch (err) {
|
|
9313
|
+
return {
|
|
9314
|
+
ok: false,
|
|
9315
|
+
message: `planify: plan not saved \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
9316
|
+
count: file.tasks.length,
|
|
9317
|
+
completed: prog.completed,
|
|
9318
|
+
inProgress: prog.inProgress
|
|
9319
|
+
};
|
|
9320
|
+
}
|
|
9019
9321
|
return {
|
|
9020
9322
|
ok: true,
|
|
9021
9323
|
message: `planify ok \u2014 added "${title}" to plan.
|
|
9022
|
-
${
|
|
9324
|
+
${formatted}`,
|
|
9023
9325
|
count: file.tasks.length,
|
|
9024
|
-
completed:
|
|
9025
|
-
inProgress:
|
|
9326
|
+
completed: prog.completed,
|
|
9327
|
+
inProgress: prog.inProgress
|
|
9026
9328
|
};
|
|
9027
9329
|
}
|
|
9028
|
-
return {
|
|
9330
|
+
return {
|
|
9331
|
+
ok: false,
|
|
9332
|
+
message: "Plan storage path not configured \u2014 cannot planify.",
|
|
9333
|
+
count: file.tasks.length,
|
|
9334
|
+
completed: prog.completed,
|
|
9335
|
+
inProgress: prog.inProgress
|
|
9336
|
+
};
|
|
9029
9337
|
}
|
|
9030
9338
|
const p = computeTaskItemProgress(file.tasks);
|
|
9031
9339
|
const summary = promoteMeta.count > 0 ? `promote ok \u2014 ${promoteMeta.count} todo(s) created from "${promoteMeta.title}".
|
|
@@ -9069,6 +9377,36 @@ var TIER1_TOOLS = [
|
|
|
9069
9377
|
jsonTool,
|
|
9070
9378
|
searchTool
|
|
9071
9379
|
];
|
|
9380
|
+
var TIER2_TOOLS = [
|
|
9381
|
+
replaceTool,
|
|
9382
|
+
execTool,
|
|
9383
|
+
fetchTool,
|
|
9384
|
+
gitTool,
|
|
9385
|
+
treeTool,
|
|
9386
|
+
lintTool,
|
|
9387
|
+
formatTool,
|
|
9388
|
+
typecheckTool,
|
|
9389
|
+
testTool,
|
|
9390
|
+
todoTool,
|
|
9391
|
+
planTool,
|
|
9392
|
+
taskTool,
|
|
9393
|
+
installTool,
|
|
9394
|
+
auditTool
|
|
9395
|
+
];
|
|
9396
|
+
var TIER3_TOOLS = [
|
|
9397
|
+
outdatedTool,
|
|
9398
|
+
logsTool,
|
|
9399
|
+
documentTool,
|
|
9400
|
+
scaffoldTool,
|
|
9401
|
+
toolSearchTool,
|
|
9402
|
+
toolUseTool,
|
|
9403
|
+
batchToolUseTool,
|
|
9404
|
+
toolHelpTool,
|
|
9405
|
+
codebaseIndexTool,
|
|
9406
|
+
codebaseSearchTool,
|
|
9407
|
+
codebaseStatsTool,
|
|
9408
|
+
setWorkingDirTool
|
|
9409
|
+
];
|
|
9072
9410
|
var builtinTools = [
|
|
9073
9411
|
readTool,
|
|
9074
9412
|
writeTool,
|
|
@@ -9115,6 +9453,162 @@ var builtinToolsPack = {
|
|
|
9115
9453
|
tools: builtinTools
|
|
9116
9454
|
};
|
|
9117
9455
|
|
|
9118
|
-
|
|
9456
|
+
// src/tool-icon-map.ts
|
|
9457
|
+
var TOOL_ICON_MAP = {
|
|
9458
|
+
// File operations
|
|
9459
|
+
read: "file",
|
|
9460
|
+
write: "file",
|
|
9461
|
+
create: "file",
|
|
9462
|
+
// File modification
|
|
9463
|
+
edit: "edit",
|
|
9464
|
+
patch: "edit",
|
|
9465
|
+
replace: "edit",
|
|
9466
|
+
// Content search
|
|
9467
|
+
grep: "search",
|
|
9468
|
+
search: "search",
|
|
9469
|
+
// File discovery
|
|
9470
|
+
glob: "folder",
|
|
9471
|
+
// Shell/command execution
|
|
9472
|
+
bash: "terminal",
|
|
9473
|
+
exec: "terminal",
|
|
9474
|
+
run: "terminal",
|
|
9475
|
+
command: "terminal",
|
|
9476
|
+
shell: "terminal",
|
|
9477
|
+
// Network
|
|
9478
|
+
fetch: "web",
|
|
9479
|
+
curl: "web",
|
|
9480
|
+
http: "web",
|
|
9481
|
+
request: "web",
|
|
9482
|
+
// Version control
|
|
9483
|
+
git: "git",
|
|
9484
|
+
// Directory structure
|
|
9485
|
+
tree: "tree",
|
|
9486
|
+
ls: "tree",
|
|
9487
|
+
list: "tree",
|
|
9488
|
+
// Code quality
|
|
9489
|
+
lint: "code",
|
|
9490
|
+
format: "code",
|
|
9491
|
+
typecheck: "code",
|
|
9492
|
+
// Testing
|
|
9493
|
+
test: "test",
|
|
9494
|
+
tests: "test",
|
|
9495
|
+
// Package management
|
|
9496
|
+
install: "package",
|
|
9497
|
+
uninstall: "package",
|
|
9498
|
+
audit: "package",
|
|
9499
|
+
outdated: "package",
|
|
9500
|
+
npm: "package",
|
|
9501
|
+
pnpm: "package",
|
|
9502
|
+
yarn: "package",
|
|
9503
|
+
// Documentation
|
|
9504
|
+
document: "document",
|
|
9505
|
+
doc: "document",
|
|
9506
|
+
jsdoc: "document",
|
|
9507
|
+
// Project scaffolding
|
|
9508
|
+
scaffold: "scaffold",
|
|
9509
|
+
generate: "scaffold",
|
|
9510
|
+
template: "scaffold",
|
|
9511
|
+
// Task management
|
|
9512
|
+
todo: "todo",
|
|
9513
|
+
todos: "todo",
|
|
9514
|
+
// Planning
|
|
9515
|
+
plan: "plan",
|
|
9516
|
+
planning: "plan",
|
|
9517
|
+
// Structured tasks
|
|
9518
|
+
task: "task",
|
|
9519
|
+
tasks: "task",
|
|
9520
|
+
// Meta/tools
|
|
9521
|
+
"tool-use": "meta",
|
|
9522
|
+
"batch-tool-use": "meta",
|
|
9523
|
+
"tool-search": "meta",
|
|
9524
|
+
"tool-help": "meta",
|
|
9525
|
+
tool_use: "meta",
|
|
9526
|
+
batch_tool_use: "meta",
|
|
9527
|
+
tool_search: "meta",
|
|
9528
|
+
tool_help: "meta",
|
|
9529
|
+
// Code indexing
|
|
9530
|
+
"codebase-index": "index",
|
|
9531
|
+
"codebase-search": "index",
|
|
9532
|
+
"codebase-stats": "index",
|
|
9533
|
+
"codebase_index": "index",
|
|
9534
|
+
"codebase_search": "index",
|
|
9535
|
+
"codebase_stats": "index",
|
|
9536
|
+
// Data
|
|
9537
|
+
json: "json",
|
|
9538
|
+
parse: "json",
|
|
9539
|
+
query: "json",
|
|
9540
|
+
// Comparison
|
|
9541
|
+
diff: "diff",
|
|
9542
|
+
compare: "diff",
|
|
9543
|
+
// Logs
|
|
9544
|
+
logs: "logs",
|
|
9545
|
+
log: "logs",
|
|
9546
|
+
// Configuration
|
|
9547
|
+
"set-working-dir": "settings",
|
|
9548
|
+
set_working_dir: "settings",
|
|
9549
|
+
cwd: "settings",
|
|
9550
|
+
cd: "settings",
|
|
9551
|
+
// AI/Agent
|
|
9552
|
+
think: "brain",
|
|
9553
|
+
reason: "brain",
|
|
9554
|
+
analyze: "brain",
|
|
9555
|
+
reasoning: "brain"
|
|
9556
|
+
};
|
|
9557
|
+
function getToolIcon(toolName) {
|
|
9558
|
+
return TOOL_ICON_MAP[toolName.toLowerCase()] ?? "fallback";
|
|
9559
|
+
}
|
|
9560
|
+
var TOOL_ICON_CONFIG = {
|
|
9561
|
+
file: { icon: "file", color: "#6366f1" },
|
|
9562
|
+
// indigo
|
|
9563
|
+
edit: { icon: "edit", color: "#f59e0b" },
|
|
9564
|
+
// amber
|
|
9565
|
+
search: { icon: "search", color: "#10b981" },
|
|
9566
|
+
// emerald
|
|
9567
|
+
folder: { icon: "folder", color: "#8b5cf6" },
|
|
9568
|
+
// violet
|
|
9569
|
+
terminal: { icon: "terminal", color: "#ef4444" },
|
|
9570
|
+
// red
|
|
9571
|
+
web: { icon: "web", color: "#06b6d4" },
|
|
9572
|
+
// cyan
|
|
9573
|
+
git: { icon: "git", color: "#f97316" },
|
|
9574
|
+
// orange
|
|
9575
|
+
tree: { icon: "tree", color: "#22c55e" },
|
|
9576
|
+
// green
|
|
9577
|
+
code: { icon: "code", color: "#3b82f6" },
|
|
9578
|
+
// blue
|
|
9579
|
+
test: { icon: "test", color: "#84cc16" },
|
|
9580
|
+
// lime
|
|
9581
|
+
package: { icon: "package", color: "#ec4899" },
|
|
9582
|
+
// pink
|
|
9583
|
+
document: { icon: "document", color: "#14b8a6" },
|
|
9584
|
+
// teal
|
|
9585
|
+
scaffold: { icon: "scaffold", color: "#f43f5e" },
|
|
9586
|
+
// rose
|
|
9587
|
+
todo: { icon: "todo", color: "#a855f7" },
|
|
9588
|
+
// purple
|
|
9589
|
+
plan: { icon: "plan", color: "#7c3aed" },
|
|
9590
|
+
// violet-dark
|
|
9591
|
+
task: { icon: "task", color: "#db2777" },
|
|
9592
|
+
// pink-dark
|
|
9593
|
+
meta: { icon: "meta", color: "#6b7280" },
|
|
9594
|
+
// gray
|
|
9595
|
+
index: { icon: "index", color: "#0ea5e9" },
|
|
9596
|
+
// sky
|
|
9597
|
+
json: { icon: "json", color: "#fbbf24" },
|
|
9598
|
+
// yellow
|
|
9599
|
+
diff: { icon: "diff", color: "#a3e635" },
|
|
9600
|
+
// lime-light
|
|
9601
|
+
logs: { icon: "logs", color: "#78716c" },
|
|
9602
|
+
// stone
|
|
9603
|
+
settings: { icon: "settings", color: "#64748b" },
|
|
9604
|
+
// slate
|
|
9605
|
+
brain: { icon: "brain", color: "#d946ef" },
|
|
9606
|
+
// fuchsia
|
|
9607
|
+
fallback: { icon: "fallback", color: "#9ca3af" }
|
|
9608
|
+
// gray-light
|
|
9609
|
+
};
|
|
9610
|
+
var FALLBACK_ICON = "fallback";
|
|
9611
|
+
|
|
9612
|
+
export { CircuitBreaker, CircuitOpenError, FALLBACK_ICON, IndexCircuitBreaker, IndexTimeoutError, OPTIONAL_TOOLS, TIER1_TOOLS, TIER2_TOOLS, TIER3_TOOLS, TOOL_ICON_CONFIG, TOOL_ICON_MAP, _resetProcessRegistry, auditTool, bashTool, batchToolUseTool, builtinTools, builtinToolsPack, cancelPendingReindexes, codebaseIndexStats, codebaseIndexTool, codebaseSearchTool, codebaseStatsTool, createModeTool, diffTool, documentTool, editTool, enqueueReindex, execTool, fetchTool, forgetTool, formatTool, getIndexState, getProcessRegistry, getToolIcon, gitTool, globTool, grepTool, indexCircuitBreaker, installTool, isIndexReady, isIndexableFile, isIndexing, jsonTool, lintTool, logsTool, onIndexStateChange, outdatedTool, patchTool, planTool, readTool, relatedMemoryTool, rememberTool, replaceTool, resetIndexCircuitBreaker, runStartupIndex, scaffoldTool, searchCodebaseIndex, searchMemoryTool, searchTool, shutdownCodebaseIndexHost, testTool, todoTool, toolHelpTool, toolSearchTool, toolUseTool, treeTool, typecheckTool, writeTool };
|
|
9119
9613
|
//# sourceMappingURL=index.js.map
|
|
9120
9614
|
//# sourceMappingURL=index.js.map
|