@ai-agent-forge/plugin-coding-tools 0.88.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 (56) hide show
  1. package/README.md +3 -0
  2. package/agent-forge.json +19 -0
  3. package/dist/bash.d.ts +49 -0
  4. package/dist/bash.d.ts.map +1 -0
  5. package/dist/bash.js +323 -0
  6. package/dist/bash.js.map +1 -0
  7. package/dist/capability.d.ts +25 -0
  8. package/dist/capability.d.ts.map +1 -0
  9. package/dist/capability.js +98 -0
  10. package/dist/capability.js.map +1 -0
  11. package/dist/config.d.ts +8 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +8 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/edit.d.ts +55 -0
  16. package/dist/edit.d.ts.map +1 -0
  17. package/dist/edit.js +308 -0
  18. package/dist/edit.js.map +1 -0
  19. package/dist/entry.d.ts +14 -0
  20. package/dist/entry.d.ts.map +1 -0
  21. package/dist/entry.js +23 -0
  22. package/dist/entry.js.map +1 -0
  23. package/dist/find.d.ts +42 -0
  24. package/dist/find.d.ts.map +1 -0
  25. package/dist/find.js +305 -0
  26. package/dist/find.js.map +1 -0
  27. package/dist/grep.d.ts +41 -0
  28. package/dist/grep.d.ts.map +1 -0
  29. package/dist/grep.js +311 -0
  30. package/dist/grep.js.map +1 -0
  31. package/dist/index.d.ts +17 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +17 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/ls.d.ts +41 -0
  36. package/dist/ls.d.ts.map +1 -0
  37. package/dist/ls.js +175 -0
  38. package/dist/ls.js.map +1 -0
  39. package/dist/powershell.d.ts +17 -0
  40. package/dist/powershell.d.ts.map +1 -0
  41. package/dist/powershell.js +42 -0
  42. package/dist/powershell.js.map +1 -0
  43. package/dist/read.d.ts +39 -0
  44. package/dist/read.d.ts.map +1 -0
  45. package/dist/read.js +288 -0
  46. package/dist/read.js.map +1 -0
  47. package/dist/types.d.ts +74 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +28 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/write.d.ts +30 -0
  52. package/dist/write.d.ts.map +1 -0
  53. package/dist/write.js +205 -0
  54. package/dist/write.js.map +1 -0
  55. package/package.json +53 -0
  56. package/plugin.json +10 -0
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Capability factory for the coding tools plugin: registers the default coding
3
+ * tool definitions through the public `tools` capability, mirroring the host's
4
+ * embedded `installSessionBootstrapTools` semantics (readonly mode keeps only
5
+ * the readonly-safe read/grep/find/ls face).
6
+ */
7
+ import { createBashToolDefinition } from "./bash.js";
8
+ import { createEditToolDefinition } from "./edit.js";
9
+ import { createFindToolDefinition } from "./find.js";
10
+ import { createGrepToolDefinition } from "./grep.js";
11
+ import { createLsToolDefinition } from "./ls.js";
12
+ import { createPowerShellToolDefinition } from "./powershell.js";
13
+ import { createReadToolDefinition } from "./read.js";
14
+ import { createWriteToolDefinition } from "./write.js";
15
+ function optionalString(value, field) {
16
+ if (value === undefined)
17
+ return undefined;
18
+ if (typeof value !== "string") {
19
+ throw new Error(`coding-tools: config.${field} must be a string`);
20
+ }
21
+ return value;
22
+ }
23
+ function optionalBoolean(value, field) {
24
+ if (value === undefined)
25
+ return undefined;
26
+ if (typeof value !== "boolean") {
27
+ throw new Error(`coding-tools: config.${field} must be a boolean`);
28
+ }
29
+ return value;
30
+ }
31
+ function configObject(value, field) {
32
+ if (value === undefined)
33
+ return {};
34
+ if (value === null || Array.isArray(value) || typeof value !== "object") {
35
+ throw new Error(`coding-tools: config.${field} must be an object`);
36
+ }
37
+ // The array member of the PluginConfigValue union cannot be excluded by
38
+ // narrowing, so project the validated object into a typed record instead
39
+ // of casting.
40
+ const record = {};
41
+ for (const [key, entry] of Object.entries(value)) {
42
+ record[key] = entry;
43
+ }
44
+ return record;
45
+ }
46
+ function readPluginConfig(config) {
47
+ const version = config.version ?? 1;
48
+ if (version !== 1) {
49
+ throw new Error(`coding-tools: unsupported config version ${JSON.stringify(version)}, expected 1`);
50
+ }
51
+ const read = configObject(config.read, "read");
52
+ const bash = configObject(config.bash, "bash");
53
+ return {
54
+ version: 1,
55
+ cwd: optionalString(config.cwd, "cwd"),
56
+ read: { autoResizeImages: optionalBoolean(read.autoResizeImages, "read.autoResizeImages") },
57
+ bash: {
58
+ commandPrefix: optionalString(bash.commandPrefix, "bash.commandPrefix"),
59
+ shellPath: optionalString(bash.shellPath, "bash.shellPath"),
60
+ },
61
+ };
62
+ }
63
+ function createToolDefinitions(cwd, options, readonlyMode) {
64
+ if (readonlyMode) {
65
+ // Equivalent of the host's createReadOnlyToolDefinitions: only read
66
+ // carries per-tool options there.
67
+ return [
68
+ createReadToolDefinition(cwd, options.read),
69
+ createGrepToolDefinition(cwd),
70
+ createFindToolDefinition(cwd),
71
+ createLsToolDefinition(cwd),
72
+ ];
73
+ }
74
+ // Equivalent of the host's createAllToolDefinitions: read and bash carry
75
+ // per-tool options, the rest keep defaults.
76
+ return [
77
+ createReadToolDefinition(cwd, options.read),
78
+ createBashToolDefinition(cwd, options.bash),
79
+ createPowerShellToolDefinition(cwd),
80
+ createEditToolDefinition(cwd),
81
+ createWriteToolDefinition(cwd),
82
+ createGrepToolDefinition(cwd),
83
+ createFindToolDefinition(cwd),
84
+ createLsToolDefinition(cwd),
85
+ ];
86
+ }
87
+ export function createCodingToolsCapability(api) {
88
+ const config = readPluginConfig(api.config);
89
+ const cwd = config.cwd ?? process.cwd();
90
+ const readonlyMode = api.host.mode === "readonly";
91
+ const definitions = createToolDefinitions(cwd, config, readonlyMode);
92
+ const registrations = [];
93
+ for (const definition of definitions) {
94
+ registrations.push(api.registerTool(definition));
95
+ }
96
+ return registrations;
97
+ }
98
+ //# sourceMappingURL=capability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability.js","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Capability factory for the coding tools plugin: registers the default coding\n * tool definitions through the public `tools` capability, mirroring the host's\n * embedded `installSessionBootstrapTools` semantics (readonly mode keeps only\n * the readonly-safe read/grep/find/ls face).\n */\n\nimport type { ToolDefinition } from \"@agent-forge/host/tool-definition\";\nimport type { CapabilityAPI, DisposableRegistration, PluginConfig, PluginConfigValue } from \"@agent-forge/plugin-sdk\";\nimport { createBashToolDefinition } from \"./bash.ts\";\nimport { createEditToolDefinition } from \"./edit.ts\";\nimport { createFindToolDefinition } from \"./find.ts\";\nimport { createGrepToolDefinition } from \"./grep.ts\";\nimport { createLsToolDefinition } from \"./ls.ts\";\nimport { createPowerShellToolDefinition } from \"./powershell.ts\";\nimport { createReadToolDefinition } from \"./read.ts\";\nimport { createWriteToolDefinition } from \"./write.ts\";\n\n/**\n * Plugin manifest config shape (`agent-forge.json` config face). Every field\n * is optional; absent fields keep each tool's existing defaults.\n */\nexport interface CodingToolsPluginConfigV1 {\n\tversion: 1;\n\t/** Working directory handed to every tool definition. */\n\tcwd?: string;\n\tread?: { autoResizeImages?: boolean };\n\tbash?: { commandPrefix?: string; shellPath?: string };\n}\n\nfunction optionalString(value: PluginConfigValue | undefined, field: string): string | undefined {\n\tif (value === undefined) return undefined;\n\tif (typeof value !== \"string\") {\n\t\tthrow new Error(`coding-tools: config.${field} must be a string`);\n\t}\n\treturn value;\n}\n\nfunction optionalBoolean(value: PluginConfigValue | undefined, field: string): boolean | undefined {\n\tif (value === undefined) return undefined;\n\tif (typeof value !== \"boolean\") {\n\t\tthrow new Error(`coding-tools: config.${field} must be a boolean`);\n\t}\n\treturn value;\n}\n\nfunction configObject(\n\tvalue: PluginConfigValue | undefined,\n\tfield: string,\n): Readonly<Record<string, PluginConfigValue>> {\n\tif (value === undefined) return {};\n\tif (value === null || Array.isArray(value) || typeof value !== \"object\") {\n\t\tthrow new Error(`coding-tools: config.${field} must be an object`);\n\t}\n\t// The array member of the PluginConfigValue union cannot be excluded by\n\t// narrowing, so project the validated object into a typed record instead\n\t// of casting.\n\tconst record: Record<string, PluginConfigValue> = {};\n\tfor (const [key, entry] of Object.entries(value)) {\n\t\trecord[key] = entry;\n\t}\n\treturn record;\n}\n\nfunction readPluginConfig(config: PluginConfig): CodingToolsPluginConfigV1 {\n\tconst version = config.version ?? 1;\n\tif (version !== 1) {\n\t\tthrow new Error(`coding-tools: unsupported config version ${JSON.stringify(version)}, expected 1`);\n\t}\n\tconst read = configObject(config.read, \"read\");\n\tconst bash = configObject(config.bash, \"bash\");\n\treturn {\n\t\tversion: 1,\n\t\tcwd: optionalString(config.cwd, \"cwd\"),\n\t\tread: { autoResizeImages: optionalBoolean(read.autoResizeImages, \"read.autoResizeImages\") },\n\t\tbash: {\n\t\t\tcommandPrefix: optionalString(bash.commandPrefix, \"bash.commandPrefix\"),\n\t\t\tshellPath: optionalString(bash.shellPath, \"bash.shellPath\"),\n\t\t},\n\t};\n}\n\nfunction createToolDefinitions(\n\tcwd: string,\n\toptions: CodingToolsPluginConfigV1,\n\treadonlyMode: boolean,\n): readonly ToolDefinition[] {\n\tif (readonlyMode) {\n\t\t// Equivalent of the host's createReadOnlyToolDefinitions: only read\n\t\t// carries per-tool options there.\n\t\treturn [\n\t\t\tcreateReadToolDefinition(cwd, options.read),\n\t\t\tcreateGrepToolDefinition(cwd),\n\t\t\tcreateFindToolDefinition(cwd),\n\t\t\tcreateLsToolDefinition(cwd),\n\t\t];\n\t}\n\t// Equivalent of the host's createAllToolDefinitions: read and bash carry\n\t// per-tool options, the rest keep defaults.\n\treturn [\n\t\tcreateReadToolDefinition(cwd, options.read),\n\t\tcreateBashToolDefinition(cwd, options.bash),\n\t\tcreatePowerShellToolDefinition(cwd),\n\t\tcreateEditToolDefinition(cwd),\n\t\tcreateWriteToolDefinition(cwd),\n\t\tcreateGrepToolDefinition(cwd),\n\t\tcreateFindToolDefinition(cwd),\n\t\tcreateLsToolDefinition(cwd),\n\t];\n}\n\nexport function createCodingToolsCapability(api: CapabilityAPI): readonly DisposableRegistration[] {\n\tconst config = readPluginConfig(api.config);\n\tconst cwd = config.cwd ?? process.cwd();\n\tconst readonlyMode = api.host.mode === \"readonly\";\n\tconst definitions = createToolDefinitions(cwd, config, readonlyMode);\n\tconst registrations: DisposableRegistration[] = [];\n\tfor (const definition of definitions) {\n\t\tregistrations.push(api.registerTool(definition));\n\t}\n\treturn registrations;\n}\n"]}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * README anchor: since D-082 批 1 the host supplies `getReadmePath` through the
3
+ * `@agent-forge/host/readme-path` virtual module (host package root resolution
4
+ * incl. the AGENT_FORGE_PACKAGE_DIR override and dist-parent handling). The
5
+ * former plugin-local reimplementation is retired — one truth, host-owned.
6
+ */
7
+ export { getReadmePath } from "@agent-forge/host/readme-path";
8
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC","sourcesContent":["/**\n * README anchor: since D-082 批 1 the host supplies `getReadmePath` through the\n * `@agent-forge/host/readme-path` virtual module (host package root resolution\n * incl. the AGENT_FORGE_PACKAGE_DIR override and dist-parent handling). The\n * former plugin-local reimplementation is retired — one truth, host-owned.\n */\nexport { getReadmePath } from \"@agent-forge/host/readme-path\";\n"]}
package/dist/config.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * README anchor: since D-082 批 1 the host supplies `getReadmePath` through the
3
+ * `@agent-forge/host/readme-path` virtual module (host package root resolution
4
+ * incl. the AGENT_FORGE_PACKAGE_DIR override and dist-parent handling). The
5
+ * former plugin-local reimplementation is retired — one truth, host-owned.
6
+ */
7
+ export { getReadmePath } from "@agent-forge/host/readme-path";
8
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC","sourcesContent":["/**\n * README anchor: since D-082 批 1 the host supplies `getReadmePath` through the\n * `@agent-forge/host/readme-path` virtual module (host package root resolution\n * incl. the AGENT_FORGE_PACKAGE_DIR override and dist-parent handling). The\n * former plugin-local reimplementation is retired — one truth, host-owned.\n */\nexport { getReadmePath } from \"@agent-forge/host/readme-path\";\n"]}
package/dist/edit.d.ts ADDED
@@ -0,0 +1,55 @@
1
+ import { type EditDiffError, type EditDiffResult } from "@agent-forge/host/edit-diff";
2
+ import type { ToolDefinition } from "@agent-forge/host/tool-definition";
3
+ import { Box } from "@agent-forge/tui";
4
+ import { type Static, Type } from "typebox";
5
+ import { type AgentTool } from "./types.ts";
6
+ type EditPreview = EditDiffResult | EditDiffError;
7
+ type EditRenderState = {
8
+ callComponent?: EditCallRenderComponent;
9
+ };
10
+ declare const editSchema: Type.TObject<{
11
+ path: Type.TString;
12
+ edits: Type.TArray<Type.TObject<{
13
+ oldText: Type.TString;
14
+ newText: Type.TString;
15
+ }>>;
16
+ }>;
17
+ export declare const editToolSystemPromptContribution: {
18
+ readonly snippet: "Make precise file edits with exact text replacement, including multiple disjoint edits in one call";
19
+ readonly guidelines: readonly ["Use edit for precise changes (edits[].oldText must match exactly)", "When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls", "Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.", "Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions."];
20
+ };
21
+ export type EditToolInput = Static<typeof editSchema>;
22
+ export interface EditToolDetails {
23
+ /** Display-oriented diff of the changes made */
24
+ diff: string;
25
+ /** Standard unified patch of the changes made */
26
+ patch: string;
27
+ /** Line number of the first change in the new file (for editor navigation) */
28
+ firstChangedLine?: number;
29
+ }
30
+ /**
31
+ * Pluggable operations for the edit tool.
32
+ * Override these to delegate file editing to remote systems (for example SSH).
33
+ */
34
+ export interface EditOperations {
35
+ /** Read file contents as a Buffer */
36
+ readFile: (absolutePath: string) => Promise<Buffer>;
37
+ /** Write content to a file */
38
+ writeFile: (absolutePath: string, content: string) => Promise<void>;
39
+ /** Check if file is readable and writable (throw if not) */
40
+ access: (absolutePath: string) => Promise<void>;
41
+ }
42
+ export interface EditToolOptions {
43
+ /** Custom operations for file editing. Default: local filesystem */
44
+ operations?: EditOperations;
45
+ }
46
+ type EditCallRenderComponent = Box & {
47
+ preview?: EditPreview;
48
+ previewArgsKey?: string;
49
+ previewPending?: boolean;
50
+ settledError?: boolean;
51
+ };
52
+ export declare function createEditToolDefinition(cwd: string, options?: EditToolOptions): ToolDefinition<typeof editSchema, EditToolDetails | undefined, EditRenderState>;
53
+ export declare function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema>;
54
+ export {};
55
+ //# sourceMappingURL=edit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../src/edit.ts"],"names":[],"mappings":"AAMA,OAAO,EAKN,KAAK,aAAa,EAClB,KAAK,cAAc,EAKnB,MAAM,6BAA6B,CAAC;AAOrC,OAAO,KAAK,EAAE,cAAc,EAAsB,MAAM,mCAAmC,CAAC;AAC5F,OAAO,EAAE,GAAG,EAA2B,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,KAAK,SAAS,EAAsB,MAAM,YAAY,CAAC;AAEhE,KAAK,WAAW,GAAG,cAAc,GAAG,aAAa,CAAC;AAElD,KAAK,eAAe,GAAG;IACtB,aAAa,CAAC,EAAE,uBAAuB,CAAC;CACxC,CAAC;AAaF,QAAA,MAAM,UAAU;;;;;;EASf,CAAC;AAEF,eAAO,MAAM,gCAAgC;;;CAQnC,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAiBtD,MAAM,WAAW,eAAe;IAC/B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,qCAAqC;IACrC,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,8BAA8B;IAC9B,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,4DAA4D;IAC5D,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAQD,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;CAC5B;AAuDD,KAAK,uBAAuB,GAAG,GAAG,GAAG;IACpC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AA8IF,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,EAAE,eAAe,CAAC,CA0IjF;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG","sourcesContent":["// Migrated from packages/agent-forge/src/core/tools/edit (batch 1, copy-then-delete);\n// host modules are imported through supply keys (@agent-forge/host/*).\n\nimport { constants } from \"node:fs\";\nimport { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from \"node:fs/promises\";\nimport { renderDiff } from \"@agent-forge/host/diff\";\nimport {\n\tapplyEditsToNormalizedContent,\n\tcomputeEditsDiff,\n\tdetectLineEnding,\n\ttype Edit,\n\ttype EditDiffError,\n\ttype EditDiffResult,\n\tgenerateDiffString,\n\tgenerateUnifiedPatch,\n\tnormalizeToLF,\n\trestoreLineEndings,\n} from \"@agent-forge/host/edit-diff\";\nimport { withFileMutationQueue } from \"@agent-forge/host/file-mutation-queue\";\nimport { t } from \"@agent-forge/host/i18n\";\nimport { resolveToCwd } from \"@agent-forge/host/path-utils\";\nimport { renderToolPath, str } from \"@agent-forge/host/render-utils\";\nimport { splitBom } from \"@agent-forge/host/text\";\nimport type { Theme } from \"@agent-forge/host/theme\";\nimport type { ToolDefinition, ToolExecuteContext } from \"@agent-forge/host/tool-definition\";\nimport { Box, Container, Spacer, Text } from \"@agent-forge/tui\";\nimport { type Static, Type } from \"typebox\";\nimport { type AgentTool, wrapToolDefinition } from \"./types.ts\";\n\ntype EditPreview = EditDiffResult | EditDiffError;\n\ntype EditRenderState = {\n\tcallComponent?: EditCallRenderComponent;\n};\n\nconst replaceEditSchema = Type.Object(\n\t{\n\t\toldText: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call.\",\n\t\t}),\n\t\tnewText: Type.String({ description: \"Replacement text for this targeted edit.\" }),\n\t},\n\t{},\n);\n\nconst editSchema = Type.Object(\n\t{\n\t\tpath: Type.String({ description: \"Path to the file to edit (relative or absolute)\" }),\n\t\tedits: Type.Array(replaceEditSchema, {\n\t\t\tdescription:\n\t\t\t\t\"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.\",\n\t\t}),\n\t},\n\t{},\n);\n\nexport const editToolSystemPromptContribution = {\n\tsnippet: \"Make precise file edits with exact text replacement, including multiple disjoint edits in one call\",\n\tguidelines: [\n\t\t\"Use edit for precise changes (edits[].oldText must match exactly)\",\n\t\t\"When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\",\n\t\t\"Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\",\n\t\t\"Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\",\n\t],\n} as const;\n\nexport type EditToolInput = Static<typeof editSchema>;\ntype LegacyEditToolInput = EditToolInput & {\n\toldText?: unknown;\n\tnewText?: unknown;\n};\n\ntype SingleEditInput = { oldText: string; newText: string };\n\nfunction isSingleEditInput(value: unknown): value is SingleEditInput {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\treturn false;\n\t}\n\n\tconst edit = value as Record<string, unknown>;\n\treturn typeof edit.oldText === \"string\" && typeof edit.newText === \"string\";\n}\n\nexport interface EditToolDetails {\n\t/** Display-oriented diff of the changes made */\n\tdiff: string;\n\t/** Standard unified patch of the changes made */\n\tpatch: string;\n\t/** Line number of the first change in the new file (for editor navigation) */\n\tfirstChangedLine?: number;\n}\n\n/**\n * Pluggable operations for the edit tool.\n * Override these to delegate file editing to remote systems (for example SSH).\n */\nexport interface EditOperations {\n\t/** Read file contents as a Buffer */\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Check if file is readable and writable (throw if not) */\n\taccess: (absolutePath: string) => Promise<void>;\n}\n\nconst defaultEditOperations: EditOperations = {\n\treadFile: (path) => fsReadFile(path),\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\taccess: (path) => fsAccess(path, constants.R_OK | constants.W_OK),\n};\n\nexport interface EditToolOptions {\n\t/** Custom operations for file editing. Default: local filesystem */\n\toperations?: EditOperations;\n}\n\nfunction prepareEditArguments(input: unknown): EditToolInput {\n\tif (!input || typeof input !== \"object\") {\n\t\treturn input as EditToolInput;\n\t}\n\n\tconst args = input as Record<string, unknown>;\n\n\t// Some models (Opus 4.6, GLM-5.1) send edits as a JSON string instead of an array.\n\t// Others send a single edit object instead of a one-element edits array.\n\tif (typeof args.edits === \"string\") {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(args.edits);\n\t\t\tif (Array.isArray(parsed)) {\n\t\t\t\targs.edits = parsed;\n\t\t\t} else if (isSingleEditInput(parsed)) {\n\t\t\t\targs.edits = [parsed];\n\t\t\t}\n\t\t} catch {}\n\t} else if (isSingleEditInput(args.edits)) {\n\t\targs.edits = [args.edits];\n\t}\n\n\tconst legacy = args as LegacyEditToolInput;\n\tif (typeof legacy.oldText !== \"string\" || typeof legacy.newText !== \"string\") {\n\t\treturn args as EditToolInput;\n\t}\n\n\tconst edits = Array.isArray(legacy.edits) ? [...legacy.edits] : [];\n\tedits.push({ oldText: legacy.oldText, newText: legacy.newText });\n\tconst { oldText: _oldText, newText: _newText, ...rest } = legacy;\n\treturn { ...rest, edits } as EditToolInput;\n}\n\nfunction validateEditInput(input: EditToolInput): { path: string; edits: Edit[] } {\n\tif (!Array.isArray(input.edits) || input.edits.length === 0) {\n\t\tthrow new Error(t(\"Edit tool input is invalid. edits must contain at least one replacement.\"));\n\t}\n\treturn { path: input.path, edits: input.edits };\n}\n\ntype RenderableEditArgs = {\n\tpath?: string;\n\tfile_path?: string;\n\tedits?: Edit[];\n\toldText?: string;\n\tnewText?: string;\n};\n\ntype EditToolResultLike = {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tdetails?: EditToolDetails;\n};\n\ntype EditCallRenderComponent = Box & {\n\tpreview?: EditPreview;\n\tpreviewArgsKey?: string;\n\tpreviewPending?: boolean;\n\tsettledError?: boolean;\n};\n\nfunction createEditCallRenderComponent(): EditCallRenderComponent {\n\treturn Object.assign(new Box(1, 1, (text: string) => text), {\n\t\tpreview: undefined as EditPreview | undefined,\n\t\tpreviewArgsKey: undefined as string | undefined,\n\t\tpreviewPending: false,\n\t\tsettledError: false,\n\t});\n}\n\nfunction getEditCallRenderComponent(state: EditRenderState, lastComponent: unknown): EditCallRenderComponent {\n\tif (lastComponent instanceof Box) {\n\t\tconst component = lastComponent as EditCallRenderComponent;\n\t\tstate.callComponent = component;\n\t\treturn component;\n\t}\n\tif (state.callComponent) {\n\t\treturn state.callComponent;\n\t}\n\tconst component = createEditCallRenderComponent();\n\tstate.callComponent = component;\n\treturn component;\n}\n\nfunction getRenderablePreviewInput(args: RenderableEditArgs | undefined): { path: string; edits: Edit[] } | null {\n\tif (!args) {\n\t\treturn null;\n\t}\n\n\tconst path = typeof args.path === \"string\" ? args.path : typeof args.file_path === \"string\" ? args.file_path : null;\n\tif (!path) {\n\t\treturn null;\n\t}\n\n\tif (\n\t\tArray.isArray(args.edits) &&\n\t\targs.edits.length > 0 &&\n\t\targs.edits.every((edit) => typeof edit?.oldText === \"string\" && typeof edit?.newText === \"string\")\n\t) {\n\t\treturn { path, edits: args.edits };\n\t}\n\n\tif (typeof args.oldText === \"string\" && typeof args.newText === \"string\") {\n\t\treturn { path, edits: [{ oldText: args.oldText, newText: args.newText }] };\n\t}\n\n\treturn null;\n}\n\nfunction formatEditCall(args: RenderableEditArgs | undefined, theme: Theme, cwd: string): string {\n\tconst pathDisplay = renderToolPath(str(args?.file_path ?? args?.path), theme, cwd);\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"edit\"))} ${pathDisplay}`;\n}\n\nfunction formatEditResult(\n\targs: RenderableEditArgs | undefined,\n\tpreview: EditPreview | undefined,\n\tresult: EditToolResultLike,\n\ttheme: Theme,\n\tisError: boolean,\n): string | undefined {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst previewDiff = preview && !(\"error\" in preview) ? preview.diff : undefined;\n\tconst previewError = preview && \"error\" in preview ? preview.error : undefined;\n\tif (isError) {\n\t\tconst errorText = result.content\n\t\t\t.filter((c) => c.type === \"text\")\n\t\t\t.map((c) => c.text || \"\")\n\t\t\t.join(\"\\n\");\n\t\tif (!errorText || errorText === previewError) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn theme.fg(\"error\", errorText);\n\t}\n\n\tconst resultDiff = result.details?.diff;\n\tif (resultDiff && resultDiff !== previewDiff) {\n\t\treturn renderDiff(resultDiff, { filePath: rawPath ?? undefined });\n\t}\n\n\treturn undefined;\n}\n\nfunction getEditHeaderBg(\n\tpreview: EditPreview | undefined,\n\tsettledError: boolean | undefined,\n\ttheme: Theme,\n): (text: string) => string {\n\tif (preview) {\n\t\tif (\"error\" in preview) {\n\t\t\treturn (text: string) => theme.bg(\"toolErrorBg\", text);\n\t\t}\n\t\treturn (text: string) => theme.bg(\"toolSuccessBg\", text);\n\t}\n\tif (settledError) {\n\t\treturn (text: string) => theme.bg(\"toolErrorBg\", text);\n\t}\n\treturn (text: string) => theme.bg(\"toolPendingBg\", text);\n}\n\nfunction buildEditCallComponent(\n\tcomponent: EditCallRenderComponent,\n\targs: RenderableEditArgs | undefined,\n\ttheme: Theme,\n\tcwd: string,\n): EditCallRenderComponent {\n\tcomponent.setBgFn(getEditHeaderBg(component.preview, component.settledError, theme));\n\tcomponent.clear();\n\tcomponent.addChild(new Text(formatEditCall(args, theme, cwd), 0, 0));\n\n\tif (!component.preview) {\n\t\treturn component;\n\t}\n\n\tconst body =\n\t\t\"error\" in component.preview ? theme.fg(\"error\", component.preview.error) : renderDiff(component.preview.diff);\n\tcomponent.addChild(new Spacer(1));\n\tcomponent.addChild(new Text(body, 0, 0));\n\treturn component;\n}\n\nfunction setEditPreview(\n\tcomponent: EditCallRenderComponent,\n\tpreview: EditPreview,\n\targsKey: string | undefined,\n): boolean {\n\tconst current = component.preview;\n\tconst changed =\n\t\tcurrent === undefined ||\n\t\t(\"error\" in current && \"error\" in preview\n\t\t\t? current.error !== preview.error\n\t\t\t: \"error\" in current !== \"error\" in preview) ||\n\t\t(!(\"error\" in current) &&\n\t\t\t!(\"error\" in preview) &&\n\t\t\t(current.diff !== preview.diff || current.firstChangedLine !== preview.firstChangedLine));\n\tcomponent.preview = preview;\n\tcomponent.previewArgsKey = argsKey;\n\tcomponent.previewPending = false;\n\treturn changed;\n}\n\nexport function createEditToolDefinition(\n\tcwd: string,\n\toptions?: EditToolOptions,\n): ToolDefinition<typeof editSchema, EditToolDetails | undefined, EditRenderState> {\n\tconst ops = options?.operations ?? defaultEditOperations;\n\treturn {\n\t\tname: \"edit\",\n\t\tlabel: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.\",\n\t\tpromptSnippet: editToolSystemPromptContribution.snippet,\n\t\tpromptGuidelines: [...editToolSystemPromptContribution.guidelines],\n\t\tparameters: editSchema,\n\t\tconstrainedSampling: { type: \"json_schema\", strict: \"prefer\" },\n\t\trenderShell: \"self\",\n\t\tprepareArguments: prepareEditArguments,\n\t\tasync execute(_toolCallId, input: EditToolInput, signal?: AbortSignal, _onUpdate?, ctx?: ToolExecuteContext) {\n\t\t\tconst { path, edits } = validateEditInput(input);\n\t\t\tconst absolutePath = resolveToCwd(path, ctx?.cwd || cwd);\n\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\t// Do not reject from an abort event listener here: that would release the\n\t\t\t\t// mutation queue while an in-flight filesystem operation may still finish.\n\t\t\t\t// Checking signal.aborted after each await observes the same aborts while\n\t\t\t\t// keeping the queue locked until the current operation has settled.\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (signal?.aborted) throw new Error(t(\"Operation aborted\"));\n\t\t\t\t};\n\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\t// Check if file exists.\n\t\t\t\ttry {\n\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\terror instanceof Error && \"code\" in error ? `Error code: ${error.code}` : String(error);\n\t\t\t\t\tthrow new Error(t(\"Could not edit file: {path}. {errorMessage}.\", { path, errorMessage }));\n\t\t\t\t}\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\t// Read the file.\n\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\tconst rawContent = buffer.toString(\"utf-8\");\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\t// Strip BOM before matching. The model will not include an invisible BOM in oldText.\n\t\t\t\tconst { bom, text: content } = splitBom(rawContent);\n\t\t\t\tconst originalEnding = detectLineEnding(content);\n\t\t\t\tconst normalizedContent = normalizeToLF(content);\n\t\t\t\tconst { baseContent, newContent } = applyEditsToNormalizedContent(normalizedContent, edits, path);\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\tconst finalContent = bom + restoreLineEndings(newContent, originalEnding);\n\t\t\t\tawait ops.writeFile(absolutePath, finalContent);\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\tconst diffResult = generateDiffString(baseContent, newContent);\n\t\t\t\tconst patch = generateUnifiedPatch(path, baseContent, newContent);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Successfully replaced ${edits.length} block(s) in ${path}.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { diff: diffResult.diff, patch, firstChangedLine: diffResult.firstChangedLine },\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst component = getEditCallRenderComponent(context.state, context.lastComponent);\n\t\t\tconst previewInput = getRenderablePreviewInput(args as RenderableEditArgs | undefined);\n\t\t\tconst argsKey = previewInput\n\t\t\t\t? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })\n\t\t\t\t: undefined;\n\n\t\t\tif (component.previewArgsKey !== argsKey) {\n\t\t\t\tcomponent.preview = undefined;\n\t\t\t\tcomponent.previewArgsKey = argsKey;\n\t\t\t\tcomponent.previewPending = false;\n\t\t\t\tcomponent.settledError = false;\n\t\t\t}\n\n\t\t\tif (context.argsComplete && previewInput && !component.preview && !component.previewPending) {\n\t\t\t\tcomponent.previewPending = true;\n\t\t\t\tconst requestKey = argsKey;\n\t\t\t\tvoid computeEditsDiff(previewInput.path, previewInput.edits, context.cwd).then((preview) => {\n\t\t\t\t\tif (component.previewArgsKey === requestKey) {\n\t\t\t\t\t\tsetEditPreview(component, preview, requestKey);\n\t\t\t\t\t\tcontext.invalidate();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn buildEditCallComponent(component, args, theme, context.cwd);\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst callComponent = context.state.callComponent;\n\t\t\tconst previewInput = getRenderablePreviewInput(context.args as RenderableEditArgs | undefined);\n\t\t\tconst argsKey = previewInput\n\t\t\t\t? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })\n\t\t\t\t: undefined;\n\t\t\tconst typedResult = result as EditToolResultLike;\n\t\t\tconst resultDiff = !context.isError ? typedResult.details?.diff : undefined;\n\t\t\tlet changed = false;\n\t\t\tif (callComponent) {\n\t\t\t\tif (typeof resultDiff === \"string\") {\n\t\t\t\t\tchanged =\n\t\t\t\t\t\tsetEditPreview(\n\t\t\t\t\t\t\tcallComponent,\n\t\t\t\t\t\t\t{ diff: resultDiff, firstChangedLine: typedResult.details?.firstChangedLine },\n\t\t\t\t\t\t\targsKey,\n\t\t\t\t\t\t) || changed;\n\t\t\t\t}\n\t\t\t\tif (callComponent.settledError !== context.isError) {\n\t\t\t\t\tcallComponent.settledError = context.isError;\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t\tif (changed) {\n\t\t\t\t\tbuildEditCallComponent(\n\t\t\t\t\t\tcallComponent,\n\t\t\t\t\t\tcontext.args as RenderableEditArgs | undefined,\n\t\t\t\t\t\ttheme,\n\t\t\t\t\t\tcontext.cwd,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst output = formatEditResult(context.args, callComponent?.preview, typedResult, theme, context.isError);\n\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\tcomponent.clear();\n\t\t\tif (!output) {\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tcomponent.addChild(new Spacer(1));\n\t\t\tcomponent.addChild(new Text(output, 1, 0));\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema> {\n\treturn wrapToolDefinition(createEditToolDefinition(cwd, options));\n}\n"]}
package/dist/edit.js ADDED
@@ -0,0 +1,308 @@
1
+ // Migrated from packages/agent-forge/src/core/tools/edit (batch 1, copy-then-delete);
2
+ // host modules are imported through supply keys (@agent-forge/host/*).
3
+ import { constants } from "node:fs";
4
+ import { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from "node:fs/promises";
5
+ import { renderDiff } from "@agent-forge/host/diff";
6
+ import { applyEditsToNormalizedContent, computeEditsDiff, detectLineEnding, generateDiffString, generateUnifiedPatch, normalizeToLF, restoreLineEndings, } from "@agent-forge/host/edit-diff";
7
+ import { withFileMutationQueue } from "@agent-forge/host/file-mutation-queue";
8
+ import { t } from "@agent-forge/host/i18n";
9
+ import { resolveToCwd } from "@agent-forge/host/path-utils";
10
+ import { renderToolPath, str } from "@agent-forge/host/render-utils";
11
+ import { splitBom } from "@agent-forge/host/text";
12
+ import { Box, Container, Spacer, Text } from "@agent-forge/tui";
13
+ import { Type } from "typebox";
14
+ import { wrapToolDefinition } from "./types.js";
15
+ const replaceEditSchema = Type.Object({
16
+ oldText: Type.String({
17
+ description: "Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call.",
18
+ }),
19
+ newText: Type.String({ description: "Replacement text for this targeted edit." }),
20
+ }, {});
21
+ const editSchema = Type.Object({
22
+ path: Type.String({ description: "Path to the file to edit (relative or absolute)" }),
23
+ edits: Type.Array(replaceEditSchema, {
24
+ description: "One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.",
25
+ }),
26
+ }, {});
27
+ export const editToolSystemPromptContribution = {
28
+ snippet: "Make precise file edits with exact text replacement, including multiple disjoint edits in one call",
29
+ guidelines: [
30
+ "Use edit for precise changes (edits[].oldText must match exactly)",
31
+ "When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls",
32
+ "Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.",
33
+ "Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.",
34
+ ],
35
+ };
36
+ function isSingleEditInput(value) {
37
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
38
+ return false;
39
+ }
40
+ const edit = value;
41
+ return typeof edit.oldText === "string" && typeof edit.newText === "string";
42
+ }
43
+ const defaultEditOperations = {
44
+ readFile: (path) => fsReadFile(path),
45
+ writeFile: (path, content) => fsWriteFile(path, content, "utf-8"),
46
+ access: (path) => fsAccess(path, constants.R_OK | constants.W_OK),
47
+ };
48
+ function prepareEditArguments(input) {
49
+ if (!input || typeof input !== "object") {
50
+ return input;
51
+ }
52
+ const args = input;
53
+ // Some models (Opus 4.6, GLM-5.1) send edits as a JSON string instead of an array.
54
+ // Others send a single edit object instead of a one-element edits array.
55
+ if (typeof args.edits === "string") {
56
+ try {
57
+ const parsed = JSON.parse(args.edits);
58
+ if (Array.isArray(parsed)) {
59
+ args.edits = parsed;
60
+ }
61
+ else if (isSingleEditInput(parsed)) {
62
+ args.edits = [parsed];
63
+ }
64
+ }
65
+ catch { }
66
+ }
67
+ else if (isSingleEditInput(args.edits)) {
68
+ args.edits = [args.edits];
69
+ }
70
+ const legacy = args;
71
+ if (typeof legacy.oldText !== "string" || typeof legacy.newText !== "string") {
72
+ return args;
73
+ }
74
+ const edits = Array.isArray(legacy.edits) ? [...legacy.edits] : [];
75
+ edits.push({ oldText: legacy.oldText, newText: legacy.newText });
76
+ const { oldText: _oldText, newText: _newText, ...rest } = legacy;
77
+ return { ...rest, edits };
78
+ }
79
+ function validateEditInput(input) {
80
+ if (!Array.isArray(input.edits) || input.edits.length === 0) {
81
+ throw new Error(t("Edit tool input is invalid. edits must contain at least one replacement."));
82
+ }
83
+ return { path: input.path, edits: input.edits };
84
+ }
85
+ function createEditCallRenderComponent() {
86
+ return Object.assign(new Box(1, 1, (text) => text), {
87
+ preview: undefined,
88
+ previewArgsKey: undefined,
89
+ previewPending: false,
90
+ settledError: false,
91
+ });
92
+ }
93
+ function getEditCallRenderComponent(state, lastComponent) {
94
+ if (lastComponent instanceof Box) {
95
+ const component = lastComponent;
96
+ state.callComponent = component;
97
+ return component;
98
+ }
99
+ if (state.callComponent) {
100
+ return state.callComponent;
101
+ }
102
+ const component = createEditCallRenderComponent();
103
+ state.callComponent = component;
104
+ return component;
105
+ }
106
+ function getRenderablePreviewInput(args) {
107
+ if (!args) {
108
+ return null;
109
+ }
110
+ const path = typeof args.path === "string" ? args.path : typeof args.file_path === "string" ? args.file_path : null;
111
+ if (!path) {
112
+ return null;
113
+ }
114
+ if (Array.isArray(args.edits) &&
115
+ args.edits.length > 0 &&
116
+ args.edits.every((edit) => typeof edit?.oldText === "string" && typeof edit?.newText === "string")) {
117
+ return { path, edits: args.edits };
118
+ }
119
+ if (typeof args.oldText === "string" && typeof args.newText === "string") {
120
+ return { path, edits: [{ oldText: args.oldText, newText: args.newText }] };
121
+ }
122
+ return null;
123
+ }
124
+ function formatEditCall(args, theme, cwd) {
125
+ const pathDisplay = renderToolPath(str(args?.file_path ?? args?.path), theme, cwd);
126
+ return `${theme.fg("toolTitle", theme.bold("edit"))} ${pathDisplay}`;
127
+ }
128
+ function formatEditResult(args, preview, result, theme, isError) {
129
+ const rawPath = str(args?.file_path ?? args?.path);
130
+ const previewDiff = preview && !("error" in preview) ? preview.diff : undefined;
131
+ const previewError = preview && "error" in preview ? preview.error : undefined;
132
+ if (isError) {
133
+ const errorText = result.content
134
+ .filter((c) => c.type === "text")
135
+ .map((c) => c.text || "")
136
+ .join("\n");
137
+ if (!errorText || errorText === previewError) {
138
+ return undefined;
139
+ }
140
+ return theme.fg("error", errorText);
141
+ }
142
+ const resultDiff = result.details?.diff;
143
+ if (resultDiff && resultDiff !== previewDiff) {
144
+ return renderDiff(resultDiff, { filePath: rawPath ?? undefined });
145
+ }
146
+ return undefined;
147
+ }
148
+ function getEditHeaderBg(preview, settledError, theme) {
149
+ if (preview) {
150
+ if ("error" in preview) {
151
+ return (text) => theme.bg("toolErrorBg", text);
152
+ }
153
+ return (text) => theme.bg("toolSuccessBg", text);
154
+ }
155
+ if (settledError) {
156
+ return (text) => theme.bg("toolErrorBg", text);
157
+ }
158
+ return (text) => theme.bg("toolPendingBg", text);
159
+ }
160
+ function buildEditCallComponent(component, args, theme, cwd) {
161
+ component.setBgFn(getEditHeaderBg(component.preview, component.settledError, theme));
162
+ component.clear();
163
+ component.addChild(new Text(formatEditCall(args, theme, cwd), 0, 0));
164
+ if (!component.preview) {
165
+ return component;
166
+ }
167
+ const body = "error" in component.preview ? theme.fg("error", component.preview.error) : renderDiff(component.preview.diff);
168
+ component.addChild(new Spacer(1));
169
+ component.addChild(new Text(body, 0, 0));
170
+ return component;
171
+ }
172
+ function setEditPreview(component, preview, argsKey) {
173
+ const current = component.preview;
174
+ const changed = current === undefined ||
175
+ ("error" in current && "error" in preview
176
+ ? current.error !== preview.error
177
+ : "error" in current !== "error" in preview) ||
178
+ (!("error" in current) &&
179
+ !("error" in preview) &&
180
+ (current.diff !== preview.diff || current.firstChangedLine !== preview.firstChangedLine));
181
+ component.preview = preview;
182
+ component.previewArgsKey = argsKey;
183
+ component.previewPending = false;
184
+ return changed;
185
+ }
186
+ export function createEditToolDefinition(cwd, options) {
187
+ const ops = options?.operations ?? defaultEditOperations;
188
+ return {
189
+ name: "edit",
190
+ label: "edit",
191
+ description: "Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.",
192
+ promptSnippet: editToolSystemPromptContribution.snippet,
193
+ promptGuidelines: [...editToolSystemPromptContribution.guidelines],
194
+ parameters: editSchema,
195
+ constrainedSampling: { type: "json_schema", strict: "prefer" },
196
+ renderShell: "self",
197
+ prepareArguments: prepareEditArguments,
198
+ async execute(_toolCallId, input, signal, _onUpdate, ctx) {
199
+ const { path, edits } = validateEditInput(input);
200
+ const absolutePath = resolveToCwd(path, ctx?.cwd || cwd);
201
+ return withFileMutationQueue(absolutePath, async () => {
202
+ // Do not reject from an abort event listener here: that would release the
203
+ // mutation queue while an in-flight filesystem operation may still finish.
204
+ // Checking signal.aborted after each await observes the same aborts while
205
+ // keeping the queue locked until the current operation has settled.
206
+ const throwIfAborted = () => {
207
+ if (signal?.aborted)
208
+ throw new Error(t("Operation aborted"));
209
+ };
210
+ throwIfAborted();
211
+ // Check if file exists.
212
+ try {
213
+ await ops.access(absolutePath);
214
+ }
215
+ catch (error) {
216
+ throwIfAborted();
217
+ const errorMessage = error instanceof Error && "code" in error ? `Error code: ${error.code}` : String(error);
218
+ throw new Error(t("Could not edit file: {path}. {errorMessage}.", { path, errorMessage }));
219
+ }
220
+ throwIfAborted();
221
+ // Read the file.
222
+ const buffer = await ops.readFile(absolutePath);
223
+ const rawContent = buffer.toString("utf-8");
224
+ throwIfAborted();
225
+ // Strip BOM before matching. The model will not include an invisible BOM in oldText.
226
+ const { bom, text: content } = splitBom(rawContent);
227
+ const originalEnding = detectLineEnding(content);
228
+ const normalizedContent = normalizeToLF(content);
229
+ const { baseContent, newContent } = applyEditsToNormalizedContent(normalizedContent, edits, path);
230
+ throwIfAborted();
231
+ const finalContent = bom + restoreLineEndings(newContent, originalEnding);
232
+ await ops.writeFile(absolutePath, finalContent);
233
+ throwIfAborted();
234
+ const diffResult = generateDiffString(baseContent, newContent);
235
+ const patch = generateUnifiedPatch(path, baseContent, newContent);
236
+ return {
237
+ content: [
238
+ {
239
+ type: "text",
240
+ text: `Successfully replaced ${edits.length} block(s) in ${path}.`,
241
+ },
242
+ ],
243
+ details: { diff: diffResult.diff, patch, firstChangedLine: diffResult.firstChangedLine },
244
+ };
245
+ });
246
+ },
247
+ renderCall(args, theme, context) {
248
+ const component = getEditCallRenderComponent(context.state, context.lastComponent);
249
+ const previewInput = getRenderablePreviewInput(args);
250
+ const argsKey = previewInput
251
+ ? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })
252
+ : undefined;
253
+ if (component.previewArgsKey !== argsKey) {
254
+ component.preview = undefined;
255
+ component.previewArgsKey = argsKey;
256
+ component.previewPending = false;
257
+ component.settledError = false;
258
+ }
259
+ if (context.argsComplete && previewInput && !component.preview && !component.previewPending) {
260
+ component.previewPending = true;
261
+ const requestKey = argsKey;
262
+ void computeEditsDiff(previewInput.path, previewInput.edits, context.cwd).then((preview) => {
263
+ if (component.previewArgsKey === requestKey) {
264
+ setEditPreview(component, preview, requestKey);
265
+ context.invalidate();
266
+ }
267
+ });
268
+ }
269
+ return buildEditCallComponent(component, args, theme, context.cwd);
270
+ },
271
+ renderResult(result, _options, theme, context) {
272
+ const callComponent = context.state.callComponent;
273
+ const previewInput = getRenderablePreviewInput(context.args);
274
+ const argsKey = previewInput
275
+ ? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })
276
+ : undefined;
277
+ const typedResult = result;
278
+ const resultDiff = !context.isError ? typedResult.details?.diff : undefined;
279
+ let changed = false;
280
+ if (callComponent) {
281
+ if (typeof resultDiff === "string") {
282
+ changed =
283
+ setEditPreview(callComponent, { diff: resultDiff, firstChangedLine: typedResult.details?.firstChangedLine }, argsKey) || changed;
284
+ }
285
+ if (callComponent.settledError !== context.isError) {
286
+ callComponent.settledError = context.isError;
287
+ changed = true;
288
+ }
289
+ if (changed) {
290
+ buildEditCallComponent(callComponent, context.args, theme, context.cwd);
291
+ }
292
+ }
293
+ const output = formatEditResult(context.args, callComponent?.preview, typedResult, theme, context.isError);
294
+ const component = context.lastComponent ?? new Container();
295
+ component.clear();
296
+ if (!output) {
297
+ return component;
298
+ }
299
+ component.addChild(new Spacer(1));
300
+ component.addChild(new Text(output, 1, 0));
301
+ return component;
302
+ },
303
+ };
304
+ }
305
+ export function createEditTool(cwd, options) {
306
+ return wrapToolDefinition(createEditToolDefinition(cwd, options));
307
+ }
308
+ //# sourceMappingURL=edit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit.js","sourceRoot":"","sources":["../src/edit.ts"],"names":[],"mappings":"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","sourcesContent":["// Migrated from packages/agent-forge/src/core/tools/edit (batch 1, copy-then-delete);\n// host modules are imported through supply keys (@agent-forge/host/*).\n\nimport { constants } from \"node:fs\";\nimport { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from \"node:fs/promises\";\nimport { renderDiff } from \"@agent-forge/host/diff\";\nimport {\n\tapplyEditsToNormalizedContent,\n\tcomputeEditsDiff,\n\tdetectLineEnding,\n\ttype Edit,\n\ttype EditDiffError,\n\ttype EditDiffResult,\n\tgenerateDiffString,\n\tgenerateUnifiedPatch,\n\tnormalizeToLF,\n\trestoreLineEndings,\n} from \"@agent-forge/host/edit-diff\";\nimport { withFileMutationQueue } from \"@agent-forge/host/file-mutation-queue\";\nimport { t } from \"@agent-forge/host/i18n\";\nimport { resolveToCwd } from \"@agent-forge/host/path-utils\";\nimport { renderToolPath, str } from \"@agent-forge/host/render-utils\";\nimport { splitBom } from \"@agent-forge/host/text\";\nimport type { Theme } from \"@agent-forge/host/theme\";\nimport type { ToolDefinition, ToolExecuteContext } from \"@agent-forge/host/tool-definition\";\nimport { Box, Container, Spacer, Text } from \"@agent-forge/tui\";\nimport { type Static, Type } from \"typebox\";\nimport { type AgentTool, wrapToolDefinition } from \"./types.ts\";\n\ntype EditPreview = EditDiffResult | EditDiffError;\n\ntype EditRenderState = {\n\tcallComponent?: EditCallRenderComponent;\n};\n\nconst replaceEditSchema = Type.Object(\n\t{\n\t\toldText: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call.\",\n\t\t}),\n\t\tnewText: Type.String({ description: \"Replacement text for this targeted edit.\" }),\n\t},\n\t{},\n);\n\nconst editSchema = Type.Object(\n\t{\n\t\tpath: Type.String({ description: \"Path to the file to edit (relative or absolute)\" }),\n\t\tedits: Type.Array(replaceEditSchema, {\n\t\t\tdescription:\n\t\t\t\t\"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead.\",\n\t\t}),\n\t},\n\t{},\n);\n\nexport const editToolSystemPromptContribution = {\n\tsnippet: \"Make precise file edits with exact text replacement, including multiple disjoint edits in one call\",\n\tguidelines: [\n\t\t\"Use edit for precise changes (edits[].oldText must match exactly)\",\n\t\t\"When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls\",\n\t\t\"Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.\",\n\t\t\"Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.\",\n\t],\n} as const;\n\nexport type EditToolInput = Static<typeof editSchema>;\ntype LegacyEditToolInput = EditToolInput & {\n\toldText?: unknown;\n\tnewText?: unknown;\n};\n\ntype SingleEditInput = { oldText: string; newText: string };\n\nfunction isSingleEditInput(value: unknown): value is SingleEditInput {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\treturn false;\n\t}\n\n\tconst edit = value as Record<string, unknown>;\n\treturn typeof edit.oldText === \"string\" && typeof edit.newText === \"string\";\n}\n\nexport interface EditToolDetails {\n\t/** Display-oriented diff of the changes made */\n\tdiff: string;\n\t/** Standard unified patch of the changes made */\n\tpatch: string;\n\t/** Line number of the first change in the new file (for editor navigation) */\n\tfirstChangedLine?: number;\n}\n\n/**\n * Pluggable operations for the edit tool.\n * Override these to delegate file editing to remote systems (for example SSH).\n */\nexport interface EditOperations {\n\t/** Read file contents as a Buffer */\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\t/** Write content to a file */\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\t/** Check if file is readable and writable (throw if not) */\n\taccess: (absolutePath: string) => Promise<void>;\n}\n\nconst defaultEditOperations: EditOperations = {\n\treadFile: (path) => fsReadFile(path),\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\taccess: (path) => fsAccess(path, constants.R_OK | constants.W_OK),\n};\n\nexport interface EditToolOptions {\n\t/** Custom operations for file editing. Default: local filesystem */\n\toperations?: EditOperations;\n}\n\nfunction prepareEditArguments(input: unknown): EditToolInput {\n\tif (!input || typeof input !== \"object\") {\n\t\treturn input as EditToolInput;\n\t}\n\n\tconst args = input as Record<string, unknown>;\n\n\t// Some models (Opus 4.6, GLM-5.1) send edits as a JSON string instead of an array.\n\t// Others send a single edit object instead of a one-element edits array.\n\tif (typeof args.edits === \"string\") {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(args.edits);\n\t\t\tif (Array.isArray(parsed)) {\n\t\t\t\targs.edits = parsed;\n\t\t\t} else if (isSingleEditInput(parsed)) {\n\t\t\t\targs.edits = [parsed];\n\t\t\t}\n\t\t} catch {}\n\t} else if (isSingleEditInput(args.edits)) {\n\t\targs.edits = [args.edits];\n\t}\n\n\tconst legacy = args as LegacyEditToolInput;\n\tif (typeof legacy.oldText !== \"string\" || typeof legacy.newText !== \"string\") {\n\t\treturn args as EditToolInput;\n\t}\n\n\tconst edits = Array.isArray(legacy.edits) ? [...legacy.edits] : [];\n\tedits.push({ oldText: legacy.oldText, newText: legacy.newText });\n\tconst { oldText: _oldText, newText: _newText, ...rest } = legacy;\n\treturn { ...rest, edits } as EditToolInput;\n}\n\nfunction validateEditInput(input: EditToolInput): { path: string; edits: Edit[] } {\n\tif (!Array.isArray(input.edits) || input.edits.length === 0) {\n\t\tthrow new Error(t(\"Edit tool input is invalid. edits must contain at least one replacement.\"));\n\t}\n\treturn { path: input.path, edits: input.edits };\n}\n\ntype RenderableEditArgs = {\n\tpath?: string;\n\tfile_path?: string;\n\tedits?: Edit[];\n\toldText?: string;\n\tnewText?: string;\n};\n\ntype EditToolResultLike = {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tdetails?: EditToolDetails;\n};\n\ntype EditCallRenderComponent = Box & {\n\tpreview?: EditPreview;\n\tpreviewArgsKey?: string;\n\tpreviewPending?: boolean;\n\tsettledError?: boolean;\n};\n\nfunction createEditCallRenderComponent(): EditCallRenderComponent {\n\treturn Object.assign(new Box(1, 1, (text: string) => text), {\n\t\tpreview: undefined as EditPreview | undefined,\n\t\tpreviewArgsKey: undefined as string | undefined,\n\t\tpreviewPending: false,\n\t\tsettledError: false,\n\t});\n}\n\nfunction getEditCallRenderComponent(state: EditRenderState, lastComponent: unknown): EditCallRenderComponent {\n\tif (lastComponent instanceof Box) {\n\t\tconst component = lastComponent as EditCallRenderComponent;\n\t\tstate.callComponent = component;\n\t\treturn component;\n\t}\n\tif (state.callComponent) {\n\t\treturn state.callComponent;\n\t}\n\tconst component = createEditCallRenderComponent();\n\tstate.callComponent = component;\n\treturn component;\n}\n\nfunction getRenderablePreviewInput(args: RenderableEditArgs | undefined): { path: string; edits: Edit[] } | null {\n\tif (!args) {\n\t\treturn null;\n\t}\n\n\tconst path = typeof args.path === \"string\" ? args.path : typeof args.file_path === \"string\" ? args.file_path : null;\n\tif (!path) {\n\t\treturn null;\n\t}\n\n\tif (\n\t\tArray.isArray(args.edits) &&\n\t\targs.edits.length > 0 &&\n\t\targs.edits.every((edit) => typeof edit?.oldText === \"string\" && typeof edit?.newText === \"string\")\n\t) {\n\t\treturn { path, edits: args.edits };\n\t}\n\n\tif (typeof args.oldText === \"string\" && typeof args.newText === \"string\") {\n\t\treturn { path, edits: [{ oldText: args.oldText, newText: args.newText }] };\n\t}\n\n\treturn null;\n}\n\nfunction formatEditCall(args: RenderableEditArgs | undefined, theme: Theme, cwd: string): string {\n\tconst pathDisplay = renderToolPath(str(args?.file_path ?? args?.path), theme, cwd);\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"edit\"))} ${pathDisplay}`;\n}\n\nfunction formatEditResult(\n\targs: RenderableEditArgs | undefined,\n\tpreview: EditPreview | undefined,\n\tresult: EditToolResultLike,\n\ttheme: Theme,\n\tisError: boolean,\n): string | undefined {\n\tconst rawPath = str(args?.file_path ?? args?.path);\n\tconst previewDiff = preview && !(\"error\" in preview) ? preview.diff : undefined;\n\tconst previewError = preview && \"error\" in preview ? preview.error : undefined;\n\tif (isError) {\n\t\tconst errorText = result.content\n\t\t\t.filter((c) => c.type === \"text\")\n\t\t\t.map((c) => c.text || \"\")\n\t\t\t.join(\"\\n\");\n\t\tif (!errorText || errorText === previewError) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn theme.fg(\"error\", errorText);\n\t}\n\n\tconst resultDiff = result.details?.diff;\n\tif (resultDiff && resultDiff !== previewDiff) {\n\t\treturn renderDiff(resultDiff, { filePath: rawPath ?? undefined });\n\t}\n\n\treturn undefined;\n}\n\nfunction getEditHeaderBg(\n\tpreview: EditPreview | undefined,\n\tsettledError: boolean | undefined,\n\ttheme: Theme,\n): (text: string) => string {\n\tif (preview) {\n\t\tif (\"error\" in preview) {\n\t\t\treturn (text: string) => theme.bg(\"toolErrorBg\", text);\n\t\t}\n\t\treturn (text: string) => theme.bg(\"toolSuccessBg\", text);\n\t}\n\tif (settledError) {\n\t\treturn (text: string) => theme.bg(\"toolErrorBg\", text);\n\t}\n\treturn (text: string) => theme.bg(\"toolPendingBg\", text);\n}\n\nfunction buildEditCallComponent(\n\tcomponent: EditCallRenderComponent,\n\targs: RenderableEditArgs | undefined,\n\ttheme: Theme,\n\tcwd: string,\n): EditCallRenderComponent {\n\tcomponent.setBgFn(getEditHeaderBg(component.preview, component.settledError, theme));\n\tcomponent.clear();\n\tcomponent.addChild(new Text(formatEditCall(args, theme, cwd), 0, 0));\n\n\tif (!component.preview) {\n\t\treturn component;\n\t}\n\n\tconst body =\n\t\t\"error\" in component.preview ? theme.fg(\"error\", component.preview.error) : renderDiff(component.preview.diff);\n\tcomponent.addChild(new Spacer(1));\n\tcomponent.addChild(new Text(body, 0, 0));\n\treturn component;\n}\n\nfunction setEditPreview(\n\tcomponent: EditCallRenderComponent,\n\tpreview: EditPreview,\n\targsKey: string | undefined,\n): boolean {\n\tconst current = component.preview;\n\tconst changed =\n\t\tcurrent === undefined ||\n\t\t(\"error\" in current && \"error\" in preview\n\t\t\t? current.error !== preview.error\n\t\t\t: \"error\" in current !== \"error\" in preview) ||\n\t\t(!(\"error\" in current) &&\n\t\t\t!(\"error\" in preview) &&\n\t\t\t(current.diff !== preview.diff || current.firstChangedLine !== preview.firstChangedLine));\n\tcomponent.preview = preview;\n\tcomponent.previewArgsKey = argsKey;\n\tcomponent.previewPending = false;\n\treturn changed;\n}\n\nexport function createEditToolDefinition(\n\tcwd: string,\n\toptions?: EditToolOptions,\n): ToolDefinition<typeof editSchema, EditToolDetails | undefined, EditRenderState> {\n\tconst ops = options?.operations ?? defaultEditOperations;\n\treturn {\n\t\tname: \"edit\",\n\t\tlabel: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.\",\n\t\tpromptSnippet: editToolSystemPromptContribution.snippet,\n\t\tpromptGuidelines: [...editToolSystemPromptContribution.guidelines],\n\t\tparameters: editSchema,\n\t\tconstrainedSampling: { type: \"json_schema\", strict: \"prefer\" },\n\t\trenderShell: \"self\",\n\t\tprepareArguments: prepareEditArguments,\n\t\tasync execute(_toolCallId, input: EditToolInput, signal?: AbortSignal, _onUpdate?, ctx?: ToolExecuteContext) {\n\t\t\tconst { path, edits } = validateEditInput(input);\n\t\t\tconst absolutePath = resolveToCwd(path, ctx?.cwd || cwd);\n\n\t\t\treturn withFileMutationQueue(absolutePath, async () => {\n\t\t\t\t// Do not reject from an abort event listener here: that would release the\n\t\t\t\t// mutation queue while an in-flight filesystem operation may still finish.\n\t\t\t\t// Checking signal.aborted after each await observes the same aborts while\n\t\t\t\t// keeping the queue locked until the current operation has settled.\n\t\t\t\tconst throwIfAborted = (): void => {\n\t\t\t\t\tif (signal?.aborted) throw new Error(t(\"Operation aborted\"));\n\t\t\t\t};\n\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\t// Check if file exists.\n\t\t\t\ttry {\n\t\t\t\t\tawait ops.access(absolutePath);\n\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\tthrowIfAborted();\n\t\t\t\t\tconst errorMessage =\n\t\t\t\t\t\terror instanceof Error && \"code\" in error ? `Error code: ${error.code}` : String(error);\n\t\t\t\t\tthrow new Error(t(\"Could not edit file: {path}. {errorMessage}.\", { path, errorMessage }));\n\t\t\t\t}\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\t// Read the file.\n\t\t\t\tconst buffer = await ops.readFile(absolutePath);\n\t\t\t\tconst rawContent = buffer.toString(\"utf-8\");\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\t// Strip BOM before matching. The model will not include an invisible BOM in oldText.\n\t\t\t\tconst { bom, text: content } = splitBom(rawContent);\n\t\t\t\tconst originalEnding = detectLineEnding(content);\n\t\t\t\tconst normalizedContent = normalizeToLF(content);\n\t\t\t\tconst { baseContent, newContent } = applyEditsToNormalizedContent(normalizedContent, edits, path);\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\tconst finalContent = bom + restoreLineEndings(newContent, originalEnding);\n\t\t\t\tawait ops.writeFile(absolutePath, finalContent);\n\t\t\t\tthrowIfAborted();\n\n\t\t\t\tconst diffResult = generateDiffString(baseContent, newContent);\n\t\t\t\tconst patch = generateUnifiedPatch(path, baseContent, newContent);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Successfully replaced ${edits.length} block(s) in ${path}.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { diff: diffResult.diff, patch, firstChangedLine: diffResult.firstChangedLine },\n\t\t\t\t};\n\t\t\t});\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst component = getEditCallRenderComponent(context.state, context.lastComponent);\n\t\t\tconst previewInput = getRenderablePreviewInput(args as RenderableEditArgs | undefined);\n\t\t\tconst argsKey = previewInput\n\t\t\t\t? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })\n\t\t\t\t: undefined;\n\n\t\t\tif (component.previewArgsKey !== argsKey) {\n\t\t\t\tcomponent.preview = undefined;\n\t\t\t\tcomponent.previewArgsKey = argsKey;\n\t\t\t\tcomponent.previewPending = false;\n\t\t\t\tcomponent.settledError = false;\n\t\t\t}\n\n\t\t\tif (context.argsComplete && previewInput && !component.preview && !component.previewPending) {\n\t\t\t\tcomponent.previewPending = true;\n\t\t\t\tconst requestKey = argsKey;\n\t\t\t\tvoid computeEditsDiff(previewInput.path, previewInput.edits, context.cwd).then((preview) => {\n\t\t\t\t\tif (component.previewArgsKey === requestKey) {\n\t\t\t\t\t\tsetEditPreview(component, preview, requestKey);\n\t\t\t\t\t\tcontext.invalidate();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn buildEditCallComponent(component, args, theme, context.cwd);\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst callComponent = context.state.callComponent;\n\t\t\tconst previewInput = getRenderablePreviewInput(context.args as RenderableEditArgs | undefined);\n\t\t\tconst argsKey = previewInput\n\t\t\t\t? JSON.stringify({ path: previewInput.path, edits: previewInput.edits })\n\t\t\t\t: undefined;\n\t\t\tconst typedResult = result as EditToolResultLike;\n\t\t\tconst resultDiff = !context.isError ? typedResult.details?.diff : undefined;\n\t\t\tlet changed = false;\n\t\t\tif (callComponent) {\n\t\t\t\tif (typeof resultDiff === \"string\") {\n\t\t\t\t\tchanged =\n\t\t\t\t\t\tsetEditPreview(\n\t\t\t\t\t\t\tcallComponent,\n\t\t\t\t\t\t\t{ diff: resultDiff, firstChangedLine: typedResult.details?.firstChangedLine },\n\t\t\t\t\t\t\targsKey,\n\t\t\t\t\t\t) || changed;\n\t\t\t\t}\n\t\t\t\tif (callComponent.settledError !== context.isError) {\n\t\t\t\t\tcallComponent.settledError = context.isError;\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t\tif (changed) {\n\t\t\t\t\tbuildEditCallComponent(\n\t\t\t\t\t\tcallComponent,\n\t\t\t\t\t\tcontext.args as RenderableEditArgs | undefined,\n\t\t\t\t\t\ttheme,\n\t\t\t\t\t\tcontext.cwd,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst output = formatEditResult(context.args, callComponent?.preview, typedResult, theme, context.isError);\n\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\tcomponent.clear();\n\t\t\tif (!output) {\n\t\t\t\treturn component;\n\t\t\t}\n\t\t\tcomponent.addChild(new Spacer(1));\n\t\t\tcomponent.addChild(new Text(output, 1, 0));\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema> {\n\treturn wrapToolDefinition(createEditToolDefinition(cwd, options));\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { CapabilityAPI } from "@agent-forge/plugin-sdk";
2
+ /**
3
+ * Default-export factory consumed by the host's `loadCapabilityPluginFromManifest`.
4
+ *
5
+ * The capability factory returns its registrations as an array, while the
6
+ * manifest loader contract expects `void` or a single `{ dispose() }` instance
7
+ * (`normalizePluginInstance` does not accept arrays). The wrapper collects the
8
+ * registrations and disposes them in reverse order on plugin teardown — the
9
+ * same disposal semantics as the host's embedded `syncRegistrationFactory`.
10
+ */
11
+ export default function createCodingToolsPluginEntry(api: CapabilityAPI): Promise<{
12
+ dispose: () => void;
13
+ }>;
14
+ //# sourceMappingURL=entry.d.ts.map