@agimon-ai/po-edit 0.0.0-alpha.0

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.
@@ -0,0 +1,170 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ let _agimon_ai_po_core_mcpFacet = require("@agimon-ai/po-core/mcpFacet");
6
+ let node_fs = require("node:fs");
7
+ let node_fs_promises = require("node:fs/promises");
8
+ let _agimon_ai_po_hashline = require("@agimon-ai/po-hashline");
9
+ let _agimon_ai_po_hashline_files = require("@agimon-ai/po-hashline/files");
10
+ let _earendil_works_pi_coding_agent = require("@earendil-works/pi-coding-agent");
11
+ let typebox = require("typebox");
12
+ //#region src/schemas/editTool.ts
13
+ const HashlineRangeSchema = typebox.Type.Object({
14
+ from: typebox.Type.String({ description: "One inclusive starting anchor, for example 5#abc. Do not paste multiple lines." }),
15
+ to: typebox.Type.String({ description: "One inclusive ending anchor, for example 8#def. Do not paste multiple lines." }),
16
+ content: typebox.Type.Optional(typebox.Type.Union([typebox.Type.String({ description: "Replacement content. Empty content deletes the selected lines." }), typebox.Type.Null({ description: "Delete the selected lines." })]))
17
+ });
18
+ const EditParamsSchema = typebox.Type.Object({
19
+ path: typebox.Type.String({ description: "Path to the file returned by a compatible hashline read or grep tool." }),
20
+ hash: typebox.Type.String({ description: "Eight-character exact-byte file tag returned by read or grep." }),
21
+ edits: typebox.Type.Array(HashlineRangeSchema, {
22
+ minItems: 1,
23
+ description: "Non-overlapping inclusive ranges from the same original snapshot."
24
+ })
25
+ });
26
+ //#endregion
27
+ //#region src/services/editTool/index.ts
28
+ function createHeadlessEditTool() {
29
+ return {
30
+ name: "edit",
31
+ label: "edit",
32
+ description: "Edit one file using its exact snapshot hash and inclusive anchors such as 5#abc from read or grep. Each from and to value must contain one anchor, not a pasted block. All ranges refer to the original snapshot. Empty or omitted content deletes a range.",
33
+ promptSnippet: "Edit files with snapshot-bound hashline ranges",
34
+ promptGuidelines: ["Copy path, hash, and anchors from the latest compatible read or grep result. Re-read after every successful edit.", "Pass one anchor such as 5#abc in each from and to value. Do not paste tagged lines or multiline blocks."],
35
+ parameters: EditParamsSchema,
36
+ executionMode: "parallel",
37
+ execute: (_toolCallId, params, signal, _onUpdate, context) => executeHashlineEdit(params, context.cwd, signal)
38
+ };
39
+ }
40
+ function assertNotAborted(signal) {
41
+ if (signal?.aborted) throw new Error("Operation aborted");
42
+ }
43
+ async function executeHashlineEdit(params, cwd, signal) {
44
+ const absolutePath = (0, _agimon_ai_po_hashline_files.resolveInputPath)(params.path, cwd);
45
+ const expectedHash = (0, _agimon_ai_po_hashline.normalizeFileTag)(params.hash);
46
+ return (0, _earendil_works_pi_coding_agent.withFileMutationQueue)(absolutePath, async () => {
47
+ assertNotAborted(signal);
48
+ await (0, node_fs_promises.access)(absolutePath, node_fs.constants.R_OK | node_fs.constants.W_OK);
49
+ const beforeBytes = await (0, node_fs_promises.readFile)(absolutePath);
50
+ assertNotAborted(signal);
51
+ const actualHash = (0, _agimon_ai_po_hashline_files.computeFileTag)(beforeBytes);
52
+ if (actualHash !== expectedHash) throw new Error(`Stale file hash ${expectedHash}. Current hash is ${actualHash}. Re-read the file and retry.`);
53
+ const hasBom = beforeBytes.subarray(0, 3).equals(Buffer.from([
54
+ 239,
55
+ 187,
56
+ 191
57
+ ]));
58
+ const decoded = (0, _agimon_ai_po_hashline_files.decodeUtf8)(beforeBytes, params.path);
59
+ const withoutBom = (0, _agimon_ai_po_hashline.stripBom)(decoded);
60
+ const before = (0, _agimon_ai_po_hashline.normalizeToLf)(withoutBom);
61
+ const applied = (0, _agimon_ai_po_hashline.applyHashlineEdits)(before, params.edits);
62
+ const editedText = restoreOriginalLineEndings(withoutBom, applied.edits);
63
+ if ((0, _agimon_ai_po_hashline.normalizeToLf)(editedText) !== applied.content) throw new Error("Could not preserve the file line endings safely. The file was not changed.");
64
+ const diff = (0, _earendil_works_pi_coding_agent.generateDiffString)(before, applied.content);
65
+ const patch = (0, _earendil_works_pi_coding_agent.generateUnifiedPatch)((0, _agimon_ai_po_hashline_files.displayPath)(absolutePath, cwd), before, applied.content);
66
+ const details = {
67
+ diff: diff.diff,
68
+ patch,
69
+ firstChangedLine: diff.firstChangedLine
70
+ };
71
+ assertNotAborted(signal);
72
+ if (!(await (0, node_fs_promises.readFile)(absolutePath)).equals(beforeBytes)) throw new Error("The file changed while the edit was being prepared. Re-read it and retry.");
73
+ assertNotAborted(signal);
74
+ if (applied.content !== before) {
75
+ const output = Buffer.from(`${hasBom ? "" : ""}${editedText}`, "utf8");
76
+ await (0, node_fs_promises.writeFile)(absolutePath, output);
77
+ }
78
+ const count = applied.edits.length;
79
+ const noun = count === 1 ? "range" : "ranges";
80
+ return {
81
+ content: [{
82
+ type: "text",
83
+ text: `${applied.content === before ? `No changes needed in ${params.path}.` : `Edited ${params.path} (${count} ${noun}).`} Re-read before editing it again.`
84
+ }],
85
+ details
86
+ };
87
+ });
88
+ }
89
+ function restoreOriginalLineEndings(original, edits) {
90
+ const tokens = tokenizeLines(original);
91
+ const defaultEnding = tokens.find((token) => token.ending !== "")?.ending ?? "\n";
92
+ for (const edit of [...edits].reverse()) {
93
+ const start = edit.from.line - 1;
94
+ const count = edit.to.line - edit.from.line + 1;
95
+ const removed = tokens.slice(start, start + count);
96
+ const inheritedEnding = removed.at(-1)?.ending ?? "";
97
+ const internalEnding = removed.find((token) => token.ending !== "")?.ending ?? defaultEnding;
98
+ const lines = replacementLines(edit.content);
99
+ if (lines.length === 0 && removed.at(-1)?.ending === "" && start > 0) {
100
+ const previous = tokens[start - 1];
101
+ if (previous) tokens[start - 1] = {
102
+ ...previous,
103
+ ending: ""
104
+ };
105
+ }
106
+ const replacements = lines.map((text, index) => ({
107
+ text,
108
+ ending: index === lines.length - 1 ? inheritedEnding : internalEnding
109
+ }));
110
+ tokens.splice(start, count, ...replacements);
111
+ }
112
+ return tokens.map((token) => `${token.text}${token.ending}`).join("");
113
+ }
114
+ function tokenizeLines(content) {
115
+ const tokens = [];
116
+ let start = 0;
117
+ for (let index = 0; index < content.length; index += 1) {
118
+ const character = content[index];
119
+ if (character !== "\r" && character !== "\n") continue;
120
+ const ending = character === "\r" && content[index + 1] === "\n" ? "\r\n" : character;
121
+ tokens.push({
122
+ text: content.slice(start, index),
123
+ ending
124
+ });
125
+ if (ending === "\r\n") index += 1;
126
+ start = index + 1;
127
+ }
128
+ tokens.push({
129
+ text: content.slice(start),
130
+ ending: ""
131
+ });
132
+ return tokens;
133
+ }
134
+ function replacementLines(content) {
135
+ if (content === null || content === "") return [];
136
+ const normalized = (0, _agimon_ai_po_hashline.normalizeToLf)(content);
137
+ const trimmed = normalized.endsWith("\n") ? normalized.slice(0, -1) : normalized;
138
+ return trimmed === "" ? [] : trimmed.split("\n");
139
+ }
140
+ //#endregion
141
+ //#region src/extensions/workspaces/sessions/(backend)/tool/edit.mcp.ts
142
+ var edit_mcp_default = (0, _agimon_ai_po_core_mcpFacet.defineMcpTool)(() => ({
143
+ ...createHeadlessEditTool(),
144
+ description: "Edit or patch an existing file in the Po session's repository on the host machine. Replace or delete line ranges using the file hash and anchors (such as 5#abc) from this server's read or grep tool. Each from and to holds one anchor; empty content deletes the range."
145
+ }));
146
+ //#endregion
147
+ //#region generated/mcp.ts
148
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
149
+ const via = (identity, value) => value === void 0 ? void 0 : {
150
+ ...identity,
151
+ ...value
152
+ };
153
+ const withWidget = (key, value) => value === void 0 ? value : {
154
+ ...value,
155
+ _meta: {
156
+ ...value._meta,
157
+ ["po/widget"]: key
158
+ }
159
+ };
160
+ const mcp = (0, _agimon_ai_po_core_mcpFacet.defineMcpPlugin)({
161
+ name: "@agimon-ai/po-edit",
162
+ session: (context) => ({ get tools() {
163
+ return [withWidget("@agimon-ai/po-edit/edit", via({ name: "edit" }, at(edit_mcp_default, context)))];
164
+ } })
165
+ });
166
+ //#endregion
167
+ exports.default = mcp;
168
+ exports.mcp = mcp;
169
+
170
+ //# sourceMappingURL=mcp.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.cjs","names":["Type","resolveInputPath","normalizeFileTag","withFileMutationQueue","access","constants","readFile","computeFileTag","decodeUtf8","stripBom","normalizeToLf","applyHashlineEdits","generateDiffString","generateUnifiedPatch","displayPath","writeFile","defineMcpTool","defineMcpPlugin","toolEdit"],"sources":["../../src/schemas/editTool.ts","../../src/services/editTool/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/edit.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { type Static, Type } from 'typebox';\n\nexport const HashlineRangeSchema = Type.Object({\n from: Type.String({ description: 'One inclusive starting anchor, for example 5#abc. Do not paste multiple lines.' }),\n to: Type.String({ description: 'One inclusive ending anchor, for example 8#def. Do not paste multiple lines.' }),\n content: Type.Optional(\n Type.Union([\n Type.String({ description: 'Replacement content. Empty content deletes the selected lines.' }),\n Type.Null({ description: 'Delete the selected lines.' }),\n ]),\n ),\n});\n\nexport const EditParamsSchema = Type.Object({\n path: Type.String({ description: 'Path to the file returned by a compatible hashline read or grep tool.' }),\n hash: Type.String({ description: 'Eight-character exact-byte file tag returned by read or grep.' }),\n edits: Type.Array(HashlineRangeSchema, {\n minItems: 1,\n description: 'Non-overlapping inclusive ranges from the same original snapshot.',\n }),\n});\n\nexport type HashlineRange = Static<typeof HashlineRangeSchema>;\nexport type EditParams = Static<typeof EditParamsSchema>;\n","import { constants } from 'node:fs';\nimport { access, readFile, writeFile } from 'node:fs/promises';\n\nimport type {\n DoomHeadlessExecutionContext,\n DoomHeadlessTool,\n DoomHeadlessToolResult,\n} from '@agimon-ai/po-core/headless';\nimport {\n applyHashlineEdits,\n normalizeFileTag,\n normalizeToLf,\n stripBom,\n type PreparedHashlineEdit,\n} from '@agimon-ai/po-hashline';\nimport { computeFileTag, decodeUtf8, displayPath, resolveInputPath } from '@agimon-ai/po-hashline/files';\nimport {\n generateDiffString,\n generateUnifiedPatch,\n withFileMutationQueue,\n type EditToolDetails,\n} from '@earendil-works/pi-coding-agent';\nimport type { ToolDefinition } from '@earendil-works/pi-coding-agent';\n\nimport { EditParamsSchema, type EditParams } from '../../schemas/editTool';\n\nexport function createHashlineEditTool(): ToolDefinition<typeof EditParamsSchema> {\n return {\n name: 'edit',\n label: 'edit',\n description:\n 'Edit one file using its exact snapshot hash and inclusive anchors such as 5#abc from read or grep. Each from and to value must contain one anchor, not a pasted block. All ranges refer to the original snapshot. Empty or omitted content deletes a range.',\n promptSnippet: 'Edit files with snapshot-bound hashline ranges',\n promptGuidelines: [\n 'Copy path, hash, and anchors from the latest compatible read or grep result. Re-read after every successful edit.',\n 'Pass one anchor such as 5#abc in each from and to value. Do not paste tagged lines or multiline blocks.',\n 'Put multiple disjoint changes to one file in one edit call. All anchors must describe the original snapshot.',\n 'Omit content or pass an empty string to delete an inclusive range. Merge overlapping ranges before calling edit.',\n ],\n parameters: EditParamsSchema,\n executionMode: 'parallel',\n async execute(_toolCallId, params, signal, _onUpdate, ctx) {\n return executeHashlineEdit(params as EditParams, ctx.cwd, signal);\n },\n };\n}\n\nexport function createHeadlessEditTool(): DoomHeadlessTool<typeof EditParamsSchema> {\n return {\n name: 'edit',\n label: 'edit',\n description:\n 'Edit one file using its exact snapshot hash and inclusive anchors such as 5#abc from read or grep. Each from and to value must contain one anchor, not a pasted block. All ranges refer to the original snapshot. Empty or omitted content deletes a range.',\n promptSnippet: 'Edit files with snapshot-bound hashline ranges',\n promptGuidelines: [\n 'Copy path, hash, and anchors from the latest compatible read or grep result. Re-read after every successful edit.',\n 'Pass one anchor such as 5#abc in each from and to value. Do not paste tagged lines or multiline blocks.',\n ],\n parameters: EditParamsSchema,\n executionMode: 'parallel',\n execute: (\n _toolCallId: string,\n params: EditParams,\n signal: AbortSignal | undefined,\n _onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n context: DoomHeadlessExecutionContext,\n ) => executeHashlineEdit(params, context.cwd, signal),\n };\n}\n\nexport function assertNotAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new Error('Operation aborted');\n}\n\nexport async function executeHashlineEdit(\n params: EditParams,\n cwd: string,\n signal: AbortSignal | undefined,\n): Promise<{ content: [{ type: 'text'; text: string }]; details: EditToolDetails }> {\n const absolutePath = resolveInputPath(params.path, cwd);\n const expectedHash = normalizeFileTag(params.hash);\n return withFileMutationQueue(absolutePath, async () => {\n assertNotAborted(signal);\n await access(absolutePath, constants.R_OK | constants.W_OK);\n const beforeBytes = await readFile(absolutePath);\n assertNotAborted(signal);\n\n const actualHash = computeFileTag(beforeBytes);\n if (actualHash !== expectedHash) {\n throw new Error(`Stale file hash ${expectedHash}. Current hash is ${actualHash}. Re-read the file and retry.`);\n }\n\n const hasBom = beforeBytes.subarray(0, 3).equals(Buffer.from([0xef, 0xbb, 0xbf]));\n const decoded = decodeUtf8(beforeBytes, params.path);\n const withoutBom = stripBom(decoded);\n const before = normalizeToLf(withoutBom);\n const applied = applyHashlineEdits(before, params.edits);\n const editedText = restoreOriginalLineEndings(withoutBom, applied.edits);\n if (normalizeToLf(editedText) !== applied.content) {\n throw new Error('Could not preserve the file line endings safely. The file was not changed.');\n }\n const diff = generateDiffString(before, applied.content);\n const patch = generateUnifiedPatch(displayPath(absolutePath, cwd), before, applied.content);\n const details: EditToolDetails = { diff: diff.diff, patch, firstChangedLine: diff.firstChangedLine };\n\n assertNotAborted(signal);\n const currentBytes = await readFile(absolutePath);\n if (!currentBytes.equals(beforeBytes)) {\n throw new Error('The file changed while the edit was being prepared. Re-read it and retry.');\n }\n assertNotAborted(signal);\n\n if (applied.content !== before) {\n const output = Buffer.from(`${hasBom ? '\\ufeff' : ''}${editedText}`, 'utf8');\n await writeFile(absolutePath, output);\n }\n\n const count = applied.edits.length;\n const noun = count === 1 ? 'range' : 'ranges';\n const message =\n applied.content === before ? `No changes needed in ${params.path}.` : `Edited ${params.path} (${count} ${noun}).`;\n return { content: [{ type: 'text', text: `${message} Re-read before editing it again.` }], details };\n });\n}\n\ninterface LineToken {\n readonly text: string;\n readonly ending: '\\r\\n' | '\\n' | '\\r' | '';\n}\n\nfunction restoreOriginalLineEndings(original: string, edits: readonly PreparedHashlineEdit[]): string {\n const tokens = tokenizeLines(original);\n const defaultEnding = tokens.find((token) => token.ending !== '')?.ending ?? '\\n';\n for (const edit of [...edits].reverse()) {\n const start = edit.from.line - 1;\n const count = edit.to.line - edit.from.line + 1;\n const removed = tokens.slice(start, start + count);\n const inheritedEnding = removed.at(-1)?.ending ?? '';\n const internalEnding = removed.find((token) => token.ending !== '')?.ending ?? defaultEnding;\n const lines = replacementLines(edit.content);\n if (lines.length === 0 && removed.at(-1)?.ending === '' && start > 0) {\n const previous = tokens[start - 1];\n if (previous) tokens[start - 1] = { ...previous, ending: '' };\n }\n const replacements = lines.map<LineToken>((text, index) => ({\n text,\n ending: index === lines.length - 1 ? inheritedEnding : internalEnding,\n }));\n tokens.splice(start, count, ...replacements);\n }\n return tokens.map((token) => `${token.text}${token.ending}`).join('');\n}\n\nfunction tokenizeLines(content: string): LineToken[] {\n const tokens: LineToken[] = [];\n let start = 0;\n for (let index = 0; index < content.length; index += 1) {\n const character = content[index];\n if (character !== '\\r' && character !== '\\n') continue;\n const ending = character === '\\r' && content[index + 1] === '\\n' ? '\\r\\n' : character;\n tokens.push({ text: content.slice(start, index), ending });\n if (ending === '\\r\\n') index += 1;\n start = index + 1;\n }\n tokens.push({ text: content.slice(start), ending: '' });\n return tokens;\n}\n\nfunction replacementLines(content: string | null): string[] {\n if (content === null || content === '') return [];\n const normalized = normalizeToLf(content);\n const trimmed = normalized.endsWith('\\n') ? normalized.slice(0, -1) : normalized;\n return trimmed === '' ? [] : trimmed.split('\\n');\n}\n","import { defineMcpTool } from '@agimon-ai/po-core/mcpFacet';\n\nimport { createHeadlessEditTool } from '../../../../../services/editTool';\n\nexport default defineMcpTool(() => ({\n ...createHeadlessEditTool(),\n description:\n \"Edit or patch an existing file in the Po session's repository on the host machine. Replace or delete line ranges using the file hash and anchors (such as 5#abc) from this server's read or grep tool. Each from and to holds one anchor; empty content deletes the range.\",\n}));\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/po-core/mcpFacet';\n\nimport toolEdit from '../src/extensions/workspaces/sessions/(backend)/tool/edit.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nconst withWidget = <T>(key: string, value: T): T => value === undefined ? value : ({\n ...value, _meta: { ...(value as { _meta?: object })._meta, [\"po/widget\"]: key },\n});\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/po-edit',\n session: (context) => ({\n get tools(): DoomMcpSessionPlugin['tools'] { return [withWidget(\"@agimon-ai/po-edit/edit\", via({ name: 'edit' }, at(toolEdit, context)))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ //#region generated/mcp.d.ts
2
+ export declare const mcp: import("@agimon-ai/po-core/mcpFacet").DoomMcpPluginDefinition;
3
+ //#endregion
4
+ export { mcp as default };
5
+ //# sourceMappingURL=mcp.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.mts","names":[],"sources":["../../generated/mcp.ts"],"mappings":";qBAea,2CAAG"}
@@ -0,0 +1,165 @@
1
+ import { defineMcpPlugin, defineMcpTool } from "@agimon-ai/po-core/mcpFacet";
2
+ import { constants } from "node:fs";
3
+ import { access, readFile, writeFile } from "node:fs/promises";
4
+ import { applyHashlineEdits, normalizeFileTag, normalizeToLf, stripBom } from "@agimon-ai/po-hashline";
5
+ import { computeFileTag, decodeUtf8, displayPath, resolveInputPath } from "@agimon-ai/po-hashline/files";
6
+ import { generateDiffString, generateUnifiedPatch, withFileMutationQueue } from "@earendil-works/pi-coding-agent";
7
+ import { Type } from "typebox";
8
+ //#region src/schemas/editTool.ts
9
+ const HashlineRangeSchema = Type.Object({
10
+ from: Type.String({ description: "One inclusive starting anchor, for example 5#abc. Do not paste multiple lines." }),
11
+ to: Type.String({ description: "One inclusive ending anchor, for example 8#def. Do not paste multiple lines." }),
12
+ content: Type.Optional(Type.Union([Type.String({ description: "Replacement content. Empty content deletes the selected lines." }), Type.Null({ description: "Delete the selected lines." })]))
13
+ });
14
+ const EditParamsSchema = Type.Object({
15
+ path: Type.String({ description: "Path to the file returned by a compatible hashline read or grep tool." }),
16
+ hash: Type.String({ description: "Eight-character exact-byte file tag returned by read or grep." }),
17
+ edits: Type.Array(HashlineRangeSchema, {
18
+ minItems: 1,
19
+ description: "Non-overlapping inclusive ranges from the same original snapshot."
20
+ })
21
+ });
22
+ //#endregion
23
+ //#region src/services/editTool/index.ts
24
+ function createHeadlessEditTool() {
25
+ return {
26
+ name: "edit",
27
+ label: "edit",
28
+ description: "Edit one file using its exact snapshot hash and inclusive anchors such as 5#abc from read or grep. Each from and to value must contain one anchor, not a pasted block. All ranges refer to the original snapshot. Empty or omitted content deletes a range.",
29
+ promptSnippet: "Edit files with snapshot-bound hashline ranges",
30
+ promptGuidelines: ["Copy path, hash, and anchors from the latest compatible read or grep result. Re-read after every successful edit.", "Pass one anchor such as 5#abc in each from and to value. Do not paste tagged lines or multiline blocks."],
31
+ parameters: EditParamsSchema,
32
+ executionMode: "parallel",
33
+ execute: (_toolCallId, params, signal, _onUpdate, context) => executeHashlineEdit(params, context.cwd, signal)
34
+ };
35
+ }
36
+ function assertNotAborted(signal) {
37
+ if (signal?.aborted) throw new Error("Operation aborted");
38
+ }
39
+ async function executeHashlineEdit(params, cwd, signal) {
40
+ const absolutePath = resolveInputPath(params.path, cwd);
41
+ const expectedHash = normalizeFileTag(params.hash);
42
+ return withFileMutationQueue(absolutePath, async () => {
43
+ assertNotAborted(signal);
44
+ await access(absolutePath, constants.R_OK | constants.W_OK);
45
+ const beforeBytes = await readFile(absolutePath);
46
+ assertNotAborted(signal);
47
+ const actualHash = computeFileTag(beforeBytes);
48
+ if (actualHash !== expectedHash) throw new Error(`Stale file hash ${expectedHash}. Current hash is ${actualHash}. Re-read the file and retry.`);
49
+ const hasBom = beforeBytes.subarray(0, 3).equals(Buffer.from([
50
+ 239,
51
+ 187,
52
+ 191
53
+ ]));
54
+ const decoded = decodeUtf8(beforeBytes, params.path);
55
+ const withoutBom = stripBom(decoded);
56
+ const before = normalizeToLf(withoutBom);
57
+ const applied = applyHashlineEdits(before, params.edits);
58
+ const editedText = restoreOriginalLineEndings(withoutBom, applied.edits);
59
+ if (normalizeToLf(editedText) !== applied.content) throw new Error("Could not preserve the file line endings safely. The file was not changed.");
60
+ const diff = generateDiffString(before, applied.content);
61
+ const patch = generateUnifiedPatch(displayPath(absolutePath, cwd), before, applied.content);
62
+ const details = {
63
+ diff: diff.diff,
64
+ patch,
65
+ firstChangedLine: diff.firstChangedLine
66
+ };
67
+ assertNotAborted(signal);
68
+ if (!(await readFile(absolutePath)).equals(beforeBytes)) throw new Error("The file changed while the edit was being prepared. Re-read it and retry.");
69
+ assertNotAborted(signal);
70
+ if (applied.content !== before) {
71
+ const output = Buffer.from(`${hasBom ? "" : ""}${editedText}`, "utf8");
72
+ await writeFile(absolutePath, output);
73
+ }
74
+ const count = applied.edits.length;
75
+ const noun = count === 1 ? "range" : "ranges";
76
+ return {
77
+ content: [{
78
+ type: "text",
79
+ text: `${applied.content === before ? `No changes needed in ${params.path}.` : `Edited ${params.path} (${count} ${noun}).`} Re-read before editing it again.`
80
+ }],
81
+ details
82
+ };
83
+ });
84
+ }
85
+ function restoreOriginalLineEndings(original, edits) {
86
+ const tokens = tokenizeLines(original);
87
+ const defaultEnding = tokens.find((token) => token.ending !== "")?.ending ?? "\n";
88
+ for (const edit of [...edits].reverse()) {
89
+ const start = edit.from.line - 1;
90
+ const count = edit.to.line - edit.from.line + 1;
91
+ const removed = tokens.slice(start, start + count);
92
+ const inheritedEnding = removed.at(-1)?.ending ?? "";
93
+ const internalEnding = removed.find((token) => token.ending !== "")?.ending ?? defaultEnding;
94
+ const lines = replacementLines(edit.content);
95
+ if (lines.length === 0 && removed.at(-1)?.ending === "" && start > 0) {
96
+ const previous = tokens[start - 1];
97
+ if (previous) tokens[start - 1] = {
98
+ ...previous,
99
+ ending: ""
100
+ };
101
+ }
102
+ const replacements = lines.map((text, index) => ({
103
+ text,
104
+ ending: index === lines.length - 1 ? inheritedEnding : internalEnding
105
+ }));
106
+ tokens.splice(start, count, ...replacements);
107
+ }
108
+ return tokens.map((token) => `${token.text}${token.ending}`).join("");
109
+ }
110
+ function tokenizeLines(content) {
111
+ const tokens = [];
112
+ let start = 0;
113
+ for (let index = 0; index < content.length; index += 1) {
114
+ const character = content[index];
115
+ if (character !== "\r" && character !== "\n") continue;
116
+ const ending = character === "\r" && content[index + 1] === "\n" ? "\r\n" : character;
117
+ tokens.push({
118
+ text: content.slice(start, index),
119
+ ending
120
+ });
121
+ if (ending === "\r\n") index += 1;
122
+ start = index + 1;
123
+ }
124
+ tokens.push({
125
+ text: content.slice(start),
126
+ ending: ""
127
+ });
128
+ return tokens;
129
+ }
130
+ function replacementLines(content) {
131
+ if (content === null || content === "") return [];
132
+ const normalized = normalizeToLf(content);
133
+ const trimmed = normalized.endsWith("\n") ? normalized.slice(0, -1) : normalized;
134
+ return trimmed === "" ? [] : trimmed.split("\n");
135
+ }
136
+ //#endregion
137
+ //#region src/extensions/workspaces/sessions/(backend)/tool/edit.mcp.ts
138
+ var edit_mcp_default = defineMcpTool(() => ({
139
+ ...createHeadlessEditTool(),
140
+ description: "Edit or patch an existing file in the Po session's repository on the host machine. Replace or delete line ranges using the file hash and anchors (such as 5#abc) from this server's read or grep tool. Each from and to holds one anchor; empty content deletes the range."
141
+ }));
142
+ //#endregion
143
+ //#region generated/mcp.ts
144
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
145
+ const via = (identity, value) => value === void 0 ? void 0 : {
146
+ ...identity,
147
+ ...value
148
+ };
149
+ const withWidget = (key, value) => value === void 0 ? value : {
150
+ ...value,
151
+ _meta: {
152
+ ...value._meta,
153
+ ["po/widget"]: key
154
+ }
155
+ };
156
+ const mcp = defineMcpPlugin({
157
+ name: "@agimon-ai/po-edit",
158
+ session: (context) => ({ get tools() {
159
+ return [withWidget("@agimon-ai/po-edit/edit", via({ name: "edit" }, at(edit_mcp_default, context)))];
160
+ } })
161
+ });
162
+ //#endregion
163
+ export { mcp as default, mcp };
164
+
165
+ //# sourceMappingURL=mcp.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.mjs","names":["toolEdit"],"sources":["../../src/schemas/editTool.ts","../../src/services/editTool/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/edit.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { type Static, Type } from 'typebox';\n\nexport const HashlineRangeSchema = Type.Object({\n from: Type.String({ description: 'One inclusive starting anchor, for example 5#abc. Do not paste multiple lines.' }),\n to: Type.String({ description: 'One inclusive ending anchor, for example 8#def. Do not paste multiple lines.' }),\n content: Type.Optional(\n Type.Union([\n Type.String({ description: 'Replacement content. Empty content deletes the selected lines.' }),\n Type.Null({ description: 'Delete the selected lines.' }),\n ]),\n ),\n});\n\nexport const EditParamsSchema = Type.Object({\n path: Type.String({ description: 'Path to the file returned by a compatible hashline read or grep tool.' }),\n hash: Type.String({ description: 'Eight-character exact-byte file tag returned by read or grep.' }),\n edits: Type.Array(HashlineRangeSchema, {\n minItems: 1,\n description: 'Non-overlapping inclusive ranges from the same original snapshot.',\n }),\n});\n\nexport type HashlineRange = Static<typeof HashlineRangeSchema>;\nexport type EditParams = Static<typeof EditParamsSchema>;\n","import { constants } from 'node:fs';\nimport { access, readFile, writeFile } from 'node:fs/promises';\n\nimport type {\n DoomHeadlessExecutionContext,\n DoomHeadlessTool,\n DoomHeadlessToolResult,\n} from '@agimon-ai/po-core/headless';\nimport {\n applyHashlineEdits,\n normalizeFileTag,\n normalizeToLf,\n stripBom,\n type PreparedHashlineEdit,\n} from '@agimon-ai/po-hashline';\nimport { computeFileTag, decodeUtf8, displayPath, resolveInputPath } from '@agimon-ai/po-hashline/files';\nimport {\n generateDiffString,\n generateUnifiedPatch,\n withFileMutationQueue,\n type EditToolDetails,\n} from '@earendil-works/pi-coding-agent';\nimport type { ToolDefinition } from '@earendil-works/pi-coding-agent';\n\nimport { EditParamsSchema, type EditParams } from '../../schemas/editTool';\n\nexport function createHashlineEditTool(): ToolDefinition<typeof EditParamsSchema> {\n return {\n name: 'edit',\n label: 'edit',\n description:\n 'Edit one file using its exact snapshot hash and inclusive anchors such as 5#abc from read or grep. Each from and to value must contain one anchor, not a pasted block. All ranges refer to the original snapshot. Empty or omitted content deletes a range.',\n promptSnippet: 'Edit files with snapshot-bound hashline ranges',\n promptGuidelines: [\n 'Copy path, hash, and anchors from the latest compatible read or grep result. Re-read after every successful edit.',\n 'Pass one anchor such as 5#abc in each from and to value. Do not paste tagged lines or multiline blocks.',\n 'Put multiple disjoint changes to one file in one edit call. All anchors must describe the original snapshot.',\n 'Omit content or pass an empty string to delete an inclusive range. Merge overlapping ranges before calling edit.',\n ],\n parameters: EditParamsSchema,\n executionMode: 'parallel',\n async execute(_toolCallId, params, signal, _onUpdate, ctx) {\n return executeHashlineEdit(params as EditParams, ctx.cwd, signal);\n },\n };\n}\n\nexport function createHeadlessEditTool(): DoomHeadlessTool<typeof EditParamsSchema> {\n return {\n name: 'edit',\n label: 'edit',\n description:\n 'Edit one file using its exact snapshot hash and inclusive anchors such as 5#abc from read or grep. Each from and to value must contain one anchor, not a pasted block. All ranges refer to the original snapshot. Empty or omitted content deletes a range.',\n promptSnippet: 'Edit files with snapshot-bound hashline ranges',\n promptGuidelines: [\n 'Copy path, hash, and anchors from the latest compatible read or grep result. Re-read after every successful edit.',\n 'Pass one anchor such as 5#abc in each from and to value. Do not paste tagged lines or multiline blocks.',\n ],\n parameters: EditParamsSchema,\n executionMode: 'parallel',\n execute: (\n _toolCallId: string,\n params: EditParams,\n signal: AbortSignal | undefined,\n _onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n context: DoomHeadlessExecutionContext,\n ) => executeHashlineEdit(params, context.cwd, signal),\n };\n}\n\nexport function assertNotAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new Error('Operation aborted');\n}\n\nexport async function executeHashlineEdit(\n params: EditParams,\n cwd: string,\n signal: AbortSignal | undefined,\n): Promise<{ content: [{ type: 'text'; text: string }]; details: EditToolDetails }> {\n const absolutePath = resolveInputPath(params.path, cwd);\n const expectedHash = normalizeFileTag(params.hash);\n return withFileMutationQueue(absolutePath, async () => {\n assertNotAborted(signal);\n await access(absolutePath, constants.R_OK | constants.W_OK);\n const beforeBytes = await readFile(absolutePath);\n assertNotAborted(signal);\n\n const actualHash = computeFileTag(beforeBytes);\n if (actualHash !== expectedHash) {\n throw new Error(`Stale file hash ${expectedHash}. Current hash is ${actualHash}. Re-read the file and retry.`);\n }\n\n const hasBom = beforeBytes.subarray(0, 3).equals(Buffer.from([0xef, 0xbb, 0xbf]));\n const decoded = decodeUtf8(beforeBytes, params.path);\n const withoutBom = stripBom(decoded);\n const before = normalizeToLf(withoutBom);\n const applied = applyHashlineEdits(before, params.edits);\n const editedText = restoreOriginalLineEndings(withoutBom, applied.edits);\n if (normalizeToLf(editedText) !== applied.content) {\n throw new Error('Could not preserve the file line endings safely. The file was not changed.');\n }\n const diff = generateDiffString(before, applied.content);\n const patch = generateUnifiedPatch(displayPath(absolutePath, cwd), before, applied.content);\n const details: EditToolDetails = { diff: diff.diff, patch, firstChangedLine: diff.firstChangedLine };\n\n assertNotAborted(signal);\n const currentBytes = await readFile(absolutePath);\n if (!currentBytes.equals(beforeBytes)) {\n throw new Error('The file changed while the edit was being prepared. Re-read it and retry.');\n }\n assertNotAborted(signal);\n\n if (applied.content !== before) {\n const output = Buffer.from(`${hasBom ? '\\ufeff' : ''}${editedText}`, 'utf8');\n await writeFile(absolutePath, output);\n }\n\n const count = applied.edits.length;\n const noun = count === 1 ? 'range' : 'ranges';\n const message =\n applied.content === before ? `No changes needed in ${params.path}.` : `Edited ${params.path} (${count} ${noun}).`;\n return { content: [{ type: 'text', text: `${message} Re-read before editing it again.` }], details };\n });\n}\n\ninterface LineToken {\n readonly text: string;\n readonly ending: '\\r\\n' | '\\n' | '\\r' | '';\n}\n\nfunction restoreOriginalLineEndings(original: string, edits: readonly PreparedHashlineEdit[]): string {\n const tokens = tokenizeLines(original);\n const defaultEnding = tokens.find((token) => token.ending !== '')?.ending ?? '\\n';\n for (const edit of [...edits].reverse()) {\n const start = edit.from.line - 1;\n const count = edit.to.line - edit.from.line + 1;\n const removed = tokens.slice(start, start + count);\n const inheritedEnding = removed.at(-1)?.ending ?? '';\n const internalEnding = removed.find((token) => token.ending !== '')?.ending ?? defaultEnding;\n const lines = replacementLines(edit.content);\n if (lines.length === 0 && removed.at(-1)?.ending === '' && start > 0) {\n const previous = tokens[start - 1];\n if (previous) tokens[start - 1] = { ...previous, ending: '' };\n }\n const replacements = lines.map<LineToken>((text, index) => ({\n text,\n ending: index === lines.length - 1 ? inheritedEnding : internalEnding,\n }));\n tokens.splice(start, count, ...replacements);\n }\n return tokens.map((token) => `${token.text}${token.ending}`).join('');\n}\n\nfunction tokenizeLines(content: string): LineToken[] {\n const tokens: LineToken[] = [];\n let start = 0;\n for (let index = 0; index < content.length; index += 1) {\n const character = content[index];\n if (character !== '\\r' && character !== '\\n') continue;\n const ending = character === '\\r' && content[index + 1] === '\\n' ? '\\r\\n' : character;\n tokens.push({ text: content.slice(start, index), ending });\n if (ending === '\\r\\n') index += 1;\n start = index + 1;\n }\n tokens.push({ text: content.slice(start), ending: '' });\n return tokens;\n}\n\nfunction replacementLines(content: string | null): string[] {\n if (content === null || content === '') return [];\n const normalized = normalizeToLf(content);\n const trimmed = normalized.endsWith('\\n') ? normalized.slice(0, -1) : normalized;\n return trimmed === '' ? [] : trimmed.split('\\n');\n}\n","import { defineMcpTool } from '@agimon-ai/po-core/mcpFacet';\n\nimport { createHeadlessEditTool } from '../../../../../services/editTool';\n\nexport default defineMcpTool(() => ({\n ...createHeadlessEditTool(),\n description:\n \"Edit or patch an existing file in the Po session's repository on the host machine. Replace or delete line ranges using the file hash and anchors (such as 5#abc) from this server's read or grep tool. Each from and to holds one anchor; empty content deletes the range.\",\n}));\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/po-core/mcpFacet';\n\nimport toolEdit from '../src/extensions/workspaces/sessions/(backend)/tool/edit.mcp';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nconst withWidget = <T>(key: string, value: T): T => value === undefined ? value : ({\n ...value, _meta: { ...(value as { _meta?: object })._meta, [\"po/widget\"]: key },\n});\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/po-edit',\n session: (context) => ({\n get tools(): DoomMcpSessionPlugin['tools'] { return [withWidget(\"@agimon-ai/po-edit/edit\", via({ name: 'edit' }, at(toolEdit, context)))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ const require_editTool = require("../editTool-BKaEjUdm.cjs");
6
+ let _agimon_ai_po_core_serverFacet = require("@agimon-ai/po-core/serverFacet");
7
+ //#region src/extensions/workspaces/sessions/(backend)/tool/edit.server.ts
8
+ var edit_server_default = (0, require("@agimon-ai/po-core/extensionFile").defineServerTool)(require_editTool.createHeadlessEditTool);
9
+ //#endregion
10
+ //#region generated/server.ts
11
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
12
+ const via = (identity, value) => value === void 0 ? void 0 : {
13
+ ...identity,
14
+ ...value
15
+ };
16
+ const facet = (0, _agimon_ai_po_core_serverFacet.defineServerPlugin)({
17
+ name: "@agimon-ai/po-edit",
18
+ session: (context) => ({ get tools() {
19
+ return [via({ name: "edit" }, at(edit_server_default, context))];
20
+ } })
21
+ });
22
+ //#endregion
23
+ exports.default = facet;
24
+ exports.facet = facet;
25
+
26
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.cjs","names":["defineServerTool","createHeadlessEditTool","defineServerPlugin","toolEdit"],"sources":["../../src/extensions/workspaces/sessions/(backend)/tool/edit.server.ts","../../generated/server.ts"],"sourcesContent":["import { defineServerTool } from '@agimon-ai/po-core/extensionFile';\n\nimport { createHeadlessEditTool } from '../../../../../services/editTool';\n\nexport default defineServerTool(createHeadlessEditTool);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\n\nimport toolEdit from '../src/extensions/workspaces/sessions/(backend)/tool/edit.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-edit',\n session: (context) => ({\n get tools(): DoomServerSessionPlugin['tools'] { return [via({ name: 'edit' }, at(toolEdit, context))]; },\n }),\n});\n\nexport default facet;\n"],"mappings":";;;;;;;AAIA,IAAA,uBAAA,6CAAeA,CAAAA,CAAAA,iBAAAA,CAAiBC,iBAAAA,sBAAsB;;;ACEtD,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAa,SAAA,GAAQC,+BAAAA,mBAAAA,CAAmB;CACtC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,QAA0C;EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,OAAO,GAAG,GAAGC,qBAAU,OAAO,CAAC,CAAC;CAAG,EACzG;AACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ //#region generated/server.d.ts
2
+ export declare const facet: import("@agimon-ai/po-core/serverFacet").DoomServerFacet;
3
+ //#endregion
4
+ export { facet as default };
5
+ //# sourceMappingURL=server.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAWa,gDAAK"}
@@ -0,0 +1,22 @@
1
+ import { n as createHeadlessEditTool } from "../editTool--RaZ-yPH.mjs";
2
+ import { defineServerPlugin } from "@agimon-ai/po-core/serverFacet";
3
+ import { defineServerTool } from "@agimon-ai/po-core/extensionFile";
4
+ //#region src/extensions/workspaces/sessions/(backend)/tool/edit.server.ts
5
+ var edit_server_default = defineServerTool(createHeadlessEditTool);
6
+ //#endregion
7
+ //#region generated/server.ts
8
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
9
+ const via = (identity, value) => value === void 0 ? void 0 : {
10
+ ...identity,
11
+ ...value
12
+ };
13
+ const facet = defineServerPlugin({
14
+ name: "@agimon-ai/po-edit",
15
+ session: (context) => ({ get tools() {
16
+ return [via({ name: "edit" }, at(edit_server_default, context))];
17
+ } })
18
+ });
19
+ //#endregion
20
+ export { facet as default, facet };
21
+
22
+ //# sourceMappingURL=server.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.mjs","names":["toolEdit"],"sources":["../../src/extensions/workspaces/sessions/(backend)/tool/edit.server.ts","../../generated/server.ts"],"sourcesContent":["import { defineServerTool } from '@agimon-ai/po-core/extensionFile';\n\nimport { createHeadlessEditTool } from '../../../../../services/editTool';\n\nexport default defineServerTool(createHeadlessEditTool);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\n\nimport toolEdit from '../src/extensions/workspaces/sessions/(backend)/tool/edit.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-edit',\n session: (context) => ({\n get tools(): DoomServerSessionPlugin['tools'] { return [via({ name: 'edit' }, at(toolEdit, context))]; },\n }),\n});\n\nexport default facet;\n"],"mappings":";;;;AAIA,IAAA,sBAAe,iBAAiB,sBAAsB;;;ACEtD,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAa,QAAQ,mBAAmB;CACtC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,QAA0C;EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,OAAO,GAAG,GAAGA,qBAAU,OAAO,CAAC,CAAC;CAAG,EACzG;AACF,CAAC"}