@agimon-ai/doompi-read 0.0.1-alpha.48 → 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.
@@ -0,0 +1,174 @@
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_promises = require("node:fs/promises");
7
+ let _agimon_ai_doompi_hashline = require("@agimon-ai/doompi-hashline");
8
+ let _agimon_ai_doompi_hashline_files = require("@agimon-ai/doompi-hashline/files");
9
+ let _earendil_works_pi_coding_agent = require("@earendil-works/pi-coding-agent");
10
+ let typebox = require("typebox");
11
+ let _agimon_ai_doompi_config_pi_config = require("@agimon-ai/doompi-config/pi-config");
12
+ //#region src/schemas/readTool.ts
13
+ const ReadParamsSchema = typebox.Type.Object({
14
+ path: typebox.Type.String({ description: "Path to the file to read." }),
15
+ offset: typebox.Type.Optional(typebox.Type.Integer({
16
+ minimum: 1,
17
+ description: "One-based line offset."
18
+ })),
19
+ limit: typebox.Type.Optional(typebox.Type.Integer({
20
+ minimum: 1,
21
+ description: "Maximum number of lines to return."
22
+ }))
23
+ });
24
+ //#endregion
25
+ //#region src/services/readImage/index.ts
26
+ const OMITTED_NOTE = "[Image omitted: could not be resized below the inline image size limit.]";
27
+ function imageLimits() {
28
+ return (0, _agimon_ai_doompi_config_pi_config.loadPiImageSettings)();
29
+ }
30
+ async function applyImageLimits(content, limits, operations) {
31
+ if (!limits.autoResize || !content.some((part) => part.type === "image")) return content;
32
+ const resolved = [];
33
+ for (const part of content) {
34
+ if (part.type !== "image") {
35
+ resolved.push(part);
36
+ continue;
37
+ }
38
+ const resized = await operations.resize(Buffer.from(part.data, "base64"), part.mimeType, {
39
+ maxWidth: limits.maxDimension,
40
+ maxHeight: limits.maxDimension
41
+ });
42
+ if (!resized) {
43
+ resolved.push({
44
+ type: "text",
45
+ text: OMITTED_NOTE
46
+ });
47
+ continue;
48
+ }
49
+ resolved.push({
50
+ type: "image",
51
+ data: resized.data,
52
+ mimeType: resized.mimeType
53
+ });
54
+ const note = operations.formatDimensionNote(resized);
55
+ if (note) resolved.push({
56
+ type: "text",
57
+ text: note
58
+ });
59
+ }
60
+ return resolved;
61
+ }
62
+ //#endregion
63
+ //#region src/services/readTool/index.ts
64
+ const resizeOperations = {
65
+ resize: _earendil_works_pi_coding_agent.resizeImage,
66
+ formatDimensionNote: (result) => (0, _earendil_works_pi_coding_agent.formatDimensionNote)(result)
67
+ };
68
+ function assertNotAborted(signal) {
69
+ if (signal?.aborted) throw new Error("Operation aborted");
70
+ }
71
+ function isImagePath(filePath) {
72
+ const extension = filePath.toLowerCase().match(/\.([a-z0-9]+)$/u)?.[1];
73
+ if (!extension) return void 0;
74
+ return {
75
+ avif: "image/avif",
76
+ gif: "image/gif",
77
+ jpeg: "image/jpeg",
78
+ jpg: "image/jpeg",
79
+ png: "image/png",
80
+ webp: "image/webp"
81
+ }[extension];
82
+ }
83
+ async function executeHeadlessRead(params, cwd, signal) {
84
+ assertNotAborted(signal);
85
+ const absolutePath = await (0, _agimon_ai_doompi_hashline_files.resolveReadInputPath)(params.path, cwd);
86
+ const bytes = await (0, node_fs_promises.readFile)(absolutePath);
87
+ assertNotAborted(signal);
88
+ const mimeType = isImagePath(absolutePath);
89
+ if (mimeType) return { content: await applyImageLimits([{
90
+ type: "text",
91
+ text: `Read image file ${(0, _agimon_ai_doompi_hashline_files.displayPath)(absolutePath, cwd)} [${mimeType}]`
92
+ }, {
93
+ type: "image",
94
+ data: bytes.toString("base64"),
95
+ mimeType
96
+ }], imageLimits(), resizeOperations) };
97
+ if (!await (0, _agimon_ai_doompi_hashline_files.isWritableFile)(absolutePath)) return { content: [{
98
+ type: "text",
99
+ text: (0, _agimon_ai_doompi_hashline_files.decodeUtf8)(bytes, (0, _agimon_ai_doompi_hashline_files.displayPath)(absolutePath, cwd))
100
+ }] };
101
+ return createTaggedReadResult(bytes, (0, _agimon_ai_doompi_hashline_files.displayPath)(absolutePath, cwd), params);
102
+ }
103
+ function createTaggedReadResult(bytes, path, params) {
104
+ const lines = (0, _agimon_ai_doompi_hashline.splitLines)((0, _agimon_ai_doompi_hashline_files.decodeUtf8)(bytes, path));
105
+ const startIndex = params.offset === void 0 ? 0 : params.offset - 1;
106
+ if (startIndex >= lines.length) throw new Error(`Offset ${params.offset} is beyond end of file (${lines.length} lines total).`);
107
+ const endIndex = params.limit === void 0 ? lines.length : Math.min(lines.length, startIndex + params.limit);
108
+ const selected = lines.slice(startIndex, endIndex);
109
+ const header = (0, _agimon_ai_doompi_hashline.formatFileHeader)(path, (0, _agimon_ai_doompi_hashline_files.computeFileTag)(bytes));
110
+ const compactedLines = [];
111
+ const headerBytes = Buffer.byteLength(header, "utf8") + 1;
112
+ const tagged = selected.map((line, index) => {
113
+ const lineNumber = startIndex + index + 1;
114
+ const full = (0, _agimon_ai_doompi_hashline.formatTaggedLine)(line, lineNumber);
115
+ if (Buffer.byteLength(full, "utf8") <= _earendil_works_pi_coding_agent.DEFAULT_MAX_BYTES - headerBytes) return full;
116
+ compactedLines.push(lineNumber);
117
+ return (0, _agimon_ai_doompi_hashline.formatTaggedLine)((0, _earendil_works_pi_coding_agent.truncateLine)(line).text, lineNumber, "", line);
118
+ });
119
+ const truncation = (0, _earendil_works_pi_coding_agent.truncateHead)([header, ...tagged].join("\n"));
120
+ let text = truncation.content;
121
+ let details;
122
+ if (truncation.truncated) {
123
+ const shownLines = Math.max(0, truncation.outputLines - 1);
124
+ const nextOffset = startIndex + shownLines + 1;
125
+ const reason = truncation.truncatedBy === "bytes" ? `, ${(0, _earendil_works_pi_coding_agent.formatSize)(_earendil_works_pi_coding_agent.DEFAULT_MAX_BYTES)} limit` : "";
126
+ text += `\n\n[Showing ${shownLines} anchored lines${reason}. Use offset=${nextOffset} to continue.]`;
127
+ details = { truncation };
128
+ } else {
129
+ const notices = [];
130
+ if (compactedLines.length > 0) notices.push(`Lines ${compactedLines.join(", ")} shown compactly. Their anchors hash the full original lines`);
131
+ if (endIndex < lines.length) notices.push(`${lines.length - endIndex} more lines in file. Use offset=${endIndex + 1} to continue`);
132
+ if (notices.length > 0) text += `\n\n[${notices.join(". ")}.]`;
133
+ }
134
+ return {
135
+ content: [{
136
+ type: "text",
137
+ text
138
+ }],
139
+ details
140
+ };
141
+ }
142
+ function createHeadlessReadTool() {
143
+ return {
144
+ name: "read",
145
+ label: "read",
146
+ description: "Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images return native-compatible content. Text is truncated to 50.0KB.",
147
+ promptSnippet: "Read file contents with snapshot-bound line anchors",
148
+ promptGuidelines: ["Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.", "Continue large reads with offset until the required anchored lines are visible."],
149
+ parameters: ReadParamsSchema,
150
+ executionMode: "parallel",
151
+ execute: async (_toolCallId, params, signal, _onUpdate, context) => executeHeadlessRead(params, context.cwd, signal)
152
+ };
153
+ }
154
+ //#endregion
155
+ //#region src/extensions/workspaces/sessions/(backend)/tool/read.mcp.ts
156
+ var read_mcp_default = (0, _agimon_ai_doompi_core_mcp_facet.defineMcpTool)(createHeadlessReadTool);
157
+ //#endregion
158
+ //#region generated/mcp.ts
159
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
160
+ const via = (identity, value) => ({
161
+ ...identity,
162
+ ...value
163
+ });
164
+ const mcp = (0, _agimon_ai_doompi_core_mcp_facet.defineMcpPlugin)({
165
+ name: "@agimon-ai/doompi-read",
166
+ session: (context) => ({ get tools() {
167
+ return [via({ name: "read" }, at(read_mcp_default, context))];
168
+ } })
169
+ });
170
+ //#endregion
171
+ exports.default = mcp;
172
+ exports.mcp = mcp;
173
+
174
+ //# sourceMappingURL=mcp.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.cjs","names":["Type","loadPiImageSettings","resizeImage","formatDimensionNote","resolveReadInputPath","readFile","displayPath","isWritableFile","decodeUtf8","splitLines","formatFileHeader","computeFileTag","formatTaggedLine","DEFAULT_MAX_BYTES","truncateLine","truncateHead","formatSize","defineMcpTool","defineMcpPlugin","toolRead"],"sources":["../../src/schemas/readTool.ts","../../src/services/readImage/index.ts","../../src/services/readTool/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/read.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { type Static, Type } from 'typebox';\n\nexport const ReadParamsSchema = Type.Object({\n path: Type.String({ description: 'Path to the file to read.' }),\n offset: Type.Optional(Type.Integer({ minimum: 1, description: 'One-based line offset.' })),\n limit: Type.Optional(Type.Integer({ minimum: 1, description: 'Maximum number of lines to return.' })),\n});\n\nexport type ReadParams = Static<typeof ReadParamsSchema>;\n","import { loadPiImageSettings, type PiImageSettings } from '@agimon-ai/doompi-config/pi-config';\n\nconst OMITTED_NOTE = '[Image omitted: could not be resized below the inline image size limit.]';\n\nexport interface ReadTextPart {\n type: 'text';\n text: string;\n}\n\nexport interface ReadImagePart {\n type: 'image';\n data: string;\n mimeType: string;\n}\n\nexport type ReadContentPart = ReadTextPart | ReadImagePart;\n\nexport interface ReadImageResizeResult {\n readonly data: string;\n readonly mimeType: string;\n readonly originalWidth: number;\n readonly originalHeight: number;\n readonly width: number;\n readonly height: number;\n readonly wasResized: boolean;\n}\n\nexport interface ReadImageResizeOperations {\n resize(\n inputBytes: Uint8Array,\n mimeType: string,\n options: { readonly maxWidth: number; readonly maxHeight: number },\n ): Promise<ReadImageResizeResult | null>;\n formatDimensionNote(result: ReadImageResizeResult): string | undefined;\n}\n\nexport function imageLimits(): PiImageSettings {\n return loadPiImageSettings();\n}\n\nexport async function applyImageLimits(\n content: ReadContentPart[],\n limits: PiImageSettings,\n operations: ReadImageResizeOperations,\n): Promise<ReadContentPart[]> {\n if (!limits.autoResize || !content.some((part) => part.type === 'image')) return content;\n const resolved: ReadContentPart[] = [];\n for (const part of content) {\n if (part.type !== 'image') {\n resolved.push(part);\n continue;\n }\n const resized = await operations.resize(Buffer.from(part.data, 'base64'), part.mimeType, {\n maxWidth: limits.maxDimension,\n maxHeight: limits.maxDimension,\n });\n if (!resized) {\n resolved.push({ type: 'text', text: OMITTED_NOTE });\n continue;\n }\n resolved.push({ type: 'image', data: resized.data, mimeType: resized.mimeType });\n const note = operations.formatDimensionNote(resized);\n if (note) resolved.push({ type: 'text', text: note });\n }\n return resolved;\n}\n","import { readFile } from 'node:fs/promises';\n\nimport type {\n DoomHeadlessExecutionContext,\n DoomHeadlessTool,\n DoomHeadlessToolResult,\n} from '@agimon-ai/doompi-core/headless';\nimport { formatFileHeader, formatTaggedLine, splitLines } from '@agimon-ai/doompi-hashline';\nimport {\n computeFileTag,\n decodeUtf8,\n displayPath,\n isWritableFile,\n resolveReadInputPath,\n} from '@agimon-ai/doompi-hashline/files';\nimport {\n DEFAULT_MAX_BYTES,\n formatDimensionNote,\n formatSize,\n resizeImage,\n truncateHead,\n truncateLine,\n type ReadToolDetails,\n} from '@earendil-works/pi-coding-agent';\n\nimport { ReadParamsSchema, type ReadParams } from '../../schemas/readTool';\nimport {\n applyImageLimits,\n imageLimits,\n type ReadContentPart,\n type ReadImageResizeOperations,\n type ReadTextPart,\n} from '../readImage';\n\nconst resizeOperations: ReadImageResizeOperations = {\n resize: resizeImage,\n formatDimensionNote: (result) => formatDimensionNote(result),\n};\n\nexport interface ReadToolResult {\n content: ReadContentPart[];\n details?: ReadToolDetails;\n}\n\ninterface ReadTextToolResult {\n content: ReadTextPart[];\n details?: ReadToolDetails;\n}\n\nexport function assertNotAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new Error('Operation aborted');\n}\n\nexport function isImagePath(filePath: string): string | undefined {\n const extension = filePath.toLowerCase().match(/\\.([a-z0-9]+)$/u)?.[1];\n if (!extension) return undefined;\n const mimeTypes: Record<string, string> = {\n avif: 'image/avif',\n gif: 'image/gif',\n jpeg: 'image/jpeg',\n jpg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n };\n return mimeTypes[extension];\n}\n\nexport async function executeHeadlessRead(\n params: ReadParams,\n cwd: string,\n signal: AbortSignal | undefined,\n): Promise<ReadToolResult> {\n assertNotAborted(signal);\n const absolutePath = await resolveReadInputPath(params.path, cwd);\n const bytes = await readFile(absolutePath);\n assertNotAborted(signal);\n const mimeType = isImagePath(absolutePath);\n if (mimeType) {\n const content: ReadContentPart[] = [\n { type: 'text', text: `Read image file ${displayPath(absolutePath, cwd)} [${mimeType}]` },\n { type: 'image', data: bytes.toString('base64'), mimeType },\n ];\n return { content: await applyImageLimits(content, imageLimits(), resizeOperations) };\n }\n if (!(await isWritableFile(absolutePath))) {\n return { content: [{ type: 'text', text: decodeUtf8(bytes, displayPath(absolutePath, cwd)) }] };\n }\n return createTaggedReadResult(bytes, displayPath(absolutePath, cwd), params);\n}\n\nexport function createTaggedReadResult(bytes: Buffer, path: string, params: ReadParams): ReadTextToolResult {\n const lines = splitLines(decodeUtf8(bytes, path));\n const startIndex = params.offset === undefined ? 0 : params.offset - 1;\n if (startIndex >= lines.length) {\n throw new Error(`Offset ${params.offset} is beyond end of file (${lines.length} lines total).`);\n }\n\n const endIndex = params.limit === undefined ? lines.length : Math.min(lines.length, startIndex + params.limit);\n const selected = lines.slice(startIndex, endIndex);\n const header = formatFileHeader(path, computeFileTag(bytes));\n const compactedLines: number[] = [];\n const headerBytes = Buffer.byteLength(header, 'utf8') + 1;\n const tagged = selected.map((line, index) => {\n const lineNumber = startIndex + index + 1;\n const full = formatTaggedLine(line, lineNumber);\n if (Buffer.byteLength(full, 'utf8') <= DEFAULT_MAX_BYTES - headerBytes) return full;\n compactedLines.push(lineNumber);\n return formatTaggedLine(truncateLine(line).text, lineNumber, '', line);\n });\n const truncation = truncateHead([header, ...tagged].join('\\n'));\n let text = truncation.content;\n let details: ReadToolDetails | undefined;\n\n if (truncation.truncated) {\n const shownLines = Math.max(0, truncation.outputLines - 1);\n const nextOffset = startIndex + shownLines + 1;\n const reason = truncation.truncatedBy === 'bytes' ? `, ${formatSize(DEFAULT_MAX_BYTES)} limit` : '';\n text += `\\n\\n[Showing ${shownLines} anchored lines${reason}. Use offset=${nextOffset} to continue.]`;\n details = { truncation };\n } else {\n const notices: string[] = [];\n if (compactedLines.length > 0) {\n notices.push(`Lines ${compactedLines.join(', ')} shown compactly. Their anchors hash the full original lines`);\n }\n if (endIndex < lines.length) {\n notices.push(`${lines.length - endIndex} more lines in file. Use offset=${endIndex + 1} to continue`);\n }\n if (notices.length > 0) text += `\\n\\n[${notices.join('. ')}.]`;\n }\n\n return { content: [{ type: 'text', text }], details };\n}\n\nexport function isImageRead(content: readonly { readonly type: string; readonly text?: string }[]): boolean {\n return content.some((part) => part.type === 'image' || part.text?.startsWith('Read image file') === true);\n}\n\nexport function createHeadlessReadTool(): DoomHeadlessTool<typeof ReadParamsSchema> {\n return {\n name: 'read',\n label: 'read',\n description:\n 'Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images return native-compatible content. Text is truncated to 50.0KB.',\n promptSnippet: 'Read file contents with snapshot-bound line anchors',\n promptGuidelines: [\n 'Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.',\n 'Continue large reads with offset until the required anchored lines are visible.',\n ],\n parameters: ReadParamsSchema,\n executionMode: 'parallel',\n execute: async (\n _toolCallId: string,\n params: ReadParams,\n signal: AbortSignal | undefined,\n _onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n context: DoomHeadlessExecutionContext,\n ) => executeHeadlessRead(params, context.cwd, signal),\n };\n}\n","import { defineMcpTool } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport { createHeadlessReadTool } from '../../../../../services/readTool';\n\nexport default defineMcpTool(createHeadlessReadTool);\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 toolRead from '../src/extensions/workspaces/sessions/(backend)/tool/read.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-read',\n session: (context) => ({\n get tools(): DoomMcpSessionPlugin['tools'] { return [via({ name: 'read' }, at(toolRead, context))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":";;;;;;;;;;;;AAEA,MAAa,mBAAmBA,QAAAA,KAAK,OAAO;CAC1C,MAAMA,QAAAA,KAAK,OAAO,EAAE,aAAa,4BAA4B,CAAC;CAC9D,QAAQA,QAAAA,KAAK,SAASA,QAAAA,KAAK,QAAQ;EAAE,SAAS;EAAG,aAAa;CAAyB,CAAC,CAAC;CACzF,OAAOA,QAAAA,KAAK,SAASA,QAAAA,KAAK,QAAQ;EAAE,SAAS;EAAG,aAAa;CAAqC,CAAC,CAAC;AACtG,CAAC;;;ACJD,MAAM,eAAe;AAkCrB,SAAgB,cAA+B;CAC7C,QAAA,GAAOC,mCAAAA,oBAAAA,CAAoB;AAC7B;AAEA,eAAsB,iBACpB,SACA,QACA,YAC4B;CAC5B,IAAI,CAAC,OAAO,cAAc,CAAC,QAAQ,MAAM,SAAS,KAAK,SAAS,OAAO,GAAG,OAAO;CACjF,MAAM,WAA8B,CAAC;CACrC,KAAK,MAAM,QAAQ,SAAS;EAC1B,IAAI,KAAK,SAAS,SAAS;GACzB,SAAS,KAAK,IAAI;GAClB;EACF;EACA,MAAM,UAAU,MAAM,WAAW,OAAO,OAAO,KAAK,KAAK,MAAM,QAAQ,GAAG,KAAK,UAAU;GACvF,UAAU,OAAO;GACjB,WAAW,OAAO;EACpB,CAAC;EACD,IAAI,CAAC,SAAS;GACZ,SAAS,KAAK;IAAE,MAAM;IAAQ,MAAM;GAAa,CAAC;GAClD;EACF;EACA,SAAS,KAAK;GAAE,MAAM;GAAS,MAAM,QAAQ;GAAM,UAAU,QAAQ;EAAS,CAAC;EAC/E,MAAM,OAAO,WAAW,oBAAoB,OAAO;EACnD,IAAI,MAAM,SAAS,KAAK;GAAE,MAAM;GAAQ,MAAM;EAAK,CAAC;CACtD;CACA,OAAO;AACT;;;AC/BA,MAAM,mBAA8C;CAClD,QAAQC,gCAAAA;CACR,sBAAsB,YAAA,GAAWC,gCAAAA,oBAAAA,CAAoB,MAAM;AAC7D;AAYA,SAAgB,iBAAiB,QAAuC;CACtE,IAAI,QAAQ,SAAS,MAAM,IAAI,MAAM,mBAAmB;AAC1D;AAEA,SAAgB,YAAY,UAAsC;CAChE,MAAM,YAAY,SAAS,YAAY,CAAC,CAAC,MAAM,iBAAiB,CAAC,GAAG;CACpE,IAAI,CAAC,WAAW,OAAO,KAAA;CASvB,OAAO;EAPL,MAAM;EACN,KAAK;EACL,MAAM;EACN,KAAK;EACL,KAAK;EACL,MAAM;CAEO,EAAE;AACnB;AAEA,eAAsB,oBACpB,QACA,KACA,QACyB;CACzB,iBAAiB,MAAM;CACvB,MAAM,eAAe,OAAA,GAAMC,iCAAAA,qBAAAA,CAAqB,OAAO,MAAM,GAAG;CAChE,MAAM,QAAQ,OAAA,GAAMC,iBAAAA,SAAAA,CAAS,YAAY;CACzC,iBAAiB,MAAM;CACvB,MAAM,WAAW,YAAY,YAAY;CACzC,IAAI,UAKF,OAAO,EAAE,SAAS,MAAM,iBAAiB,CAHvC;EAAE,MAAM;EAAQ,MAAM,oBAAA,GAAmBC,iCAAAA,YAAAA,CAAY,cAAc,GAAG,EAAE,IAAI,SAAS;CAAG,GACxF;EAAE,MAAM;EAAS,MAAM,MAAM,SAAS,QAAQ;EAAG;CAAS,CAEnB,GAAS,YAAY,GAAG,gBAAgB,EAAE;CAErF,IAAI,CAAE,OAAA,GAAMC,iCAAAA,eAAAA,CAAe,YAAY,GACrC,OAAO,EAAE,SAAS,CAAC;EAAE,MAAM;EAAQ,OAAA,GAAMC,iCAAAA,WAAAA,CAAW,QAAA,GAAOF,iCAAAA,YAAAA,CAAY,cAAc,GAAG,CAAC;CAAE,CAAC,EAAE;CAEhG,OAAO,uBAAuB,QAAA,GAAOA,iCAAAA,YAAAA,CAAY,cAAc,GAAG,GAAG,MAAM;AAC7E;AAEA,SAAgB,uBAAuB,OAAe,MAAc,QAAwC;CAC1G,MAAM,SAAA,GAAQG,2BAAAA,WAAAA,EAAAA,GAAWD,iCAAAA,WAAAA,CAAW,OAAO,IAAI,CAAC;CAChD,MAAM,aAAa,OAAO,WAAW,KAAA,IAAY,IAAI,OAAO,SAAS;CACrE,IAAI,cAAc,MAAM,QACtB,MAAM,IAAI,MAAM,UAAU,OAAO,OAAO,0BAA0B,MAAM,OAAO,eAAe;CAGhG,MAAM,WAAW,OAAO,UAAU,KAAA,IAAY,MAAM,SAAS,KAAK,IAAI,MAAM,QAAQ,aAAa,OAAO,KAAK;CAC7G,MAAM,WAAW,MAAM,MAAM,YAAY,QAAQ;CACjD,MAAM,UAAA,GAASE,2BAAAA,iBAAAA,CAAiB,OAAA,GAAMC,iCAAAA,eAAAA,CAAe,KAAK,CAAC;CAC3D,MAAM,iBAA2B,CAAC;CAClC,MAAM,cAAc,OAAO,WAAW,QAAQ,MAAM,IAAI;CACxD,MAAM,SAAS,SAAS,KAAK,MAAM,UAAU;EAC3C,MAAM,aAAa,aAAa,QAAQ;EACxC,MAAM,QAAA,GAAOC,2BAAAA,iBAAAA,CAAiB,MAAM,UAAU;EAC9C,IAAI,OAAO,WAAW,MAAM,MAAM,KAAKC,gCAAAA,oBAAoB,aAAa,OAAO;EAC/E,eAAe,KAAK,UAAU;EAC9B,QAAA,GAAOD,2BAAAA,iBAAAA,EAAAA,GAAiBE,gCAAAA,aAAAA,CAAa,IAAI,CAAC,CAAC,MAAM,YAAY,IAAI,IAAI;CACvE,CAAC;CACD,MAAM,cAAA,GAAaC,gCAAAA,aAAAA,CAAa,CAAC,QAAQ,GAAG,MAAM,CAAC,CAAC,KAAK,IAAI,CAAC;CAC9D,IAAI,OAAO,WAAW;CACtB,IAAI;CAEJ,IAAI,WAAW,WAAW;EACxB,MAAM,aAAa,KAAK,IAAI,GAAG,WAAW,cAAc,CAAC;EACzD,MAAM,aAAa,aAAa,aAAa;EAC7C,MAAM,SAAS,WAAW,gBAAgB,UAAU,MAAA,GAAKC,gCAAAA,WAAAA,CAAWH,gCAAAA,iBAAiB,EAAE,UAAU;EACjG,QAAQ,gBAAgB,WAAW,iBAAiB,OAAO,eAAe,WAAW;EACrF,UAAU,EAAE,WAAW;CACzB,OAAO;EACL,MAAM,UAAoB,CAAC;EAC3B,IAAI,eAAe,SAAS,GAC1B,QAAQ,KAAK,SAAS,eAAe,KAAK,IAAI,EAAE,6DAA6D;EAE/G,IAAI,WAAW,MAAM,QACnB,QAAQ,KAAK,GAAG,MAAM,SAAS,SAAS,kCAAkC,WAAW,EAAE,aAAa;EAEtG,IAAI,QAAQ,SAAS,GAAG,QAAQ,QAAQ,QAAQ,KAAK,IAAI,EAAE;CAC7D;CAEA,OAAO;EAAE,SAAS,CAAC;GAAE,MAAM;GAAQ;EAAK,CAAC;EAAG;CAAQ;AACtD;AAMA,SAAgB,yBAAoE;CAClF,OAAO;EACL,MAAM;EACN,OAAO;EACP,aACE;EACF,eAAe;EACf,kBAAkB,CAChB,uHACA,iFACF;EACA,YAAY;EACZ,eAAe;EACf,SAAS,OACP,aACA,QACA,QACA,WACA,YACG,oBAAoB,QAAQ,QAAQ,KAAK,MAAM;CACtD;AACF;;;AC1JA,IAAA,oBAAA,GAAeI,iCAAAA,cAAAA,CAAc,sBAAsB;;;ACEnD,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,WAAuB;CAAE,GAAG;CAAU,GAAI;AAAiB;AAE9G,MAAa,OAAA,GAAMC,iCAAAA,gBAAAA,CAAgB;CACjC,MAAM;CACN,UAAU,aAAa,EACrB,IAAI,QAAuC;EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,OAAO,GAAG,GAAGC,kBAAU,OAAO,CAAC,CAAC;CAAG,EACtG;AACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ //#region generated/mcp.d.ts
2
+ export declare const mcp: import("@agimon-ai/doompi-core/mcp-facet").DoomMcpPluginDefinition;
3
+ //#endregion
4
+ export { mcp as default };
5
+ //# sourceMappingURL=mcp.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.cts","names":[],"sources":["../../generated/mcp.ts"],"mappings":";qBAUa,gDAAG"}
@@ -0,0 +1,5 @@
1
+ //#region generated/mcp.d.ts
2
+ export declare const mcp: import("@agimon-ai/doompi-core/mcp-facet").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":";qBAUa,gDAAG"}
@@ -0,0 +1,169 @@
1
+ import { defineMcpPlugin, defineMcpTool } from "@agimon-ai/doompi-core/mcp-facet";
2
+ import { readFile } from "node:fs/promises";
3
+ import { formatFileHeader, formatTaggedLine, splitLines } from "@agimon-ai/doompi-hashline";
4
+ import { computeFileTag, decodeUtf8, displayPath, isWritableFile, resolveReadInputPath } from "@agimon-ai/doompi-hashline/files";
5
+ import { DEFAULT_MAX_BYTES, formatDimensionNote, formatSize, resizeImage, truncateHead, truncateLine } from "@earendil-works/pi-coding-agent";
6
+ import { Type } from "typebox";
7
+ import { loadPiImageSettings } from "@agimon-ai/doompi-config/pi-config";
8
+ //#region src/schemas/readTool.ts
9
+ const ReadParamsSchema = Type.Object({
10
+ path: Type.String({ description: "Path to the file to read." }),
11
+ offset: Type.Optional(Type.Integer({
12
+ minimum: 1,
13
+ description: "One-based line offset."
14
+ })),
15
+ limit: Type.Optional(Type.Integer({
16
+ minimum: 1,
17
+ description: "Maximum number of lines to return."
18
+ }))
19
+ });
20
+ //#endregion
21
+ //#region src/services/readImage/index.ts
22
+ const OMITTED_NOTE = "[Image omitted: could not be resized below the inline image size limit.]";
23
+ function imageLimits() {
24
+ return loadPiImageSettings();
25
+ }
26
+ async function applyImageLimits(content, limits, operations) {
27
+ if (!limits.autoResize || !content.some((part) => part.type === "image")) return content;
28
+ const resolved = [];
29
+ for (const part of content) {
30
+ if (part.type !== "image") {
31
+ resolved.push(part);
32
+ continue;
33
+ }
34
+ const resized = await operations.resize(Buffer.from(part.data, "base64"), part.mimeType, {
35
+ maxWidth: limits.maxDimension,
36
+ maxHeight: limits.maxDimension
37
+ });
38
+ if (!resized) {
39
+ resolved.push({
40
+ type: "text",
41
+ text: OMITTED_NOTE
42
+ });
43
+ continue;
44
+ }
45
+ resolved.push({
46
+ type: "image",
47
+ data: resized.data,
48
+ mimeType: resized.mimeType
49
+ });
50
+ const note = operations.formatDimensionNote(resized);
51
+ if (note) resolved.push({
52
+ type: "text",
53
+ text: note
54
+ });
55
+ }
56
+ return resolved;
57
+ }
58
+ //#endregion
59
+ //#region src/services/readTool/index.ts
60
+ const resizeOperations = {
61
+ resize: resizeImage,
62
+ formatDimensionNote: (result) => formatDimensionNote(result)
63
+ };
64
+ function assertNotAborted(signal) {
65
+ if (signal?.aborted) throw new Error("Operation aborted");
66
+ }
67
+ function isImagePath(filePath) {
68
+ const extension = filePath.toLowerCase().match(/\.([a-z0-9]+)$/u)?.[1];
69
+ if (!extension) return void 0;
70
+ return {
71
+ avif: "image/avif",
72
+ gif: "image/gif",
73
+ jpeg: "image/jpeg",
74
+ jpg: "image/jpeg",
75
+ png: "image/png",
76
+ webp: "image/webp"
77
+ }[extension];
78
+ }
79
+ async function executeHeadlessRead(params, cwd, signal) {
80
+ assertNotAborted(signal);
81
+ const absolutePath = await resolveReadInputPath(params.path, cwd);
82
+ const bytes = await readFile(absolutePath);
83
+ assertNotAborted(signal);
84
+ const mimeType = isImagePath(absolutePath);
85
+ if (mimeType) return { content: await applyImageLimits([{
86
+ type: "text",
87
+ text: `Read image file ${displayPath(absolutePath, cwd)} [${mimeType}]`
88
+ }, {
89
+ type: "image",
90
+ data: bytes.toString("base64"),
91
+ mimeType
92
+ }], imageLimits(), resizeOperations) };
93
+ if (!await isWritableFile(absolutePath)) return { content: [{
94
+ type: "text",
95
+ text: decodeUtf8(bytes, displayPath(absolutePath, cwd))
96
+ }] };
97
+ return createTaggedReadResult(bytes, displayPath(absolutePath, cwd), params);
98
+ }
99
+ function createTaggedReadResult(bytes, path, params) {
100
+ const lines = splitLines(decodeUtf8(bytes, path));
101
+ const startIndex = params.offset === void 0 ? 0 : params.offset - 1;
102
+ if (startIndex >= lines.length) throw new Error(`Offset ${params.offset} is beyond end of file (${lines.length} lines total).`);
103
+ const endIndex = params.limit === void 0 ? lines.length : Math.min(lines.length, startIndex + params.limit);
104
+ const selected = lines.slice(startIndex, endIndex);
105
+ const header = formatFileHeader(path, computeFileTag(bytes));
106
+ const compactedLines = [];
107
+ const headerBytes = Buffer.byteLength(header, "utf8") + 1;
108
+ const tagged = selected.map((line, index) => {
109
+ const lineNumber = startIndex + index + 1;
110
+ const full = formatTaggedLine(line, lineNumber);
111
+ if (Buffer.byteLength(full, "utf8") <= DEFAULT_MAX_BYTES - headerBytes) return full;
112
+ compactedLines.push(lineNumber);
113
+ return formatTaggedLine(truncateLine(line).text, lineNumber, "", line);
114
+ });
115
+ const truncation = truncateHead([header, ...tagged].join("\n"));
116
+ let text = truncation.content;
117
+ let details;
118
+ if (truncation.truncated) {
119
+ const shownLines = Math.max(0, truncation.outputLines - 1);
120
+ const nextOffset = startIndex + shownLines + 1;
121
+ const reason = truncation.truncatedBy === "bytes" ? `, ${formatSize(DEFAULT_MAX_BYTES)} limit` : "";
122
+ text += `\n\n[Showing ${shownLines} anchored lines${reason}. Use offset=${nextOffset} to continue.]`;
123
+ details = { truncation };
124
+ } else {
125
+ const notices = [];
126
+ if (compactedLines.length > 0) notices.push(`Lines ${compactedLines.join(", ")} shown compactly. Their anchors hash the full original lines`);
127
+ if (endIndex < lines.length) notices.push(`${lines.length - endIndex} more lines in file. Use offset=${endIndex + 1} to continue`);
128
+ if (notices.length > 0) text += `\n\n[${notices.join(". ")}.]`;
129
+ }
130
+ return {
131
+ content: [{
132
+ type: "text",
133
+ text
134
+ }],
135
+ details
136
+ };
137
+ }
138
+ function createHeadlessReadTool() {
139
+ return {
140
+ name: "read",
141
+ label: "read",
142
+ description: "Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images return native-compatible content. Text is truncated to 50.0KB.",
143
+ promptSnippet: "Read file contents with snapshot-bound line anchors",
144
+ promptGuidelines: ["Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.", "Continue large reads with offset until the required anchored lines are visible."],
145
+ parameters: ReadParamsSchema,
146
+ executionMode: "parallel",
147
+ execute: async (_toolCallId, params, signal, _onUpdate, context) => executeHeadlessRead(params, context.cwd, signal)
148
+ };
149
+ }
150
+ //#endregion
151
+ //#region src/extensions/workspaces/sessions/(backend)/tool/read.mcp.ts
152
+ var read_mcp_default = defineMcpTool(createHeadlessReadTool);
153
+ //#endregion
154
+ //#region generated/mcp.ts
155
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
156
+ const via = (identity, value) => ({
157
+ ...identity,
158
+ ...value
159
+ });
160
+ const mcp = defineMcpPlugin({
161
+ name: "@agimon-ai/doompi-read",
162
+ session: (context) => ({ get tools() {
163
+ return [via({ name: "read" }, at(read_mcp_default, context))];
164
+ } })
165
+ });
166
+ //#endregion
167
+ export { mcp as default, mcp };
168
+
169
+ //# sourceMappingURL=mcp.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.mjs","names":["toolRead"],"sources":["../../src/schemas/readTool.ts","../../src/services/readImage/index.ts","../../src/services/readTool/index.ts","../../src/extensions/workspaces/sessions/(backend)/tool/read.mcp.ts","../../generated/mcp.ts"],"sourcesContent":["import { type Static, Type } from 'typebox';\n\nexport const ReadParamsSchema = Type.Object({\n path: Type.String({ description: 'Path to the file to read.' }),\n offset: Type.Optional(Type.Integer({ minimum: 1, description: 'One-based line offset.' })),\n limit: Type.Optional(Type.Integer({ minimum: 1, description: 'Maximum number of lines to return.' })),\n});\n\nexport type ReadParams = Static<typeof ReadParamsSchema>;\n","import { loadPiImageSettings, type PiImageSettings } from '@agimon-ai/doompi-config/pi-config';\n\nconst OMITTED_NOTE = '[Image omitted: could not be resized below the inline image size limit.]';\n\nexport interface ReadTextPart {\n type: 'text';\n text: string;\n}\n\nexport interface ReadImagePart {\n type: 'image';\n data: string;\n mimeType: string;\n}\n\nexport type ReadContentPart = ReadTextPart | ReadImagePart;\n\nexport interface ReadImageResizeResult {\n readonly data: string;\n readonly mimeType: string;\n readonly originalWidth: number;\n readonly originalHeight: number;\n readonly width: number;\n readonly height: number;\n readonly wasResized: boolean;\n}\n\nexport interface ReadImageResizeOperations {\n resize(\n inputBytes: Uint8Array,\n mimeType: string,\n options: { readonly maxWidth: number; readonly maxHeight: number },\n ): Promise<ReadImageResizeResult | null>;\n formatDimensionNote(result: ReadImageResizeResult): string | undefined;\n}\n\nexport function imageLimits(): PiImageSettings {\n return loadPiImageSettings();\n}\n\nexport async function applyImageLimits(\n content: ReadContentPart[],\n limits: PiImageSettings,\n operations: ReadImageResizeOperations,\n): Promise<ReadContentPart[]> {\n if (!limits.autoResize || !content.some((part) => part.type === 'image')) return content;\n const resolved: ReadContentPart[] = [];\n for (const part of content) {\n if (part.type !== 'image') {\n resolved.push(part);\n continue;\n }\n const resized = await operations.resize(Buffer.from(part.data, 'base64'), part.mimeType, {\n maxWidth: limits.maxDimension,\n maxHeight: limits.maxDimension,\n });\n if (!resized) {\n resolved.push({ type: 'text', text: OMITTED_NOTE });\n continue;\n }\n resolved.push({ type: 'image', data: resized.data, mimeType: resized.mimeType });\n const note = operations.formatDimensionNote(resized);\n if (note) resolved.push({ type: 'text', text: note });\n }\n return resolved;\n}\n","import { readFile } from 'node:fs/promises';\n\nimport type {\n DoomHeadlessExecutionContext,\n DoomHeadlessTool,\n DoomHeadlessToolResult,\n} from '@agimon-ai/doompi-core/headless';\nimport { formatFileHeader, formatTaggedLine, splitLines } from '@agimon-ai/doompi-hashline';\nimport {\n computeFileTag,\n decodeUtf8,\n displayPath,\n isWritableFile,\n resolveReadInputPath,\n} from '@agimon-ai/doompi-hashline/files';\nimport {\n DEFAULT_MAX_BYTES,\n formatDimensionNote,\n formatSize,\n resizeImage,\n truncateHead,\n truncateLine,\n type ReadToolDetails,\n} from '@earendil-works/pi-coding-agent';\n\nimport { ReadParamsSchema, type ReadParams } from '../../schemas/readTool';\nimport {\n applyImageLimits,\n imageLimits,\n type ReadContentPart,\n type ReadImageResizeOperations,\n type ReadTextPart,\n} from '../readImage';\n\nconst resizeOperations: ReadImageResizeOperations = {\n resize: resizeImage,\n formatDimensionNote: (result) => formatDimensionNote(result),\n};\n\nexport interface ReadToolResult {\n content: ReadContentPart[];\n details?: ReadToolDetails;\n}\n\ninterface ReadTextToolResult {\n content: ReadTextPart[];\n details?: ReadToolDetails;\n}\n\nexport function assertNotAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new Error('Operation aborted');\n}\n\nexport function isImagePath(filePath: string): string | undefined {\n const extension = filePath.toLowerCase().match(/\\.([a-z0-9]+)$/u)?.[1];\n if (!extension) return undefined;\n const mimeTypes: Record<string, string> = {\n avif: 'image/avif',\n gif: 'image/gif',\n jpeg: 'image/jpeg',\n jpg: 'image/jpeg',\n png: 'image/png',\n webp: 'image/webp',\n };\n return mimeTypes[extension];\n}\n\nexport async function executeHeadlessRead(\n params: ReadParams,\n cwd: string,\n signal: AbortSignal | undefined,\n): Promise<ReadToolResult> {\n assertNotAborted(signal);\n const absolutePath = await resolveReadInputPath(params.path, cwd);\n const bytes = await readFile(absolutePath);\n assertNotAborted(signal);\n const mimeType = isImagePath(absolutePath);\n if (mimeType) {\n const content: ReadContentPart[] = [\n { type: 'text', text: `Read image file ${displayPath(absolutePath, cwd)} [${mimeType}]` },\n { type: 'image', data: bytes.toString('base64'), mimeType },\n ];\n return { content: await applyImageLimits(content, imageLimits(), resizeOperations) };\n }\n if (!(await isWritableFile(absolutePath))) {\n return { content: [{ type: 'text', text: decodeUtf8(bytes, displayPath(absolutePath, cwd)) }] };\n }\n return createTaggedReadResult(bytes, displayPath(absolutePath, cwd), params);\n}\n\nexport function createTaggedReadResult(bytes: Buffer, path: string, params: ReadParams): ReadTextToolResult {\n const lines = splitLines(decodeUtf8(bytes, path));\n const startIndex = params.offset === undefined ? 0 : params.offset - 1;\n if (startIndex >= lines.length) {\n throw new Error(`Offset ${params.offset} is beyond end of file (${lines.length} lines total).`);\n }\n\n const endIndex = params.limit === undefined ? lines.length : Math.min(lines.length, startIndex + params.limit);\n const selected = lines.slice(startIndex, endIndex);\n const header = formatFileHeader(path, computeFileTag(bytes));\n const compactedLines: number[] = [];\n const headerBytes = Buffer.byteLength(header, 'utf8') + 1;\n const tagged = selected.map((line, index) => {\n const lineNumber = startIndex + index + 1;\n const full = formatTaggedLine(line, lineNumber);\n if (Buffer.byteLength(full, 'utf8') <= DEFAULT_MAX_BYTES - headerBytes) return full;\n compactedLines.push(lineNumber);\n return formatTaggedLine(truncateLine(line).text, lineNumber, '', line);\n });\n const truncation = truncateHead([header, ...tagged].join('\\n'));\n let text = truncation.content;\n let details: ReadToolDetails | undefined;\n\n if (truncation.truncated) {\n const shownLines = Math.max(0, truncation.outputLines - 1);\n const nextOffset = startIndex + shownLines + 1;\n const reason = truncation.truncatedBy === 'bytes' ? `, ${formatSize(DEFAULT_MAX_BYTES)} limit` : '';\n text += `\\n\\n[Showing ${shownLines} anchored lines${reason}. Use offset=${nextOffset} to continue.]`;\n details = { truncation };\n } else {\n const notices: string[] = [];\n if (compactedLines.length > 0) {\n notices.push(`Lines ${compactedLines.join(', ')} shown compactly. Their anchors hash the full original lines`);\n }\n if (endIndex < lines.length) {\n notices.push(`${lines.length - endIndex} more lines in file. Use offset=${endIndex + 1} to continue`);\n }\n if (notices.length > 0) text += `\\n\\n[${notices.join('. ')}.]`;\n }\n\n return { content: [{ type: 'text', text }], details };\n}\n\nexport function isImageRead(content: readonly { readonly type: string; readonly text?: string }[]): boolean {\n return content.some((part) => part.type === 'image' || part.text?.startsWith('Read image file') === true);\n}\n\nexport function createHeadlessReadTool(): DoomHeadlessTool<typeof ReadParamsSchema> {\n return {\n name: 'read',\n label: 'read',\n description:\n 'Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images return native-compatible content. Text is truncated to 50.0KB.',\n promptSnippet: 'Read file contents with snapshot-bound line anchors',\n promptGuidelines: [\n 'Use read before edit. When hashline metadata is present, preserve the @file hash and anchors such as 5#abc exactly.',\n 'Continue large reads with offset until the required anchored lines are visible.',\n ],\n parameters: ReadParamsSchema,\n executionMode: 'parallel',\n execute: async (\n _toolCallId: string,\n params: ReadParams,\n signal: AbortSignal | undefined,\n _onUpdate: ((result: DoomHeadlessToolResult) => void) | undefined,\n context: DoomHeadlessExecutionContext,\n ) => executeHeadlessRead(params, context.cwd, signal),\n };\n}\n","import { defineMcpTool } from '@agimon-ai/doompi-core/mcp-facet';\n\nimport { createHeadlessReadTool } from '../../../../../services/readTool';\n\nexport default defineMcpTool(createHeadlessReadTool);\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 toolRead from '../src/extensions/workspaces/sessions/(backend)/tool/read.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-read',\n session: (context) => ({\n get tools(): DoomMcpSessionPlugin['tools'] { return [via({ name: 'read' }, at(toolRead, context))]; },\n }) satisfies DoomMcpSessionPlugin,\n});\n\nexport default mcp;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-read",
3
- "version": "0.0.1-alpha.48",
3
+ "version": "0.0.1-alpha.49",
4
4
  "description": "Snapshot-bound hashline read tool for Pi and DoomPi.",
5
5
  "keywords": [
6
6
  "ai",
@@ -62,16 +62,16 @@
62
62
  "access": "public"
63
63
  },
64
64
  "dependencies": {
65
- "@agimon-ai/doompi-config": "0.0.1-alpha.75",
66
- "@agimon-ai/doompi-core": "0.0.1-alpha.76",
67
- "@agimon-ai/doompi-hashline": "0.0.1-alpha.45",
68
- "@agimon-ai/doompi-ui": "0.0.1-alpha.76",
69
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.34",
65
+ "@agimon-ai/doompi-config": "0.0.1-alpha.76",
66
+ "@agimon-ai/doompi-core": "0.0.1-alpha.77",
67
+ "@agimon-ai/doompi-hashline": "0.0.1-alpha.46",
68
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.77",
69
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.35",
70
70
  "@deepseek-ai/cordis": "4.0.2",
71
71
  "typebox": "1.3.30"
72
72
  },
73
73
  "devDependencies": {
74
- "@agimon-ai/doompi-build": "0.0.1-alpha.5",
74
+ "@agimon-ai/doompi-build": "0.0.1-alpha.6",
75
75
  "@earendil-works/pi-coding-agent": "0.85.1",
76
76
  "@earendil-works/pi-tui": "0.85.1",
77
77
  "@tanstack/react-store": "0.11.1",
@@ -100,6 +100,13 @@
100
100
  "engines": {
101
101
  "node": ">=22.19.0"
102
102
  },
103
+ "doompiMcp": {
104
+ "entry": "./generated/mcp.ts",
105
+ "dist": "./dist/extensions/mcp.mjs",
106
+ "scopes": [
107
+ "session"
108
+ ]
109
+ },
103
110
  "doompiServer": {
104
111
  "entry": "./generated/server.ts",
105
112
  "dist": "./dist/extensions/server.mjs",
@@ -125,7 +132,8 @@
125
132
  ]
126
133
  },
127
134
  "scripts": {
128
- "build": "tsdown",
135
+ "build": "tsdown && tsdown --config tsdown.mcp.config.ts",
136
+ "build:mcp": "tsdown --config tsdown.mcp.config.ts",
129
137
  "test": "vitest --run",
130
138
  "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.web.json",
131
139
  "lint": "oxlint . && oxfmt . --check",