@agimon-ai/doompi-read 0.0.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vuong Ngo
4
+ Copyright (c) 2026 pulseaiclub
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # @agimon-ai/doompi-read
2
+
3
+ Snapshot-bound hashline `read` tool for Pi and DoomPi.
4
+
5
+ Part of the [DoomPi distribution](https://www.npmjs.com/package/@agimon-ai/doompi).
6
+
7
+ The package replaces only Pi's `read` registration. Writable text reads attach an exact file tag and
8
+ a stable three-letter anchor to every returned line. Non-writable files retain Pi's native output,
9
+ which avoids spending model context on anchors that cannot be edited. The full hashline protocol
10
+ remains available to the model, while DoomPi's renderer hides the protocol tags and anchors from
11
+ people and preserves syntax highlighting. Image reads retain Pi's native attachment behavior.
12
+
13
+ > **Alpha:** tool and protocol contracts may change between releases.
14
+
15
+ ## Requirements
16
+
17
+ - Node.js 22.19.0 or newer
18
+ - Pi 0.84.2 and Pi TUI 0.84.2
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pi install npm:@agimon-ai/doompi-read
24
+ ```
25
+
26
+ Pi discovers `@agimon-ai/doompi-read/extensions/pi` from the package manifest. Install
27
+ `@agimon-ai/doompi-edit` when the model should apply the anchors emitted by this package.
28
+
29
+ When `@agimon-ai/doompi-ui` is installed directly in standalone Pi, list
30
+ `@agimon-ai/doompi-read` first so its `read` registration owns the shared tool name. DoomPi's composed
31
+ runtime loads selectable packages after the UI package, so the hashline tool owns `read` there.
32
+
33
+ ## Tool protocol
34
+
35
+ A writable text result starts with one file header, followed by tagged lines:
36
+
37
+ ```text
38
+ @file src/example.ts#A1b2C3d4
39
+ 1#abc|const value = 1;
40
+ ```
41
+
42
+ The eight-character file tag is the base64url SHA-256 prefix of the exact file bytes. The
43
+ three-letter line anchor is a whitespace-insensitive FNV-1a64 tag. Large reads retain Pi's output
44
+ limits and include an offset hint when more lines are available.
45
+
46
+ ## Attribution
47
+
48
+ The hashline protocol and FNV anchor approach are adapted from
49
+ [Phi's writetool](https://github.com/pulseaiclub/phi/tree/main/internal/tools/writetool), licensed
50
+ under MIT by pulseaiclub. DoomPi uses an exact-byte SHA-256 file tag as an intentional compatibility
51
+ boundary.
52
+
53
+ ## Public API
54
+
55
+ ```ts
56
+ import { ReadParamsSchema, registerHashlineReadTool } from '@agimon-ai/doompi-read';
57
+ import type { ReadParams } from '@agimon-ai/doompi-read';
58
+ ```
59
+
60
+ ## Development
61
+
62
+ ```bash
63
+ pnpm build
64
+ pnpm typecheck
65
+ pnpm test
66
+ pnpm lint
67
+ ```
68
+
69
+ Maintained by [Agimon](https://agimon.ai/about).
70
+
71
+ ## License
72
+
73
+ MIT
@@ -0,0 +1,2 @@
1
+ const e=require("./readTool.cjs");let t=require("@agimon-ai/doompi-extension-contracts/cordis-host"),n=require("@agimon-ai/doompi-extension-contracts/tool-overrides");const r=`@agimon-ai/doompi-read`;function i(t,i){t.inject([n.DOOM_TOOL_OVERRIDES_SERVICE],t=>{let a=(0,n.requireDoomToolOverrides)(t).claim({source:r,tools:[`read`]});if(a.granted){try{e.registerHashlineReadTool(i)}catch(e){throw a.dispose(),e}return()=>a.dispose()}})}async function a(e){let n=await(0,t.connectDoomCordisHost)(e,r),i=n.root.plugin(o,{pi:e});try{await i}catch(e){try{await i.dispose()}finally{await n.dispose()}throw e}let a;e.on(`session_shutdown`,()=>a??=(async()=>{try{await i.dispose()}finally{await n.dispose()}})())}function o(e,t){i(e,t.pi)}exports.activateDoomPiReadExtension=a,exports.default=a,exports.installDoomPiReadRuntime=i;
2
+ //# sourceMappingURL=extension.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.cjs","names":["DOOM_TOOL_OVERRIDES_SERVICE","requireDoomToolOverrides","connectDoomCordisHost"],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n DOOM_TOOL_OVERRIDES_SERVICE,\n requireDoomToolOverrides,\n} from '@agimon-ai/doompi-extension-contracts/tool-overrides';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { registerHashlineReadTool } from './readTool.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-read';\n\n/** Install the hashline read tool after claiming Pi's shared read name. */\nexport function installDoomPiReadRuntime(cordis: Context, pi: ExtensionAPI): void {\n cordis.inject([DOOM_TOOL_OVERRIDES_SERVICE], (overridesContext) => {\n const registration = requireDoomToolOverrides(overridesContext).claim({\n source: PACKAGE_SOURCE,\n tools: ['read'],\n });\n if (!registration.granted) return;\n try {\n registerHashlineReadTool(pi);\n } catch (error) {\n registration.dispose();\n throw error;\n }\n return () => registration.dispose();\n });\n}\n\n/** Connect the package's standard Pi entry to DoomPi's shared Cordis host. */\nexport async function activateDoomPiReadExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin(doomPiReadPlugin, { pi });\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\ninterface DoomPiReadPluginConfig {\n readonly pi: ExtensionAPI;\n}\n\nfunction doomPiReadPlugin(cordis: Context, config: DoomPiReadPluginConfig): void {\n installDoomPiReadRuntime(cordis, config.pi);\n}\n\nexport default activateDoomPiReadExtension;\n"],"mappings":"uKASA,MAAM,EAAiB,yBAGvB,SAAgB,EAAyB,EAAiB,EAAwB,CAChF,EAAO,OAAO,CAACA,EAAAA,2BAA2B,EAAI,GAAqB,CACjE,IAAM,GAAA,EAAeC,EAAAA,yBAAAA,CAAyB,CAAgB,CAAC,CAAC,MAAM,CACpE,OAAQ,EACR,MAAO,CAAC,MAAM,CAChB,CAAC,EACI,KAAa,QAClB,IAAI,CACF,EAAA,yBAAyB,CAAE,CAC7B,OAAS,EAAO,CAEd,MADA,EAAa,QAAQ,EACf,CACR,CACA,UAAa,EAAa,QAAQ,CADlC,CAEF,CAAC,CACH,CAGA,eAAsB,EAA4B,EAAiC,CACjF,IAAM,EAAa,MAAA,EAAMC,EAAAA,sBAAAA,CAAsB,EAAI,CAAc,EAC3D,EAAQ,EAAW,KAAK,OAAO,EAAkB,CAAE,IAAG,CAAC,EAC7D,GAAI,CACF,MAAM,CACR,OAAS,EAAO,CACd,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACA,MAAM,CACR,CAEA,IAAI,EACJ,EAAG,GACD,uBAEG,KAAc,SAAY,CACzB,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACF,EAAA,CAAG,CACP,CACF,CAMA,SAAS,EAAiB,EAAiB,EAAsC,CAC/E,EAAyB,EAAQ,EAAO,EAAE,CAC5C"}
@@ -0,0 +1,10 @@
1
+ import { Context } from "@deepseek-ai/cordis";
2
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
+ //#region src/adapters/pi/extension.d.ts
4
+ /** Install the hashline read tool after claiming Pi's shared read name. */
5
+ declare function installDoomPiReadRuntime(cordis: Context, pi: ExtensionAPI): void;
6
+ /** Connect the package's standard Pi entry to DoomPi's shared Cordis host. */
7
+ declare function activateDoomPiReadExtension(pi: ExtensionAPI): Promise<void>;
8
+ //#endregion
9
+ export { activateDoomPiReadExtension, activateDoomPiReadExtension as default, installDoomPiReadRuntime };
10
+ //# sourceMappingURL=extension.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.d.cts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBAYgB,yBAAyB,QAAQ,SAAS,IAAI;;iBAkBxC,4BAA4B,IAAI,eAAe"}
@@ -0,0 +1,10 @@
1
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { Context } from "@deepseek-ai/cordis";
3
+ //#region src/adapters/pi/extension.d.ts
4
+ /** Install the hashline read tool after claiming Pi's shared read name. */
5
+ declare function installDoomPiReadRuntime(cordis: Context, pi: ExtensionAPI): void;
6
+ /** Connect the package's standard Pi entry to DoomPi's shared Cordis host. */
7
+ declare function activateDoomPiReadExtension(pi: ExtensionAPI): Promise<void>;
8
+ //#endregion
9
+ export { activateDoomPiReadExtension, activateDoomPiReadExtension as default, installDoomPiReadRuntime };
10
+ //# sourceMappingURL=extension.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.d.mts","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"mappings":";;;;iBAYgB,yBAAyB,QAAQ,SAAS,IAAI;;iBAkBxC,4BAA4B,IAAI,eAAe"}
@@ -0,0 +1,2 @@
1
+ import{registerHashlineReadTool as e}from"./readTool.mjs";import{connectDoomCordisHost as t}from"@agimon-ai/doompi-extension-contracts/cordis-host";import{DOOM_TOOL_OVERRIDES_SERVICE as n,requireDoomToolOverrides as r}from"@agimon-ai/doompi-extension-contracts/tool-overrides";const i=`@agimon-ai/doompi-read`;function a(t,a){t.inject([n],t=>{let n=r(t).claim({source:i,tools:[`read`]});if(n.granted){try{e(a)}catch(e){throw n.dispose(),e}return()=>n.dispose()}})}async function o(e){let n=await t(e,i),r=n.root.plugin(s,{pi:e});try{await r}catch(e){try{await r.dispose()}finally{await n.dispose()}throw e}let a;e.on(`session_shutdown`,()=>a??=(async()=>{try{await r.dispose()}finally{await n.dispose()}})())}function s(e,t){a(e,t.pi)}export{o as activateDoomPiReadExtension,o as default,a as installDoomPiReadRuntime};
2
+ //# sourceMappingURL=extension.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extension.mjs","names":[],"sources":["../../../src/adapters/pi/extension.ts"],"sourcesContent":["import { connectDoomCordisHost } from '@agimon-ai/doompi-extension-contracts/cordis-host';\nimport {\n DOOM_TOOL_OVERRIDES_SERVICE,\n requireDoomToolOverrides,\n} from '@agimon-ai/doompi-extension-contracts/tool-overrides';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { registerHashlineReadTool } from './readTool.ts';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-read';\n\n/** Install the hashline read tool after claiming Pi's shared read name. */\nexport function installDoomPiReadRuntime(cordis: Context, pi: ExtensionAPI): void {\n cordis.inject([DOOM_TOOL_OVERRIDES_SERVICE], (overridesContext) => {\n const registration = requireDoomToolOverrides(overridesContext).claim({\n source: PACKAGE_SOURCE,\n tools: ['read'],\n });\n if (!registration.granted) return;\n try {\n registerHashlineReadTool(pi);\n } catch (error) {\n registration.dispose();\n throw error;\n }\n return () => registration.dispose();\n });\n}\n\n/** Connect the package's standard Pi entry to DoomPi's shared Cordis host. */\nexport async function activateDoomPiReadExtension(pi: ExtensionAPI): Promise<void> {\n const connection = await connectDoomCordisHost(pi, PACKAGE_SOURCE);\n const fiber = connection.root.plugin(doomPiReadPlugin, { pi });\n try {\n await fiber;\n } catch (error) {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n throw error;\n }\n\n let disposal: Promise<void> | undefined;\n pi.on(\n 'session_shutdown',\n () =>\n (disposal ??= (async () => {\n try {\n await fiber.dispose();\n } finally {\n await connection.dispose();\n }\n })()),\n );\n}\n\ninterface DoomPiReadPluginConfig {\n readonly pi: ExtensionAPI;\n}\n\nfunction doomPiReadPlugin(cordis: Context, config: DoomPiReadPluginConfig): void {\n installDoomPiReadRuntime(cordis, config.pi);\n}\n\nexport default activateDoomPiReadExtension;\n"],"mappings":"qRASA,MAAM,EAAiB,yBAGvB,SAAgB,EAAyB,EAAiB,EAAwB,CAChF,EAAO,OAAO,CAAC,CAA2B,EAAI,GAAqB,CACjE,IAAM,EAAe,EAAyB,CAAgB,CAAC,CAAC,MAAM,CACpE,OAAQ,EACR,MAAO,CAAC,MAAM,CAChB,CAAC,EACI,KAAa,QAClB,IAAI,CACF,EAAyB,CAAE,CAC7B,OAAS,EAAO,CAEd,MADA,EAAa,QAAQ,EACf,CACR,CACA,UAAa,EAAa,QAAQ,CADlC,CAEF,CAAC,CACH,CAGA,eAAsB,EAA4B,EAAiC,CACjF,IAAM,EAAa,MAAM,EAAsB,EAAI,CAAc,EAC3D,EAAQ,EAAW,KAAK,OAAO,EAAkB,CAAE,IAAG,CAAC,EAC7D,GAAI,CACF,MAAM,CACR,OAAS,EAAO,CACd,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACA,MAAM,CACR,CAEA,IAAI,EACJ,EAAG,GACD,uBAEG,KAAc,SAAY,CACzB,GAAI,CACF,MAAM,EAAM,QAAQ,CACtB,QAAU,CACR,MAAM,EAAW,QAAQ,CAC3B,CACF,EAAA,CAAG,CACP,CACF,CAMA,SAAS,EAAiB,EAAiB,EAAsC,CAC/E,EAAyB,EAAQ,EAAO,EAAE,CAC5C"}
@@ -0,0 +1,3 @@
1
+ const e=require("../../schemas/readTool.cjs");let t=require("@agimon-ai/doompi-hashline"),n=require("@agimon-ai/doompi-hashline/files"),r=require("@agimon-ai/doompi-ui/hashlineRendering"),i=require("node:fs/promises"),a=require("@earendil-works/pi-coding-agent");function o(t,o=n.isWritableFile){t.registerTool({name:`read`,label:`read`,description:`Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images retain Pi's native behavior. Text is truncated to ${(0,a.formatSize)(a.DEFAULT_MAX_BYTES)}.`,promptSnippet:`Read file contents with snapshot-bound line anchors`,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.`],parameters:e.ReadParamsSchema,executionMode:`parallel`,renderShell:`self`,async execute(e,t,r,u,d){let f=t;l(r);let p=await(0,n.resolveReadInputPath)(f.path,d.cwd),m=await(0,a.createReadToolDefinition)(d.cwd).execute(e,{...f,path:p},r,u,d);if(c(m.content))return m;l(r);let h=await o(p);if(l(r),!h)return m;let g=await(0,i.readFile)(p);return l(r),s(g,(0,n.displayPath)(p,d.cwd),f)},renderCall(e,t){let n=e,i=[n.offset===void 0?void 0:`from ${n.offset}`,n.limit===void 0?void 0:`${n.limit} lines`].filter(e=>e!==void 0);return(0,r.renderHashlineCall)(`read`,n.path,i,t)},renderResult(e,t,n,i){return(0,r.renderHashlineResult)(e,t,n,i,`read`)}})}function s(e,r,i){let o=(0,t.splitLines)((0,n.decodeUtf8)(e,r)),s=i.offset===void 0?0:i.offset-1;if(s>=o.length)throw Error(`Offset ${i.offset} is beyond end of file (${o.length} lines total).`);let c=i.limit===void 0?o.length:Math.min(o.length,s+i.limit),l=o.slice(s,c),u=(0,t.formatFileHeader)(r,(0,n.computeFileTag)(e)),d=[],f=Buffer.byteLength(u,`utf8`)+1,p=l.map((e,n)=>{let r=s+n+1,i=(0,t.formatTaggedLine)(e,r);return Buffer.byteLength(i,`utf8`)<=a.DEFAULT_MAX_BYTES-f?i:(d.push(r),(0,t.formatTaggedLine)((0,a.truncateLine)(e).text,r,``,e))}),m=(0,a.truncateHead)([u,...p].join(`
2
+ `)),h=m.content,g;if(m.truncated){let e=Math.max(0,m.outputLines-1),t=s+e+1,n=m.truncatedBy===`bytes`?`, ${(0,a.formatSize)(a.DEFAULT_MAX_BYTES)} limit`:``;h+=`\n\n[Showing ${e} anchored lines${n}. Use offset=${t} to continue.]`,g={truncation:m}}else{let e=[];d.length>0&&e.push(`Lines ${d.join(`, `)} shown compactly. Their anchors hash the full original lines`),c<o.length&&e.push(`${o.length-c} more lines in file. Use offset=${c+1} to continue`),e.length>0&&(h+=`\n\n[${e.join(`. `)}.]`)}return{content:[{type:`text`,text:h}],details:g}}function c(e){return e.some(e=>e.type===`image`||e.text?.startsWith(`Read image file`)===!0)}function l(e){if(e?.aborted)throw Error(`Operation aborted`)}exports.assertNotAborted=l,exports.createTaggedReadResult=s,exports.isImageRead=c,exports.registerHashlineReadTool=o;
3
+ //# sourceMappingURL=readTool.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readTool.cjs","names":["isWritableFile","formatSize","DEFAULT_MAX_BYTES","ReadParamsSchema","resolveReadInputPath","createReadToolDefinition","readFile","displayPath","renderHashlineCall","renderHashlineResult","splitLines","decodeUtf8","formatFileHeader","computeFileTag","formatTaggedLine","truncateLine","truncateHead"],"sources":["../../../src/adapters/pi/readTool.ts"],"sourcesContent":["import { 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 { renderHashlineCall, renderHashlineResult } from '@agimon-ai/doompi-ui/hashlineRendering';\nimport { readFile } from 'node:fs/promises';\nimport {\n createReadToolDefinition,\n DEFAULT_MAX_BYTES,\n formatSize,\n truncateHead,\n truncateLine,\n type ExtensionAPI,\n type ReadToolDetails,\n} from '@earendil-works/pi-coding-agent';\nimport { ReadParamsSchema, type ReadParams } from '../../schemas/readTool.ts';\n\ntype WritableCheck = (path: string) => Promise<boolean>;\n\n/** Register the snapshot-bound replacement for Pi's read tool. */\nexport function registerHashlineReadTool(\n pi: Pick<ExtensionAPI, 'registerTool'>,\n writable: WritableCheck = isWritableFile,\n): void {\n pi.registerTool({\n name: 'read',\n label: 'read',\n description: `Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images retain Pi's native behavior. Text is truncated to ${formatSize(DEFAULT_MAX_BYTES)}.`,\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 renderShell: 'self',\n async execute(toolCallId, params, signal, onUpdate, ctx) {\n const input = params as ReadParams;\n assertNotAborted(signal);\n const absolutePath = await resolveReadInputPath(input.path, ctx.cwd);\n const nativeRead = createReadToolDefinition(ctx.cwd);\n const nativeResult = await nativeRead.execute(\n toolCallId,\n { ...input, path: absolutePath },\n signal,\n onUpdate,\n ctx,\n );\n if (isImageRead(nativeResult.content)) return nativeResult;\n\n assertNotAborted(signal);\n const canEdit = await writable(absolutePath);\n assertNotAborted(signal);\n if (!canEdit) return nativeResult;\n const bytes = await readFile(absolutePath);\n assertNotAborted(signal);\n return createTaggedReadResult(bytes, displayPath(absolutePath, ctx.cwd), input);\n },\n renderCall(args, theme) {\n const input = args as ReadParams;\n const details = [\n input.offset === undefined ? undefined : `from ${input.offset}`,\n input.limit === undefined ? undefined : `${input.limit} lines`,\n ].filter((value): value is string => value !== undefined);\n return renderHashlineCall('read', input.path, details, theme);\n },\n renderResult(result, options, theme, context) {\n return renderHashlineResult(result, options, theme, context, 'read');\n },\n });\n}\n\nexport function createTaggedReadResult(\n bytes: Buffer,\n path: string,\n params: ReadParams,\n): { content: [{ type: 'text'; text: string }]; details: ReadToolDetails | undefined } {\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 assertNotAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new Error('Operation aborted');\n}\n"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import "../../schemas/readTool.cjs";
2
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
+ //#region src/adapters/pi/readTool.d.ts
4
+ type WritableCheck = (path: string) => Promise<boolean>;
5
+ /** Register the snapshot-bound replacement for Pi's read tool. */
6
+ declare function registerHashlineReadTool(pi: Pick<ExtensionAPI, 'registerTool'>, writable?: WritableCheck): void;
7
+ //#endregion
8
+ export { registerHashlineReadTool };
9
+ //# sourceMappingURL=readTool.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readTool.d.cts","names":[],"sources":["../../../src/adapters/pi/readTool.ts"],"mappings":";;;KAqBK,iBAAiB,iBAAiB;;iBAGvB,yBACd,IAAI,KAAK,+BACT,WAAU"}
@@ -0,0 +1,9 @@
1
+ import "../../schemas/readTool.mjs";
2
+ import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
+ //#region src/adapters/pi/readTool.d.ts
4
+ type WritableCheck = (path: string) => Promise<boolean>;
5
+ /** Register the snapshot-bound replacement for Pi's read tool. */
6
+ declare function registerHashlineReadTool(pi: Pick<ExtensionAPI, 'registerTool'>, writable?: WritableCheck): void;
7
+ //#endregion
8
+ export { registerHashlineReadTool };
9
+ //# sourceMappingURL=readTool.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readTool.d.mts","names":[],"sources":["../../../src/adapters/pi/readTool.ts"],"mappings":";;;KAqBK,iBAAiB,iBAAiB;;iBAGvB,yBACd,IAAI,KAAK,+BACT,WAAU"}
@@ -0,0 +1,3 @@
1
+ import{ReadParamsSchema as e}from"../../schemas/readTool.mjs";import{formatFileHeader as t,formatTaggedLine as n,splitLines as r}from"@agimon-ai/doompi-hashline";import{computeFileTag as i,decodeUtf8 as a,displayPath as o,isWritableFile as s,resolveReadInputPath as c}from"@agimon-ai/doompi-hashline/files";import{renderHashlineCall as l,renderHashlineResult as u}from"@agimon-ai/doompi-ui/hashlineRendering";import{readFile as d}from"node:fs/promises";import{DEFAULT_MAX_BYTES as f,createReadToolDefinition as p,formatSize as m,truncateHead as h,truncateLine as g}from"@earendil-works/pi-coding-agent";function _(t,n=s){t.registerTool({name:`read`,label:`read`,description:`Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images retain Pi's native behavior. Text is truncated to ${m(f)}.`,promptSnippet:`Read file contents with snapshot-bound line anchors`,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.`],parameters:e,executionMode:`parallel`,renderShell:`self`,async execute(e,t,r,i,a){let s=t;b(r);let l=await c(s.path,a.cwd),u=await p(a.cwd).execute(e,{...s,path:l},r,i,a);if(y(u.content))return u;b(r);let f=await n(l);if(b(r),!f)return u;let m=await d(l);return b(r),v(m,o(l,a.cwd),s)},renderCall(e,t){let n=e,r=[n.offset===void 0?void 0:`from ${n.offset}`,n.limit===void 0?void 0:`${n.limit} lines`].filter(e=>e!==void 0);return l(`read`,n.path,r,t)},renderResult(e,t,n,r){return u(e,t,n,r,`read`)}})}function v(e,o,s){let c=r(a(e,o)),l=s.offset===void 0?0:s.offset-1;if(l>=c.length)throw Error(`Offset ${s.offset} is beyond end of file (${c.length} lines total).`);let u=s.limit===void 0?c.length:Math.min(c.length,l+s.limit),d=c.slice(l,u),p=t(o,i(e)),_=[],v=Buffer.byteLength(p,`utf8`)+1,y=d.map((e,t)=>{let r=l+t+1,i=n(e,r);return Buffer.byteLength(i,`utf8`)<=f-v?i:(_.push(r),n(g(e).text,r,``,e))}),b=h([p,...y].join(`
2
+ `)),x=b.content,S;if(b.truncated){let e=Math.max(0,b.outputLines-1),t=l+e+1,n=b.truncatedBy===`bytes`?`, ${m(f)} limit`:``;x+=`\n\n[Showing ${e} anchored lines${n}. Use offset=${t} to continue.]`,S={truncation:b}}else{let e=[];_.length>0&&e.push(`Lines ${_.join(`, `)} shown compactly. Their anchors hash the full original lines`),u<c.length&&e.push(`${c.length-u} more lines in file. Use offset=${u+1} to continue`),e.length>0&&(x+=`\n\n[${e.join(`. `)}.]`)}return{content:[{type:`text`,text:x}],details:S}}function y(e){return e.some(e=>e.type===`image`||e.text?.startsWith(`Read image file`)===!0)}function b(e){if(e?.aborted)throw Error(`Operation aborted`)}export{b as assertNotAborted,v as createTaggedReadResult,y as isImageRead,_ as registerHashlineReadTool};
3
+ //# sourceMappingURL=readTool.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readTool.mjs","names":[],"sources":["../../../src/adapters/pi/readTool.ts"],"sourcesContent":["import { 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 { renderHashlineCall, renderHashlineResult } from '@agimon-ai/doompi-ui/hashlineRendering';\nimport { readFile } from 'node:fs/promises';\nimport {\n createReadToolDefinition,\n DEFAULT_MAX_BYTES,\n formatSize,\n truncateHead,\n truncateLine,\n type ExtensionAPI,\n type ReadToolDetails,\n} from '@earendil-works/pi-coding-agent';\nimport { ReadParamsSchema, type ReadParams } from '../../schemas/readTool.ts';\n\ntype WritableCheck = (path: string) => Promise<boolean>;\n\n/** Register the snapshot-bound replacement for Pi's read tool. */\nexport function registerHashlineReadTool(\n pi: Pick<ExtensionAPI, 'registerTool'>,\n writable: WritableCheck = isWritableFile,\n): void {\n pi.registerTool({\n name: 'read',\n label: 'read',\n description: `Read a writable text file with an exact-byte file tag and stable line anchors. Non-writable files and images retain Pi's native behavior. Text is truncated to ${formatSize(DEFAULT_MAX_BYTES)}.`,\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 renderShell: 'self',\n async execute(toolCallId, params, signal, onUpdate, ctx) {\n const input = params as ReadParams;\n assertNotAborted(signal);\n const absolutePath = await resolveReadInputPath(input.path, ctx.cwd);\n const nativeRead = createReadToolDefinition(ctx.cwd);\n const nativeResult = await nativeRead.execute(\n toolCallId,\n { ...input, path: absolutePath },\n signal,\n onUpdate,\n ctx,\n );\n if (isImageRead(nativeResult.content)) return nativeResult;\n\n assertNotAborted(signal);\n const canEdit = await writable(absolutePath);\n assertNotAborted(signal);\n if (!canEdit) return nativeResult;\n const bytes = await readFile(absolutePath);\n assertNotAborted(signal);\n return createTaggedReadResult(bytes, displayPath(absolutePath, ctx.cwd), input);\n },\n renderCall(args, theme) {\n const input = args as ReadParams;\n const details = [\n input.offset === undefined ? undefined : `from ${input.offset}`,\n input.limit === undefined ? undefined : `${input.limit} lines`,\n ].filter((value): value is string => value !== undefined);\n return renderHashlineCall('read', input.path, details, theme);\n },\n renderResult(result, options, theme, context) {\n return renderHashlineResult(result, options, theme, context, 'read');\n },\n });\n}\n\nexport function createTaggedReadResult(\n bytes: Buffer,\n path: string,\n params: ReadParams,\n): { content: [{ type: 'text'; text: string }]; details: ReadToolDetails | undefined } {\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 assertNotAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) throw new Error('Operation aborted');\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ const e=require("../adapters/pi/extension.cjs");module.exports=e.activateDoomPiReadExtension;
@@ -0,0 +1,2 @@
1
+ import { activateDoomPiReadExtension } from "../adapters/pi/extension.cjs";
2
+ export = activateDoomPiReadExtension;
@@ -0,0 +1,2 @@
1
+ import { activateDoomPiReadExtension } from "../adapters/pi/extension.mjs";
2
+ export { activateDoomPiReadExtension as default };
@@ -0,0 +1 @@
1
+ import{activateDoomPiReadExtension as e}from"../adapters/pi/extension.mjs";export{e as default};
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./schemas/readTool.cjs"),t=require("./adapters/pi/readTool.cjs"),n=require("./adapters/pi/extension.cjs");exports.ReadParamsSchema=e.ReadParamsSchema,exports.activateDoomPiReadExtension=n.activateDoomPiReadExtension,exports.installDoomPiReadRuntime=n.installDoomPiReadRuntime,exports.registerHashlineReadTool=t.registerHashlineReadTool;
@@ -0,0 +1,4 @@
1
+ import { activateDoomPiReadExtension, installDoomPiReadRuntime } from "./adapters/pi/extension.cjs";
2
+ import { ReadParams, ReadParamsSchema } from "./schemas/readTool.cjs";
3
+ import { registerHashlineReadTool } from "./adapters/pi/readTool.cjs";
4
+ export { type ReadParams, ReadParamsSchema, activateDoomPiReadExtension, installDoomPiReadRuntime, registerHashlineReadTool };
@@ -0,0 +1,4 @@
1
+ import { activateDoomPiReadExtension, installDoomPiReadRuntime } from "./adapters/pi/extension.mjs";
2
+ import { ReadParams, ReadParamsSchema } from "./schemas/readTool.mjs";
3
+ import { registerHashlineReadTool } from "./adapters/pi/readTool.mjs";
4
+ export { type ReadParams, ReadParamsSchema, activateDoomPiReadExtension, installDoomPiReadRuntime, registerHashlineReadTool };
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{ReadParamsSchema as e}from"./schemas/readTool.mjs";import{registerHashlineReadTool as t}from"./adapters/pi/readTool.mjs";import{activateDoomPiReadExtension as n,installDoomPiReadRuntime as r}from"./adapters/pi/extension.mjs";export{e as ReadParamsSchema,n as activateDoomPiReadExtension,r as installDoomPiReadRuntime,t as registerHashlineReadTool};
@@ -0,0 +1,2 @@
1
+ let e=require("typebox");const t=e.Type.Object({path:e.Type.String({description:`Path to the file to read.`}),offset:e.Type.Optional(e.Type.Integer({minimum:1,description:`One-based line offset.`})),limit:e.Type.Optional(e.Type.Integer({minimum:1,description:`Maximum number of lines to return.`}))});exports.ReadParamsSchema=t;
2
+ //# sourceMappingURL=readTool.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readTool.cjs","names":["Type"],"sources":["../../src/schemas/readTool.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"],"mappings":"yBAEA,MAAa,EAAmBA,EAAAA,KAAK,OAAO,CAC1C,KAAMA,EAAAA,KAAK,OAAO,CAAE,YAAa,2BAA4B,CAAC,EAC9D,OAAQA,EAAAA,KAAK,SAASA,EAAAA,KAAK,QAAQ,CAAE,QAAS,EAAG,YAAa,wBAAyB,CAAC,CAAC,EACzF,MAAOA,EAAAA,KAAK,SAASA,EAAAA,KAAK,QAAQ,CAAE,QAAS,EAAG,YAAa,oCAAqC,CAAC,CAAC,CACtG,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { Static, Type } from "typebox";
2
+ //#region src/schemas/readTool.d.ts
3
+ declare const ReadParamsSchema: Type.TObject<{
4
+ path: Type.TString;
5
+ offset: Type.TOptional<Type.TInteger>;
6
+ limit: Type.TOptional<Type.TInteger>;
7
+ }>;
8
+ type ReadParams = Static<typeof ReadParamsSchema>;
9
+ //#endregion
10
+ export { ReadParams, ReadParamsSchema };
11
+ //# sourceMappingURL=readTool.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readTool.d.cts","names":[],"sources":["../../src/schemas/readTool.ts"],"mappings":";;cAEa,kBAAgB,KAAA;;;;;KAMjB,aAAa,cAAc"}
@@ -0,0 +1,11 @@
1
+ import { Static, Type } from "typebox";
2
+ //#region src/schemas/readTool.d.ts
3
+ declare const ReadParamsSchema: Type.TObject<{
4
+ path: Type.TString;
5
+ offset: Type.TOptional<Type.TInteger>;
6
+ limit: Type.TOptional<Type.TInteger>;
7
+ }>;
8
+ type ReadParams = Static<typeof ReadParamsSchema>;
9
+ //#endregion
10
+ export { ReadParams, ReadParamsSchema };
11
+ //# sourceMappingURL=readTool.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readTool.d.mts","names":[],"sources":["../../src/schemas/readTool.ts"],"mappings":";;cAEa,kBAAgB,KAAA;;;;;KAMjB,aAAa,cAAc"}
@@ -0,0 +1,2 @@
1
+ import{Type as e}from"typebox";const t=e.Object({path:e.String({description:`Path to the file to read.`}),offset:e.Optional(e.Integer({minimum:1,description:`One-based line offset.`})),limit:e.Optional(e.Integer({minimum:1,description:`Maximum number of lines to return.`}))});export{t as ReadParamsSchema};
2
+ //# sourceMappingURL=readTool.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readTool.mjs","names":[],"sources":["../../src/schemas/readTool.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"],"mappings":"+BAEA,MAAa,EAAmB,EAAK,OAAO,CAC1C,KAAM,EAAK,OAAO,CAAE,YAAa,2BAA4B,CAAC,EAC9D,OAAQ,EAAK,SAAS,EAAK,QAAQ,CAAE,QAAS,EAAG,YAAa,wBAAyB,CAAC,CAAC,EACzF,MAAO,EAAK,SAAS,EAAK,QAAQ,CAAE,QAAS,EAAG,YAAa,oCAAqC,CAAC,CAAC,CACtG,CAAC"}
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@agimon-ai/doompi-read",
3
+ "version": "0.0.1-alpha.1",
4
+ "description": "Snapshot-bound hashline read tool for Pi and DoomPi.",
5
+ "keywords": [
6
+ "ai",
7
+ "coding-agent",
8
+ "developer-tools",
9
+ "doompi",
10
+ "hashline",
11
+ "pi-coding-agent",
12
+ "pi-extension",
13
+ "pi-package",
14
+ "read"
15
+ ],
16
+ "homepage": "https://agimon.ai",
17
+ "license": "MIT",
18
+ "author": "Vuong Ngo",
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "type": "module",
23
+ "main": "./dist/index.cjs",
24
+ "types": "./dist/index.d.mts",
25
+ "jsnext:main": "./dist/index.mjs",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.mts",
29
+ "import": "./dist/index.mjs",
30
+ "require": "./dist/index.cjs"
31
+ },
32
+ "./extensions/pi": {
33
+ "types": "./dist/extensions/pi.d.mts",
34
+ "import": "./dist/extensions/pi.mjs",
35
+ "require": "./dist/extensions/pi.cjs"
36
+ },
37
+ "./package.json": "./package.json"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "dependencies": {
43
+ "@deepseek-ai/cordis": "4.0.1",
44
+ "typebox": "1.3.16",
45
+ "@agimon-ai/doompi-hashline": "0.0.1-alpha.1",
46
+ "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.29",
47
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.29"
48
+ },
49
+ "devDependencies": {
50
+ "@earendil-works/pi-coding-agent": "0.84.2",
51
+ "@earendil-works/pi-tui": "0.84.2",
52
+ "@types/node": "26.2.0",
53
+ "@vitest/coverage-v8": "4.1.11",
54
+ "tsdown": "0.22.14",
55
+ "typescript": "7.0.2",
56
+ "vitest": "4.1.11"
57
+ },
58
+ "peerDependencies": {
59
+ "@earendil-works/pi-coding-agent": "0.84.2",
60
+ "@earendil-works/pi-tui": "0.84.2"
61
+ },
62
+ "peerDependenciesMeta": {
63
+ "@earendil-works/pi-coding-agent": {
64
+ "optional": true
65
+ },
66
+ "@earendil-works/pi-tui": {
67
+ "optional": true
68
+ }
69
+ },
70
+ "engines": {
71
+ "node": ">=22.19.0"
72
+ },
73
+ "pi": {
74
+ "extensions": [
75
+ "./dist/extensions/pi.mjs"
76
+ ]
77
+ },
78
+ "scripts": {
79
+ "build": "tsdown",
80
+ "test": "vitest --run",
81
+ "typecheck": "tsc --noEmit",
82
+ "lint": "oxlint . && oxfmt . --check",
83
+ "fixcode": "oxlint . --fix && oxfmt ."
84
+ }
85
+ }