@agimon-ai/doompi-edit 0.0.1-alpha.47 → 0.0.1-alpha.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-contracts.cjs +7 -0
- package/dist/api-contracts.d.cts +2 -0
- package/dist/api-contracts.d.mts +2 -0
- package/dist/api-contracts.mjs +2 -0
- package/dist/extensions/mcp.cjs +160 -0
- package/dist/extensions/mcp.cjs.map +1 -0
- package/dist/extensions/mcp.d.cts +5 -0
- package/dist/extensions/mcp.d.cts.map +1 -0
- package/dist/extensions/mcp.d.mts +5 -0
- package/dist/extensions/mcp.d.mts.map +1 -0
- package/dist/extensions/mcp.mjs +155 -0
- package/dist/extensions/mcp.mjs.map +1 -0
- package/dist/extensions/pi.cjs +19 -0
- package/dist/extensions/pi.cjs.map +1 -0
- package/dist/extensions/pi.d.cts +5 -0
- package/dist/extensions/pi.d.cts.map +1 -0
- package/dist/extensions/pi.d.mts +5 -0
- package/dist/extensions/pi.d.mts.map +1 -0
- package/dist/extensions/pi.mjs +14 -0
- package/dist/extensions/pi.mjs.map +1 -0
- package/dist/extensions/server.cjs +23 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.cts +5 -0
- package/dist/extensions/server.d.cts.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +18 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/extensions/web.mjs +179 -0
- package/dist/extensions/web.mjs.map +1 -0
- package/dist/index.cjs +8 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +3 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/edit.cli.cjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/edit.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/edit.cli.mjs +8 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/edit.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/edit.server.cjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/edit.server.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/edit.server.mjs +8 -0
- package/dist/src/extensions/workspaces/sessions/(backend)/tool/edit.server.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/tool/_lib/editRenderer.cli.cjs +18 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/tool/_lib/editRenderer.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/tool/_lib/editRenderer.cli.mjs +18 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/tool/_lib/editRenderer.cli.mjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/tool/edit.cli.cjs +7 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/tool/edit.cli.cjs.map +1 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/tool/edit.cli.mjs +8 -0
- package/dist/src/extensions/workspaces/sessions/(frontend)/tool/edit.cli.mjs.map +1 -0
- package/dist/src/schemas/apiContracts.cjs +13 -0
- package/dist/src/schemas/apiContracts.cjs.map +1 -0
- package/dist/src/schemas/apiContracts.d.cts +53 -0
- package/dist/src/schemas/apiContracts.d.cts.map +1 -0
- package/dist/src/schemas/apiContracts.d.mts +53 -0
- package/dist/src/schemas/apiContracts.d.mts.map +1 -0
- package/dist/src/schemas/apiContracts.mjs +13 -0
- package/dist/src/schemas/apiContracts.mjs.map +1 -0
- package/dist/src/schemas/editTool.cjs +20 -0
- package/dist/src/schemas/editTool.cjs.map +1 -0
- package/dist/src/schemas/editTool.d.cts +20 -0
- package/dist/src/schemas/editTool.d.cts.map +1 -0
- package/dist/src/schemas/editTool.d.mts +20 -0
- package/dist/src/schemas/editTool.d.mts.map +1 -0
- package/dist/src/schemas/editTool.mjs +19 -0
- package/dist/src/schemas/editTool.mjs.map +1 -0
- package/dist/src/services/editTool/index.cjs +145 -0
- package/dist/src/services/editTool/index.cjs.map +1 -0
- package/dist/src/services/editTool/index.d.cts +15 -0
- package/dist/src/services/editTool/index.d.cts.map +1 -0
- package/dist/src/services/editTool/index.d.mts +15 -0
- package/dist/src/services/editTool/index.d.mts.map +1 -0
- package/dist/src/services/editTool/index.mjs +142 -0
- package/dist/src/services/editTool/index.mjs.map +1 -0
- package/package.json +15 -7
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
const require_apiContracts = require("./src/schemas/apiContracts.cjs");
|
|
6
|
+
exports.apiContracts = require_apiContracts.apiContracts;
|
|
7
|
+
exports.default = require_apiContracts.apiContracts;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
let _agimon_ai_doompi_core_mcp_facet = require("@agimon-ai/doompi-core/mcp-facet");
|
|
6
|
+
let node_fs = require("node:fs");
|
|
7
|
+
let node_fs_promises = require("node:fs/promises");
|
|
8
|
+
let _agimon_ai_doompi_hashline = require("@agimon-ai/doompi-hashline");
|
|
9
|
+
let _agimon_ai_doompi_hashline_files = require("@agimon-ai/doompi-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_doompi_hashline_files.resolveInputPath)(params.path, cwd);
|
|
45
|
+
const expectedHash = (0, _agimon_ai_doompi_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_doompi_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_doompi_hashline_files.decodeUtf8)(beforeBytes, params.path);
|
|
59
|
+
const withoutBom = (0, _agimon_ai_doompi_hashline.stripBom)(decoded);
|
|
60
|
+
const before = (0, _agimon_ai_doompi_hashline.normalizeToLf)(withoutBom);
|
|
61
|
+
const applied = (0, _agimon_ai_doompi_hashline.applyHashlineEdits)(before, params.edits);
|
|
62
|
+
const editedText = restoreOriginalLineEndings(withoutBom, applied.edits);
|
|
63
|
+
if ((0, _agimon_ai_doompi_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_doompi_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_doompi_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_doompi_core_mcp_facet.defineMcpTool)(createHeadlessEditTool);
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region generated/mcp.ts
|
|
145
|
+
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
146
|
+
const via = (identity, value) => ({
|
|
147
|
+
...identity,
|
|
148
|
+
...value
|
|
149
|
+
});
|
|
150
|
+
const mcp = (0, _agimon_ai_doompi_core_mcp_facet.defineMcpPlugin)({
|
|
151
|
+
name: "@agimon-ai/doompi-edit",
|
|
152
|
+
session: (context) => ({ get tools() {
|
|
153
|
+
return [via({ name: "edit" }, at(edit_mcp_default, context))];
|
|
154
|
+
} })
|
|
155
|
+
});
|
|
156
|
+
//#endregion
|
|
157
|
+
exports.default = mcp;
|
|
158
|
+
exports.mcp = mcp;
|
|
159
|
+
|
|
160
|
+
//# 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/doompi-core/headless';\nimport {\n applyHashlineEdits,\n normalizeFileTag,\n normalizeToLf,\n stripBom,\n type PreparedHashlineEdit,\n} from '@agimon-ai/doompi-hashline';\nimport { computeFileTag, decodeUtf8, displayPath, resolveInputPath } from '@agimon-ai/doompi-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/doompi-core/mcp-facet';\n\nimport { createHeadlessEditTool } from '../../../../../services/editTool';\n\nexport default defineMcpTool(createHeadlessEditTool);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/doompi-core/mcp-facet';\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 => ({ ...identity, ...(value as object) }) as T;\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/doompi-edit',\n session: (context) => ({\n get tools(): DoomMcpSessionPlugin['tools'] { return [via({ name: 'edit' }, at(toolEdit, context))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.cts","names":[],"sources":["../../generated/mcp.ts"],"mappings":";qBAUa,gDAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.mts","names":[],"sources":["../../generated/mcp.ts"],"mappings":";qBAUa,gDAAG"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { defineMcpPlugin, defineMcpTool } from "@agimon-ai/doompi-core/mcp-facet";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { access, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { applyHashlineEdits, normalizeFileTag, normalizeToLf, stripBom } from "@agimon-ai/doompi-hashline";
|
|
5
|
+
import { computeFileTag, decodeUtf8, displayPath, resolveInputPath } from "@agimon-ai/doompi-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(createHeadlessEditTool);
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region generated/mcp.ts
|
|
141
|
+
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
142
|
+
const via = (identity, value) => ({
|
|
143
|
+
...identity,
|
|
144
|
+
...value
|
|
145
|
+
});
|
|
146
|
+
const mcp = defineMcpPlugin({
|
|
147
|
+
name: "@agimon-ai/doompi-edit",
|
|
148
|
+
session: (context) => ({ get tools() {
|
|
149
|
+
return [via({ name: "edit" }, at(edit_mcp_default, context))];
|
|
150
|
+
} })
|
|
151
|
+
});
|
|
152
|
+
//#endregion
|
|
153
|
+
export { mcp as default, mcp };
|
|
154
|
+
|
|
155
|
+
//# 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/doompi-core/headless';\nimport {\n applyHashlineEdits,\n normalizeFileTag,\n normalizeToLf,\n stripBom,\n type PreparedHashlineEdit,\n} from '@agimon-ai/doompi-hashline';\nimport { computeFileTag, decodeUtf8, displayPath, resolveInputPath } from '@agimon-ai/doompi-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/doompi-core/mcp-facet';\n\nimport { createHeadlessEditTool } from '../../../../../services/editTool';\n\nexport default defineMcpTool(createHeadlessEditTool);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineMcpPlugin, type DoomMcpSessionPlugin } from '@agimon-ai/doompi-core/mcp-facet';\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 => ({ ...identity, ...(value as object) }) as T;\n\nexport const mcp = defineMcpPlugin({\n name: '@agimon-ai/doompi-edit',\n session: (context) => ({\n get tools(): DoomMcpSessionPlugin['tools'] { return [via({ name: 'edit' }, at(toolEdit, context))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
const require_edit_cli = require("../src/extensions/workspaces/sessions/(backend)/tool/edit.cli.cjs");
|
|
6
|
+
const require_edit_cli$1 = require("../src/extensions/workspaces/sessions/(frontend)/tool/edit.cli.cjs");
|
|
7
|
+
let _agimon_ai_doompi_core_pi_extension = require("@agimon-ai/doompi-core/pi-extension");
|
|
8
|
+
//#region generated/pi.ts
|
|
9
|
+
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
10
|
+
const via = (identity, value) => ({
|
|
11
|
+
...identity,
|
|
12
|
+
...value
|
|
13
|
+
});
|
|
14
|
+
const extension = (0, _agimon_ai_doompi_core_pi_extension.definePiExtension)("@agimon-ai/doompi-edit", (context) => ({ ...(0, _agimon_ai_doompi_core_pi_extension.piToolContributions)("@agimon-ai/doompi-edit", [via({ name: "edit" }, (0, _agimon_ai_doompi_core_pi_extension.withPiRenderers)(at(require_edit_cli.default, context), at(require_edit_cli$1.default, context)))]) }));
|
|
15
|
+
//#endregion
|
|
16
|
+
exports.default = extension;
|
|
17
|
+
exports.extension = extension;
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=pi.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.cjs","names":["definePiExtension","piToolContributions","withPiRenderers","toolEdit","renderEdit"],"sources":["../../generated/pi.ts"],"sourcesContent":["// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { definePiExtension, piToolContributions, withPiRenderers, type PiPluginContext } from '@agimon-ai/doompi-core/pi-extension';\n\nimport toolEdit from '../src/extensions/workspaces/sessions/(backend)/tool/edit.cli';\nimport renderEdit from '../src/extensions/workspaces/sessions/(frontend)/tool/edit.cli';\n\ntype OptionsOf<T> = T extends (context: PiPluginContext<infer Options>) => unknown ? Options : never;\ntype InferredOptions = Exclude<OptionsOf<typeof toolEdit>, undefined>;\ntype Intersect<Union> =\n (Union extends unknown ? (value: Union) => void : never) extends (value: infer Value) => void ? Value : never;\ntype ExtensionOptions = [InferredOptions] extends [never] ? undefined : Intersect<InferredOptions>;\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 => ({ ...identity, ...(value as object) }) as T;\n\nexport const extension = definePiExtension<ExtensionOptions>('@agimon-ai/doompi-edit', (context) => ({\n ...piToolContributions('@agimon-ai/doompi-edit', [via({ name: 'edit' }, withPiRenderers(at(toolEdit, context), at(renderEdit, context)))]),\n}));\n\nexport default extension;\n"],"mappings":";;;;;;;;AAaA,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,WAAuB;CAAE,GAAG;CAAU,GAAI;AAAiB;AAE9G,MAAa,aAAA,GAAYA,oCAAAA,kBAAAA,CAAoC,2BAA2B,aAAa,EACnG,IAAA,GAAGC,oCAAAA,oBAAAA,CAAoB,0BAA0B,CAAC,IAAI,EAAE,MAAM,OAAO,IAAA,GAAGC,oCAAAA,gBAAAA,CAAgB,GAAGC,iBAAAA,SAAU,OAAO,GAAG,GAAGC,mBAAAA,SAAY,OAAO,CAAC,CAAC,CAAC,CAAC,EAC3I,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.d.cts","names":[],"sources":["../../generated/pi.ts"],"mappings":";qBAiBa,iEAAS"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.d.mts","names":[],"sources":["../../generated/pi.ts"],"mappings":";qBAiBa,iEAAS"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import edit_cli_default from "../src/extensions/workspaces/sessions/(backend)/tool/edit.cli.mjs";
|
|
2
|
+
import edit_cli_default$1 from "../src/extensions/workspaces/sessions/(frontend)/tool/edit.cli.mjs";
|
|
3
|
+
import { definePiExtension, piToolContributions, withPiRenderers } from "@agimon-ai/doompi-core/pi-extension";
|
|
4
|
+
//#region generated/pi.ts
|
|
5
|
+
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
6
|
+
const via = (identity, value) => ({
|
|
7
|
+
...identity,
|
|
8
|
+
...value
|
|
9
|
+
});
|
|
10
|
+
const extension = definePiExtension("@agimon-ai/doompi-edit", (context) => ({ ...piToolContributions("@agimon-ai/doompi-edit", [via({ name: "edit" }, withPiRenderers(at(edit_cli_default, context), at(edit_cli_default$1, context)))]) }));
|
|
11
|
+
//#endregion
|
|
12
|
+
export { extension as default, extension };
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=pi.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pi.mjs","names":["toolEdit","renderEdit"],"sources":["../../generated/pi.ts"],"sourcesContent":["// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { definePiExtension, piToolContributions, withPiRenderers, type PiPluginContext } from '@agimon-ai/doompi-core/pi-extension';\n\nimport toolEdit from '../src/extensions/workspaces/sessions/(backend)/tool/edit.cli';\nimport renderEdit from '../src/extensions/workspaces/sessions/(frontend)/tool/edit.cli';\n\ntype OptionsOf<T> = T extends (context: PiPluginContext<infer Options>) => unknown ? Options : never;\ntype InferredOptions = Exclude<OptionsOf<typeof toolEdit>, undefined>;\ntype Intersect<Union> =\n (Union extends unknown ? (value: Union) => void : never) extends (value: infer Value) => void ? Value : never;\ntype ExtensionOptions = [InferredOptions] extends [never] ? undefined : Intersect<InferredOptions>;\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 => ({ ...identity, ...(value as object) }) as T;\n\nexport const extension = definePiExtension<ExtensionOptions>('@agimon-ai/doompi-edit', (context) => ({\n ...piToolContributions('@agimon-ai/doompi-edit', [via({ name: 'edit' }, withPiRenderers(at(toolEdit, context), at(renderEdit, context)))]),\n}));\n\nexport default extension;\n"],"mappings":";;;;AAaA,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,WAAuB;CAAE,GAAG;CAAU,GAAI;AAAiB;AAE9G,MAAa,YAAY,kBAAoC,2BAA2B,aAAa,EACnG,GAAG,oBAAoB,0BAA0B,CAAC,IAAI,EAAE,MAAM,OAAO,GAAG,gBAAgB,GAAGA,kBAAU,OAAO,GAAG,GAAGC,oBAAY,OAAO,CAAC,CAAC,CAAC,CAAC,EAC3I,EAAE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
const require_edit_server = require("../src/extensions/workspaces/sessions/(backend)/tool/edit.server.cjs");
|
|
6
|
+
let _agimon_ai_doompi_core_server_facet = require("@agimon-ai/doompi-core/server-facet");
|
|
7
|
+
//#region generated/server.ts
|
|
8
|
+
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
9
|
+
const via = (identity, value) => ({
|
|
10
|
+
...identity,
|
|
11
|
+
...value
|
|
12
|
+
});
|
|
13
|
+
const facet = (0, _agimon_ai_doompi_core_server_facet.defineServerPlugin)({
|
|
14
|
+
name: "@agimon-ai/doompi-edit",
|
|
15
|
+
session: (context) => ({ get tools() {
|
|
16
|
+
return [via({ name: "edit" }, at(require_edit_server.default, context))];
|
|
17
|
+
} })
|
|
18
|
+
});
|
|
19
|
+
//#endregion
|
|
20
|
+
exports.default = facet;
|
|
21
|
+
exports.facet = facet;
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=server.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.cjs","names":["defineServerPlugin","toolEdit"],"sources":["../../generated/server.ts"],"sourcesContent":["// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/server-facet';\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 => ({ ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/doompi-edit',\n session: (context) => ({\n get tools(): DoomServerSessionPlugin['tools'] { return [via({ name: 'edit' }, at(toolEdit, context))]; },\n }),\n});\n\nexport default facet;\n"],"mappings":";;;;;;;AAMA,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,WAAuB;CAAE,GAAG;CAAU,GAAI;AAAiB;AAE9G,MAAa,SAAA,GAAQA,oCAAAA,mBAAAA,CAAmB;CACtC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,QAA0C;EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,OAAO,GAAG,GAAGC,oBAAAA,SAAU,OAAO,CAAC,CAAC;CAAG,EACzG;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.cts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAUa,qDAAK"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAUa,qDAAK"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import edit_server_default from "../src/extensions/workspaces/sessions/(backend)/tool/edit.server.mjs";
|
|
2
|
+
import { defineServerPlugin } from "@agimon-ai/doompi-core/server-facet";
|
|
3
|
+
//#region generated/server.ts
|
|
4
|
+
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
5
|
+
const via = (identity, value) => ({
|
|
6
|
+
...identity,
|
|
7
|
+
...value
|
|
8
|
+
});
|
|
9
|
+
const facet = defineServerPlugin({
|
|
10
|
+
name: "@agimon-ai/doompi-edit",
|
|
11
|
+
session: (context) => ({ get tools() {
|
|
12
|
+
return [via({ name: "edit" }, at(edit_server_default, context))];
|
|
13
|
+
} })
|
|
14
|
+
});
|
|
15
|
+
//#endregion
|
|
16
|
+
export { facet as default, facet };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=server.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.mjs","names":["toolEdit"],"sources":["../../generated/server.ts"],"sourcesContent":["// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/doompi-core/server-facet';\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 => ({ ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/doompi-edit',\n session: (context) => ({\n get tools(): DoomServerSessionPlugin['tools'] { return [via({ name: 'edit' }, at(toolEdit, context))]; },\n }),\n});\n\nexport default facet;\n"],"mappings":";;;AAMA,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,WAAuB;CAAE,GAAG;CAAU,GAAI;AAAiB;AAE9G,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"}
|