@wrongstack/tools 0.319.0 → 0.319.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/_edit-match.d.ts +1 -3
- package/dist/_util.d.ts +7 -0
- package/dist/audit.js +4 -4
- package/dist/bash.js +2 -2
- package/dist/builtin.d.ts +10 -0
- package/dist/builtin.js +44 -42
- package/dist/{chunk-2472NT76.js → chunk-2OXHS3KZ.js} +17 -5
- package/dist/{chunk-X5K3U6GI.js → chunk-3M7LLN7W.js} +71 -7
- package/dist/{chunk-VFF3Q5Z3.js → chunk-5EUJF3WC.js} +36 -8
- package/dist/{chunk-QZDOQRF7.js → chunk-64VIJPFV.js} +34 -13
- package/dist/{chunk-UMXVT7ST.js → chunk-6AAOT4PW.js} +58 -10
- package/dist/{chunk-WNUJR3H4.js → chunk-AKHYMB4V.js} +36 -12
- package/dist/{chunk-ZDUGI5BC.js → chunk-ATSKPZUK.js} +2 -2
- package/dist/{chunk-H6AWAZ2Y.js → chunk-B4HTSV6Z.js} +57 -6
- package/dist/{chunk-5ZYEGP2P.js → chunk-BK732J7I.js} +72 -27
- package/dist/{chunk-PMHVSHMU.js → chunk-BSJSNVGA.js} +73 -14
- package/dist/{chunk-KD5EHYA2.js → chunk-C2DQKVLO.js} +61 -28
- package/dist/{chunk-PRXZOIEW.js → chunk-CFLJFDYV.js} +100 -58
- package/dist/{chunk-TX3R5NVV.js → chunk-CMOUCKGC.js} +2 -2
- package/dist/{chunk-SYXW3Y5Z.js → chunk-DC4KMPJO.js} +2 -2
- package/dist/{chunk-MRKWZCHZ.js → chunk-DP6CXMJZ.js} +3 -3
- package/dist/{chunk-ODFVG54U.js → chunk-EADULOZV.js} +36 -12
- package/dist/{chunk-6RNIFLG4.js → chunk-ELRRTWNP.js} +38 -6
- package/dist/{chunk-YZHK4TLF.js → chunk-FQNXYA6W.js} +45 -6
- package/dist/{chunk-65EMYN5G.js → chunk-GMZYW52L.js} +3 -3
- package/dist/{chunk-HBM4QNXU.js → chunk-IU4DV2PA.js} +18 -8
- package/dist/{chunk-COUNXG4X.js → chunk-IWS56OY7.js} +30 -7
- package/dist/{chunk-Z4VBTVUM.js → chunk-IYKXK356.js} +98 -11
- package/dist/{chunk-OJ4FIBAN.js → chunk-JDPQOR7C.js} +14 -7
- package/dist/{chunk-TFMVHYJI.js → chunk-KY4PXYSO.js} +9 -15
- package/dist/{chunk-ZKSBHXVB.js → chunk-LOBRWKCR.js} +63 -14
- package/dist/{chunk-HWDB2G7J.js → chunk-MNP3GE7H.js} +38 -27
- package/dist/{chunk-MYRMD6PZ.js → chunk-O44IMQCN.js} +31 -7
- package/dist/{chunk-7WV3D4S3.js → chunk-OG2BEUBY.js} +69 -34
- package/dist/{chunk-VALQFAY2.js → chunk-OTINMOKK.js} +153 -40
- package/dist/{chunk-PQTONQ4P.js → chunk-QPNYDH5B.js} +19 -5
- package/dist/{chunk-4Q7LFU4T.js → chunk-RLMX75EM.js} +19 -8
- package/dist/{chunk-C3W3V54D.js → chunk-RNWUCZ2U.js} +53 -25
- package/dist/{chunk-RK47IWO3.js → chunk-RTZWBNTY.js} +59 -28
- package/dist/{chunk-UC3RKRIV.js → chunk-TTZOK6IY.js} +2 -2
- package/dist/{chunk-W4GAAI3M.js → chunk-UDVKRX5R.js} +2 -2
- package/dist/{chunk-Y7QXSE7N.js → chunk-UQQS3VNT.js} +43 -9
- package/dist/{chunk-D4CXUZIF.js → chunk-VD2LV5WO.js} +82 -13
- package/dist/{chunk-DPKTXJQS.js → chunk-VPALDFYM.js} +2 -2
- package/dist/{chunk-FJEQZHZN.js → chunk-W2CECGTG.js} +24 -14
- package/dist/{chunk-OYVPBPCL.js → chunk-X5FOFGXX.js} +72 -33
- package/dist/{chunk-CWALNI6K.js → chunk-X6KXNHUO.js} +1 -1
- package/dist/{chunk-OCUXN6Y3.js → chunk-XRNN44QY.js} +1 -1
- package/dist/{chunk-N5O3FK5Q.js → chunk-YOKD4GWJ.js} +67 -27
- package/dist/{chunk-QLF74MSV.js → chunk-ZUFHAHTE.js} +12 -6
- package/dist/codebase-index/index.js +7 -7
- package/dist/codebase-index/parser-worker-script.js +1 -1
- package/dist/codebase-index/project-server.js +3 -3
- package/dist/codebase-index/worker.js +2 -2
- package/dist/design.d.ts +1 -1
- package/dist/design.js +1 -1
- package/dist/diff.js +3 -2
- package/dist/document.js +2 -2
- package/dist/e2e.js +2 -2
- package/dist/edit.js +6 -6
- package/dist/exec.js +2 -2
- package/dist/fetch.js +2 -2
- package/dist/format.js +4 -4
- package/dist/git.js +2 -2
- package/dist/glob.js +2 -2
- package/dist/grep.d.ts +2 -0
- package/dist/grep.js +4 -2
- package/dist/index.js +44 -44
- package/dist/install.js +4 -4
- package/dist/json.js +2 -2
- package/dist/kanban-tool-types.d.ts +2 -0
- package/dist/kanban.js +2 -2
- package/dist/languages/index.js +2 -2
- package/dist/{legacy-bridge-EC7GM2IW.js → legacy-bridge-3T7SR4SK.js} +4 -4
- package/dist/lint.js +4 -4
- package/dist/logs.js +2 -2
- package/dist/outdated.js +4 -2
- package/dist/pack.js +43 -43
- package/dist/{parser-dispatch-3PDBKIQ7.js → parser-dispatch-QGC4HSP5.js} +2 -2
- package/dist/patch.js +6 -6
- package/dist/plan.js +4 -4
- package/dist/read.js +6 -6
- package/dist/replace.js +7 -6
- package/dist/scaffold.js +2 -2
- package/dist/search.js +1 -1
- package/dist/session-kanban.js +1 -1
- package/dist/task.js +4 -4
- package/dist/test.js +4 -4
- package/dist/todo.js +3 -3
- package/dist/tool-tier.js +44 -44
- package/dist/{tree-sitter-parser-FGKHJGSX.js → tree-sitter-parser-GILYQLS4.js} +2 -2
- package/dist/tree.js +2 -2
- package/dist/typecheck.js +4 -4
- package/dist/write.js +6 -6
- package/package.json +5 -5
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
COMMAND_OUTPUT_MAX_BYTES,
|
|
3
3
|
normalizeCommandOutput
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZUFHAHTE.js";
|
|
5
5
|
|
|
6
6
|
// src/git.ts
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
8
|
import { statSync } from "node:fs";
|
|
9
9
|
import { dirname, resolve, sep } from "node:path";
|
|
10
|
+
import { StringDecoder } from "node:string_decoder";
|
|
10
11
|
import { assessCommitSafety } from "@wrongstack/core/coordination";
|
|
12
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
11
13
|
import { buildChildEnv } from "@wrongstack/core/utils";
|
|
12
14
|
var TIMEOUT_MS = 3e4;
|
|
13
15
|
var MAX_OUTPUT = 1e5;
|
|
@@ -89,7 +91,18 @@ var gitTool = {
|
|
|
89
91
|
required: ["command"]
|
|
90
92
|
},
|
|
91
93
|
async execute(input, ctx, opts) {
|
|
92
|
-
if (!input?.command
|
|
94
|
+
if (!input?.command || typeof input.command !== "string" || !input.command.trim()) {
|
|
95
|
+
throw new ToolValidationError({
|
|
96
|
+
message: "git: command is required and cannot be empty",
|
|
97
|
+
field: "command"
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (input.command.startsWith("-") || input.command.includes(" --")) {
|
|
101
|
+
throw new ToolValidationError({
|
|
102
|
+
message: `git: unsafe subcommand name "${input.command}"`,
|
|
103
|
+
field: "command"
|
|
104
|
+
});
|
|
105
|
+
}
|
|
93
106
|
if (input.command === "commit" && !input.message) {
|
|
94
107
|
return {
|
|
95
108
|
command: "commit",
|
|
@@ -187,7 +200,7 @@ function findGitDir(cwd, projectRoot) {
|
|
|
187
200
|
let dir = cwd;
|
|
188
201
|
for (let i = 0; i < 20; i++) {
|
|
189
202
|
try {
|
|
190
|
-
const stat = statSync(
|
|
203
|
+
const stat = statSync(resolve(dir, ".git"));
|
|
191
204
|
if (stat.isDirectory() || stat.isFile()) return dir;
|
|
192
205
|
} catch {
|
|
193
206
|
}
|
|
@@ -199,8 +212,9 @@ function findGitDir(cwd, projectRoot) {
|
|
|
199
212
|
return null;
|
|
200
213
|
}
|
|
201
214
|
function buildArgs(input) {
|
|
202
|
-
const
|
|
203
|
-
const
|
|
215
|
+
const rawLimit = typeof input.limit === "number" && Number.isFinite(input.limit) && input.limit > 0 ? Math.floor(input.limit) : 20;
|
|
216
|
+
const limit = Math.max(1, rawLimit);
|
|
217
|
+
const files = input.files ? (Array.isArray(input.files) ? input.files : input.files.split(",")).map((s) => s.trim().replace(/\\/g, "/")).filter(Boolean) : [];
|
|
204
218
|
switch (input.command) {
|
|
205
219
|
case "status":
|
|
206
220
|
return ["status", ...files.length ? ["--", ...files] : []];
|
|
@@ -269,9 +283,22 @@ function buildArgs(input) {
|
|
|
269
283
|
}
|
|
270
284
|
}
|
|
271
285
|
function runGit(args, cwd, signal) {
|
|
286
|
+
if (signal.aborted) {
|
|
287
|
+
return Promise.resolve({
|
|
288
|
+
command: args[0],
|
|
289
|
+
stdout: "",
|
|
290
|
+
stderr: "Aborted",
|
|
291
|
+
exitCode: 124,
|
|
292
|
+
truncated: false
|
|
293
|
+
});
|
|
294
|
+
}
|
|
272
295
|
return new Promise((resolve2) => {
|
|
273
296
|
let stdout = "";
|
|
274
297
|
let stderr = "";
|
|
298
|
+
let stdoutBytes = 0;
|
|
299
|
+
let stderrBytes = 0;
|
|
300
|
+
const stdoutDecoder = new StringDecoder("utf8");
|
|
301
|
+
const stderrDecoder = new StringDecoder("utf8");
|
|
275
302
|
const child = spawn("git", args, {
|
|
276
303
|
cwd,
|
|
277
304
|
signal,
|
|
@@ -280,31 +307,52 @@ function runGit(args, cwd, signal) {
|
|
|
280
307
|
windowsHide: true
|
|
281
308
|
});
|
|
282
309
|
child.stdout?.on("data", (chunk) => {
|
|
310
|
+
stdoutBytes += chunk.byteLength;
|
|
283
311
|
if (stdout.length < MAX_OUTPUT) {
|
|
284
|
-
|
|
312
|
+
const text = stdoutDecoder.write(chunk);
|
|
313
|
+
if (text) stdout += text.slice(0, MAX_OUTPUT - stdout.length);
|
|
285
314
|
}
|
|
286
315
|
});
|
|
287
316
|
child.stderr?.on("data", (chunk) => {
|
|
317
|
+
stderrBytes += chunk.byteLength;
|
|
288
318
|
if (stderr.length < MAX_OUTPUT) {
|
|
289
|
-
|
|
319
|
+
const text = stderrDecoder.write(chunk);
|
|
320
|
+
if (text) stderr += text.slice(0, MAX_OUTPUT - stderr.length);
|
|
290
321
|
}
|
|
291
322
|
});
|
|
292
323
|
child.on("error", (err) => {
|
|
324
|
+
const stdoutTail = stdoutDecoder.end();
|
|
325
|
+
if (stdoutTail && stdout.length < MAX_OUTPUT) {
|
|
326
|
+
stdout += stdoutTail.slice(0, MAX_OUTPUT - stdout.length);
|
|
327
|
+
}
|
|
328
|
+
const stderrTail = stderrDecoder.end();
|
|
329
|
+
if (stderrTail && stderr.length < MAX_OUTPUT) {
|
|
330
|
+
stderr += stderrTail.slice(0, MAX_OUTPUT - stderr.length);
|
|
331
|
+
}
|
|
293
332
|
resolve2({
|
|
294
333
|
command: args[0],
|
|
295
334
|
stdout: normalizeCommandOutput(stdout),
|
|
296
335
|
stderr: err.message,
|
|
297
336
|
exitCode: 1,
|
|
298
|
-
truncated: Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES
|
|
337
|
+
truncated: stdoutBytes > MAX_OUTPUT || Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES
|
|
299
338
|
});
|
|
300
339
|
});
|
|
301
340
|
child.on("close", (code) => {
|
|
341
|
+
const stdoutTail = stdoutDecoder.end();
|
|
342
|
+
if (stdoutTail && stdout.length < MAX_OUTPUT) {
|
|
343
|
+
stdout += stdoutTail.slice(0, MAX_OUTPUT - stdout.length);
|
|
344
|
+
}
|
|
345
|
+
const stderrTail = stderrDecoder.end();
|
|
346
|
+
if (stderrTail && stderr.length < MAX_OUTPUT) {
|
|
347
|
+
stderr += stderrTail.slice(0, MAX_OUTPUT - stderr.length);
|
|
348
|
+
}
|
|
349
|
+
const isTruncated = stdoutBytes > MAX_OUTPUT || stderrBytes > MAX_OUTPUT || Buffer.byteLength(stdout, "utf8") > COMMAND_OUTPUT_MAX_BYTES || Buffer.byteLength(stderr, "utf8") > COMMAND_OUTPUT_MAX_BYTES;
|
|
302
350
|
resolve2({
|
|
303
351
|
command: args[0],
|
|
304
352
|
stdout: normalizeCommandOutput(stdout),
|
|
305
353
|
stderr: normalizeCommandOutput(stderr),
|
|
306
354
|
exitCode: code ?? 1,
|
|
307
|
-
truncated:
|
|
355
|
+
truncated: isTruncated
|
|
308
356
|
});
|
|
309
357
|
});
|
|
310
358
|
});
|
|
@@ -313,4 +361,4 @@ function runGit(args, cwd, signal) {
|
|
|
313
361
|
export {
|
|
314
362
|
gitTool
|
|
315
363
|
};
|
|
316
|
-
//# sourceMappingURL=chunk-
|
|
364
|
+
//# sourceMappingURL=chunk-6AAOT4PW.js.map
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from "./chunk-V43EQ6VR.js";
|
|
18
18
|
import {
|
|
19
19
|
normalizeCommandOutput
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-ZUFHAHTE.js";
|
|
21
21
|
|
|
22
22
|
// src/bash.ts
|
|
23
23
|
import { spawn } from "node:child_process";
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
emitProcessOutput,
|
|
29
29
|
emitProcessStarted
|
|
30
30
|
} from "@wrongstack/core/observability";
|
|
31
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
31
32
|
|
|
32
33
|
// src/_shell-pick.ts
|
|
33
34
|
var POSIX_DEFAULT = "cmd";
|
|
@@ -539,7 +540,12 @@ var bashTool = {
|
|
|
539
540
|
return final;
|
|
540
541
|
},
|
|
541
542
|
async *executeStream(input, ctx, opts) {
|
|
542
|
-
if (!input?.command
|
|
543
|
+
if (!input?.command || typeof input.command !== "string" || !input.command.trim()) {
|
|
544
|
+
throw new ToolValidationError({
|
|
545
|
+
message: "bash: command is required and cannot be empty",
|
|
546
|
+
field: "command"
|
|
547
|
+
});
|
|
548
|
+
}
|
|
543
549
|
const registry = getProcessRegistry();
|
|
544
550
|
const bypassBreaker = !!input.background;
|
|
545
551
|
if (!registry.beforeCall(bypassBreaker)) {
|
|
@@ -571,7 +577,8 @@ var bashTool = {
|
|
|
571
577
|
}
|
|
572
578
|
const PIPE_TO_SHELL_PATTERN = /\|\s*(sh|bash|ksh|zsh|fish|cmd|powershell|pwsh)/i;
|
|
573
579
|
const pipeToShellNote = PIPE_TO_SHELL_PATTERN.test(input.command) ? "\n\n[wrongstack] Caution: this command pipes output into a shell interpreter (pipe-to-shell). Piped content executes as arbitrary code \u2014 review the source before trusting the result, and prefer downloading to a file and inspecting it first." : "";
|
|
574
|
-
const
|
|
580
|
+
const rawTimeout = typeof input.timeout_ms === "number" && !Number.isNaN(input.timeout_ms) ? input.timeout_ms : DEFAULT_TIMEOUT_MS;
|
|
581
|
+
const timeoutMs = Math.max(1, Math.min(rawTimeout, 6e5));
|
|
575
582
|
const isWin2 = os2.platform() === "win32";
|
|
576
583
|
let plan;
|
|
577
584
|
let winShellKind;
|
|
@@ -605,6 +612,18 @@ var bashTool = {
|
|
|
605
612
|
const env = buildChildEnv(ctx.session?.id);
|
|
606
613
|
const spawnCwd = ctx.workingDir ?? ctx.projectRoot;
|
|
607
614
|
const callerSignal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
|
|
615
|
+
if (callerSignal.aborted) {
|
|
616
|
+
yield {
|
|
617
|
+
type: "final",
|
|
618
|
+
output: {
|
|
619
|
+
output: "Aborted",
|
|
620
|
+
exit_code: 124,
|
|
621
|
+
timed_out: true,
|
|
622
|
+
pid: null
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
608
627
|
const detached = !isWin2;
|
|
609
628
|
const startedAt = Date.now();
|
|
610
629
|
if (input.background) {
|
|
@@ -829,13 +848,15 @@ var bashTool = {
|
|
|
829
848
|
if (stream === "stdout") stdoutBytes += chunk.byteLength;
|
|
830
849
|
else stderrBytes += chunk.byteLength;
|
|
831
850
|
emitProcessOutput({ pid, stream, chunk });
|
|
832
|
-
if (
|
|
833
|
-
|
|
851
|
+
if (text.length > 0) {
|
|
852
|
+
if (buf.length < MAX_OUTPUT) {
|
|
853
|
+
buf += text.slice(0, MAX_OUTPUT - buf.length);
|
|
854
|
+
}
|
|
855
|
+
spool.write(text);
|
|
856
|
+
pending += text;
|
|
857
|
+
push({ kind: "data", text });
|
|
858
|
+
pauseIfFlooded();
|
|
834
859
|
}
|
|
835
|
-
spool.write(text);
|
|
836
|
-
pending += text;
|
|
837
|
-
push({ kind: "data", text });
|
|
838
|
-
pauseIfFlooded();
|
|
839
860
|
};
|
|
840
861
|
const onStdoutData = (chunk) => onData(chunk, "stdout");
|
|
841
862
|
const onStderrData = (chunk) => onData(chunk, "stderr");
|
|
@@ -872,14 +893,17 @@ var bashTool = {
|
|
|
872
893
|
}
|
|
873
894
|
const spooled = spool.finalize();
|
|
874
895
|
const hint = !timedOut && typeof c.code === "number" && c.code !== 0 && winShellKind ? diagnoseBashism(input.command, winShellKind) : void 0;
|
|
896
|
+
const isAborted = callerSignal.aborted;
|
|
875
897
|
yield {
|
|
876
898
|
type: "final",
|
|
877
899
|
output: {
|
|
878
900
|
output: normalizeCommandOutput(buf) + (spooled ? spoolNote(spooled) : "") + (hint ? `
|
|
879
901
|
|
|
880
902
|
${hint}` : "") + pipeToShellNote,
|
|
881
|
-
exit_code: c.code,
|
|
882
|
-
timed_out: timedOut
|
|
903
|
+
exit_code: timedOut || isAborted ? 124 : c.code,
|
|
904
|
+
timed_out: timedOut || isAborted,
|
|
905
|
+
pid: pid ?? null,
|
|
906
|
+
error: isAborted ? "Command aborted by user or signal" : void 0
|
|
883
907
|
}
|
|
884
908
|
};
|
|
885
909
|
ctx.recordSideEffect?.({
|
|
@@ -953,4 +977,4 @@ export {
|
|
|
953
977
|
checkAndBlockKillCommand,
|
|
954
978
|
bashTool
|
|
955
979
|
};
|
|
956
|
-
//# sourceMappingURL=chunk-
|
|
980
|
+
//# sourceMappingURL=chunk-AKHYMB4V.js.map
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
executeLanguagePlan,
|
|
3
3
|
executePackagePlan,
|
|
4
4
|
planLanguageOperation
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-TTZOK6IY.js";
|
|
6
6
|
|
|
7
7
|
// src/languages/legacy-bridge.ts
|
|
8
8
|
import * as fs from "node:fs/promises";
|
|
@@ -98,4 +98,4 @@ export {
|
|
|
98
98
|
tryLegacyCodeOperation,
|
|
99
99
|
tryLegacyPackageOperation
|
|
100
100
|
};
|
|
101
|
-
//# sourceMappingURL=chunk-
|
|
101
|
+
//# sourceMappingURL=chunk-ATSKPZUK.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
todoTool
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-BK732J7I.js";
|
|
4
4
|
import {
|
|
5
5
|
projectSessionPlanToKanban
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-X6KXNHUO.js";
|
|
7
7
|
|
|
8
8
|
// src/plan.ts
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
@@ -13,11 +13,13 @@ import {
|
|
|
13
13
|
deriveTodosFromPlanItem,
|
|
14
14
|
formatPlan,
|
|
15
15
|
getPlanTemplate,
|
|
16
|
+
loadPlan,
|
|
16
17
|
mutatePlan,
|
|
17
18
|
mutateTasks,
|
|
18
19
|
removePlanItem,
|
|
19
20
|
setPlanItemStatus
|
|
20
21
|
} from "@wrongstack/core/storage";
|
|
22
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
21
23
|
import { formatTaskList } from "@wrongstack/core/utils";
|
|
22
24
|
var planTool = {
|
|
23
25
|
name: "plan",
|
|
@@ -85,8 +87,35 @@ var planTool = {
|
|
|
85
87
|
},
|
|
86
88
|
required: ["action"]
|
|
87
89
|
},
|
|
88
|
-
async execute(input, ctx) {
|
|
89
|
-
const
|
|
90
|
+
async execute(input, ctx, _opts) {
|
|
91
|
+
const signal = _opts?.signal ?? ctx?.signal;
|
|
92
|
+
signal?.throwIfAborted();
|
|
93
|
+
const VALID_ACTIONS = /* @__PURE__ */ new Set([
|
|
94
|
+
"show",
|
|
95
|
+
"add",
|
|
96
|
+
"status",
|
|
97
|
+
"start",
|
|
98
|
+
"done",
|
|
99
|
+
"remove",
|
|
100
|
+
"promote",
|
|
101
|
+
"template_use",
|
|
102
|
+
"clear",
|
|
103
|
+
"taskify"
|
|
104
|
+
]);
|
|
105
|
+
if (!input?.action || !VALID_ACTIONS.has(input.action)) {
|
|
106
|
+
throw new ToolValidationError({
|
|
107
|
+
message: `plan: unknown or missing action "${input?.action}". Allowed actions: ${[...VALID_ACTIONS].join(", ")}`,
|
|
108
|
+
field: "action"
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (input.scope !== void 0 && input.scope !== "session" && input.scope !== "project") {
|
|
112
|
+
throw new ToolValidationError({
|
|
113
|
+
message: `plan: invalid scope "${input.scope}". Allowed: session, project`,
|
|
114
|
+
field: "scope"
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const meta = ctx.meta ??= {};
|
|
118
|
+
const sessionPlanPath = meta["plan.path"];
|
|
90
119
|
let planPath;
|
|
91
120
|
if (input.scope === "project") {
|
|
92
121
|
if (typeof sessionPlanPath === "string") {
|
|
@@ -118,6 +147,16 @@ var planTool = {
|
|
|
118
147
|
};
|
|
119
148
|
}
|
|
120
149
|
const sessionId = ctx.session?.id ?? "unknown";
|
|
150
|
+
if (input.action === "show") {
|
|
151
|
+
meta["plan.path.resolved"] = planPath;
|
|
152
|
+
const plan2 = await loadPlan(planPath) ?? {
|
|
153
|
+
version: 1,
|
|
154
|
+
sessionId,
|
|
155
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
156
|
+
items: []
|
|
157
|
+
};
|
|
158
|
+
return mkResult(plan2, true, "Plan show ok.");
|
|
159
|
+
}
|
|
121
160
|
let early = null;
|
|
122
161
|
const taskifyMeta = { title: "", details: "" };
|
|
123
162
|
let didTaskify = false;
|
|
@@ -159,6 +198,7 @@ var planTool = {
|
|
|
159
198
|
return next;
|
|
160
199
|
}
|
|
161
200
|
case "status": {
|
|
201
|
+
const VALID_PLAN_STATUSES = /* @__PURE__ */ new Set(["open", "in_progress", "done"]);
|
|
162
202
|
if (!input.target || !input.status) {
|
|
163
203
|
early = mkResult(
|
|
164
204
|
p,
|
|
@@ -167,6 +207,14 @@ var planTool = {
|
|
|
167
207
|
);
|
|
168
208
|
return p;
|
|
169
209
|
}
|
|
210
|
+
if (!VALID_PLAN_STATUSES.has(input.status)) {
|
|
211
|
+
early = mkResult(
|
|
212
|
+
p,
|
|
213
|
+
false,
|
|
214
|
+
`status requires valid status ('open' | 'in_progress' | 'done'), got "${input.status}".`
|
|
215
|
+
);
|
|
216
|
+
return p;
|
|
217
|
+
}
|
|
170
218
|
const next = setPlanItemStatus(p, input.target, input.status);
|
|
171
219
|
if (next === p) {
|
|
172
220
|
early = mkResult(p, false, `No plan item matched "${input.target}".`);
|
|
@@ -348,7 +396,10 @@ ${formatTaskList(taskFile.tasks)}`
|
|
|
348
396
|
}
|
|
349
397
|
};
|
|
350
398
|
function mkResult(plan, ok, message, todos) {
|
|
351
|
-
|
|
399
|
+
let open = 0;
|
|
400
|
+
for (let i = 0; i < plan.items.length; i++) {
|
|
401
|
+
if (plan.items[i].status !== "done") open++;
|
|
402
|
+
}
|
|
352
403
|
const result = {
|
|
353
404
|
ok,
|
|
354
405
|
message,
|
|
@@ -363,4 +414,4 @@ function mkResult(plan, ok, message, todos) {
|
|
|
363
414
|
export {
|
|
364
415
|
planTool
|
|
365
416
|
};
|
|
366
|
-
//# sourceMappingURL=chunk-
|
|
417
|
+
//# sourceMappingURL=chunk-B4HTSV6Z.js.map
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
kanbanTool
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-W2CECGTG.js";
|
|
4
4
|
import {
|
|
5
5
|
applyManagedKanbanBoardToTodos,
|
|
6
6
|
blockingTitles,
|
|
7
7
|
mirrorSessionTodosToKanban,
|
|
8
8
|
takeSessionMirrorFailure
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-X6KXNHUO.js";
|
|
10
10
|
|
|
11
11
|
// src/todo.ts
|
|
12
12
|
import {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
saveTasks,
|
|
17
17
|
setPlanItemStatus
|
|
18
18
|
} from "@wrongstack/core/storage";
|
|
19
|
+
import { ToolValidationError } from "@wrongstack/core/types";
|
|
19
20
|
import { addTask, getBoard } from "@wrongstack/kanban";
|
|
20
21
|
function normalizedTitle(value) {
|
|
21
22
|
return value.trim().toLocaleLowerCase().replace(/\s+/g, " ");
|
|
@@ -27,6 +28,10 @@ function activeBoardId(items, ctx) {
|
|
|
27
28
|
}
|
|
28
29
|
function bindTodosToBoard(items, previous, board) {
|
|
29
30
|
const previousById = new Map(previous.map((item) => [item.id, item]));
|
|
31
|
+
const taskById = new Map(board.tasks.map((task) => [task.id, task]));
|
|
32
|
+
const taskByOriginId = new Map(
|
|
33
|
+
board.tasks.filter((task) => task.origin?.taskId).map((task) => [task.origin.taskId, task])
|
|
34
|
+
);
|
|
30
35
|
const available = board.tasks.filter(
|
|
31
36
|
(task) => task.status !== "archived" && task.mergedIntoTaskId === void 0 && (!task.childTaskIds || task.childTaskIds.length === 0)
|
|
32
37
|
).sort(
|
|
@@ -38,9 +43,9 @@ function bindTodosToBoard(items, previous, board) {
|
|
|
38
43
|
const requestedTaskId = item.kanbanBoardId === board.id ? item.kanbanTaskId : previousItem?.kanbanBoardId === board.id ? previousItem.kanbanTaskId : void 0;
|
|
39
44
|
const title = normalizedTitle(item.content);
|
|
40
45
|
const candidates = [
|
|
41
|
-
requestedTaskId ?
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
requestedTaskId ? taskById.get(requestedTaskId) : void 0,
|
|
47
|
+
taskById.get(item.id),
|
|
48
|
+
taskByOriginId.get(item.id),
|
|
44
49
|
available.find((task2) => !used.has(task2.id) && normalizedTitle(task2.title) === title)
|
|
45
50
|
];
|
|
46
51
|
const task = candidates.find((candidate) => candidate && !used.has(candidate.id));
|
|
@@ -80,7 +85,10 @@ async function createMissingManagedCards(items, board, ctx, warnings) {
|
|
|
80
85
|
description: item.activeForm?.trim() || `Added from the session todo list: ${item.content}`
|
|
81
86
|
},
|
|
82
87
|
// The card mirrors this session's todo row, so the session owns it.
|
|
83
|
-
{
|
|
88
|
+
{
|
|
89
|
+
sessionId: ctx.eventSessionId?.() ?? ctx.session?.id ?? "default-session",
|
|
90
|
+
actor: "todo"
|
|
91
|
+
}
|
|
84
92
|
);
|
|
85
93
|
if (!result) {
|
|
86
94
|
warnings.push(`Could not open a Kanban card for "${item.content}": board not found.`);
|
|
@@ -287,8 +295,40 @@ var todoTool = {
|
|
|
287
295
|
required: ["todos"]
|
|
288
296
|
},
|
|
289
297
|
async execute(input, ctx, call) {
|
|
298
|
+
const signal = call?.signal ?? ctx.signal ?? new AbortController().signal;
|
|
299
|
+
signal.throwIfAborted();
|
|
290
300
|
if (!Array.isArray(input?.todos)) {
|
|
291
|
-
throw new
|
|
301
|
+
throw new ToolValidationError({
|
|
302
|
+
message: "todo: todos must be an array",
|
|
303
|
+
field: "todos"
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
const VALID_STATUSES = /* @__PURE__ */ new Set(["pending", "in_progress", "completed"]);
|
|
307
|
+
for (const item of input.todos) {
|
|
308
|
+
if (!item || typeof item !== "object") {
|
|
309
|
+
throw new ToolValidationError({
|
|
310
|
+
message: "todo: each item in todos must be an object",
|
|
311
|
+
field: "todos"
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
if (!item.id || typeof item.id !== "string" || !item.id.trim()) {
|
|
315
|
+
throw new ToolValidationError({
|
|
316
|
+
message: 'todo: each item must have a non-empty string "id"',
|
|
317
|
+
field: "todos"
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
if (item.content === void 0 || typeof item.content !== "string") {
|
|
321
|
+
throw new ToolValidationError({
|
|
322
|
+
message: `todo: item "${item.id}" must have a string "content"`,
|
|
323
|
+
field: "todos"
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
if (item.status !== void 0 && !VALID_STATUSES.has(item.status)) {
|
|
327
|
+
throw new ToolValidationError({
|
|
328
|
+
message: `todo: item "${item.id}" has invalid status "${item.status}". Allowed: pending, in_progress, completed`,
|
|
329
|
+
field: "todos"
|
|
330
|
+
});
|
|
331
|
+
}
|
|
292
332
|
}
|
|
293
333
|
const items = input.todos.filter((t) => Boolean(t?.id && t.content));
|
|
294
334
|
const todoIdentity = (item) => item.kanbanBoardId && item.kanbanTaskId ? `kanban:${item.kanbanBoardId}:${item.kanbanTaskId}` : item.promotedFromTask ? `task:${item.promotedFromTask}` : item.promotedFromPlan ? `plan:${item.promotedFromPlan}` : `todo:${item.id}`;
|
|
@@ -330,7 +370,7 @@ var todoTool = {
|
|
|
330
370
|
boundItems = demoteBlockedInProgress(boundItems, creationWarnings);
|
|
331
371
|
}
|
|
332
372
|
ctx.state.replaceTodos(boundItems);
|
|
333
|
-
const kanbanSync = managed && board ? await synchronizeManagedKanban(boundItems, board, ctx,
|
|
373
|
+
const kanbanSync = managed && board ? await synchronizeManagedKanban(boundItems, board, ctx, signal) : { synced: 0, warnings: [] };
|
|
334
374
|
kanbanSync.warnings.unshift(...creationWarnings);
|
|
335
375
|
if (managed && board) {
|
|
336
376
|
const unresolved = boundItems.filter(
|
|
@@ -371,34 +411,39 @@ var todoTool = {
|
|
|
371
411
|
);
|
|
372
412
|
}
|
|
373
413
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
const planPath = meta["plan.path.resolved"] ?? meta["plan.path"];
|
|
378
|
-
if (typeof planPath !== "string" || !planPath) continue;
|
|
414
|
+
const meta = ctx.meta;
|
|
415
|
+
const planPath = meta?.["plan.path.resolved"] ?? meta?.["plan.path"];
|
|
416
|
+
if (typeof planPath === "string" && planPath && completedPlanIds.size > 0) {
|
|
379
417
|
try {
|
|
380
|
-
|
|
418
|
+
let plan = await loadPlan(planPath);
|
|
381
419
|
if (plan) {
|
|
382
|
-
|
|
383
|
-
|
|
420
|
+
let modified = false;
|
|
421
|
+
for (const planId of completedPlanIds) {
|
|
422
|
+
if (pendingPlanIds.has(planId)) continue;
|
|
423
|
+
plan = setPlanItemStatus(plan, planId, "done");
|
|
424
|
+
modified = true;
|
|
425
|
+
}
|
|
426
|
+
if (modified) await savePlan(planPath, plan);
|
|
384
427
|
}
|
|
385
428
|
} catch {
|
|
386
429
|
}
|
|
387
430
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const meta = ctx.meta;
|
|
391
|
-
const taskPath = meta["task.path.resolved"] ?? meta["task.path"];
|
|
392
|
-
if (typeof taskPath !== "string" || !taskPath) continue;
|
|
431
|
+
const taskPath = meta?.["task.path.resolved"] ?? meta?.["task.path"];
|
|
432
|
+
if (typeof taskPath === "string" && taskPath && completedTaskIds.size > 0) {
|
|
393
433
|
try {
|
|
394
434
|
const file = await loadTasks(taskPath);
|
|
395
435
|
if (file) {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
task
|
|
400
|
-
|
|
436
|
+
let modified = false;
|
|
437
|
+
for (const taskId of completedTaskIds) {
|
|
438
|
+
if (pendingTaskIds.has(taskId)) continue;
|
|
439
|
+
const task = file.tasks.find((t) => t.id === taskId);
|
|
440
|
+
if (task && task.status !== "completed") {
|
|
441
|
+
task.status = "completed";
|
|
442
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
443
|
+
modified = true;
|
|
444
|
+
}
|
|
401
445
|
}
|
|
446
|
+
if (modified) await saveTasks(taskPath, file);
|
|
402
447
|
}
|
|
403
448
|
} catch {
|
|
404
449
|
}
|
|
@@ -431,4 +476,4 @@ var todoTool = {
|
|
|
431
476
|
export {
|
|
432
477
|
todoTool
|
|
433
478
|
};
|
|
434
|
-
//# sourceMappingURL=chunk-
|
|
479
|
+
//# sourceMappingURL=chunk-BK732J7I.js.map
|