@linghun/tools 0.1.1 → 0.1.3

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 (54) hide show
  1. package/dist/chunk-236QJATI.js +678 -0
  2. package/dist/{chunk-VZWMPUSS.js → chunk-2DIXRINW.js} +1 -1
  3. package/dist/chunk-A6FUAM64.js +11 -0
  4. package/dist/{chunk-3JCXABUK.js → chunk-F7UOI3YR.js} +19 -11
  5. package/dist/chunk-FI4YPDFE.js +38 -0
  6. package/dist/chunk-GI5XI4HN.js +11 -0
  7. package/dist/{chunk-QNZMO55X.js → chunk-J7O24FJB.js} +19 -11
  8. package/dist/chunk-PKMIDA5A.js +6 -0
  9. package/dist/chunk-SPAD4DZS.js +319 -0
  10. package/dist/chunk-WEWXKRKL.js +6 -0
  11. package/dist/chunk-XG6HNLY6.js +166 -0
  12. package/dist/chunk-YCO6C7KF.js +6 -0
  13. package/dist/index.d.ts +105 -3
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1404 -28
  16. package/dist/tools/Bash/BashTool.d.ts +1 -1
  17. package/dist/tools/Bash/BashTool.js +2 -2
  18. package/dist/tools/Bash/artifact-checker.d.ts +33 -0
  19. package/dist/tools/Bash/artifact-checker.d.ts.map +1 -0
  20. package/dist/tools/Bash/artifact-checker.js +6 -0
  21. package/dist/tools/Bash/binary-helper.d.ts +7 -0
  22. package/dist/tools/Bash/binary-helper.d.ts.map +1 -0
  23. package/dist/tools/Bash/binary-helper.js +6 -0
  24. package/dist/tools/Bash/command-semantics.d.ts +11 -0
  25. package/dist/tools/Bash/command-semantics.d.ts.map +1 -0
  26. package/dist/tools/Bash/command-semantics.js +6 -0
  27. package/dist/tools/Bash/prompt.d.ts +1 -1
  28. package/dist/tools/Bash/prompt.d.ts.map +1 -1
  29. package/dist/tools/Bash/prompt.js +1 -1
  30. package/dist/tools/Bash/service-kernel.d.ts +75 -0
  31. package/dist/tools/Bash/service-kernel.d.ts.map +1 -0
  32. package/dist/tools/Bash/service-kernel.js +8 -0
  33. package/dist/tools/Glob/GlobTool.js +2 -2
  34. package/dist/tools/ReadSnippets/ReadSnippetsTool.d.ts +6 -0
  35. package/dist/tools/ReadSnippets/ReadSnippetsTool.d.ts.map +1 -0
  36. package/dist/tools/ReadSnippets/ReadSnippetsTool.js +7 -0
  37. package/dist/tools/ReadSnippets/prompt.d.ts +2 -0
  38. package/dist/tools/ReadSnippets/prompt.d.ts.map +1 -0
  39. package/dist/tools/ReadSnippets/prompt.js +6 -0
  40. package/dist/tools/SourcePack/SourcePackTool.d.ts +6 -0
  41. package/dist/tools/SourcePack/SourcePackTool.d.ts.map +1 -0
  42. package/dist/tools/SourcePack/SourcePackTool.js +7 -0
  43. package/dist/tools/SourcePack/prompt.d.ts +2 -0
  44. package/dist/tools/SourcePack/prompt.d.ts.map +1 -0
  45. package/dist/tools/SourcePack/prompt.js +6 -0
  46. package/dist/tools/Todo/TodoTool.js +2 -2
  47. package/dist/tools/prompts.d.ts.map +1 -1
  48. package/dist/tools/prompts.js +20 -16
  49. package/dist/tools/ui.d.ts.map +1 -1
  50. package/dist/tools/ui.js +20 -16
  51. package/package.json +4 -1
  52. package/dist/chunk-E75PYM7V.js +0 -6
  53. package/dist/{chunk-MFHY7PBM.js → chunk-4567YAXM.js} +3 -3
  54. package/dist/{chunk-DN3KJZRV.js → chunk-Z3H6SZBS.js} +3 -3
@@ -4,37 +4,45 @@ import {
4
4
  import {
5
5
  WriteTool
6
6
  } from "./chunk-I3Z4OKMS.js";
7
- import {
8
- TodoTool
9
- } from "./chunk-MFHY7PBM.js";
10
- import {
11
- WebFetchTool
12
- } from "./chunk-LGZOG7IQ.js";
13
7
  import {
14
8
  MultiEditTool
15
9
  } from "./chunk-VJDLOFCF.js";
16
10
  import {
17
- GlobTool
18
- } from "./chunk-DN3KJZRV.js";
11
+ WebFetchTool
12
+ } from "./chunk-LGZOG7IQ.js";
19
13
  import {
20
14
  ReadTool
21
15
  } from "./chunk-TZ6QK52L.js";
16
+ import {
17
+ ReadSnippetsTool
18
+ } from "./chunk-GI5XI4HN.js";
19
+ import {
20
+ SourcePackTool
21
+ } from "./chunk-A6FUAM64.js";
22
+ import {
23
+ TodoTool
24
+ } from "./chunk-4567YAXM.js";
22
25
  import {
23
26
  EditTool
24
27
  } from "./chunk-WLQ7DGMG.js";
25
28
  import {
26
29
  GrepTool
27
30
  } from "./chunk-AZCQL24J.js";
28
- import {
29
- BashTool
30
- } from "./chunk-VZWMPUSS.js";
31
31
  import {
32
32
  DiffTool
33
33
  } from "./chunk-7ZOADACP.js";
34
+ import {
35
+ GlobTool
36
+ } from "./chunk-Z3H6SZBS.js";
37
+ import {
38
+ BashTool
39
+ } from "./chunk-2DIXRINW.js";
34
40
 
35
41
  // src/tools/prompts.ts
36
42
  var toolPrompts = {
37
43
  Read: ReadTool.prompt,
44
+ ReadSnippets: ReadSnippetsTool.prompt,
45
+ SourcePack: SourcePackTool.prompt,
38
46
  Write: WriteTool.prompt,
39
47
  Edit: EditTool.prompt,
40
48
  MultiEdit: MultiEditTool.prompt,
@@ -0,0 +1,38 @@
1
+ // src/tools/Bash/command-semantics.ts
2
+ var GREP_LIKE = /* @__PURE__ */ new Set(["grep", "rg", "egrep", "fgrep", "ag", "ack"]);
3
+ var DIFF_LIKE = /* @__PURE__ */ new Set(["diff", "colordiff", "icdiff"]);
4
+ var TEST_LIKE = /* @__PURE__ */ new Set(["test", "["]);
5
+ var FIND_LIKE = /* @__PURE__ */ new Set(["find", "fd"]);
6
+ function extractLastCommand(command) {
7
+ const segments = command.split(/\s*(?:\|\|?|&&|;)\s*/);
8
+ const last = (segments[segments.length - 1] ?? "").trimStart();
9
+ const envPrefixStripped = last.replace(/^(?:\w+=\S+\s+)+/, "");
10
+ const first = envPrefixStripped.split(/\s/)[0] ?? "";
11
+ const base = first.replace(/^.*[\\/]/, "");
12
+ return base.toLowerCase();
13
+ }
14
+ function interpretCommandResult(command, exitCode) {
15
+ if (exitCode === 0) return { isError: false };
16
+ const cmd = extractLastCommand(command);
17
+ if (GREP_LIKE.has(cmd)) {
18
+ if (exitCode === 1) return { isError: false, message: "no matches found" };
19
+ return { isError: true, message: `${cmd} error (exit ${exitCode})` };
20
+ }
21
+ if (DIFF_LIKE.has(cmd)) {
22
+ if (exitCode === 1) return { isError: false, message: "files differ" };
23
+ return { isError: true, message: `${cmd} error (exit ${exitCode})` };
24
+ }
25
+ if (TEST_LIKE.has(cmd)) {
26
+ if (exitCode === 1) return { isError: false, message: "condition false" };
27
+ return { isError: true, message: `${cmd} error (exit ${exitCode})` };
28
+ }
29
+ if (FIND_LIKE.has(cmd)) {
30
+ if (exitCode === 1) return { isError: false, message: "partial results" };
31
+ return { isError: true, message: `${cmd} error (exit ${exitCode})` };
32
+ }
33
+ return { isError: true };
34
+ }
35
+
36
+ export {
37
+ interpretCommandResult
38
+ };
@@ -0,0 +1,11 @@
1
+ import {
2
+ prompt
3
+ } from "./chunk-WEWXKRKL.js";
4
+
5
+ // src/tools/ReadSnippets/ReadSnippetsTool.ts
6
+ var userFacingName = "\u8BFB\u53D6\u4EE3\u7801\u7247\u6BB5";
7
+ var ReadSnippetsTool = { name: "ReadSnippets", prompt, userFacingName };
8
+
9
+ export {
10
+ ReadSnippetsTool
11
+ };
@@ -4,37 +4,45 @@ import {
4
4
  import {
5
5
  WriteTool
6
6
  } from "./chunk-I3Z4OKMS.js";
7
- import {
8
- TodoTool
9
- } from "./chunk-MFHY7PBM.js";
10
- import {
11
- WebFetchTool
12
- } from "./chunk-LGZOG7IQ.js";
13
7
  import {
14
8
  MultiEditTool
15
9
  } from "./chunk-VJDLOFCF.js";
16
10
  import {
17
- GlobTool
18
- } from "./chunk-DN3KJZRV.js";
11
+ WebFetchTool
12
+ } from "./chunk-LGZOG7IQ.js";
19
13
  import {
20
14
  ReadTool
21
15
  } from "./chunk-TZ6QK52L.js";
16
+ import {
17
+ ReadSnippetsTool
18
+ } from "./chunk-GI5XI4HN.js";
19
+ import {
20
+ SourcePackTool
21
+ } from "./chunk-A6FUAM64.js";
22
+ import {
23
+ TodoTool
24
+ } from "./chunk-4567YAXM.js";
22
25
  import {
23
26
  EditTool
24
27
  } from "./chunk-WLQ7DGMG.js";
25
28
  import {
26
29
  GrepTool
27
30
  } from "./chunk-AZCQL24J.js";
28
- import {
29
- BashTool
30
- } from "./chunk-VZWMPUSS.js";
31
31
  import {
32
32
  DiffTool
33
33
  } from "./chunk-7ZOADACP.js";
34
+ import {
35
+ GlobTool
36
+ } from "./chunk-Z3H6SZBS.js";
37
+ import {
38
+ BashTool
39
+ } from "./chunk-2DIXRINW.js";
34
40
 
35
41
  // src/tools/ui.ts
36
42
  var toolUserFacingNames = {
37
43
  Read: ReadTool.userFacingName,
44
+ ReadSnippets: ReadSnippetsTool.userFacingName,
45
+ SourcePack: SourcePackTool.userFacingName,
38
46
  Write: WriteTool.userFacingName,
39
47
  Edit: EditTool.userFacingName,
40
48
  MultiEdit: MultiEditTool.userFacingName,
@@ -0,0 +1,6 @@
1
+ // src/tools/Bash/prompt.ts
2
+ var prompt = "Use Bash for verification and project commands after considering risk. Prefer read-only commands unless the user asked for changes and permissions allow them. Prefer python3 over bare python; probe imports before relying on optional Python packages. After starting a server, poll the port or health endpoint and inspect logs before claiming it is ready.";
3
+
4
+ export {
5
+ prompt
6
+ };
@@ -0,0 +1,319 @@
1
+ // src/tools/Bash/artifact-checker.ts
2
+ import { createHash } from "crypto";
3
+ import { access, readFile, stat } from "fs/promises";
4
+ import { constants } from "fs";
5
+ import { isAbsolute, relative, resolve } from "path";
6
+ import { parse, parseFragment } from "parse5";
7
+ async function checkArtifact(input) {
8
+ const diagnostics = [];
9
+ const protectedFiles = [];
10
+ let artifact;
11
+ try {
12
+ const info = await stat(input.target.absolute);
13
+ const isFile = info.isFile();
14
+ const isDirectory = info.isDirectory();
15
+ const content = isFile ? await readFile(input.target.absolute) : Buffer.alloc(0);
16
+ const sha256 = isFile ? hashBuffer(content) : void 0;
17
+ const checks = {
18
+ header: checkHeader(content, input.input.expectHeader ?? input.input.expectMagic),
19
+ json: input.input.json ? checkJson(content) : void 0,
20
+ executable: input.input.executable ? await checkExecutable(input.target.absolute) : void 0,
21
+ text: checkText(content, input.input.text),
22
+ preserve: await checkPreserve(content, input.input.preserve, input.context)
23
+ };
24
+ artifact = {
25
+ path: input.target.relative,
26
+ exists: true,
27
+ isFile,
28
+ isDirectory,
29
+ size: info.size,
30
+ sha256,
31
+ permissions: {
32
+ mode: `0o${(info.mode & 511).toString(8)}`,
33
+ executable: await canExecute(input.target.absolute)
34
+ },
35
+ checks
36
+ };
37
+ addCheckDiagnostics(diagnostics, input.target.relative, checks);
38
+ } catch (error) {
39
+ const message = error instanceof Error ? error.message : String(error);
40
+ addDiagnostic(
41
+ diagnostics,
42
+ "artifact_preservation",
43
+ "blocking",
44
+ `artifact missing or unreadable ${input.target.relative}: ${message}`,
45
+ "Verify the artifact path and preserve expected outputs before retrying.",
46
+ input.target.relative
47
+ );
48
+ artifact = {
49
+ path: input.target.relative,
50
+ exists: false,
51
+ error: message
52
+ };
53
+ }
54
+ for (const protectedPath of input.protectPaths) {
55
+ const result = await checkProtectedPath(protectedPath, input.context);
56
+ protectedFiles.push(result);
57
+ if (result.modified) {
58
+ addDiagnostic(
59
+ diagnostics,
60
+ "artifact_preservation",
61
+ "blocking",
62
+ `protected input modified ${result.path}`,
63
+ "Restore or intentionally account for protected input files before continuing.",
64
+ result.path
65
+ );
66
+ }
67
+ }
68
+ const text = [
69
+ `Artifact check ${input.target.relative}`,
70
+ `exists ${artifact.exists ? "yes" : "no"}`,
71
+ "size" in artifact && typeof artifact.size === "number" ? `size ${artifact.size}` : "",
72
+ "sha256" in artifact && typeof artifact.sha256 === "string" ? `sha256 ${artifact.sha256}` : "",
73
+ formatCheckSummary("header", artifact.checks?.header),
74
+ formatCheckSummary("json", artifact.checks?.json),
75
+ formatCheckSummary("executable", artifact.checks?.executable),
76
+ formatCheckSummary("text", artifact.checks?.text),
77
+ formatCheckSummary("preserve", artifact.checks?.preserve),
78
+ ...protectedFiles.map((item) => `protect ${item.path} ${item.modified ? "modified" : "clean"}`),
79
+ diagnostics.length > 0 ? `diagnostics ${diagnostics.length}` : ""
80
+ ].filter(Boolean).join("\n");
81
+ return {
82
+ text,
83
+ data: {
84
+ exitCode: diagnostics.some((item) => item.severity === "blocking") ? 1 : 0,
85
+ outcome: diagnostics.length > 0 ? "artifact_check_failed" : "artifact_checked",
86
+ artifact,
87
+ protectPaths: protectedFiles,
88
+ ...diagnostics.length > 0 ? { diagnostics } : {}
89
+ }
90
+ };
91
+ }
92
+ function checkHeader(content, expected) {
93
+ if (expected === void 0) return void 0;
94
+ const actual = content.subarray(0, Buffer.byteLength(expected)).toString("utf8");
95
+ return {
96
+ ok: actual === expected,
97
+ expected,
98
+ actual
99
+ };
100
+ }
101
+ function checkJson(content) {
102
+ try {
103
+ JSON.parse(content.toString("utf8"));
104
+ return { ok: true };
105
+ } catch (error) {
106
+ return {
107
+ ok: false,
108
+ error: error instanceof Error ? error.message : String(error)
109
+ };
110
+ }
111
+ }
112
+ function checkText(content, expected) {
113
+ if (!expected) return void 0;
114
+ const actual = content.toString("utf8");
115
+ const contains = normalizeStringList(expected.contains);
116
+ const exact = expected.exact === void 0 ? void 0 : actual === expected.exact;
117
+ const missingContains = contains.filter((item) => !actual.includes(item));
118
+ const lines = new Set(actual.split(/\r?\n/u));
119
+ const missingLines = (expected.lineSet ?? []).filter((line) => !lines.has(line));
120
+ return {
121
+ ok: exact !== false && missingContains.length === 0 && missingLines.length === 0,
122
+ ...expected.exact !== void 0 ? { exact: { ok: exact === true } } : {},
123
+ ...contains.length > 0 ? { contains: { ok: missingContains.length === 0, missing: missingContains } } : {},
124
+ ...expected.lineSet && expected.lineSet.length > 0 ? { lineSet: { ok: missingLines.length === 0, missing: missingLines } } : {}
125
+ };
126
+ }
127
+ async function checkPreserve(content, preserve, context) {
128
+ if (!preserve) return void 0;
129
+ const actual = content.toString("utf8");
130
+ const expected = await readPreserveExpected(preserve, context);
131
+ if (expected.error) return { ok: false, error: expected.error };
132
+ const expectedText = expected.text ?? "";
133
+ const ok = preserve.mode === "rawPreserve" ? actual === expectedText : canonicalizeHtmlForPreserve(actual) === canonicalizeHtmlForPreserve(expectedText);
134
+ return { ok, mode: preserve.mode };
135
+ }
136
+ async function readPreserveExpected(preserve, context) {
137
+ if (preserve.expectedText !== void 0) return { text: preserve.expectedText };
138
+ if (!preserve.expectedPath) return { error: "preserve expectedPath or expectedText is required" };
139
+ try {
140
+ const target = resolve(context.workspaceRoot, preserve.expectedPath);
141
+ const rel = relative(context.workspaceRoot, target);
142
+ if (rel.startsWith("..") || isAbsolute(rel)) {
143
+ return { error: `path outside workspace: ${preserve.expectedPath}` };
144
+ }
145
+ return { text: await readFile(target, "utf8") };
146
+ } catch (error) {
147
+ return { error: error instanceof Error ? error.message : String(error) };
148
+ }
149
+ }
150
+ function normalizeStringList(value) {
151
+ if (value === void 0) return [];
152
+ return Array.isArray(value) ? value : [value];
153
+ }
154
+ function canonicalizeHtmlForPreserve(value) {
155
+ const normalized = value.replace(/\r\n/gu, "\n");
156
+ const document = parse(normalized);
157
+ const fragment = parseFragment(normalized);
158
+ const documentSemantic = canonicalizeHtmlDocument(document);
159
+ const fragmentSemantic = canonicalizeHtmlFragment(fragment);
160
+ return [
161
+ documentSemantic,
162
+ fragmentSemantic === documentSemantic ? "" : fragmentSemantic
163
+ ].join("\n---fragment---\n");
164
+ }
165
+ var RAW_TEXT_PRESERVE_TAGS = /* @__PURE__ */ new Set(["script", "style", "pre", "textarea"]);
166
+ function canonicalizeHtmlNode(node, parentTag = "") {
167
+ const nodeName = node.nodeName ?? "";
168
+ if (nodeName === "#text") {
169
+ const text = RAW_TEXT_PRESERVE_TAGS.has(parentTag) ? node.value ?? "" : normalizeHtmlText(node.value ?? "");
170
+ return text ? `text:${text}` : "";
171
+ }
172
+ if (nodeName === "#comment") return `comment:${node.data ?? ""}`;
173
+ if (nodeName === "#documentType") {
174
+ return `doctype:${node.name ?? "html"}:${node.publicId ?? ""}:${node.systemId ?? ""}`;
175
+ }
176
+ const tag = (node.tagName ?? nodeName).toLowerCase();
177
+ const attrs = (node.attrs ?? []).map((attr) => `${attr.name.toLowerCase()}=${JSON.stringify(normalizeHtmlText(attr.value))}`).sort().join(",");
178
+ const children = (node.childNodes ?? []).map((child) => canonicalizeHtmlNode(child, tag)).filter(Boolean).join("");
179
+ return `<${tag}${attrs ? ` ${attrs}` : ""}>${children}</${tag}>`;
180
+ }
181
+ function canonicalizeHtmlDocument(document) {
182
+ const html = findChild(document, "html");
183
+ if (!html) return canonicalizeHtmlChildren(document.childNodes ?? []);
184
+ const head = findChild(html, "head");
185
+ const body = findChild(html, "body");
186
+ return [
187
+ canonicalizeHtmlChildren(head?.childNodes ?? []),
188
+ canonicalizeHtmlChildren(body?.childNodes ?? [])
189
+ ].filter(Boolean).join("");
190
+ }
191
+ function canonicalizeHtmlFragment(fragment) {
192
+ const children = (fragment.childNodes ?? []).filter((child) => child.nodeName !== "#documentType");
193
+ const html = children.find((child) => (child.tagName ?? child.nodeName ?? "").toLowerCase() === "html");
194
+ if (!html) return canonicalizeHtmlChildren(children);
195
+ const head = findChild(html, "head");
196
+ const body = findChild(html, "body");
197
+ return [
198
+ canonicalizeHtmlChildren(head?.childNodes ?? []),
199
+ canonicalizeHtmlChildren(body?.childNodes ?? [])
200
+ ].filter(Boolean).join("");
201
+ }
202
+ function canonicalizeHtmlChildren(children) {
203
+ return children.map((child) => canonicalizeHtmlNode(child)).filter(Boolean).join("");
204
+ }
205
+ function findChild(node, name) {
206
+ return (node.childNodes ?? []).find((child) => (child.tagName ?? child.nodeName ?? "").toLowerCase() === name);
207
+ }
208
+ function normalizeHtmlText(value) {
209
+ return value.replace(/\s+/gu, " ").trim();
210
+ }
211
+ async function checkExecutable(path) {
212
+ return { ok: await canExecute(path) };
213
+ }
214
+ async function canExecute(path) {
215
+ try {
216
+ await access(path, constants.X_OK);
217
+ return true;
218
+ } catch {
219
+ return false;
220
+ }
221
+ }
222
+ async function checkProtectedPath(path, context) {
223
+ try {
224
+ const content = await readFile(path.absolute);
225
+ const sha256 = hashBuffer(content);
226
+ const snapshot = context.readSnapshots?.[path.relative];
227
+ const changed = context.changedFiles.includes(path.relative);
228
+ const modified = snapshot ? snapshot.hash !== hashText(content.toString("utf8")) : changed;
229
+ return {
230
+ path: path.relative,
231
+ exists: true,
232
+ size: content.length,
233
+ sha256,
234
+ modified,
235
+ baseline: snapshot ? "readSnapshot" : changed ? "changedFiles" : "none"
236
+ };
237
+ } catch (error) {
238
+ return {
239
+ path: path.relative,
240
+ exists: false,
241
+ modified: true,
242
+ error: error instanceof Error ? error.message : String(error),
243
+ baseline: "missing"
244
+ };
245
+ }
246
+ }
247
+ function addCheckDiagnostics(diagnostics, path, checks) {
248
+ if (checks.header && !checks.header.ok) {
249
+ addDiagnostic(
250
+ diagnostics,
251
+ "artifact_preservation",
252
+ "blocking",
253
+ `wrong header ${path}: expected ${checks.header.expected}`,
254
+ "Preserve the expected artifact format and regenerate only the output if needed.",
255
+ path
256
+ );
257
+ }
258
+ if (checks.json && !checks.json.ok) {
259
+ addDiagnostic(
260
+ diagnostics,
261
+ "artifact_preservation",
262
+ "blocking",
263
+ `output format mismatch ${path}: invalid JSON`,
264
+ "Fix the artifact format before verification.",
265
+ path
266
+ );
267
+ }
268
+ if (checks.executable && !checks.executable.ok) {
269
+ addDiagnostic(
270
+ diagnostics,
271
+ "artifact_preservation",
272
+ "blocking",
273
+ `permission denied ${path}: executable bit is not set`,
274
+ "Set executable permission when the artifact is expected to run.",
275
+ path
276
+ );
277
+ }
278
+ if (checks.text && !checks.text.ok) {
279
+ const missing = [
280
+ ...checks.text.contains?.missing ?? [],
281
+ ...checks.text.lineSet?.missing ?? []
282
+ ];
283
+ addDiagnostic(
284
+ diagnostics,
285
+ "artifact_preservation",
286
+ "blocking",
287
+ `text expectation mismatch ${path}${missing.length > 0 ? `: missing ${missing.join(", ")}` : ""}`,
288
+ "Update the artifact so the explicitly requested text expectations pass.",
289
+ path
290
+ );
291
+ }
292
+ if (checks.preserve && !checks.preserve.ok) {
293
+ addDiagnostic(
294
+ diagnostics,
295
+ "artifact_preservation",
296
+ "blocking",
297
+ `preservation mismatch ${path}${checks.preserve.mode ? ` mode=${checks.preserve.mode}` : ""}`,
298
+ "Preserve the explicitly requested source content or confirm intentional changes before continuing.",
299
+ path
300
+ );
301
+ }
302
+ }
303
+ function addDiagnostic(diagnostics, type, severity, evidence, suggestion, path) {
304
+ diagnostics.push({ type, severity, evidence, suggestion, ...path ? { path } : {} });
305
+ }
306
+ function formatCheckSummary(name, check) {
307
+ if (!check) return "";
308
+ return `${name} ${check.ok ? "ok" : "failed"}`;
309
+ }
310
+ function hashBuffer(content) {
311
+ return createHash("sha256").update(content).digest("hex");
312
+ }
313
+ function hashText(content) {
314
+ return createHash("sha256").update(content, "utf8").digest("hex");
315
+ }
316
+
317
+ export {
318
+ checkArtifact
319
+ };
@@ -0,0 +1,6 @@
1
+ // src/tools/ReadSnippets/prompt.ts
2
+ var prompt = "Use ReadSnippets to read multiple known file line ranges in one call. Prefer it after search, index, or codebase-memory hits return path/range candidates.";
3
+
4
+ export {
5
+ prompt
6
+ };
@@ -0,0 +1,166 @@
1
+ // src/tools/Bash/binary-helper.ts
2
+ import { createHash } from "crypto";
3
+ import { createReadStream } from "fs";
4
+ import { open, stat } from "fs/promises";
5
+ var DEFAULT_PREVIEW_BYTES = 64;
6
+ var MAX_PREVIEW_BYTES = 512;
7
+ var ELF64_HEADER_BYTES = 64;
8
+ var SQLITE_HEADER = "SQLite format 3\0";
9
+ async function inspectBinaryFile(input, targetPath) {
10
+ const previewBytes = Math.min(input.previewBytes ?? DEFAULT_PREVIEW_BYTES, MAX_PREVIEW_BYTES);
11
+ try {
12
+ const fileStat = await stat(targetPath);
13
+ const header = await readFileHeader(
14
+ targetPath,
15
+ Math.min(fileStat.size, Math.max(previewBytes, ELF64_HEADER_BYTES, SQLITE_HEADER.length))
16
+ );
17
+ const preview = header.subarray(0, previewBytes);
18
+ const magic = detectMagic(header);
19
+ const elf = magic.type === "elf" ? parseElf(header) : void 0;
20
+ const sha256 = await hashFileSha256(targetPath);
21
+ const text = [
22
+ `Binary inspect ${input.path}`,
23
+ `size ${fileStat.size}`,
24
+ `sha256 ${sha256}`,
25
+ `magic ${magic.label}`,
26
+ elf ? formatElfSummary(elf) : "",
27
+ `hex ${formatHex(preview)}`,
28
+ `ascii ${formatAscii(preview)}`
29
+ ].filter(Boolean).join("\n");
30
+ return {
31
+ text,
32
+ data: {
33
+ exitCode: 0,
34
+ outcome: "binary_inspected",
35
+ binary: {
36
+ path: targetPath,
37
+ size: fileStat.size,
38
+ sha256,
39
+ previewBytes: preview.length,
40
+ hexPreview: formatHex(preview),
41
+ asciiPreview: formatAscii(preview),
42
+ magic,
43
+ ...elf ? { elf } : {}
44
+ }
45
+ }
46
+ };
47
+ } catch (error) {
48
+ const message = error instanceof Error ? error.message : String(error);
49
+ const diagnostic = {
50
+ type: "artifact_preservation",
51
+ severity: "blocking",
52
+ evidence: `binary inspect failed for ${input.path}: ${message}`,
53
+ suggestion: "Verify the file path and preserve expected input artifacts before retrying.",
54
+ path: input.path
55
+ };
56
+ return {
57
+ text: [
58
+ `Binary inspect failed ${input.path}`,
59
+ diagnostic.evidence
60
+ ].join("\n"),
61
+ data: {
62
+ exitCode: 1,
63
+ outcome: "binary_inspect_failed",
64
+ diagnostics: [diagnostic]
65
+ }
66
+ };
67
+ }
68
+ }
69
+ function detectMagic(content) {
70
+ if (content.length >= 4 && content[0] === 127 && content[1] === 69 && content[2] === 76 && content[3] === 70) {
71
+ return { type: "elf", label: "ELF executable/shared object" };
72
+ }
73
+ if (content.length >= 2 && content[0] === 31 && content[1] === 139) {
74
+ return { type: "gzip", label: "gzip compressed data" };
75
+ }
76
+ if (content.length >= 4 && content[0] === 80 && content[1] === 75 && content[2] === 3 && content[3] === 4) {
77
+ return { type: "zip", label: "zip archive" };
78
+ }
79
+ if (content.length >= 6 && content[0] === 55 && content[1] === 122 && content[2] === 188 && content[3] === 175 && content[4] === 39 && content[5] === 28) {
80
+ return { type: "7z", label: "7z archive" };
81
+ }
82
+ if (content.length >= 8 && content[0] === 137 && content[1] === 80 && content[2] === 78 && content[3] === 71 && content[4] === 13 && content[5] === 10 && content[6] === 26 && content[7] === 10) {
83
+ return { type: "png", label: "PNG image" };
84
+ }
85
+ if (content.length >= 3 && content[0] === 255 && content[1] === 216 && content[2] === 255) {
86
+ return { type: "jpeg", label: "JPEG image" };
87
+ }
88
+ if (content.length >= 4 && content.subarray(0, 4).toString("ascii") === "%PDF") {
89
+ return { type: "pdf", label: "PDF document" };
90
+ }
91
+ if (content.length >= SQLITE_HEADER.length && content.subarray(0, SQLITE_HEADER.length).toString("binary") === SQLITE_HEADER) {
92
+ return { type: "sqlite", label: "SQLite database" };
93
+ }
94
+ return { type: "unknown", label: "unknown binary/text" };
95
+ }
96
+ function parseElf(content) {
97
+ const elfClass = content[4] === 1 ? "32" : content[4] === 2 ? "64" : "unknown";
98
+ const requiredBytes = elfClass === "64" ? 64 : 52;
99
+ if (content.length < requiredBytes) {
100
+ return { error: "ELF header is truncated" };
101
+ }
102
+ const endian = content[5] === 1 ? "little" : content[5] === 2 ? "big" : "unknown";
103
+ const littleEndian = endian !== "big";
104
+ const readUInt16 = (offset) => littleEndian ? content.readUInt16LE(offset) : content.readUInt16BE(offset);
105
+ const readUInt32 = (offset) => littleEndian ? content.readUInt32LE(offset) : content.readUInt32BE(offset);
106
+ const readUInt64 = (offset) => {
107
+ const value = littleEndian ? content.readBigUInt64LE(offset) : content.readBigUInt64BE(offset);
108
+ return `0x${value.toString(16)}`;
109
+ };
110
+ const is64 = elfClass === "64";
111
+ return {
112
+ class: elfClass,
113
+ endianness: endian,
114
+ type: elfTypeName(readUInt16(16)),
115
+ machine: elfMachineName(readUInt16(18)),
116
+ entry: is64 ? readUInt64(24) : `0x${readUInt32(24).toString(16)}`,
117
+ programHeaderOffset: is64 ? readUInt64(32) : `0x${readUInt32(28).toString(16)}`,
118
+ sectionHeaderOffset: is64 ? readUInt64(40) : `0x${readUInt32(32).toString(16)}`,
119
+ headerSize: readUInt16(is64 ? 52 : 40),
120
+ programHeaderEntrySize: readUInt16(is64 ? 54 : 42),
121
+ programHeaderCount: readUInt16(is64 ? 56 : 44),
122
+ sectionHeaderEntrySize: readUInt16(is64 ? 58 : 46),
123
+ sectionHeaderCount: readUInt16(is64 ? 60 : 48),
124
+ sectionNameStringTableIndex: readUInt16(is64 ? 62 : 50)
125
+ };
126
+ }
127
+ function formatElfSummary(elf) {
128
+ if ("error" in elf) {
129
+ return `elf ${elf.error}`;
130
+ }
131
+ return `elf ${elf.class} ${elf.endianness} type=${elf.type} machine=${elf.machine} entry=${elf.entry}`;
132
+ }
133
+ async function readFileHeader(path, bytes) {
134
+ if (bytes <= 0) return Buffer.alloc(0);
135
+ const handle = await open(path, "r");
136
+ try {
137
+ const buffer = Buffer.alloc(bytes);
138
+ const result = await handle.read(buffer, 0, bytes, 0);
139
+ return buffer.subarray(0, result.bytesRead);
140
+ } finally {
141
+ await handle.close();
142
+ }
143
+ }
144
+ async function hashFileSha256(path) {
145
+ const hash = createHash("sha256");
146
+ for await (const chunk of createReadStream(path)) {
147
+ hash.update(chunk);
148
+ }
149
+ return hash.digest("hex");
150
+ }
151
+ function elfTypeName(value) {
152
+ return { 0: "none", 1: "relocatable", 2: "executable", 3: "shared", 4: "core" }[value] ?? `unknown(${value})`;
153
+ }
154
+ function elfMachineName(value) {
155
+ return { 3: "x86", 40: "arm", 62: "x86-64", 183: "aarch64", 243: "riscv" }[value] ?? `unknown(${value})`;
156
+ }
157
+ function formatHex(buffer) {
158
+ return [...buffer].map((byte) => byte.toString(16).padStart(2, "0")).join(" ");
159
+ }
160
+ function formatAscii(buffer) {
161
+ return [...buffer].map((byte) => byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : ".").join("");
162
+ }
163
+
164
+ export {
165
+ inspectBinaryFile
166
+ };
@@ -0,0 +1,6 @@
1
+ // src/tools/SourcePack/prompt.ts
2
+ var prompt = "Use SourcePack to locate a small evidence pack of candidate source snippets for a query. If index/codebase-memory already returned paths or ranges, call ReadSnippets on those candidates first.";
3
+
4
+ export {
5
+ prompt
6
+ };