@wrongstack/tools 0.319.0 → 0.320.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 +8 -8
- package/dist/bash.js +6 -6
- package/dist/browser/index.js +1 -1
- package/dist/builtin.d.ts +10 -0
- package/dist/builtin.js +54 -52
- package/dist/{chunk-65EMYN5G.js → chunk-23EX5WDO.js} +6 -3
- package/dist/{chunk-2472NT76.js → chunk-2OXHS3KZ.js} +17 -5
- package/dist/{chunk-7WV3D4S3.js → chunk-34VLZ35Z.js} +75 -36
- package/dist/{chunk-MYRMD6PZ.js → chunk-3GIKYHLA.js} +32 -8
- package/dist/{chunk-X5K3U6GI.js → chunk-3M7LLN7W.js} +71 -7
- package/dist/{chunk-VALQFAY2.js → chunk-4BABEJC2.js} +158 -45
- package/dist/{chunk-42BHI7TN.js → chunk-4CQWNZ62.js} +2 -2
- package/dist/{chunk-WNUJR3H4.js → chunk-5UFVCIUB.js} +40 -16
- package/dist/{chunk-UMXVT7ST.js → chunk-6AAOT4PW.js} +58 -10
- package/dist/{chunk-H6AWAZ2Y.js → chunk-B4HTSV6Z.js} +57 -6
- package/dist/{chunk-5ZYEGP2P.js → chunk-BK732J7I.js} +72 -27
- package/dist/{chunk-KD5EHYA2.js → chunk-C2DQKVLO.js} +61 -28
- package/dist/{chunk-Z4VBTVUM.js → chunk-DFQDJT22.js} +99 -12
- package/dist/{chunk-6RNIFLG4.js → chunk-ELRRTWNP.js} +38 -6
- package/dist/{chunk-3PVYQFRQ.js → chunk-ESLPBT5A.js} +2 -1
- package/dist/{chunk-YZHK4TLF.js → chunk-FQNXYA6W.js} +45 -6
- package/dist/{chunk-OYVPBPCL.js → chunk-HR67OWEP.js} +72 -33
- package/dist/{chunk-ZDUGI5BC.js → chunk-I7FBZOEP.js} +2 -2
- package/dist/{chunk-HBM4QNXU.js → chunk-IU4DV2PA.js} +18 -8
- package/dist/{chunk-TFMVHYJI.js → chunk-J43O3BOX.js} +9 -15
- package/dist/{chunk-ODFVG54U.js → chunk-J74TMK4M.js} +37 -13
- package/dist/{chunk-COUNXG4X.js → chunk-JLYA4X7K.js} +75 -8
- package/dist/{chunk-FQZOHLG3.js → chunk-JSNSMG5R.js} +3 -2
- package/dist/{chunk-SYXW3Y5Z.js → chunk-JVRLTEO7.js} +2 -2
- package/dist/{chunk-V43EQ6VR.js → chunk-KE7A76VC.js} +3 -2
- package/dist/{chunk-ZKSBHXVB.js → chunk-KWXIKGE3.js} +64 -15
- package/dist/{chunk-RK47IWO3.js → chunk-LRDIRJJL.js} +62 -30
- package/dist/{chunk-UC3RKRIV.js → chunk-NDIT3SBZ.js} +3 -3
- package/dist/{chunk-C3W3V54D.js → chunk-NSPNFOJ7.js} +57 -27
- package/dist/{chunk-TX3R5NVV.js → chunk-NU2IZ72J.js} +7 -7
- package/dist/{chunk-WHIIIREL.js → chunk-NYRLWDT5.js} +7 -3
- package/dist/{chunk-PMHVSHMU.js → chunk-OTPTQBYE.js} +80 -19
- package/dist/{chunk-ZHH3BEWU.js → chunk-OYP27E3I.js} +4 -4
- package/dist/{chunk-W4GAAI3M.js → chunk-PV6HIZQD.js} +2 -2
- package/dist/{chunk-NKNOBUBU.js → chunk-QGF2C7TF.js} +2 -2
- package/dist/{chunk-PQTONQ4P.js → chunk-QPNYDH5B.js} +19 -5
- package/dist/{chunk-VFF3Q5Z3.js → chunk-QSNVDHOZ.js} +37 -9
- package/dist/{chunk-45JPRMC2.js → chunk-QWKSKFKZ.js} +7 -3
- package/dist/{chunk-2VSZC6QM.js → chunk-SCMRM6NL.js} +6 -4
- package/dist/{chunk-RJZQWEMS.js → chunk-T77XOX7E.js} +3 -2
- package/dist/{chunk-QZDOQRF7.js → chunk-UZFY3CKD.js} +35 -14
- package/dist/{chunk-D4CXUZIF.js → chunk-VD2LV5WO.js} +82 -13
- package/dist/{chunk-DPKTXJQS.js → chunk-VPALDFYM.js} +2 -2
- package/dist/{chunk-IIF5HIVF.js → chunk-VVZPQPTY.js} +2 -2
- package/dist/{chunk-HWDB2G7J.js → chunk-W232EFEM.js} +49 -36
- package/dist/{chunk-FJEQZHZN.js → chunk-W2CECGTG.js} +24 -14
- package/dist/{chunk-4Q7LFU4T.js → chunk-WAQASTXB.js} +20 -9
- 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-XSSFBS5I.js} +70 -29
- package/dist/{chunk-Y7QXSE7N.js → chunk-YTKSNTT4.js} +44 -10
- package/dist/{chunk-MRKWZCHZ.js → chunk-Z5Q3TXRY.js} +3 -3
- package/dist/{chunk-PRXZOIEW.js → chunk-Z6IHMJMC.js} +104 -59
- package/dist/{chunk-NWZ6XGBJ.js → chunk-ZBMANLYH.js} +5 -3
- package/dist/{chunk-OJ4FIBAN.js → chunk-ZMW6WJFT.js} +30 -23
- package/dist/{chunk-QLF74MSV.js → chunk-ZUFHAHTE.js} +12 -6
- package/dist/codebase-index/index.js +12 -12
- package/dist/codebase-index/parser-worker-script.js +3 -3
- package/dist/codebase-index/project-server.js +5 -5
- package/dist/codebase-index/worker.js +4 -4
- 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 +9 -9
- package/dist/exec.js +6 -6
- package/dist/fetch.js +2 -2
- package/dist/format.js +8 -8
- package/dist/git.js +2 -2
- package/dist/glob.js +3 -3
- package/dist/{go-parser-IKHW67WE.js → go-parser-D7ELWQ7F.js} +2 -2
- package/dist/grep.d.ts +2 -0
- package/dist/grep.js +5 -3
- package/dist/index.js +56 -56
- package/dist/install.js +8 -8
- package/dist/{json-parser-2EOVP5MR.js → json-parser-34DHYCPW.js} +1 -18
- 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 +6 -6
- package/dist/legacy-bridge-FOV5ODDI.js +18 -0
- package/dist/lint.js +8 -8
- package/dist/logs.js +2 -2
- package/dist/memory.js +1 -1
- package/dist/next-steps.d.ts +7 -0
- package/dist/next-steps.js +82 -38
- package/dist/outdated.js +5 -3
- package/dist/pack.js +53 -53
- package/dist/{parser-dispatch-3PDBKIQ7.js → parser-dispatch-BDVCJXTV.js} +4 -4
- package/dist/patch.js +8 -8
- package/dist/plan.js +4 -4
- package/dist/process-registry.js +1 -1
- package/dist/ps-slash.js +3 -3
- package/dist/{py-parser-RFWG2HYI.js → py-parser-QC6MR4GB.js} +3 -3
- package/dist/read.js +8 -8
- package/dist/replace.js +9 -8
- 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 +8 -8
- package/dist/todo.js +3 -3
- package/dist/tool-search.js +1 -1
- package/dist/tool-tier.js +54 -54
- package/dist/{tree-sitter-parser-FGKHJGSX.js → tree-sitter-parser-GILYQLS4.js} +2 -2
- package/dist/tree.js +2 -2
- package/dist/typecheck.js +8 -8
- package/dist/win32.js +1 -1
- package/dist/write.js +9 -9
- package/package.json +5 -5
- package/dist/legacy-bridge-EC7GM2IW.js +0 -18
|
@@ -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
|
|
@@ -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
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-NBDZMMG2.js";
|
|
6
6
|
import {
|
|
7
7
|
truncateMiddle
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ZUFHAHTE.js";
|
|
9
9
|
|
|
10
10
|
// src/fetch.ts
|
|
11
11
|
import { FetchError, ToolError, ToolValidationError } from "@wrongstack/core/types";
|
|
@@ -79,13 +79,29 @@ var fetchTool = {
|
|
|
79
79
|
return final;
|
|
80
80
|
},
|
|
81
81
|
async *executeStream(input, ctx, opts) {
|
|
82
|
-
if (!input?.url) {
|
|
82
|
+
if (!input?.url || typeof input.url !== "string" || !input.url.trim()) {
|
|
83
83
|
throw new ToolValidationError({
|
|
84
|
-
message: "fetch: url is required",
|
|
84
|
+
message: "fetch: url is required and cannot be empty",
|
|
85
85
|
field: "url"
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
|
-
const
|
|
88
|
+
const trimmedUrl = input.url.trim();
|
|
89
|
+
let u;
|
|
90
|
+
try {
|
|
91
|
+
u = new URL(trimmedUrl);
|
|
92
|
+
} catch {
|
|
93
|
+
throw new ToolValidationError({
|
|
94
|
+
message: `fetch: invalid URL "${input.url}"`,
|
|
95
|
+
field: "url"
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const ALLOWED_FORMATS = /* @__PURE__ */ new Set(["markdown", "text", "raw"]);
|
|
99
|
+
if (input.format !== void 0 && !ALLOWED_FORMATS.has(input.format)) {
|
|
100
|
+
throw new ToolValidationError({
|
|
101
|
+
message: `fetch: unsupported format "${input.format}". Allowed formats: markdown, text, raw`,
|
|
102
|
+
field: "format"
|
|
103
|
+
});
|
|
104
|
+
}
|
|
89
105
|
if (u.username || u.password) {
|
|
90
106
|
throw new ToolValidationError({
|
|
91
107
|
message: "fetch: URLs with embedded credentials (user:pass@host) are not allowed",
|
|
@@ -105,8 +121,9 @@ var fetchTool = {
|
|
|
105
121
|
});
|
|
106
122
|
}
|
|
107
123
|
await assertNotPrivate(u.hostname);
|
|
108
|
-
yield { type: "log", text: `GET ${
|
|
124
|
+
yield { type: "log", text: `GET ${trimmedUrl}` };
|
|
109
125
|
const signal = opts?.signal ?? ctx.signal ?? new AbortController().signal;
|
|
126
|
+
signal.throwIfAborted();
|
|
110
127
|
const ctrl = new AbortController();
|
|
111
128
|
const timer = setTimeout(
|
|
112
129
|
() => ctrl.abort(
|
|
@@ -122,10 +139,10 @@ var fetchTool = {
|
|
|
122
139
|
try {
|
|
123
140
|
let res;
|
|
124
141
|
try {
|
|
125
|
-
res = await guardedFetch(
|
|
142
|
+
res = await guardedFetch(trimmedUrl, 5, combined);
|
|
126
143
|
} catch (err) {
|
|
127
144
|
if (signal.aborted) throw err;
|
|
128
|
-
throw describeFetchError(err,
|
|
145
|
+
throw describeFetchError(err, trimmedUrl, ctrl.signal.aborted);
|
|
129
146
|
}
|
|
130
147
|
const ct = res.headers.get("content-type") ?? "application/octet-stream";
|
|
131
148
|
if (/^image\/|^audio\/|^video\/|application\/octet-stream/.test(ct)) {
|
|
@@ -146,31 +163,46 @@ var fetchTool = {
|
|
|
146
163
|
let pendingBytes = 0;
|
|
147
164
|
const FLUSH_AT = 4 * 1024;
|
|
148
165
|
if (reader) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
166
|
+
try {
|
|
167
|
+
for (; ; ) {
|
|
168
|
+
const { value, done } = await reader.read();
|
|
169
|
+
if (done) break;
|
|
170
|
+
if (!value) continue;
|
|
171
|
+
received += value.byteLength;
|
|
172
|
+
pendingBytes += value.byteLength;
|
|
173
|
+
chunks.push(value);
|
|
174
|
+
if (pendingBytes >= FLUSH_AT) {
|
|
175
|
+
const recent = Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString(
|
|
176
|
+
"utf8"
|
|
177
|
+
);
|
|
178
|
+
yield {
|
|
179
|
+
type: "partial_output",
|
|
180
|
+
text: recent,
|
|
181
|
+
data: { received }
|
|
182
|
+
};
|
|
183
|
+
pendingBytes = 0;
|
|
184
|
+
}
|
|
185
|
+
if (received > MAX_BYTES) {
|
|
186
|
+
await reader.cancel().catch(() => {
|
|
187
|
+
});
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
164
190
|
}
|
|
165
|
-
|
|
191
|
+
} finally {
|
|
192
|
+
reader.releaseLock();
|
|
166
193
|
}
|
|
167
194
|
}
|
|
168
|
-
const text = Buffer.concat(chunks
|
|
169
|
-
const
|
|
195
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
196
|
+
const isHtml = ct.includes("text/html");
|
|
197
|
+
const isJson = /[\/+]json(;|$)/i.test(ct);
|
|
198
|
+
const format = input.format ?? (isHtml ? "markdown" : "text");
|
|
170
199
|
let content;
|
|
171
200
|
if (format === "raw") content = text;
|
|
172
|
-
else if (format === "markdown" &&
|
|
173
|
-
else if (
|
|
201
|
+
else if (format === "markdown" && isHtml) content = TD.turndown(text);
|
|
202
|
+
else if (format === "markdown" && isJson) content = `\`\`\`json
|
|
203
|
+
${prettyJson(text)}
|
|
204
|
+
\`\`\``;
|
|
205
|
+
else if (isJson) content = prettyJson(text);
|
|
174
206
|
else content = text;
|
|
175
207
|
yield {
|
|
176
208
|
type: "final",
|
|
@@ -226,6 +258,7 @@ function describeFetchError(err, url, timedOut) {
|
|
|
226
258
|
});
|
|
227
259
|
}
|
|
228
260
|
function prettyJson(s) {
|
|
261
|
+
if (!s || s.length > 5e5) return s;
|
|
229
262
|
try {
|
|
230
263
|
return JSON.stringify(JSON.parse(s), null, 2);
|
|
231
264
|
} catch {
|
|
@@ -236,4 +269,4 @@ function prettyJson(s) {
|
|
|
236
269
|
export {
|
|
237
270
|
fetchTool
|
|
238
271
|
};
|
|
239
|
-
//# sourceMappingURL=chunk-
|
|
272
|
+
//# sourceMappingURL=chunk-C2DQKVLO.js.map
|