@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.
Files changed (182) hide show
  1. package/dist/_edit-match.d.ts +3 -2
  2. package/dist/_output-spool.d.ts +4 -3
  3. package/dist/_shell-pick.d.ts +2 -1
  4. package/dist/_spawn-stream.d.ts +2 -1
  5. package/dist/_syntax-check.d.ts +2 -1
  6. package/dist/audit.js +10 -4055
  7. package/dist/bash-kill-guard.d.ts +3 -6
  8. package/dist/bash.js +9 -2308
  9. package/dist/batch-tool-use.js +2 -187
  10. package/dist/browser/index.js +25 -1339
  11. package/dist/browser/network-guard-proxy.d.ts +2 -1
  12. package/dist/builtin.js +69 -33949
  13. package/dist/chunk-22B7GE5T.js +260 -0
  14. package/dist/chunk-2VSZC6QM.js +113 -0
  15. package/dist/chunk-3PVYQFRQ.js +472 -0
  16. package/dist/chunk-42BHI7TN.js +337 -0
  17. package/dist/chunk-4BHUSMNL.js +104 -0
  18. package/dist/chunk-4I4LWFOO.js +431 -0
  19. package/dist/chunk-4UESMQDP.js +122 -0
  20. package/dist/chunk-553IQUMO.js +478 -0
  21. package/dist/chunk-5H4GSJAI.js +223 -0
  22. package/dist/chunk-6RNIFLG4.js +305 -0
  23. package/dist/chunk-7EY5DCRO.js +434 -0
  24. package/dist/chunk-A6BRWIXN.js +317 -0
  25. package/dist/chunk-AHHDWNXC.js +200 -0
  26. package/dist/chunk-BSLVR56S.js +199 -0
  27. package/dist/chunk-BTSFTGIL.js +7 -0
  28. package/dist/chunk-D33S4RQG.js +222 -0
  29. package/dist/chunk-DA4KNE2H.js +4412 -0
  30. package/dist/chunk-DR7JOQTC.js +520 -0
  31. package/dist/chunk-EEOFQDT6.js +898 -0
  32. package/dist/chunk-EJWSCQ6D.js +137 -0
  33. package/dist/chunk-EMRGN4KM.js +170 -0
  34. package/dist/chunk-FQZOHLG3.js +89 -0
  35. package/dist/chunk-GOIZNNOA.js +221 -0
  36. package/dist/chunk-HFXSNKCV.js +196 -0
  37. package/dist/chunk-HYX2TZ7O.js +1272 -0
  38. package/dist/chunk-I64BS3VC.js +15 -0
  39. package/dist/chunk-IVWV2UM6.js +955 -0
  40. package/dist/chunk-IXMNTNW5.js +238 -0
  41. package/dist/chunk-IYK7XGDZ.js +892 -0
  42. package/dist/chunk-J5SKD6LN.js +235 -0
  43. package/dist/chunk-JNNVRHMV.js +2111 -0
  44. package/dist/chunk-JOHXDF3D.js +295 -0
  45. package/dist/chunk-JRJJX3L5.js +424 -0
  46. package/dist/chunk-K3WNSHUF.js +1371 -0
  47. package/dist/chunk-L25CVUVR.js +2830 -0
  48. package/dist/chunk-LIJQMYYX.js +59 -0
  49. package/dist/chunk-LWKUTMD5.js +197 -0
  50. package/dist/chunk-MQX2KXVW.js +54 -0
  51. package/dist/chunk-MZZN7VCD.js +359 -0
  52. package/dist/chunk-NBDZMMG2.js +169 -0
  53. package/dist/chunk-NKJW5GFO.js +366 -0
  54. package/dist/chunk-NKNOBUBU.js +522 -0
  55. package/dist/chunk-O4MBV4AX.js +134 -0
  56. package/dist/chunk-OUBDL3WH.js +3515 -0
  57. package/dist/chunk-PQKK5JH7.js +101 -0
  58. package/dist/chunk-PY2IOS7C.js +1000 -0
  59. package/dist/chunk-Q5JPPCZC.js +12 -0
  60. package/dist/chunk-QBV725YG.js +128 -0
  61. package/dist/chunk-QZQSPUBF.js +315 -0
  62. package/dist/chunk-RGISBZBD.js +231 -0
  63. package/dist/chunk-RJZQWEMS.js +84 -0
  64. package/dist/chunk-RXISBFR5.js +315 -0
  65. package/dist/chunk-S5CCLZL3.js +99 -0
  66. package/dist/chunk-SDLVH5RZ.js +178 -0
  67. package/dist/chunk-T2F4S7U4.js +1723 -0
  68. package/dist/chunk-TEBQ4VPZ.js +163 -0
  69. package/dist/chunk-U4D5S3VQ.js +21 -0
  70. package/dist/chunk-U4VAATKP.js +193 -0
  71. package/dist/chunk-UCYQTZ7F.js +113 -0
  72. package/dist/chunk-UKU2ZX4C.js +203 -0
  73. package/dist/chunk-UNSKG6DC.js +159 -0
  74. package/dist/chunk-V43EQ6VR.js +68 -0
  75. package/dist/chunk-VB3MJGCV.js +610 -0
  76. package/dist/chunk-VFKPWSOX.js +154 -0
  77. package/dist/chunk-VJNHFZ7J.js +190 -0
  78. package/dist/chunk-VXECAJJ7.js +1091 -0
  79. package/dist/chunk-WHIIIREL.js +223 -0
  80. package/dist/chunk-Z6DAKQ3U.js +1 -0
  81. package/dist/chunk-ZJQ4IGXK.js +474 -0
  82. package/dist/chunk-ZLTFLC25.js +385 -0
  83. package/dist/circuit-breaker.js +3 -191
  84. package/dist/codebase-index/ast-invariant-engine.d.ts +5 -4
  85. package/dist/codebase-index/binary-frame.d.ts +0 -2
  86. package/dist/codebase-index/bm25.d.ts +1 -1
  87. package/dist/codebase-index/circuit-breaker.d.ts +2 -1
  88. package/dist/codebase-index/codebase-ast-replace-tool.d.ts +3 -2
  89. package/dist/codebase-index/codebase-impact-analysis-tool.d.ts +4 -3
  90. package/dist/codebase-index/codebase-invariant-check-tool.d.ts +3 -2
  91. package/dist/codebase-index/codebase-repo-map-tool.d.ts +3 -2
  92. package/dist/codebase-index/codebase-skeleton-tool.d.ts +3 -2
  93. package/dist/codebase-index/codebase-targeted-test-tool.d.ts +3 -2
  94. package/dist/codebase-index/content-hash.d.ts +0 -2
  95. package/dist/codebase-index/import-extractor.d.ts +0 -13
  96. package/dist/codebase-index/index-service.d.ts +1 -1
  97. package/dist/codebase-index/index.js +84 -13688
  98. package/dist/codebase-index/languages.d.ts +2 -1
  99. package/dist/codebase-index/module-roots.d.ts +3 -2
  100. package/dist/codebase-index/parser-batch.d.ts +0 -5
  101. package/dist/codebase-index/parser-dispatch.d.ts +2 -1
  102. package/dist/codebase-index/parser-output.d.ts +4 -3
  103. package/dist/codebase-index/parser-worker-pool.d.ts +0 -2
  104. package/dist/codebase-index/parser-worker-script.d.ts +1 -16
  105. package/dist/codebase-index/parser-worker-script.js +8 -3995
  106. package/dist/codebase-index/project-server-client-state.d.ts +2 -2
  107. package/dist/codebase-index/project-server-endpoint.d.ts +0 -15
  108. package/dist/codebase-index/project-server.js +60 -8548
  109. package/dist/codebase-index/tree-sitter/visitor.d.ts +2 -1
  110. package/dist/codebase-index/tree-sitter-parser.d.ts +0 -2
  111. package/dist/codebase-index/ts-parser.d.ts +1 -1
  112. package/dist/codebase-index/vector-search.d.ts +2 -1
  113. package/dist/codebase-index/wal-maintenance.d.ts +3 -2
  114. package/dist/codebase-index/worker.js +20 -8416
  115. package/dist/codebase-index/writer-admin.d.ts +0 -1
  116. package/dist/codebase-index/writer-search.d.ts +0 -9
  117. package/dist/design.js +2 -299
  118. package/dist/diff.js +4 -256
  119. package/dist/document.js +4 -222
  120. package/dist/e2e.js +5 -476
  121. package/dist/edit.js +14 -10200
  122. package/dist/exec.js +16 -3131
  123. package/dist/fetch.js +8 -398
  124. package/dist/format.js +10 -3750
  125. package/dist/generic-parser-VUXDIAOZ.js +13 -0
  126. package/dist/git.js +4 -380
  127. package/dist/glob.js +6 -280
  128. package/dist/go-parser-JXYKJHRC.js +469 -0
  129. package/dist/grep.js +7 -604
  130. package/dist/index.js +318 -35387
  131. package/dist/install.js +10 -4091
  132. package/dist/json-parser-IBHZ4BZC.js +213 -0
  133. package/dist/json.js +4 -665
  134. package/dist/kanban-contract-actions.d.ts +1 -1
  135. package/dist/kanban-detail-actions.d.ts +2 -1
  136. package/dist/kanban-presence.d.ts +2 -1
  137. package/dist/kanban-serializer.d.ts +0 -8
  138. package/dist/kanban-split-task-handler.d.ts +2 -2
  139. package/dist/kanban-tool-types.d.ts +2 -1
  140. package/dist/kanban.js +3 -2096
  141. package/dist/languages/diagnostics.d.ts +2 -1
  142. package/dist/languages/index.js +23 -4664
  143. package/dist/languages/legacy-bridge.d.ts +3 -2
  144. package/dist/languages/types.d.ts +2 -1
  145. package/dist/legacy-bridge-7M6QLUQN.js +18 -0
  146. package/dist/lint.js +10 -3706
  147. package/dist/logs.js +4 -287
  148. package/dist/memory.js +6 -215
  149. package/dist/mode.js +3 -99
  150. package/dist/outdated.js +4 -5170
  151. package/dist/pack.js +65 -33897
  152. package/dist/parser-dispatch-QH4XPGG6.js +14 -0
  153. package/dist/patch.js +13 -9935
  154. package/dist/plan.js +5 -3286
  155. package/dist/process-registry.js +8 -651
  156. package/dist/ps-slash.js +12 -1439
  157. package/dist/pwsh.d.ts +0 -1
  158. package/dist/py-parser-IS4HKYMF.js +16 -0
  159. package/dist/read.js +13 -9816
  160. package/dist/replace.js +15 -10014
  161. package/dist/rs-parser-XHUUO2KZ.js +81 -0
  162. package/dist/scaffold.js +4 -253
  163. package/dist/search.js +5 -585
  164. package/dist/session-kanban-sync.d.ts +0 -6
  165. package/dist/session-kanban.js +30 -972
  166. package/dist/skill.js +2 -215
  167. package/dist/spawn-background.d.ts +1 -1
  168. package/dist/task.js +5 -3410
  169. package/dist/test.js +10 -3773
  170. package/dist/todo.js +4 -2891
  171. package/dist/tool-help.js +3 -123
  172. package/dist/tool-search.js +3 -79
  173. package/dist/tool-tier.js +68 -33984
  174. package/dist/tool-use.js +2 -93
  175. package/dist/tree-sitter-parser-AIERW3YI.js +15 -0
  176. package/dist/tree.js +4 -288
  177. package/dist/ts-parser-RCTYKSVI.js +232 -0
  178. package/dist/typecheck.js +10 -3753
  179. package/dist/win32.js +6 -60
  180. package/dist/write.js +14 -9882
  181. package/dist/yaml-parser-4SAXV3PL.js +157 -0
  182. package/package.json +5 -5
@@ -0,0 +1,12 @@
1
+ // src/_regex.ts
2
+ import {
3
+ capSubject,
4
+ compileUserRegex,
5
+ MAX_SUBJECT_LEN
6
+ } from "@wrongstack/primitives";
7
+
8
+ export {
9
+ capSubject,
10
+ compileUserRegex
11
+ };
12
+ //# sourceMappingURL=chunk-Q5JPPCZC.js.map
@@ -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