@wrongstack/tools 0.313.0 → 0.316.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 +3 -2
- package/dist/_output-spool.d.ts +4 -3
- package/dist/_shell-pick.d.ts +2 -1
- package/dist/_spawn-stream.d.ts +2 -1
- package/dist/_syntax-check.d.ts +2 -1
- package/dist/audit.js +10 -4055
- package/dist/bash-kill-guard.d.ts +3 -6
- package/dist/bash.js +9 -2308
- package/dist/batch-tool-use.js +2 -187
- package/dist/browser/index.js +25 -1339
- package/dist/browser/network-guard-proxy.d.ts +2 -1
- package/dist/builtin.js +69 -33949
- package/dist/chunk-22B7GE5T.js +260 -0
- package/dist/chunk-2VSZC6QM.js +113 -0
- package/dist/chunk-3PVYQFRQ.js +472 -0
- package/dist/chunk-42BHI7TN.js +337 -0
- package/dist/chunk-4BHUSMNL.js +104 -0
- package/dist/chunk-4I4LWFOO.js +431 -0
- package/dist/chunk-4UESMQDP.js +122 -0
- package/dist/chunk-553IQUMO.js +478 -0
- package/dist/chunk-5H4GSJAI.js +223 -0
- package/dist/chunk-6RNIFLG4.js +305 -0
- package/dist/chunk-7EY5DCRO.js +434 -0
- package/dist/chunk-A6BRWIXN.js +317 -0
- package/dist/chunk-AHHDWNXC.js +200 -0
- package/dist/chunk-BSLVR56S.js +199 -0
- package/dist/chunk-BTSFTGIL.js +7 -0
- package/dist/chunk-D33S4RQG.js +222 -0
- package/dist/chunk-DA4KNE2H.js +4412 -0
- package/dist/chunk-DR7JOQTC.js +520 -0
- package/dist/chunk-EEOFQDT6.js +898 -0
- package/dist/chunk-EJWSCQ6D.js +137 -0
- package/dist/chunk-EMRGN4KM.js +170 -0
- package/dist/chunk-FQZOHLG3.js +89 -0
- package/dist/chunk-GOIZNNOA.js +221 -0
- package/dist/chunk-HFXSNKCV.js +196 -0
- package/dist/chunk-HYX2TZ7O.js +1272 -0
- package/dist/chunk-I64BS3VC.js +15 -0
- package/dist/chunk-IVWV2UM6.js +955 -0
- package/dist/chunk-IXMNTNW5.js +238 -0
- package/dist/chunk-IYK7XGDZ.js +892 -0
- package/dist/chunk-J5SKD6LN.js +235 -0
- package/dist/chunk-JNNVRHMV.js +2111 -0
- package/dist/chunk-JOHXDF3D.js +295 -0
- package/dist/chunk-JRJJX3L5.js +424 -0
- package/dist/chunk-K3WNSHUF.js +1371 -0
- package/dist/chunk-L25CVUVR.js +2830 -0
- package/dist/chunk-LIJQMYYX.js +59 -0
- package/dist/chunk-LWKUTMD5.js +197 -0
- package/dist/chunk-MQX2KXVW.js +54 -0
- package/dist/chunk-MZZN7VCD.js +359 -0
- package/dist/chunk-NBDZMMG2.js +169 -0
- package/dist/chunk-NKJW5GFO.js +366 -0
- package/dist/chunk-NKNOBUBU.js +522 -0
- package/dist/chunk-O4MBV4AX.js +134 -0
- package/dist/chunk-OUBDL3WH.js +3515 -0
- package/dist/chunk-PQKK5JH7.js +101 -0
- package/dist/chunk-PY2IOS7C.js +1000 -0
- package/dist/chunk-Q5JPPCZC.js +12 -0
- package/dist/chunk-QBV725YG.js +128 -0
- package/dist/chunk-QZQSPUBF.js +315 -0
- package/dist/chunk-RGISBZBD.js +231 -0
- package/dist/chunk-RJZQWEMS.js +84 -0
- package/dist/chunk-RXISBFR5.js +315 -0
- package/dist/chunk-S5CCLZL3.js +99 -0
- package/dist/chunk-SDLVH5RZ.js +178 -0
- package/dist/chunk-T2F4S7U4.js +1723 -0
- package/dist/chunk-TEBQ4VPZ.js +163 -0
- package/dist/chunk-U4D5S3VQ.js +21 -0
- package/dist/chunk-U4VAATKP.js +193 -0
- package/dist/chunk-UCYQTZ7F.js +113 -0
- package/dist/chunk-UKU2ZX4C.js +203 -0
- package/dist/chunk-UNSKG6DC.js +159 -0
- package/dist/chunk-V43EQ6VR.js +68 -0
- package/dist/chunk-VB3MJGCV.js +610 -0
- package/dist/chunk-VFKPWSOX.js +154 -0
- package/dist/chunk-VJNHFZ7J.js +190 -0
- package/dist/chunk-VXECAJJ7.js +1091 -0
- package/dist/chunk-WHIIIREL.js +223 -0
- package/dist/chunk-Z6DAKQ3U.js +1 -0
- package/dist/chunk-ZJQ4IGXK.js +474 -0
- package/dist/chunk-ZLTFLC25.js +385 -0
- package/dist/circuit-breaker.js +3 -191
- package/dist/codebase-index/ast-invariant-engine.d.ts +5 -4
- package/dist/codebase-index/binary-frame.d.ts +0 -2
- package/dist/codebase-index/bm25.d.ts +1 -1
- package/dist/codebase-index/circuit-breaker.d.ts +2 -1
- package/dist/codebase-index/codebase-ast-replace-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-impact-analysis-tool.d.ts +4 -3
- package/dist/codebase-index/codebase-invariant-check-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-repo-map-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-skeleton-tool.d.ts +3 -2
- package/dist/codebase-index/codebase-targeted-test-tool.d.ts +3 -2
- package/dist/codebase-index/content-hash.d.ts +0 -2
- package/dist/codebase-index/import-extractor.d.ts +0 -13
- package/dist/codebase-index/index-service.d.ts +1 -1
- package/dist/codebase-index/index.js +84 -13688
- package/dist/codebase-index/languages.d.ts +2 -1
- package/dist/codebase-index/module-roots.d.ts +3 -2
- package/dist/codebase-index/parser-batch.d.ts +0 -5
- package/dist/codebase-index/parser-dispatch.d.ts +2 -1
- package/dist/codebase-index/parser-output.d.ts +4 -3
- package/dist/codebase-index/parser-worker-pool.d.ts +0 -2
- package/dist/codebase-index/parser-worker-script.d.ts +1 -16
- package/dist/codebase-index/parser-worker-script.js +8 -3995
- package/dist/codebase-index/project-server-client-state.d.ts +2 -2
- package/dist/codebase-index/project-server-endpoint.d.ts +0 -15
- package/dist/codebase-index/project-server.js +60 -8548
- package/dist/codebase-index/tree-sitter/visitor.d.ts +2 -1
- package/dist/codebase-index/tree-sitter-parser.d.ts +0 -2
- package/dist/codebase-index/ts-parser.d.ts +1 -1
- package/dist/codebase-index/vector-search.d.ts +2 -1
- package/dist/codebase-index/wal-maintenance.d.ts +3 -2
- package/dist/codebase-index/worker.js +20 -8416
- package/dist/codebase-index/writer-admin.d.ts +0 -1
- package/dist/codebase-index/writer-search.d.ts +0 -9
- package/dist/design.js +2 -299
- package/dist/diff.js +4 -256
- package/dist/document.js +4 -222
- package/dist/e2e.js +5 -476
- package/dist/edit.js +14 -10200
- package/dist/exec.js +16 -3131
- package/dist/fetch.js +8 -398
- package/dist/format.js +10 -3750
- package/dist/generic-parser-VUXDIAOZ.js +13 -0
- package/dist/git.js +4 -380
- package/dist/glob.js +6 -280
- package/dist/go-parser-JXYKJHRC.js +469 -0
- package/dist/grep.js +7 -604
- package/dist/index.js +318 -35387
- package/dist/install.js +10 -4091
- package/dist/json-parser-IBHZ4BZC.js +213 -0
- package/dist/json.js +4 -665
- package/dist/kanban-contract-actions.d.ts +1 -1
- package/dist/kanban-detail-actions.d.ts +2 -1
- package/dist/kanban-presence.d.ts +2 -1
- package/dist/kanban-serializer.d.ts +0 -8
- package/dist/kanban-split-task-handler.d.ts +2 -2
- package/dist/kanban-tool-types.d.ts +2 -1
- package/dist/kanban.js +3 -2096
- package/dist/languages/diagnostics.d.ts +2 -1
- package/dist/languages/index.js +23 -4664
- package/dist/languages/legacy-bridge.d.ts +3 -2
- package/dist/languages/types.d.ts +2 -1
- package/dist/legacy-bridge-7M6QLUQN.js +18 -0
- package/dist/lint.js +10 -3706
- package/dist/logs.js +4 -287
- package/dist/memory.js +6 -215
- package/dist/mode.js +3 -99
- package/dist/outdated.js +4 -5170
- package/dist/pack.js +65 -33897
- package/dist/parser-dispatch-QH4XPGG6.js +14 -0
- package/dist/patch.js +13 -9935
- package/dist/plan.js +5 -3286
- package/dist/process-registry.js +8 -651
- package/dist/ps-slash.js +12 -1439
- package/dist/pwsh.d.ts +0 -1
- package/dist/py-parser-IS4HKYMF.js +16 -0
- package/dist/read.js +13 -9816
- package/dist/replace.js +15 -10014
- package/dist/rs-parser-XHUUO2KZ.js +81 -0
- package/dist/scaffold.js +4 -253
- package/dist/search.js +5 -585
- package/dist/session-kanban-sync.d.ts +0 -6
- package/dist/session-kanban.js +30 -972
- package/dist/skill.js +2 -215
- package/dist/spawn-background.d.ts +1 -1
- package/dist/task.js +5 -3410
- package/dist/test.js +10 -3773
- package/dist/todo.js +4 -2891
- package/dist/tool-help.js +3 -123
- package/dist/tool-search.js +3 -79
- package/dist/tool-tier.js +68 -33984
- package/dist/tool-use.js +2 -93
- package/dist/tree-sitter-parser-AIERW3YI.js +15 -0
- package/dist/tree.js +4 -288
- package/dist/ts-parser-RCTYKSVI.js +232 -0
- package/dist/typecheck.js +10 -3753
- package/dist/win32.js +6 -60
- package/dist/write.js +14 -9882
- package/dist/yaml-parser-4SAXV3PL.js +157 -0
- package/package.json +5 -5
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// src/tool-help.ts
|
|
2
|
+
var toolHelpTool = {
|
|
3
|
+
name: "tool_help",
|
|
4
|
+
category: "Meta",
|
|
5
|
+
description: "Get detailed help for a specific tool, including its full input schema and usage guidance. If you do not know which tool to use, search with `tool_search` first, then call this with the tool name.",
|
|
6
|
+
usageHint: "USE WHEN YOU NEED PRECISE TOOL INFORMATION:\n\n- Call with a specific `tool` name when you want the full schema and current usageHint.\n- Omit `tool` to get an overview of all available tools.\n- Different `format` options give you different levels of detail.\n- Tip: use `tool_search` to find the right tool name, then `tool_help` for the full schema.\nThis tool is extremely valuable for self-correction when you are unsure about a tool's interface.",
|
|
7
|
+
permission: "auto",
|
|
8
|
+
mutating: false,
|
|
9
|
+
timeoutMs: 5e3,
|
|
10
|
+
capabilities: ["tool.meta"],
|
|
11
|
+
icon: "meta",
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: {
|
|
15
|
+
tool: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "Specific tool name to get detailed help for. Omit to get a list of all tools."
|
|
18
|
+
},
|
|
19
|
+
format: {
|
|
20
|
+
type: "string",
|
|
21
|
+
enum: ["short", "full", "markdown"],
|
|
22
|
+
description: 'Level of detail: "short" (summary), "full" (with full schema), "markdown" (human readable).'
|
|
23
|
+
},
|
|
24
|
+
include_examples: {
|
|
25
|
+
type: "boolean",
|
|
26
|
+
description: "Whether to include example usage in the response."
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
async execute(input, ctx) {
|
|
31
|
+
const format = input.format ?? "short";
|
|
32
|
+
const includeExamples = input.include_examples ?? false;
|
|
33
|
+
if (input.tool) {
|
|
34
|
+
const tool = (ctx.catalogTools ?? ctx.tools).find((t) => t.name === input.tool);
|
|
35
|
+
if (!tool) {
|
|
36
|
+
return {
|
|
37
|
+
tool: input.tool,
|
|
38
|
+
help: `No tool found with name "${input.tool}"`,
|
|
39
|
+
tools: [],
|
|
40
|
+
total: 0
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
tool: tool.name,
|
|
45
|
+
help: formatToolHelp(tool, format, includeExamples),
|
|
46
|
+
tools: [
|
|
47
|
+
{
|
|
48
|
+
name: tool.name,
|
|
49
|
+
description: tool.description,
|
|
50
|
+
usageHint: tool.usageHint ?? "",
|
|
51
|
+
inputSchema: tool.inputSchema,
|
|
52
|
+
permission: tool.permission,
|
|
53
|
+
mutating: tool.mutating
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
total: 1
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const allTools = (ctx.catalogTools ?? ctx.tools).map((t) => ({
|
|
60
|
+
name: t.name,
|
|
61
|
+
description: t.description,
|
|
62
|
+
usageHint: t.usageHint ?? "",
|
|
63
|
+
inputSchema: format === "full" ? t.inputSchema : void 0,
|
|
64
|
+
permission: t.permission,
|
|
65
|
+
mutating: t.mutating
|
|
66
|
+
}));
|
|
67
|
+
return {
|
|
68
|
+
help: format === "markdown" ? formatAllToolsMarkdown(allTools) : formatAllToolsShort(allTools),
|
|
69
|
+
tools: allTools,
|
|
70
|
+
total: allTools.length
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
function formatToolHelp(tool, format, includeExamples) {
|
|
75
|
+
const lines = [];
|
|
76
|
+
if (format === "short") {
|
|
77
|
+
lines.push(`${tool.name}: ${tool.description}`);
|
|
78
|
+
if (tool.usageHint) lines.push(`Hint: ${tool.usageHint}`);
|
|
79
|
+
return lines.join("\n");
|
|
80
|
+
}
|
|
81
|
+
if (format === "markdown") {
|
|
82
|
+
lines.push(`## ${tool.name}`);
|
|
83
|
+
lines.push("");
|
|
84
|
+
lines.push(tool.description);
|
|
85
|
+
lines.push("");
|
|
86
|
+
lines.push("**Permission:** " + tool.permission);
|
|
87
|
+
lines.push("**Mutating:** " + (tool.mutating ? "yes" : "no"));
|
|
88
|
+
if (tool.usageHint) {
|
|
89
|
+
lines.push("");
|
|
90
|
+
lines.push("### Usage Hint");
|
|
91
|
+
lines.push(tool.usageHint);
|
|
92
|
+
}
|
|
93
|
+
if (includeExamples && tool.inputSchema) {
|
|
94
|
+
lines.push("");
|
|
95
|
+
lines.push("### Input Schema");
|
|
96
|
+
lines.push("```json");
|
|
97
|
+
lines.push(JSON.stringify(tool.inputSchema, null, 2));
|
|
98
|
+
lines.push("```");
|
|
99
|
+
}
|
|
100
|
+
return lines.join("\n");
|
|
101
|
+
}
|
|
102
|
+
lines.push(`Tool: ${tool.name}`);
|
|
103
|
+
lines.push(`Description: ${tool.description}`);
|
|
104
|
+
lines.push(`Permission: ${tool.permission}`);
|
|
105
|
+
lines.push(`Mutating: ${tool.mutating}`);
|
|
106
|
+
if (tool.usageHint) lines.push(`Usage: ${tool.usageHint}`);
|
|
107
|
+
if (format === "full" && tool.inputSchema) {
|
|
108
|
+
lines.push("Schema: " + JSON.stringify(tool.inputSchema, null, 2));
|
|
109
|
+
}
|
|
110
|
+
return lines.join("\n");
|
|
111
|
+
}
|
|
112
|
+
function formatAllToolsShort(tools) {
|
|
113
|
+
return tools.map((t) => ` ${t.name.padEnd(16)} ${t.description}`).join("\n");
|
|
114
|
+
}
|
|
115
|
+
function formatAllToolsMarkdown(tools) {
|
|
116
|
+
const lines = ["## Available Tools", ""];
|
|
117
|
+
lines.push("| Tool | Description |");
|
|
118
|
+
lines.push("|------|-------------|");
|
|
119
|
+
for (const t of tools) {
|
|
120
|
+
lines.push(`| \`${t.name}\` | ${t.description} |`);
|
|
121
|
+
}
|
|
122
|
+
return lines.join("\n");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export {
|
|
126
|
+
toolHelpTool
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=chunk-QBV725YG.js.map
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import {
|
|
2
|
+
enqueueReindex
|
|
3
|
+
} from "./chunk-HYX2TZ7O.js";
|
|
4
|
+
import {
|
|
5
|
+
resolveRealInsideRoot,
|
|
6
|
+
safeResolveReal,
|
|
7
|
+
sha256hex
|
|
8
|
+
} from "./chunk-D33S4RQG.js";
|
|
9
|
+
|
|
10
|
+
// src/patch.ts
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
import * as fs from "node:fs/promises";
|
|
13
|
+
import * as os from "node:os";
|
|
14
|
+
import * as path from "node:path";
|
|
15
|
+
import { buildChildEnv, toErrorMessage } from "@wrongstack/core/utils";
|
|
16
|
+
var patchTool = {
|
|
17
|
+
name: "patch",
|
|
18
|
+
category: "Filesystem",
|
|
19
|
+
description: "Apply a unified diff (patch) to the project. This is the correct tool when you have a diff that needs to be applied precisely, including handling of rejects.",
|
|
20
|
+
usageHint: "Best used when you already have a diff (from generation, external source, or previous step).\n- Use `dry_run: true` to see what would happen without modifying files.\n- Applied with `--merge`: a conflicting hunk writes git-style conflict\n markers (<<<<<<< / ======= / >>>>>>>) INTO the file and reports failure.\n It does NOT create .rej/.orig files. `files` lists what changed on disk\n even when the patch failed, so read those back before retrying.\nOften cleaner than many small `edit` operations for larger changes.",
|
|
21
|
+
selection: {
|
|
22
|
+
doNotUseWhen: "you do not already have a unified diff or only need one precise replacement.",
|
|
23
|
+
useInstead: ["edit"]
|
|
24
|
+
},
|
|
25
|
+
permission: "confirm",
|
|
26
|
+
// WS-046: gives permission decisions something to key on.
|
|
27
|
+
// The target directory. The patch body itself is unstable (every diff
|
|
28
|
+
// differs), so it could never re-match its own stored rule.
|
|
29
|
+
subjectKey: "directory",
|
|
30
|
+
mutating: true,
|
|
31
|
+
capabilities: ["fs.write"],
|
|
32
|
+
icon: "edit",
|
|
33
|
+
timeoutMs: 3e4,
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {
|
|
37
|
+
patch: { type: "string", description: "Unified diff patch content" },
|
|
38
|
+
directory: { type: "string", description: "Root directory for patch (default: cwd)" },
|
|
39
|
+
strip: { type: "integer", description: "Strip leading path components (default: 1)" },
|
|
40
|
+
dry_run: { type: "boolean", description: "Preview without applying" }
|
|
41
|
+
},
|
|
42
|
+
required: ["patch"]
|
|
43
|
+
},
|
|
44
|
+
async execute(input, ctx, opts) {
|
|
45
|
+
if (!input?.patch) throw new Error("patch: patch content is required");
|
|
46
|
+
const strip = Math.max(1, input.strip ?? 1);
|
|
47
|
+
const dryRun = input.dry_run ?? false;
|
|
48
|
+
const refuse = (message) => ({
|
|
49
|
+
applied: 0,
|
|
50
|
+
rejected: 1,
|
|
51
|
+
files: [],
|
|
52
|
+
dry_run: dryRun,
|
|
53
|
+
message
|
|
54
|
+
});
|
|
55
|
+
let dir;
|
|
56
|
+
try {
|
|
57
|
+
dir = input.directory ? await safeResolveReal(input.directory, ctx) : ctx.cwd;
|
|
58
|
+
} catch (err) {
|
|
59
|
+
return refuse(`patch refused: ${toErrorMessage(err)}`);
|
|
60
|
+
}
|
|
61
|
+
const realRoot = await fs.realpath(ctx.projectRoot).catch(() => path.resolve(ctx.projectRoot));
|
|
62
|
+
const targets = extractDiffTargets(input.patch);
|
|
63
|
+
const resolvedTargets = [];
|
|
64
|
+
for (const t of targets) {
|
|
65
|
+
const stripped = stripPathComponents(t.raw, strip);
|
|
66
|
+
if (!stripped) continue;
|
|
67
|
+
if (path.isAbsolute(stripped)) {
|
|
68
|
+
return refuse(`patch refused: target "${t.raw}" strips to absolute path`);
|
|
69
|
+
}
|
|
70
|
+
const candidate = path.resolve(dir, stripped);
|
|
71
|
+
let real;
|
|
72
|
+
try {
|
|
73
|
+
real = await resolveRealInsideRoot(candidate, ctx);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
return refuse(`patch refused: target "${t.raw}" ${toErrorMessage(err)}`);
|
|
76
|
+
}
|
|
77
|
+
const rel = path.relative(realRoot, real);
|
|
78
|
+
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
79
|
+
return refuse(`patch refused: target "${t.raw}" resolves outside project root`);
|
|
80
|
+
}
|
|
81
|
+
resolvedTargets.push({ raw: t.raw, deleted: t.deleted, abs: real });
|
|
82
|
+
}
|
|
83
|
+
const beforeContents = /* @__PURE__ */ new Map();
|
|
84
|
+
const beforeExisted = /* @__PURE__ */ new Set();
|
|
85
|
+
if (!dryRun) {
|
|
86
|
+
for (const target of resolvedTargets) {
|
|
87
|
+
const existed = (await fs.stat(target.abs).catch(() => null))?.isFile() ?? false;
|
|
88
|
+
if (existed) beforeExisted.add(target.abs);
|
|
89
|
+
beforeContents.set(target.abs, await readTextForTracking(target.abs));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), ".wstack_patch_"));
|
|
93
|
+
try {
|
|
94
|
+
await fs.chmod(tmpDir, 448).catch(() => {
|
|
95
|
+
});
|
|
96
|
+
const patchFile = path.join(tmpDir, "in.diff");
|
|
97
|
+
await fs.writeFile(patchFile, input.patch, { mode: 384 });
|
|
98
|
+
const args = [`-p${strip}`, "--merge", ...dryRun ? ["--dry-run"] : [], "-i", patchFile];
|
|
99
|
+
const result = await runPatch(args, dir, opts.signal, {
|
|
100
|
+
patchFile,
|
|
101
|
+
strip,
|
|
102
|
+
dryRun
|
|
103
|
+
});
|
|
104
|
+
const touched = [];
|
|
105
|
+
if (!dryRun) {
|
|
106
|
+
for (const target of resolvedTargets) {
|
|
107
|
+
const abs = target.abs;
|
|
108
|
+
const before = beforeContents.get(abs) ?? null;
|
|
109
|
+
const stat2 = await fs.stat(abs).catch(() => null);
|
|
110
|
+
if (!stat2?.isFile()) {
|
|
111
|
+
if (beforeExisted.has(abs)) {
|
|
112
|
+
touched.push(abs);
|
|
113
|
+
ctx.session?.recordFileChange?.({
|
|
114
|
+
path: abs,
|
|
115
|
+
action: "deleted",
|
|
116
|
+
before,
|
|
117
|
+
after: null
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const after = await readTextForTracking(abs);
|
|
123
|
+
if (after === null || after === before) continue;
|
|
124
|
+
touched.push(abs);
|
|
125
|
+
ctx.recordRead?.(abs, stat2.mtimeMs, "write", sha256hex(after));
|
|
126
|
+
ctx.session?.recordFileChange?.({
|
|
127
|
+
path: abs,
|
|
128
|
+
action: before === null ? "created" : "modified",
|
|
129
|
+
before,
|
|
130
|
+
after
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
if (touched.length > 0) {
|
|
134
|
+
try {
|
|
135
|
+
enqueueReindex({ projectRoot: ctx.projectRoot, files: touched });
|
|
136
|
+
} catch {
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (result.exitCode !== 0) {
|
|
141
|
+
if (!dryRun) {
|
|
142
|
+
const partial = touched.length > 0 ? ` ${touched.length} file(s) were still modified on disk and have been recorded for rewind: ${touched.map((p) => path.relative(realRoot, p) || p).join(", ")}.` : "";
|
|
143
|
+
return {
|
|
144
|
+
applied: touched.length,
|
|
145
|
+
rejected: 1,
|
|
146
|
+
// Normalize to relative-to-realRoot for API consistency with the
|
|
147
|
+
// success path (which returns GNU patch's dir-relative names).
|
|
148
|
+
// `touched` entries are realpaths from resolveRealInsideRoot, and
|
|
149
|
+
// realRoot is also a realpath, so path.relative is like-for-like.
|
|
150
|
+
files: touched.map((p) => path.relative(realRoot, p) || p),
|
|
151
|
+
dry_run: dryRun,
|
|
152
|
+
message: `patch failed: ${result.stderr || result.stdout}${partial}`
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const wouldPatch = extractPatchedFiles(result.stdout);
|
|
156
|
+
return {
|
|
157
|
+
applied: wouldPatch.length,
|
|
158
|
+
rejected: 1,
|
|
159
|
+
files: wouldPatch,
|
|
160
|
+
dry_run: dryRun,
|
|
161
|
+
message: `patch preview: would conflict \u2014 ${result.stderr || result.stdout}`
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const patched = result.engine === "git" ? [
|
|
165
|
+
...new Set(
|
|
166
|
+
resolvedTargets.map((target) => path.relative(dir, target.abs) || target.abs)
|
|
167
|
+
)
|
|
168
|
+
] : extractPatchedFiles(result.stdout);
|
|
169
|
+
return {
|
|
170
|
+
applied: patched.length,
|
|
171
|
+
rejected: 0,
|
|
172
|
+
files: patched,
|
|
173
|
+
dry_run: dryRun,
|
|
174
|
+
message: result.stdout || "patch applied"
|
|
175
|
+
};
|
|
176
|
+
} finally {
|
|
177
|
+
await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var MAX_TRACKING_BYTES = 5 * 1024 * 1024;
|
|
183
|
+
async function readTextForTracking(absPath) {
|
|
184
|
+
try {
|
|
185
|
+
const stat2 = await fs.stat(absPath);
|
|
186
|
+
if (!stat2.isFile() || stat2.size > MAX_TRACKING_BYTES) return null;
|
|
187
|
+
const buf = await fs.readFile(absPath);
|
|
188
|
+
if (buf.includes(0)) return null;
|
|
189
|
+
return buf.toString("utf8");
|
|
190
|
+
} catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function extractDiffTargets(patch) {
|
|
195
|
+
const out = [];
|
|
196
|
+
const clean = (raw) => {
|
|
197
|
+
if (!raw) return "";
|
|
198
|
+
return (raw.length > 4096 ? raw.slice(0, 4096) : raw).trim();
|
|
199
|
+
};
|
|
200
|
+
let lastOld;
|
|
201
|
+
let inHunk = false;
|
|
202
|
+
let oldLinesLeft = 0;
|
|
203
|
+
let newLinesLeft = 0;
|
|
204
|
+
for (const line of patch.split(/\r?\n/)) {
|
|
205
|
+
const hunkMatch = /^@@ -\d+(?:,(\d+))? \+\d+(?:,(\d+))? @@/.exec(line);
|
|
206
|
+
if (hunkMatch) {
|
|
207
|
+
inHunk = true;
|
|
208
|
+
oldLinesLeft = hunkMatch[1] ? Number(hunkMatch[1]) : 1;
|
|
209
|
+
newLinesLeft = hunkMatch[2] ? Number(hunkMatch[2]) : 1;
|
|
210
|
+
lastOld = void 0;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (inHunk) {
|
|
214
|
+
const ch = line[0];
|
|
215
|
+
if (ch === "-") oldLinesLeft--;
|
|
216
|
+
else if (ch === "+") newLinesLeft--;
|
|
217
|
+
else if (ch === " " || ch === void 0) {
|
|
218
|
+
oldLinesLeft--;
|
|
219
|
+
newLinesLeft--;
|
|
220
|
+
}
|
|
221
|
+
if (oldLinesLeft <= 0 && newLinesLeft <= 0) inHunk = false;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
const oldMatch = /^---\s+([^\t\r\n]+)/.exec(line);
|
|
225
|
+
if (oldMatch) {
|
|
226
|
+
lastOld = clean(oldMatch[1]);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
const newMatch = /^\+\+\+\s+([^\t\r\n]+)/.exec(line);
|
|
230
|
+
if (!newMatch) continue;
|
|
231
|
+
const newTarget = clean(newMatch[1]);
|
|
232
|
+
if (newTarget && newTarget !== "/dev/null") {
|
|
233
|
+
out.push({ raw: newTarget, deleted: false });
|
|
234
|
+
} else if (lastOld && lastOld !== "/dev/null") {
|
|
235
|
+
out.push({ raw: lastOld, deleted: true });
|
|
236
|
+
}
|
|
237
|
+
lastOld = void 0;
|
|
238
|
+
}
|
|
239
|
+
return out;
|
|
240
|
+
}
|
|
241
|
+
function stripPathComponents(p, strip) {
|
|
242
|
+
const s = p.replace(/\\/g, "/");
|
|
243
|
+
let idx = 0;
|
|
244
|
+
for (let i = 0; i < strip; i++) {
|
|
245
|
+
while (idx < s.length && s[idx] !== "/") idx++;
|
|
246
|
+
let hadSlash = false;
|
|
247
|
+
while (idx < s.length && s[idx] === "/") {
|
|
248
|
+
idx++;
|
|
249
|
+
hadSlash = true;
|
|
250
|
+
}
|
|
251
|
+
if (!hadSlash) return void 0;
|
|
252
|
+
}
|
|
253
|
+
return s.slice(idx) || void 0;
|
|
254
|
+
}
|
|
255
|
+
function runPatch(args, cwd, signal, fallback) {
|
|
256
|
+
return runPatchProcess("patch", args, cwd, signal).then(async (result) => {
|
|
257
|
+
if (!result.unavailable) return { ...result, engine: "patch" };
|
|
258
|
+
const gitArgs = [
|
|
259
|
+
"apply",
|
|
260
|
+
"--unsafe-paths",
|
|
261
|
+
`-p${fallback.strip}`,
|
|
262
|
+
"--verbose",
|
|
263
|
+
...fallback.dryRun ? ["--check"] : [],
|
|
264
|
+
fallback.patchFile
|
|
265
|
+
];
|
|
266
|
+
const gitResult = await runPatchProcess("git", gitArgs, cwd, signal);
|
|
267
|
+
return { ...gitResult, engine: "git" };
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function runPatchProcess(command, args, cwd, signal) {
|
|
271
|
+
return new Promise((resolve2) => {
|
|
272
|
+
let stdout = "";
|
|
273
|
+
let stderr = "";
|
|
274
|
+
const env = { ...buildChildEnv(), LANG: "C", LC_ALL: "C" };
|
|
275
|
+
const child = spawn(command, args, {
|
|
276
|
+
cwd,
|
|
277
|
+
signal,
|
|
278
|
+
env,
|
|
279
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
280
|
+
windowsHide: true
|
|
281
|
+
});
|
|
282
|
+
child.stdout?.on("data", (c) => {
|
|
283
|
+
stdout += c.toString();
|
|
284
|
+
});
|
|
285
|
+
child.stderr?.on("data", (c) => {
|
|
286
|
+
stderr += c.toString();
|
|
287
|
+
});
|
|
288
|
+
child.on(
|
|
289
|
+
"close",
|
|
290
|
+
(code) => resolve2({ exitCode: code ?? 1, stdout, stderr, unavailable: false })
|
|
291
|
+
);
|
|
292
|
+
child.on(
|
|
293
|
+
"error",
|
|
294
|
+
(e) => resolve2({
|
|
295
|
+
exitCode: 1,
|
|
296
|
+
stdout: "",
|
|
297
|
+
stderr: e.message,
|
|
298
|
+
unavailable: e.code === "ENOENT"
|
|
299
|
+
})
|
|
300
|
+
);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
function extractPatchedFiles(output) {
|
|
304
|
+
const files = [];
|
|
305
|
+
const re = /(?:patching|checking) file (.+)/gi;
|
|
306
|
+
for (const m of output.matchAll(re)) {
|
|
307
|
+
if (m[1]) files.push(m[1]);
|
|
308
|
+
}
|
|
309
|
+
return files;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export {
|
|
313
|
+
patchTool
|
|
314
|
+
};
|
|
315
|
+
//# sourceMappingURL=chunk-QZQSPUBF.js.map
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compileUserRegex
|
|
3
|
+
} from "./chunk-Q5JPPCZC.js";
|
|
4
|
+
import {
|
|
5
|
+
safeResolveReal
|
|
6
|
+
} from "./chunk-D33S4RQG.js";
|
|
7
|
+
|
|
8
|
+
// src/logs.ts
|
|
9
|
+
import { spawn } from "node:child_process";
|
|
10
|
+
import { buildChildEnv } from "@wrongstack/core/utils";
|
|
11
|
+
var logsTool = {
|
|
12
|
+
name: "logs",
|
|
13
|
+
category: "Logs",
|
|
14
|
+
description: "Read logs from files or Docker containers. Useful for debugging running applications.",
|
|
15
|
+
usageHint: "DEBUGGING TOOL \u2014 USE CAREFULLY IN AUTONOMOUS MODE:\n\n- Prefer `path` for local files or `service` for Docker containers.\n- Always use `filter` (regex) when possible to reduce noise and token usage.\n- `since` narrows Docker logs to a recent window.",
|
|
16
|
+
permission: "confirm",
|
|
17
|
+
mutating: false,
|
|
18
|
+
timeoutMs: 3e4,
|
|
19
|
+
maxOutputBytes: 262144,
|
|
20
|
+
capabilities: ["shell.restricted"],
|
|
21
|
+
icon: "logs",
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
service: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Docker container name (passed to `docker logs`)"
|
|
28
|
+
},
|
|
29
|
+
path: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "Path to log file (alternative to service)"
|
|
32
|
+
},
|
|
33
|
+
lines: {
|
|
34
|
+
type: "integer",
|
|
35
|
+
description: "Number of log lines to fetch (default: 100, 0 for all)",
|
|
36
|
+
minimum: 0,
|
|
37
|
+
maximum: 1e4
|
|
38
|
+
},
|
|
39
|
+
filter: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Regex pattern to filter log lines"
|
|
42
|
+
},
|
|
43
|
+
since: {
|
|
44
|
+
type: "string",
|
|
45
|
+
enum: ["1h", "6h", "24h", "all"],
|
|
46
|
+
description: 'Only show Docker logs since duration (ignored for files; "all" = no limit)'
|
|
47
|
+
},
|
|
48
|
+
cwd: { type: "string", description: "Working directory (default: cwd)" }
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
async execute(input, ctx, opts) {
|
|
52
|
+
const cwd = input.cwd ? await safeResolveReal(input.cwd, ctx) : ctx.cwd;
|
|
53
|
+
const lines = input.lines ?? 100;
|
|
54
|
+
let filterRe = null;
|
|
55
|
+
if (input.filter) {
|
|
56
|
+
const compiled = compileUserRegex(input.filter, "i");
|
|
57
|
+
if (!compiled.ok) {
|
|
58
|
+
throw new Error(`logs: ${compiled.reason}`);
|
|
59
|
+
}
|
|
60
|
+
filterRe = compiled.regex;
|
|
61
|
+
}
|
|
62
|
+
if (input.service) {
|
|
63
|
+
return await dockerLogs(input.service, lines, filterRe, cwd, opts.signal, input.since);
|
|
64
|
+
}
|
|
65
|
+
if (input.path) {
|
|
66
|
+
return await fileLogs(await safeResolveReal(input.path, ctx), lines, filterRe);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
source: "none",
|
|
70
|
+
entries: [],
|
|
71
|
+
total: 0,
|
|
72
|
+
truncated: false,
|
|
73
|
+
stream_mode: false
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
async function dockerLogs(service, lines, filterRe, cwd, signal, since) {
|
|
78
|
+
const args = ["logs"];
|
|
79
|
+
if (lines > 0) args.push("--tail", String(lines));
|
|
80
|
+
if (since && since !== "all") {
|
|
81
|
+
const sinceMap = { "1h": "1h", "6h": "6h", "24h": "24h" };
|
|
82
|
+
args.push("--since", sinceMap[since] ?? "1h");
|
|
83
|
+
}
|
|
84
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._:-]+$/.test(service)) {
|
|
85
|
+
return {
|
|
86
|
+
source: `docker:${service}`,
|
|
87
|
+
entries: [],
|
|
88
|
+
total: 0,
|
|
89
|
+
truncated: false,
|
|
90
|
+
stream_mode: false
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
args.push("--timestamps", service);
|
|
94
|
+
return new Promise((resolve) => {
|
|
95
|
+
let stdout = "";
|
|
96
|
+
let stderr = "";
|
|
97
|
+
const MAX = 2e5;
|
|
98
|
+
let settled = false;
|
|
99
|
+
const empty = () => ({
|
|
100
|
+
source: `docker:${service}`,
|
|
101
|
+
entries: [],
|
|
102
|
+
total: 0,
|
|
103
|
+
truncated: false,
|
|
104
|
+
stream_mode: false
|
|
105
|
+
});
|
|
106
|
+
const finish = (result) => {
|
|
107
|
+
if (settled) return;
|
|
108
|
+
settled = true;
|
|
109
|
+
clearTimeout(timer);
|
|
110
|
+
resolve(result);
|
|
111
|
+
};
|
|
112
|
+
const child = spawn("docker", args, {
|
|
113
|
+
cwd,
|
|
114
|
+
signal,
|
|
115
|
+
env: buildChildEnv(),
|
|
116
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
117
|
+
windowsHide: true
|
|
118
|
+
});
|
|
119
|
+
const timer = setTimeout(() => {
|
|
120
|
+
child.kill("SIGTERM");
|
|
121
|
+
finish(empty());
|
|
122
|
+
}, DOCKER_LOGS_TIMEOUT_MS);
|
|
123
|
+
child.stdout?.on("data", (c) => {
|
|
124
|
+
if (stdout.length < MAX) stdout += c.toString();
|
|
125
|
+
});
|
|
126
|
+
child.stderr?.on("data", (c) => {
|
|
127
|
+
if (stderr.length < MAX) stderr += c.toString();
|
|
128
|
+
});
|
|
129
|
+
child.stdout?.on("error", (e) => {
|
|
130
|
+
console.log(
|
|
131
|
+
JSON.stringify({ level: "debug", event: "pipe_error", stream: "stdout", error: e.message })
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
child.stderr?.on("error", (e) => {
|
|
135
|
+
console.log(
|
|
136
|
+
JSON.stringify({ level: "debug", event: "pipe_error", stream: "stderr", error: e.message })
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
child.on("close", () => {
|
|
140
|
+
const output = stdout + stderr;
|
|
141
|
+
const entries = parseLogLines(output, filterRe);
|
|
142
|
+
finish({
|
|
143
|
+
source: `docker:${service}`,
|
|
144
|
+
entries,
|
|
145
|
+
total: entries.length,
|
|
146
|
+
truncated: output.length >= MAX,
|
|
147
|
+
stream_mode: false
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
child.on("error", () => finish(empty()));
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
var DOCKER_LOGS_TIMEOUT_MS = 3e3;
|
|
154
|
+
var MAX_TAIL_LINES = 1e5;
|
|
155
|
+
async function fileLogs(path, lines, filterRe) {
|
|
156
|
+
const { createInterface } = await import("node:readline");
|
|
157
|
+
const { createReadStream } = await import("node:fs");
|
|
158
|
+
const entries = [];
|
|
159
|
+
const effLines = lines > 0 ? Math.min(lines, MAX_TAIL_LINES) : MAX_TAIL_LINES;
|
|
160
|
+
const window = new Array(effLines);
|
|
161
|
+
let writeIdx = 0;
|
|
162
|
+
let totalLines = 0;
|
|
163
|
+
const rl = createInterface({
|
|
164
|
+
input: createReadStream(path),
|
|
165
|
+
crlfDelay: Number.POSITIVE_INFINITY
|
|
166
|
+
});
|
|
167
|
+
for await (const line of rl) {
|
|
168
|
+
if (filterRe && !filterRe.test(line)) continue;
|
|
169
|
+
window[writeIdx] = line;
|
|
170
|
+
writeIdx = (writeIdx + 1) % effLines;
|
|
171
|
+
totalLines++;
|
|
172
|
+
}
|
|
173
|
+
const ordered = [];
|
|
174
|
+
const start = totalLines >= effLines ? writeIdx : 0;
|
|
175
|
+
const count = Math.min(totalLines, effLines);
|
|
176
|
+
for (let i = 0; i < count; i++) {
|
|
177
|
+
const v = window[(start + i) % effLines];
|
|
178
|
+
if (v !== void 0) ordered.push(v);
|
|
179
|
+
}
|
|
180
|
+
for (const line of ordered) {
|
|
181
|
+
const parsed = parseLine(line);
|
|
182
|
+
if (parsed) entries.push(parsed);
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
source: path,
|
|
186
|
+
entries,
|
|
187
|
+
total: entries.length,
|
|
188
|
+
truncated: totalLines > effLines,
|
|
189
|
+
stream_mode: false
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function parseLogLines(output, filterRe) {
|
|
193
|
+
const lines = output.split("\n").filter(Boolean);
|
|
194
|
+
const entries = [];
|
|
195
|
+
for (const line of lines) {
|
|
196
|
+
if (filterRe && !filterRe.test(line)) continue;
|
|
197
|
+
const parsed = parseLine(line);
|
|
198
|
+
if (parsed) entries.push(parsed);
|
|
199
|
+
}
|
|
200
|
+
return entries;
|
|
201
|
+
}
|
|
202
|
+
function parseLine(line) {
|
|
203
|
+
const tsRe = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?)\s+(?:\[?(\w+)\]?)\s*(.*)/;
|
|
204
|
+
const match = tsRe.exec(line);
|
|
205
|
+
if (match) {
|
|
206
|
+
return {
|
|
207
|
+
timestamp: match[1] ?? "",
|
|
208
|
+
level: match[2]?.toLowerCase() ?? "info",
|
|
209
|
+
message: match[3] ?? ""
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const levelRe = /(ERROR|WARN|INFO|DEBUG|TRACE)\s+(.*)/i;
|
|
213
|
+
const levelMatch = levelRe.exec(line);
|
|
214
|
+
if (levelMatch) {
|
|
215
|
+
return {
|
|
216
|
+
timestamp: "",
|
|
217
|
+
level: levelMatch[1]?.toLowerCase() ?? "info",
|
|
218
|
+
message: levelMatch[2] ?? line
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
timestamp: "",
|
|
223
|
+
level: "info",
|
|
224
|
+
message: line
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export {
|
|
229
|
+
logsTool
|
|
230
|
+
};
|
|
231
|
+
//# sourceMappingURL=chunk-RGISBZBD.js.map
|