@linghun/tools 0.1.3 → 0.1.5

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/index.js CHANGED
@@ -1,71 +1,61 @@
1
- import {
2
- formatFetchOutput,
3
- webFetch
4
- } from "./chunk-4UHQWBFA.js";
5
1
  import {
6
2
  applyDomainFilter,
7
3
  bingSearch,
8
4
  formatSearchOutput
9
5
  } from "./chunk-EPIB4FSE.js";
10
6
  import {
11
- runBashServiceLifecycleAction,
12
- startBashService
13
- } from "./chunk-236QJATI.js";
7
+ formatFetchOutput,
8
+ webFetch
9
+ } from "./chunk-4UHQWBFA.js";
14
10
  import {
15
11
  createTool
16
12
  } from "./chunk-MMJX44V6.js";
17
13
  import {
18
14
  toolPrompts
19
- } from "./chunk-F7UOI3YR.js";
15
+ } from "./chunk-XGK6YYZO.js";
20
16
  import {
21
17
  toolUserFacingNames
22
- } from "./chunk-J7O24FJB.js";
18
+ } from "./chunk-PNXZO2UE.js";
23
19
  import "./chunk-4EZHIDZ6.js";
20
+ import "./chunk-YR4HWOR2.js";
21
+ import "./chunk-VAFW24RN.js";
22
+ import "./chunk-LGZOG7IQ.js";
23
+ import "./chunk-MFHY7PBM.js";
24
+ import "./chunk-SZFZPQGL.js";
25
+ import "./chunk-XZWSO5CM.js";
24
26
  import "./chunk-I3Z4OKMS.js";
25
27
  import "./chunk-PS2ZOFZ6.js";
26
28
  import "./chunk-NPKLGVDQ.js";
27
- import "./chunk-VJDLOFCF.js";
28
- import "./chunk-3RP665TH.js";
29
- import "./chunk-IOC3BSPT.js";
30
- import "./chunk-LGZOG7IQ.js";
31
- import "./chunk-ZYAEUABC.js";
32
- import "./chunk-SWK6XRZQ.js";
33
- import "./chunk-YR4HWOR2.js";
34
- import "./chunk-VAFW24RN.js";
35
29
  import "./chunk-TZ6QK52L.js";
36
30
  import "./chunk-K3YDFDLP.js";
37
31
  import "./chunk-GI5XI4HN.js";
38
32
  import "./chunk-WEWXKRKL.js";
39
33
  import "./chunk-A6FUAM64.js";
40
34
  import "./chunk-YCO6C7KF.js";
41
- import "./chunk-4567YAXM.js";
42
- import "./chunk-XZWSO5CM.js";
43
- import "./chunk-SZFZPQGL.js";
44
- import "./chunk-WLQ7DGMG.js";
45
- import "./chunk-SQP6SJRP.js";
46
- import "./chunk-BRG64AEC.js";
35
+ import "./chunk-ZYAEUABC.js";
36
+ import "./chunk-SWK6XRZQ.js";
47
37
  import "./chunk-AZCQL24J.js";
48
38
  import "./chunk-HUIZOB4A.js";
49
39
  import "./chunk-MK3YP2VR.js";
40
+ import "./chunk-VJDLOFCF.js";
41
+ import "./chunk-3RP665TH.js";
42
+ import "./chunk-IOC3BSPT.js";
50
43
  import "./chunk-HNIVNZIS.js";
44
+ import "./chunk-WLQ7DGMG.js";
45
+ import "./chunk-SQP6SJRP.js";
46
+ import "./chunk-BRG64AEC.js";
51
47
  import "./chunk-7ZOADACP.js";
52
48
  import "./chunk-H5OOD2OS.js";
53
49
  import "./chunk-IQP3XE32.js";
54
50
  import "./chunk-Z3H6SZBS.js";
55
51
  import "./chunk-ITBZHMZQ.js";
56
52
  import "./chunk-2U4BZXAI.js";
57
- import {
58
- checkArtifact
59
- } from "./chunk-SPAD4DZS.js";
60
53
  import "./chunk-2DIXRINW.js";
61
- import "./chunk-PKMIDA5A.js";
62
- import "./chunk-GAWMEMXB.js";
63
- import {
64
- inspectBinaryFile
65
- } from "./chunk-XG6HNLY6.js";
66
54
  import {
67
55
  interpretCommandResult
68
56
  } from "./chunk-FI4YPDFE.js";
57
+ import "./chunk-PKMIDA5A.js";
58
+ import "./chunk-GAWMEMXB.js";
69
59
 
70
60
  // src/index.ts
71
61
  import { spawn } from "child_process";
@@ -99,8 +89,7 @@ function createToolContext(workspaceRoot = process.cwd(), options = {}) {
99
89
  readSnapshots: {},
100
90
  sourcePackCandidates: options.sourcePackCandidates,
101
91
  patchSummaries: {},
102
- recentDiagnostics: [],
103
- services: []
92
+ recentDiagnostics: []
104
93
  };
105
94
  }
106
95
  async function runTool(name, input, context) {
@@ -308,7 +297,7 @@ var toolDefinitions = {
308
297
  call: bashTool,
309
298
  prompt: () => toolPrompts.Bash,
310
299
  userFacingName: () => toolUserFacingNames.Bash,
311
- getToolUseSummary: (input) => input.artifact ? `Bash artifact ${input.artifact.path}` : input.binary ? `Bash binary ${input.binary.path}` : `Bash ${input.command}`,
300
+ getToolUseSummary: (input) => `Bash ${input.command}`,
312
301
  getActivityDescription: () => "Running command"
313
302
  }),
314
303
  Todo: defineTool({
@@ -588,120 +577,17 @@ function validateGlobInput(input) {
588
577
  function validateBashInput(input) {
589
578
  const record = validateRecord(input, "Bash");
590
579
  const command = readOptionalString(record, "command", "Bash");
591
- const binary = validateOptionalBashBinaryInspect(record.binary);
592
- const artifact = validateOptionalBashArtifactCheck(record.artifact);
593
- const service = validateOptionalBashServiceInput(record.service);
594
- const serviceAction = isBashServiceLifecycleAction(service);
595
- if (command === void 0 && binary === void 0 && artifact === void 0 && !serviceAction) {
596
- throw new Error("Bash.command\u3001Bash.binary\u3001Bash.artifact \u6216 Bash.service action \u5FC5\u987B\u63D0\u4F9B\u4E00\u4E2A\u3002");
597
- }
598
- const modeCount = [
599
- command !== void 0,
600
- binary !== void 0,
601
- artifact !== void 0,
602
- serviceAction
603
- ].filter(Boolean).length;
604
- if (modeCount > 1 || (binary !== void 0 || artifact !== void 0) && service !== void 0) {
605
- throw new Error("Bash.command\u3001Bash.binary\u3001Bash.artifact \u548C Bash.service \u4E0D\u80FD\u6DF7\u7528\u3002");
606
- }
607
- if (service !== void 0 && !serviceAction && command === void 0) {
608
- throw new Error("Bash.service readiness \u5FC5\u987B\u548C Bash.command \u4E00\u8D77\u4F7F\u7528\u3002");
580
+ if (command === void 0) {
581
+ throw new Error("Bash.command \u5FC5\u987B\u63D0\u4F9B\u3002");
609
582
  }
610
583
  const runInBackground = record.runInBackground === true || record.run_in_background === true;
611
584
  return {
612
585
  command,
586
+ description: readOptionalString(record, "description", "Bash"),
613
587
  timeoutMs: readOptionalPositiveInteger(record, "timeoutMs", "Bash"),
614
- service,
615
- binary,
616
- artifact,
617
588
  ...runInBackground ? { runInBackground: true } : {}
618
589
  };
619
590
  }
620
- function validateOptionalBashBinaryInspect(input) {
621
- if (input === void 0) return void 0;
622
- if (isEmptyOptionalObject(input)) return void 0;
623
- const record = validateRecord(input, "Bash");
624
- return {
625
- path: readString(record, "path", "Bash"),
626
- previewBytes: readOptionalPositiveInteger(record, "previewBytes", "Bash")
627
- };
628
- }
629
- function validateOptionalBashArtifactCheck(input) {
630
- if (input === void 0) return void 0;
631
- if (isEmptyOptionalObject(input)) return void 0;
632
- const record = validateRecord(input, "Bash");
633
- return {
634
- path: readString(record, "path", "Bash"),
635
- expectHeader: readOptionalString(record, "expectHeader", "Bash"),
636
- expectMagic: readOptionalString(record, "expectMagic", "Bash"),
637
- json: readOptionalBoolean(record, "json", "Bash"),
638
- executable: readOptionalBoolean(record, "executable", "Bash"),
639
- protectPaths: readOptionalStringArray(record, "protectPaths", "Bash"),
640
- text: validateOptionalBashArtifactTextCheck(record.text),
641
- preserve: validateOptionalBashArtifactPreserveCheck(record.preserve)
642
- };
643
- }
644
- function validateOptionalBashServiceInput(input) {
645
- if (input === void 0) return void 0;
646
- if (isEmptyOptionalObject(input)) return void 0;
647
- const record = validateRecord(input, "Bash");
648
- const action = readOptionalString(record, "action", "Bash");
649
- if (action !== void 0) {
650
- if (action === "fetch") {
651
- const url = readString(record, "url", "Bash");
652
- validateHttpUrl(url);
653
- return {
654
- action,
655
- url,
656
- expectStatus: readOptionalPositiveInteger(record, "expectStatus", "Bash"),
657
- bodyContains: readOptionalStringOrStringArray(record, "bodyContains", "Bash"),
658
- timeoutMs: readOptionalPositiveInteger(record, "timeoutMs", "Bash"),
659
- retry: readOptionalPositiveInteger(record, "retry", "Bash"),
660
- intervalMs: readOptionalPositiveInteger(record, "intervalMs", "Bash")
661
- };
662
- }
663
- if (action === "status" || action === "probe" || action === "logs" || action === "stop") {
664
- return {
665
- action,
666
- serviceId: readString(record, "serviceId", "Bash"),
667
- ...action === "logs" ? { tailBytes: readOptionalPositiveInteger(record, "tailBytes", "Bash") } : {}
668
- };
669
- }
670
- throw new Error("Bash.service.action \u5FC5\u987B\u662F status/probe/logs/stop/fetch\u3002");
671
- }
672
- const type = readString(record, "type", "Bash");
673
- const timeoutMs = readOptionalPositiveInteger(record, "timeoutMs", "Bash");
674
- const intervalMs = readOptionalPositiveInteger(record, "intervalMs", "Bash");
675
- if (type === "tcp") {
676
- const rawPort = record.port;
677
- if (!Number.isInteger(rawPort) || rawPort <= 0) {
678
- throw new Error("Bash.service.port \u5FC5\u987B\u662F\u6B63\u6574\u6570\u3002");
679
- }
680
- const port = rawPort;
681
- if (port > 65535) {
682
- throw new Error("Bash.service.port \u5FC5\u987B\u5728 1-65535 \u8303\u56F4\u5185\u3002");
683
- }
684
- return {
685
- type,
686
- port,
687
- host: readOptionalString(record, "host", "Bash"),
688
- timeoutMs,
689
- intervalMs
690
- };
691
- }
692
- if (type === "http") {
693
- const url = readString(record, "url", "Bash");
694
- validateHttpUrl(url);
695
- return { type, url, timeoutMs, intervalMs };
696
- }
697
- throw new Error("Bash.service.type \u5FC5\u987B\u662F tcp \u6216 http\u3002");
698
- }
699
- function isEmptyOptionalObject(input) {
700
- return input === null || typeof input === "object" && !Array.isArray(input) && Object.keys(input).length === 0;
701
- }
702
- function isBashServiceLifecycleAction(input) {
703
- return Boolean(input && "action" in input);
704
- }
705
591
  function validateTodoInput(input) {
706
592
  const record = validateRecord(input, "Todo");
707
593
  const action = readString(record, "action", "Todo");
@@ -759,53 +645,6 @@ function readOptionalStringArray(record, key, toolName) {
759
645
  }
760
646
  return value;
761
647
  }
762
- function readOptionalBoolean(record, key, toolName) {
763
- const value = record[key];
764
- if (value === void 0) return void 0;
765
- if (typeof value !== "boolean") {
766
- throw new Error(`${toolName}.${key} \u5FC5\u987B\u662F boolean \u6216 undefined\u3002`);
767
- }
768
- return value;
769
- }
770
- function readOptionalStringOrStringArray(record, key, toolName) {
771
- const value = record[key];
772
- if (value === void 0) return void 0;
773
- if (typeof value === "string") return value;
774
- if (Array.isArray(value) && value.every((item) => typeof item === "string")) return value;
775
- throw new Error(`${toolName}.${key} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u3001\u5B57\u7B26\u4E32\u6570\u7EC4\u6216 undefined\u3002`);
776
- }
777
- function validateOptionalBashArtifactTextCheck(input) {
778
- if (input === void 0) return void 0;
779
- const record = validateRecord(input, "Bash");
780
- return {
781
- exact: readOptionalString(record, "exact", "Bash"),
782
- contains: readOptionalStringOrStringArray(record, "contains", "Bash"),
783
- lineSet: readOptionalStringArray(record, "lineSet", "Bash")
784
- };
785
- }
786
- function validateOptionalBashArtifactPreserveCheck(input) {
787
- if (input === void 0) return void 0;
788
- const record = validateRecord(input, "Bash");
789
- const mode = readString(record, "mode", "Bash");
790
- if (mode !== "rawPreserve" && mode !== "compareNormalizedHtml") {
791
- throw new Error("Bash.artifact.preserve.mode \u5FC5\u987B\u662F rawPreserve \u6216 compareNormalizedHtml\u3002");
792
- }
793
- return {
794
- mode,
795
- expectedPath: readOptionalString(record, "expectedPath", "Bash"),
796
- expectedText: readOptionalString(record, "expectedText", "Bash")
797
- };
798
- }
799
- function validateHttpUrl(url) {
800
- try {
801
- const parsed = new URL(url);
802
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
803
- throw new Error("protocol");
804
- }
805
- } catch {
806
- throw new Error("Bash.service.url \u5FC5\u987B\u662F http/https URL\u3002");
807
- }
808
- }
809
648
  async function readTool(input, context) {
810
649
  const filePath = resolveWorkspacePath(context.workspaceRoot, input.path);
811
650
  const content = await readFile(filePath, "utf8");
@@ -1182,43 +1021,14 @@ function sanitizeSecrets(text) {
1182
1021
  return sanitized;
1183
1022
  }
1184
1023
  async function bashTool(input, context) {
1185
- if (input.artifact) {
1186
- return checkArtifact({
1187
- input: input.artifact,
1188
- target: resolveArtifactPath(context, input.artifact.path),
1189
- protectPaths: (input.artifact.protectPaths ?? []).map((path) => resolveArtifactPath(context, path)),
1190
- context
1191
- });
1192
- }
1193
- if (input.binary) {
1194
- return inspectBinaryFile(input.binary, resolveWorkspacePath(context.workspaceRoot, input.binary.path));
1195
- }
1196
1024
  if (!input.command) {
1197
- if (isBashServiceLifecycleAction(input.service)) {
1198
- return runBashServiceLifecycleAction(input.service, context);
1199
- }
1200
- throw new Error("Bash.command\u3001Bash.binary \u6216 Bash.service action \u5FC5\u987B\u63D0\u4F9B\u4E00\u4E2A\u3002");
1025
+ throw new Error("Bash.command \u5FC5\u987B\u63D0\u4F9B\u3002");
1201
1026
  }
1202
1027
  const logRoot = context.logRoot ?? join(context.workspaceRoot, ".linghun", "logs", "tools");
1203
1028
  await mkdir(logRoot, { recursive: true });
1204
1029
  const fullOutputPath = join(logRoot, `bash-${Date.now()}-${randomUUID()}.log`);
1205
1030
  const timeoutMs = input.timeoutMs ?? BASH_TIMEOUT_MS;
1206
1031
  const adapted = adaptShellCommand(input.command);
1207
- if (input.service && !isBashServiceLifecycleAction(input.service)) {
1208
- const readiness = withDefaultServiceReadiness(input.service, context);
1209
- return startBashService({
1210
- command: adapted.command,
1211
- logCommand: adapted.logCommand ?? adapted.command,
1212
- cwd: context.workspaceRoot,
1213
- fullOutputPath,
1214
- readiness,
1215
- context,
1216
- abortSignal: context.abortSignal,
1217
- onProgress: (stream, text) => void context.onProgress?.({ toolName: "Bash", stream, text }),
1218
- trackChildProcess: context.trackChildProcess,
1219
- sanitizeText: sanitizeSecrets
1220
- });
1221
- }
1222
1032
  if (input.runInBackground === true) {
1223
1033
  const taskId = randomUUID();
1224
1034
  void runBackgroundBash({
@@ -1787,14 +1597,6 @@ function isHeadlessBenchContext(context) {
1787
1597
  const record = context;
1788
1598
  return record.headlessBench?.enabled === true || record.isHeadlessBench === true;
1789
1599
  }
1790
- function withDefaultServiceReadiness(readiness, context) {
1791
- if (readiness.timeoutMs !== void 0) return readiness;
1792
- const hasReadinessRisk = (context.recentDiagnostics ?? []).some(
1793
- (diagnostic) => diagnostic.type === "service_readiness" || diagnostic.type === "timeout"
1794
- );
1795
- const timeoutMs = hasReadinessRisk ? 15e3 : isHeadlessBenchContext(context) ? 1e4 : 5e3;
1796
- return { ...readiness, timeoutMs };
1797
- }
1798
1600
  function createBashOutcomeDiagnostics(outcome) {
1799
1601
  if (outcome === "timeout") {
1800
1602
  return [
@@ -1817,13 +1619,20 @@ function adaptShellCommand(command) {
1817
1619
  }
1818
1620
  function adaptShellCommandForPlatform(command, platform) {
1819
1621
  if (platform !== "win32") return { command, adapter: "native" };
1622
+ if (isExplicitPowerShellCommand(command)) return { command, adapter: "native" };
1623
+ const fileWriteBlock = blockWindowsShellFileWriteCommand(command);
1624
+ if (fileWriteBlock) return fileWriteBlock;
1820
1625
  const heredoc = convertNodeHereDocForPowerShell(command);
1821
1626
  if (heredoc) return heredoc;
1627
+ const nativePowerShell = convertNativePowerShellCommand(command);
1628
+ if (nativePowerShell) return nativePowerShell;
1822
1629
  if (looksLikeUnsupportedUnixMultiline(command)) {
1823
1630
  return createBlockedPowerShellAdapter(
1824
1631
  "Unsupported multi-line Unix shell syntax on Windows PowerShell; use PowerShell-safe commands or Node one-liners."
1825
1632
  );
1826
1633
  }
1634
+ const unsupportedPosix = blockUnsupportedPosixShellSyntax(command);
1635
+ if (unsupportedPosix) return unsupportedPosix;
1827
1636
  const converted = convertUnixPipelineForPowerShell(command);
1828
1637
  if (converted) return { command: converted, adapter: "powershell-adapted" };
1829
1638
  const readOnlyCommand = convertUnixReadOnlyCommandForPowerShell(command);
@@ -1836,6 +1645,73 @@ function adaptShellCommandForPlatform(command, platform) {
1836
1645
  if (unsupportedReadOnlyCommand) return unsupportedReadOnlyCommand;
1837
1646
  return { command, adapter: "native" };
1838
1647
  }
1648
+ function isExplicitPowerShellCommand(command) {
1649
+ return /^(?:powershell(?:\.exe)?|pwsh(?:\.exe)?)\b/iu.test(command.trim());
1650
+ }
1651
+ var WINDOWS_SHELL_WRITE_BLOCK_MESSAGE = "Linghun Bash does not support shell apply_patch or heredoc file writes on Windows; use Edit/MultiEdit/Write structured tools instead.";
1652
+ function blockWindowsShellFileWriteCommand(command) {
1653
+ const normalized = command.replace(/\r\n/gu, "\n").trim();
1654
+ if (!normalized) return void 0;
1655
+ if (/\bapply_patch\b/iu.test(normalized) && (/(?:^|\n|;)\s*apply_patch\s*<</iu.test(normalized) || /@['"][\s\S]*?['"]@\s*\|\s*apply_patch\b/iu.test(normalized) || /\$[A-Za-z_][\w]*\s*=\s*@['"][\s\S]*?['"]@/u.test(normalized) || /\$[A-Za-z_][\w]*\s*\|\s*apply_patch\b/u.test(normalized))) {
1656
+ return createBlockedPowerShellAdapter(WINDOWS_SHELL_WRITE_BLOCK_MESSAGE);
1657
+ }
1658
+ if (/(?:^|\n|;)\s*cat\b[^\n;]*>\s*\S+/iu.test(normalized) || /(?:^|\n|;)\s*cat\b[^\n;]*<<[^\n]*>\s*\S+/iu.test(normalized) || /(?:^|\n|;)\s*tee\b\s+\S+/iu.test(normalized)) {
1659
+ return createBlockedPowerShellAdapter(WINDOWS_SHELL_WRITE_BLOCK_MESSAGE);
1660
+ }
1661
+ return void 0;
1662
+ }
1663
+ function convertNativePowerShellCommand(command) {
1664
+ const normalized = command.trim();
1665
+ if (!normalized) return void 0;
1666
+ if (isExplicitPowerShellCommand(normalized)) return void 0;
1667
+ if (!looksLikePowerShellScript(normalized)) {
1668
+ return void 0;
1669
+ }
1670
+ return {
1671
+ command: [
1672
+ "powershell.exe -NoProfile -NonInteractive -Command",
1673
+ quoteCmdArg(`$ErrorActionPreference='Stop'; ${normalized}`)
1674
+ ].join(" "),
1675
+ adapter: "powershell-adapted",
1676
+ logCommand: `powershell.exe -NoProfile -NonInteractive -Command <powershell script>`
1677
+ };
1678
+ }
1679
+ function looksLikePowerShellScript(command) {
1680
+ const normalized = command.trim();
1681
+ if (/^\$[A-Za-z_][\w]*\s*=/u.test(normalized)) return true;
1682
+ if (/^\$PWD\.Path\b/iu.test(normalized)) return true;
1683
+ if (/@['"][\s\S]*?['"]@/u.test(normalized)) return true;
1684
+ if (/^\$[A-Za-z_][\w]*\s*\|/u.test(normalized) || /;\s*\$[A-Za-z_][\w]*\s*\|/u.test(normalized)) {
1685
+ return true;
1686
+ }
1687
+ const cmdlet = /\b(?:Get|Set|New|Remove|Select|ForEach|Where|Test|Resolve|Join|Split|Copy|Move|Write|Out|Measure|Compare|Sort|Format)-[A-Za-z]+\b/u;
1688
+ if (!cmdlet.test(normalized)) return false;
1689
+ if (/^(?:Get|Set|New|Remove|Select|ForEach|Where|Test|Resolve|Join|Split|Copy|Move|Write|Out|Measure|Compare|Sort|Format)-[A-Za-z]+\b/u.test(normalized)) {
1690
+ return true;
1691
+ }
1692
+ return /[;|]/u.test(normalized) || /\$[A-Za-z_][\w]*(?:\.|\s*\|)/u.test(normalized);
1693
+ }
1694
+ function blockUnsupportedPosixShellSyntax(command) {
1695
+ const normalized = command.replace(/\r\n/gu, "\n").trim();
1696
+ if (!normalized) return void 0;
1697
+ const message = "Unsupported POSIX shell syntax on Windows; use PowerShell-safe commands, Node one-liners, or structured tools.";
1698
+ if (/<<\s*['"]?[A-Za-z_][\w.-]*['"]?/u.test(normalized)) {
1699
+ return createBlockedPowerShellAdapter(message);
1700
+ }
1701
+ if (/(?:^|\n|;)\s*export\s+[A-Za-z_][\w]*=/u.test(normalized)) {
1702
+ return createBlockedPowerShellAdapter(message);
1703
+ }
1704
+ if (/^(?:[A-Za-z_][\w]*=[^\s]+\s+)+\S+/u.test(normalized)) {
1705
+ return createBlockedPowerShellAdapter(message);
1706
+ }
1707
+ if (/\$\([^)]*\)/u.test(normalized)) {
1708
+ return createBlockedPowerShellAdapter(message);
1709
+ }
1710
+ if (/\n/u.test(normalized)) {
1711
+ return createBlockedPowerShellAdapter(message);
1712
+ }
1713
+ return void 0;
1714
+ }
1839
1715
  function convertNodeHereDocForPowerShell(command) {
1840
1716
  const normalized = command.replace(/\r\n/gu, "\n");
1841
1717
  const match = normalized.match(
@@ -2182,14 +2058,6 @@ function resolveWorkspacePath(workspaceRoot, inputPath) {
2182
2058
  function relativePath(workspaceRoot, filePath) {
2183
2059
  return relative(workspaceRoot, filePath).replaceAll("\\", "/") || ".";
2184
2060
  }
2185
- function resolveArtifactPath(context, inputPath) {
2186
- const absolute = resolveWorkspacePath(context.workspaceRoot, inputPath);
2187
- return {
2188
- input: inputPath,
2189
- absolute,
2190
- relative: relativePath(context.workspaceRoot, absolute)
2191
- };
2192
- }
2193
2061
  function ensureUnique(content, oldText) {
2194
2062
  if (!oldText) {
2195
2063
  throw new Error("oldText \u4E0D\u80FD\u4E3A\u7A7A\u3002\u5EFA\u8BAE\uFF1A\u63D0\u4F9B\u8DB3\u591F\u4E0A\u4E0B\u6587\u7684\u552F\u4E00\u7247\u6BB5\u3002");
@@ -2256,6 +2124,8 @@ function rememberReadSnapshot(context, filePath, content, info) {
2256
2124
  function createEditOutput(context, operation, filePath, before, after, readGuard, editCount = 1) {
2257
2125
  const rel = relativePath(context.workspaceRoot, filePath);
2258
2126
  const summary = createPatchSummary([rel], before, after);
2127
+ const structuredPatch = createStructuredPatch(rel, before, after);
2128
+ const patchHunks = structuredPatch.files.flatMap((file) => file.hunks);
2259
2129
  context.patchSummaries = { ...context.patchSummaries ?? {}, [rel]: summary };
2260
2130
  const newlineBefore = detectNewlineStyle(before);
2261
2131
  const newlineAfter = detectNewlineStyle(after);
@@ -2281,11 +2151,46 @@ function createEditOutput(context, operation, filePath, before, after, readGuard
2281
2151
  afterHash: hashText(after),
2282
2152
  newlineBefore,
2283
2153
  newlineAfter,
2284
- encoding: "utf8"
2154
+ encoding: "utf8",
2155
+ structuredPatch,
2156
+ patchHunks
2285
2157
  },
2286
2158
  changedFiles: [rel]
2287
2159
  };
2288
2160
  }
2161
+ var STRUCTURED_PATCH_LINE_LIMIT = 120;
2162
+ function createStructuredPatch(path, before, after) {
2163
+ const beforeLines = splitPatchLines(before);
2164
+ const afterLines = splitPatchLines(after);
2165
+ const hunk = createPatchHunk(beforeLines, afterLines);
2166
+ return { files: [{ path, hunks: hunk ? [hunk] : [] }] };
2167
+ }
2168
+ function splitPatchLines(content) {
2169
+ if (content.length === 0) return [];
2170
+ return content.replace(/\r\n/gu, "\n").replace(/\n$/u, "").split("\n");
2171
+ }
2172
+ function createPatchHunk(beforeLines, afterLines) {
2173
+ let prefix = 0;
2174
+ while (prefix < beforeLines.length && prefix < afterLines.length && beforeLines[prefix] === afterLines[prefix]) {
2175
+ prefix += 1;
2176
+ }
2177
+ let suffix = 0;
2178
+ while (suffix < beforeLines.length - prefix && suffix < afterLines.length - prefix && beforeLines[beforeLines.length - 1 - suffix] === afterLines[afterLines.length - 1 - suffix]) {
2179
+ suffix += 1;
2180
+ }
2181
+ const oldLines = beforeLines.slice(prefix, beforeLines.length - suffix);
2182
+ const newLines = afterLines.slice(prefix, afterLines.length - suffix);
2183
+ if (oldLines.length === 0 && newLines.length === 0) return void 0;
2184
+ return {
2185
+ oldStart: oldLines.length > 0 ? prefix + 1 : prefix,
2186
+ newStart: newLines.length > 0 ? prefix + 1 : prefix,
2187
+ oldLines: oldLines.slice(0, STRUCTURED_PATCH_LINE_LIMIT),
2188
+ newLines: newLines.slice(0, STRUCTURED_PATCH_LINE_LIMIT),
2189
+ oldLineCount: oldLines.length,
2190
+ newLineCount: newLines.length,
2191
+ truncated: oldLines.length > STRUCTURED_PATCH_LINE_LIMIT || newLines.length > STRUCTURED_PATCH_LINE_LIMIT
2192
+ };
2193
+ }
2289
2194
  function createPatchSummary(changedFiles, before, after) {
2290
2195
  const beforeLines = before.length > 0 ? before.split(/\r?\n/u) : [];
2291
2196
  const afterLines = after.length > 0 ? after.split(/\r?\n/u) : [];
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  TodoTool
3
- } from "../../chunk-4567YAXM.js";
4
- import "../../chunk-XZWSO5CM.js";
3
+ } from "../../chunk-MFHY7PBM.js";
5
4
  import "../../chunk-SZFZPQGL.js";
5
+ import "../../chunk-XZWSO5CM.js";
6
6
  export {
7
7
  TodoTool
8
8
  };
@@ -1,34 +1,34 @@
1
1
  import {
2
2
  toolPrompts
3
- } from "../chunk-F7UOI3YR.js";
3
+ } from "../chunk-XGK6YYZO.js";
4
4
  import "../chunk-4EZHIDZ6.js";
5
+ import "../chunk-YR4HWOR2.js";
6
+ import "../chunk-VAFW24RN.js";
7
+ import "../chunk-LGZOG7IQ.js";
8
+ import "../chunk-MFHY7PBM.js";
9
+ import "../chunk-SZFZPQGL.js";
10
+ import "../chunk-XZWSO5CM.js";
5
11
  import "../chunk-I3Z4OKMS.js";
6
12
  import "../chunk-PS2ZOFZ6.js";
7
13
  import "../chunk-NPKLGVDQ.js";
8
- import "../chunk-VJDLOFCF.js";
9
- import "../chunk-3RP665TH.js";
10
- import "../chunk-IOC3BSPT.js";
11
- import "../chunk-LGZOG7IQ.js";
12
- import "../chunk-ZYAEUABC.js";
13
- import "../chunk-SWK6XRZQ.js";
14
- import "../chunk-YR4HWOR2.js";
15
- import "../chunk-VAFW24RN.js";
16
14
  import "../chunk-TZ6QK52L.js";
17
15
  import "../chunk-K3YDFDLP.js";
18
16
  import "../chunk-GI5XI4HN.js";
19
17
  import "../chunk-WEWXKRKL.js";
20
18
  import "../chunk-A6FUAM64.js";
21
19
  import "../chunk-YCO6C7KF.js";
22
- import "../chunk-4567YAXM.js";
23
- import "../chunk-XZWSO5CM.js";
24
- import "../chunk-SZFZPQGL.js";
25
- import "../chunk-WLQ7DGMG.js";
26
- import "../chunk-SQP6SJRP.js";
27
- import "../chunk-BRG64AEC.js";
20
+ import "../chunk-ZYAEUABC.js";
21
+ import "../chunk-SWK6XRZQ.js";
28
22
  import "../chunk-AZCQL24J.js";
29
23
  import "../chunk-HUIZOB4A.js";
30
24
  import "../chunk-MK3YP2VR.js";
25
+ import "../chunk-VJDLOFCF.js";
26
+ import "../chunk-3RP665TH.js";
27
+ import "../chunk-IOC3BSPT.js";
31
28
  import "../chunk-HNIVNZIS.js";
29
+ import "../chunk-WLQ7DGMG.js";
30
+ import "../chunk-SQP6SJRP.js";
31
+ import "../chunk-BRG64AEC.js";
32
32
  import "../chunk-7ZOADACP.js";
33
33
  import "../chunk-H5OOD2OS.js";
34
34
  import "../chunk-IQP3XE32.js";
package/dist/tools/ui.js CHANGED
@@ -1,34 +1,34 @@
1
1
  import {
2
2
  toolUserFacingNames
3
- } from "../chunk-J7O24FJB.js";
3
+ } from "../chunk-PNXZO2UE.js";
4
4
  import "../chunk-4EZHIDZ6.js";
5
+ import "../chunk-YR4HWOR2.js";
6
+ import "../chunk-VAFW24RN.js";
7
+ import "../chunk-LGZOG7IQ.js";
8
+ import "../chunk-MFHY7PBM.js";
9
+ import "../chunk-SZFZPQGL.js";
10
+ import "../chunk-XZWSO5CM.js";
5
11
  import "../chunk-I3Z4OKMS.js";
6
12
  import "../chunk-PS2ZOFZ6.js";
7
13
  import "../chunk-NPKLGVDQ.js";
8
- import "../chunk-VJDLOFCF.js";
9
- import "../chunk-3RP665TH.js";
10
- import "../chunk-IOC3BSPT.js";
11
- import "../chunk-LGZOG7IQ.js";
12
- import "../chunk-ZYAEUABC.js";
13
- import "../chunk-SWK6XRZQ.js";
14
- import "../chunk-YR4HWOR2.js";
15
- import "../chunk-VAFW24RN.js";
16
14
  import "../chunk-TZ6QK52L.js";
17
15
  import "../chunk-K3YDFDLP.js";
18
16
  import "../chunk-GI5XI4HN.js";
19
17
  import "../chunk-WEWXKRKL.js";
20
18
  import "../chunk-A6FUAM64.js";
21
19
  import "../chunk-YCO6C7KF.js";
22
- import "../chunk-4567YAXM.js";
23
- import "../chunk-XZWSO5CM.js";
24
- import "../chunk-SZFZPQGL.js";
25
- import "../chunk-WLQ7DGMG.js";
26
- import "../chunk-SQP6SJRP.js";
27
- import "../chunk-BRG64AEC.js";
20
+ import "../chunk-ZYAEUABC.js";
21
+ import "../chunk-SWK6XRZQ.js";
28
22
  import "../chunk-AZCQL24J.js";
29
23
  import "../chunk-HUIZOB4A.js";
30
24
  import "../chunk-MK3YP2VR.js";
25
+ import "../chunk-VJDLOFCF.js";
26
+ import "../chunk-3RP665TH.js";
27
+ import "../chunk-IOC3BSPT.js";
31
28
  import "../chunk-HNIVNZIS.js";
29
+ import "../chunk-WLQ7DGMG.js";
30
+ import "../chunk-SQP6SJRP.js";
31
+ import "../chunk-BRG64AEC.js";
32
32
  import "../chunk-7ZOADACP.js";
33
33
  import "../chunk-H5OOD2OS.js";
34
34
  import "../chunk-IQP3XE32.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linghun/tools",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",