@earendil-works/pi-codemode 0.0.1 → 0.99.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +184 -1
  2. package/dist/declarations.d.ts +53 -0
  3. package/dist/declarations.d.ts.map +1 -0
  4. package/dist/declarations.js +330 -0
  5. package/dist/declarations.js.map +1 -0
  6. package/dist/identifier.d.ts +6 -0
  7. package/dist/identifier.d.ts.map +1 -0
  8. package/dist/identifier.js +13 -0
  9. package/dist/identifier.js.map +1 -0
  10. package/dist/index.d.ts +8 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +7 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/runtime/host.d.ts +35 -0
  15. package/dist/runtime/host.d.ts.map +1 -0
  16. package/dist/runtime/host.js +314 -0
  17. package/dist/runtime/host.js.map +1 -0
  18. package/dist/runtime/prelude-source.d.ts +31 -0
  19. package/dist/runtime/prelude-source.d.ts.map +1 -0
  20. package/dist/runtime/prelude-source.js +322 -0
  21. package/dist/runtime/prelude-source.js.map +1 -0
  22. package/dist/runtime/protocol.d.ts +69 -0
  23. package/dist/runtime/protocol.d.ts.map +1 -0
  24. package/dist/runtime/protocol.js +10 -0
  25. package/dist/runtime/protocol.js.map +1 -0
  26. package/dist/runtime/worker.d.ts +2 -0
  27. package/dist/runtime/worker.d.ts.map +1 -0
  28. package/dist/runtime/worker.js +137 -0
  29. package/dist/runtime/worker.js.map +1 -0
  30. package/dist/source.d.ts +35 -0
  31. package/dist/source.d.ts.map +1 -0
  32. package/dist/source.js +92 -0
  33. package/dist/source.js.map +1 -0
  34. package/dist/types.d.ts +136 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +2 -0
  37. package/dist/types.js.map +1 -0
  38. package/dist/wasm.d.ts +12 -0
  39. package/dist/wasm.d.ts.map +1 -0
  40. package/dist/wasm.js +24 -0
  41. package/dist/wasm.js.map +1 -0
  42. package/package.json +54 -4
package/README.md CHANGED
@@ -1,3 +1,186 @@
1
1
  # @earendil-works/pi-codemode
2
2
 
3
- Placeholder release that registers the package name. Real releases are published from https://github.com/earendil-works/pi.
3
+ Runs model-written JavaScript in a QuickJS VM (compiled to WebAssembly) where the only capability is calling injected tools. Nested tool calls never enter the LLM context; only the script's output and return value do.
4
+
5
+ Scripts use `tools`, `ALL_TOOLS`, `text`, `image`, `exit`, `store`, and `load`, and may start with a `// @options:` line. The coding agent uses it for its built-in `codemode` tool. It has no pi dependencies and can be used on its own to expose any functions (remote APIs, MCP servers, application services) to model-written scripts.
6
+
7
+ ## Usage
8
+
9
+ ```ts
10
+ import { CodemodeSandbox } from "@earendil-works/pi-codemode";
11
+
12
+ const sandbox = new CodemodeSandbox({
13
+ timeoutMs: 60_000,
14
+ tools: [
15
+ {
16
+ name: "read",
17
+ execute: async (args, { signal }) => {
18
+ const { path } = args as { path: string };
19
+ return await readFile(path, "utf8");
20
+ },
21
+ },
22
+ ],
23
+ });
24
+
25
+ const result = await sandbox.execute(`
26
+ const source = await tools.read({ path: "package.json" });
27
+ text("bytes " + source.length);
28
+ return JSON.parse(source).name;
29
+ `);
30
+
31
+ console.log(result.output); // [{ type: "text", text: "bytes 1234" }]
32
+ if (result.ok) console.log(result.value); // "@earendil-works/pi-codemode"
33
+ else console.error(result.error.kind, result.error.message);
34
+
35
+ await sandbox.close();
36
+ ```
37
+
38
+ `code` is the body of an async function: `return` and top-level `await` work. Inside the script:
39
+
40
+ - `tools.<name>(args)` returns a promise. Arguments and results make a JSON round trip. A tool that throws rejects with an `Error` carrying the same message. Tool names are also exposed as identifiers: characters that are not valid in identifiers become `_` (`toCodemodeIdentifier`), so `my-tool` is `tools.my_tool` as well as `tools["my-tool"]`.
41
+ - `ALL_TOOLS` lists `{ name, description }` for every tool, with `name` as the identifier.
42
+ - `text(value)` appends a text item to `result.output`; values other than strings are JSON-stringified. `console.log/info/warn/error/debug` append text items too.
43
+ - `image(urlOrItem)` appends an image item. It accepts a base64 `data:` URL, `{ image_url }`, or an MCP `ImageContent` block (`{ type: "image", data, mimeType }`). Remote URLs are rejected.
44
+ - `exit()` ends the script successfully right away, keeping its output and store writes.
45
+ - `globals` passed to the sandbox are called as top-level functions, for example a host helper `image(ref)`. They behave like tools but are not recorded in `result.calls`. A name like `models.classify` puts the function on a frozen `models` object. With `spread: true`, `execute` receives all call arguments as an array instead of the first one, and `signature` replaces the declaration generated from the schemas.
46
+ - `store(key, value)` and `load(key)` read and write JSON values synchronously. See [Store](#store).
47
+ - Nothing else: no timers, `fetch`, `process`, `require`, modules, or `WebAssembly`. `eval` and `Function` work but only produce more code inside the same VM.
48
+
49
+ `timeoutMs: Infinity` disables the deadline; the script then runs until it settles or `signal` aborts it. A script that waits on a promise nothing can settle (no tool call pending, and the VM has no timers or I/O) fails right away instead of hanging.
50
+
51
+ `memoryLimitBytes` caps the VM's heap. Allocations beyond it fail inside the script as `InternalError: out of memory`.
52
+
53
+ ## Store
54
+
55
+ `store`/`load` let scripts keep values across executions. The sandbox does not persist anything itself: pass the current values as `options.store`, and a successful result reports what the script changed as `result.storeWrites` (`{ set, delete }`). Failed executions report no writes.
56
+
57
+ ```ts
58
+ const result = await sandbox.execute(`store("runs", (load("runs") ?? 0) + 1)`, { store: saved });
59
+ if (result.ok) {
60
+ for (const key of result.storeWrites.delete) delete saved[key];
61
+ Object.assign(saved, result.storeWrites.set);
62
+ }
63
+ ```
64
+
65
+ `load` returns a copy, so mutating it does not change the store. Storing `undefined` deletes the key. A value may be at most `MAX_STORE_VALUE_CHARS` (256 Ki) characters of JSON and all values together at most `MAX_STORE_TOTAL_CHARS` (1 Mi); larger writes throw a `RangeError` inside the script.
66
+
67
+ ## Source format
68
+
69
+ `parseCodemodeSource()` accepts a script whose first line may be an options line:
70
+
71
+ ```js
72
+ // @options: {"max_output_tokens": 2000, "timeout_ms": 30000}
73
+ const source = await tools.read({ path: "package.json" });
74
+ text(JSON.parse(source).name);
75
+ ```
76
+
77
+ Supported fields are `max_output_tokens`, a token budget for the output, and `timeout_ms`, a hard deadline. The sandbox does not act on them; the caller decides. The options line is replaced by an empty line, so line numbers in stack traces still match the input. Empty input, invalid JSON, unknown fields, or an options line without code throw `CodemodeSourceError`. `CODEMODE_SOURCE_GRAMMAR` is a Lark grammar for providers that support grammar-constrained tool input. Both are also available from the lightweight `@earendil-works/pi-codemode/source` entry.
78
+
79
+ ## Bundled hosts
80
+
81
+ By default the sandbox loads `quickjs-wasi/quickjs.wasm` from the installed package and starts the worker file that sits next to this package's module. Neither exists on disk when the host is bundled, so pass both:
82
+
83
+ ```ts
84
+ import { CodemodeSandbox, loadQuickJSWasm } from "@earendil-works/pi-codemode";
85
+
86
+ const sandbox = new CodemodeSandbox({
87
+ tools,
88
+ // Compiled once per path and cached.
89
+ wasm: loadQuickJSWasm(pathToQuickJSWasm),
90
+ // A file of your build containing `import "@earendil-works/pi-codemode/worker";`
91
+ workerUrl: new URL("./codemode-worker.js", import.meta.url),
92
+ });
93
+ ```
94
+
95
+ ## Declarations for the model
96
+
97
+ Tools and globals can carry `description`, `inputSchema`, and `outputSchema` (JSON Schema). `renderDeclarations()` turns them into TypeScript declarations for a model-facing tool description:
98
+
99
+ ```ts
100
+ renderDeclarations({ tools: sandbox.tools, globals: sandbox.globals });
101
+ // declare const tools: {
102
+ // /** Read a file */
103
+ // read(args: {
104
+ // path: string;
105
+ // }): Promise<string>;
106
+ // };
107
+ ```
108
+
109
+ Schemas only shape the declarations; values are not validated against them. Local references (`#/$defs/...`, `#/definitions/...`) are expanded; recursive and remote references render as `unknown`.
110
+
111
+ ## Using with pi-agent-core
112
+
113
+ To give an `Agent` a codemode tool, expose its other tools to the sandbox and wrap `execute()` as an `AgentTool`:
114
+
115
+ ```ts
116
+ import type { AgentTool } from "@earendil-works/pi-agent-core";
117
+ import {
118
+ type CodemodeJsonSchema,
119
+ CodemodeSandbox,
120
+ type CodemodeTool,
121
+ renderDeclarations,
122
+ } from "@earendil-works/pi-codemode";
123
+ import { Type } from "typebox";
124
+
125
+ const sandboxTools: CodemodeTool[] = agentTools.map((tool) => ({
126
+ name: tool.name,
127
+ description: tool.description,
128
+ inputSchema: tool.parameters as CodemodeJsonSchema,
129
+ outputSchema: (tool.outputSchema as CodemodeJsonSchema | undefined) ?? { type: "string" },
130
+ execute: async (args, { signal }) => {
131
+ const result = await tool.execute("nested", args as never, signal);
132
+ if (tool.outputSchema && result.structuredContent !== undefined) return result.structuredContent;
133
+ return result.content.map((block) => (block.type === "text" ? block.text : "")).join("\n");
134
+ },
135
+ }));
136
+
137
+ const codemodeTool: AgentTool = {
138
+ name: "codemode",
139
+ label: "Codemode",
140
+ description: `Run JavaScript that calls tools as \`await tools.<name>(args)\`. Output with text() or return.\n\n${renderDeclarations({ tools: sandboxTools })}`,
141
+ parameters: Type.Object({ code: Type.String() }),
142
+ execute: async (_toolCallId, { code }, signal) => {
143
+ const sandbox = new CodemodeSandbox({ tools: sandboxTools });
144
+ try {
145
+ const result = await sandbox.execute(code, { signal });
146
+ const content = [...result.output];
147
+ if (result.ok && result.value !== undefined) content.push({ type: "text", text: JSON.stringify(result.value) });
148
+ if (!result.ok) content.push({ type: "text", text: result.error.stack ?? result.error.message });
149
+ return { content, details: undefined, isError: !result.ok };
150
+ } finally {
151
+ await sandbox.close();
152
+ }
153
+ },
154
+ };
155
+ ```
156
+
157
+ `result.output` items already have the shape of `@earendil-works/pi-ai`'s `TextContent` and `ImageContent`. Calling `tool.execute()` directly skips the agent's `beforeToolCall` and `afterToolCall` hooks. To apply them to nested calls too, run each call through `runToolCall()` from `@earendil-works/pi-agent-core`, as the [mcp-codemode example](https://github.com/earendil-works/pi/tree/main/packages/agent/examples/mcp-codemode) does. That example also rejects failed nested calls inside the script and combines codemode with MCP tools.
158
+
159
+ ## Results
160
+
161
+ `execute()` never rejects for script failures. `result.error.kind` is one of:
162
+
163
+ | kind | meaning |
164
+ | --------- | --------------------------------------------------------------------------- |
165
+ | `script` | the script threw or failed to parse; `stack` points at `codemode.js:<line>` |
166
+ | `timeout` | the deadline expired; the worker was terminated |
167
+ | `aborted` | `options.signal` fired or `close()` was called; the worker was terminated |
168
+ | `sandbox` | the worker or VM failed, for example a wasm trap or a missing worker file |
169
+
170
+ `result.output` holds the text and image items in the order the script produced them, also for failed executions. `result.calls` lists every tool call with `status: "ok" | "error" | "cancelled"`. A call that is still running when the script returns (not awaited) is aborted through the tool's `signal` and reported as `cancelled`.
171
+
172
+ ## How it works
173
+
174
+ Each `execute()` starts a worker thread (about 20 ms including VM creation) that instantiates a fresh QuickJS VM from the compiled wasm module. The VM is a separate wasm instance with its own linear memory. Its only imports are a WASI shim (clock, random, and stdout/stderr writes, which the worker discards) and one host-call entry point, so the script cannot reach the host except through the functions the worker registers.
175
+
176
+ The worker evaluates a prelude inside the VM that holds the single host bridge in a closure and builds `tools`, `console`, and globals on top of it. The script is compiled as an async function body.
177
+
178
+ Tool calls are relayed to the host thread as messages; the host runs the tool and posts the JSON result back. The host owns the deadline and the abort signal. When either fires, it sets a shared interrupt flag that the VM polls, then calls `worker.terminate()`. The flag is needed on Bun, where `terminate()` cannot stop a thread that is spinning in wasm (`while (true) {}` or `while (true) await null`).
179
+
180
+ The worker keeps script execution off the host thread: QuickJS runs synchronously, so a spinning script on the host thread would block its event loop.
181
+
182
+ ## Runtime notes
183
+
184
+ - Works the same on Node and Bun, including memory limits, interrupts, and a catchable `RangeError` for deep recursion (QuickJS's stack guard is enabled; without it the wasm stack overflows and traps).
185
+ - Stack traces use QuickJS frames (`at f (codemode.js:2:31)`), prefixed with `Name: message` like V8. The wrapper prefix shares line 1 with the script, so line numbers are exact; column numbers on line 1 are shifted.
186
+ - QuickJS is an interpreter. Glue code and filtering tool results are fast enough; heavy computation is slower than in V8.
@@ -0,0 +1,53 @@
1
+ import { toCodemodeIdentifier } from "./identifier.ts";
2
+ export { toCodemodeIdentifier };
3
+ import type { CodemodeJsonSchema, CodemodeTool } from "./types.ts";
4
+ /** Largest rendered input type, in characters, before it becomes `unknown`. */
5
+ export declare const DEFAULT_INPUT_SCHEMA_MAX_CHARS = 16000;
6
+ /**
7
+ * TypeScript types for MCP results, from the MCP `CallToolResult` schema, so `CallToolResult<T>`
8
+ * declarations can refer to them.
9
+ */
10
+ export declare const MCP_TYPESCRIPT_PREAMBLE = "type Role = \"user\" | \"assistant\";\ntype MetaObject = Record<string, unknown>;\ntype Annotations = {\n audience?: Role[];\n priority?: number;\n lastModified?: string;\n};\ntype Icon = {\n src: string;\n mimeType?: string;\n sizes?: string[];\n theme?: \"light\" | \"dark\";\n};\ntype TextResourceContents = {\n uri: string;\n mimeType?: string;\n _meta?: MetaObject;\n text: string;\n};\ntype BlobResourceContents = {\n uri: string;\n mimeType?: string;\n _meta?: MetaObject;\n blob: string;\n};\ntype TextContent = {\n type: \"text\";\n text: string;\n annotations?: Annotations;\n _meta?: MetaObject;\n};\ntype ImageContent = {\n type: \"image\";\n data: string;\n mimeType: string;\n annotations?: Annotations;\n _meta?: MetaObject;\n};\ntype AudioContent = {\n type: \"audio\";\n data: string;\n mimeType: string;\n annotations?: Annotations;\n _meta?: MetaObject;\n};\ntype ResourceLink = {\n icons?: Icon[];\n name: string;\n title?: string;\n uri: string;\n description?: string;\n mimeType?: string;\n annotations?: Annotations;\n size?: number;\n _meta?: MetaObject;\n type: \"resource_link\";\n};\ntype EmbeddedResource = {\n type: \"resource\";\n resource: TextResourceContents | BlobResourceContents;\n annotations?: Annotations;\n _meta?: MetaObject;\n};\ntype ContentBlock =\n | TextContent\n | ImageContent\n | AudioContent\n | ResourceLink\n | EmbeddedResource;\ntype CallToolResult<TStructured = { [key: string]: unknown }> = {\n _meta?: MetaObject;\n content: ContentBlock[];\n isError?: boolean;\n structuredContent?: TStructured;\n [key: string]: unknown;\n};";
11
+ export interface RenderDeclarationsOptions {
12
+ tools?: readonly CodemodeTool[];
13
+ globals?: readonly CodemodeTool[];
14
+ }
15
+ /**
16
+ * Render TypeScript declarations for the script-visible API. Tools become members of
17
+ * `declare const tools`, globals become `declare function` statements, and `ns.member` globals
18
+ * members of `declare const ns`. Descriptions become doc comments; schemas become types.
19
+ */
20
+ export declare function renderDeclarations(options: RenderDeclarationsOptions): string;
21
+ /**
22
+ * One tool as a member of the `tools` object: `name(args: T): Promise<R>;` with the
23
+ * name as the identifier scripts use. Input types longer than `inputMaxChars` render as `unknown`.
24
+ * Tools whose output schema is an MCP `CallToolResult` render as `Promise<CallToolResult<T>>`,
25
+ * which needs {@link MCP_TYPESCRIPT_PREAMBLE}.
26
+ */
27
+ export declare function renderToolSignature(tool: Pick<CodemodeTool, "name" | "inputSchema" | "outputSchema">, options?: {
28
+ inputMaxChars?: number;
29
+ }): string;
30
+ /**
31
+ * A tool's sample: the description followed by the tool's declaration. Used for tool
32
+ * listings and `ALL_TOOLS` entries.
33
+ */
34
+ export declare function renderToolSample(tool: Pick<CodemodeTool, "name" | "description" | "inputSchema" | "outputSchema">, options?: {
35
+ inputMaxChars?: number;
36
+ }): string;
37
+ /**
38
+ * The `structuredContent` schema of an MCP `CallToolResult` output schema (detected by a
39
+ * `content` array of objects, boolean `isError`, and object `_meta`), `true` when it declares none, or
40
+ * `undefined` when the schema is not a `CallToolResult`.
41
+ */
42
+ export declare function mcpStructuredContentSchema(schema: CodemodeJsonSchema | undefined): CodemodeJsonSchema | undefined;
43
+ /**
44
+ * Convert a JSON Schema to a TypeScript type expression: objects on one line (`{ a: string; b?: number; }`) with properties sorted by name,
45
+ * or one property per line with `//` comments when a property has a description; `Array<T>` for
46
+ * arrays. Local references (`#/$defs/...`, `#/definitions/...`) resolve against `schema`;
47
+ * recursive and remote references render as `unknown`. A result longer than `maxChars` renders as
48
+ * `unknown`.
49
+ */
50
+ export declare function schemaToType(schema: CodemodeJsonSchema, options?: {
51
+ maxChars?: number;
52
+ }): string;
53
+ //# sourceMappingURL=declarations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declarations.d.ts","sourceRoot":"","sources":["../src/declarations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAEhC,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAInE,+EAA+E;AAC/E,eAAO,MAAM,8BAA8B,QAAS,CAAC;AAIrD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,ymDA2EjC,CAAC;AAEJ,MAAM,WAAW,yBAAyB;IACzC,KAAK,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAChC,OAAO,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;CAClC;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,CAyB7E;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,GAAG,cAAc,CAAC,EACjE,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GACtC,MAAM,CAMR;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,aAAa,GAAG,aAAa,GAAG,cAAc,CAAC,EACjF,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GACtC,MAAM,CAGR;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,kBAAkB,GAAG,SAAS,GAAG,kBAAkB,GAAG,SAAS,CASjH;AA0CD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,MAAM,CAGpG"}
@@ -0,0 +1,330 @@
1
+ import { toCodemodeIdentifier } from "./identifier.js";
2
+ export { toCodemodeIdentifier };
3
+ const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
4
+ const INDENT = " ";
5
+ /** Largest rendered input type, in characters, before it becomes `unknown`. */
6
+ export const DEFAULT_INPUT_SCHEMA_MAX_CHARS = 16_000;
7
+ /** Local `$ref` expansions per rendered schema, so shared definitions cannot blow up the output. */
8
+ const MAX_REF_EXPANSIONS = 32;
9
+ /**
10
+ * TypeScript types for MCP results, from the MCP `CallToolResult` schema, so `CallToolResult<T>`
11
+ * declarations can refer to them.
12
+ */
13
+ export const MCP_TYPESCRIPT_PREAMBLE = `type Role = "user" | "assistant";
14
+ type MetaObject = Record<string, unknown>;
15
+ type Annotations = {
16
+ audience?: Role[];
17
+ priority?: number;
18
+ lastModified?: string;
19
+ };
20
+ type Icon = {
21
+ src: string;
22
+ mimeType?: string;
23
+ sizes?: string[];
24
+ theme?: "light" | "dark";
25
+ };
26
+ type TextResourceContents = {
27
+ uri: string;
28
+ mimeType?: string;
29
+ _meta?: MetaObject;
30
+ text: string;
31
+ };
32
+ type BlobResourceContents = {
33
+ uri: string;
34
+ mimeType?: string;
35
+ _meta?: MetaObject;
36
+ blob: string;
37
+ };
38
+ type TextContent = {
39
+ type: "text";
40
+ text: string;
41
+ annotations?: Annotations;
42
+ _meta?: MetaObject;
43
+ };
44
+ type ImageContent = {
45
+ type: "image";
46
+ data: string;
47
+ mimeType: string;
48
+ annotations?: Annotations;
49
+ _meta?: MetaObject;
50
+ };
51
+ type AudioContent = {
52
+ type: "audio";
53
+ data: string;
54
+ mimeType: string;
55
+ annotations?: Annotations;
56
+ _meta?: MetaObject;
57
+ };
58
+ type ResourceLink = {
59
+ icons?: Icon[];
60
+ name: string;
61
+ title?: string;
62
+ uri: string;
63
+ description?: string;
64
+ mimeType?: string;
65
+ annotations?: Annotations;
66
+ size?: number;
67
+ _meta?: MetaObject;
68
+ type: "resource_link";
69
+ };
70
+ type EmbeddedResource = {
71
+ type: "resource";
72
+ resource: TextResourceContents | BlobResourceContents;
73
+ annotations?: Annotations;
74
+ _meta?: MetaObject;
75
+ };
76
+ type ContentBlock =
77
+ | TextContent
78
+ | ImageContent
79
+ | AudioContent
80
+ | ResourceLink
81
+ | EmbeddedResource;
82
+ type CallToolResult<TStructured = { [key: string]: unknown }> = {
83
+ _meta?: MetaObject;
84
+ content: ContentBlock[];
85
+ isError?: boolean;
86
+ structuredContent?: TStructured;
87
+ [key: string]: unknown;
88
+ };`;
89
+ /**
90
+ * Render TypeScript declarations for the script-visible API. Tools become members of
91
+ * `declare const tools`, globals become `declare function` statements, and `ns.member` globals
92
+ * members of `declare const ns`. Descriptions become doc comments; schemas become types.
93
+ */
94
+ export function renderDeclarations(options) {
95
+ const sections = [];
96
+ const tools = options.tools ?? [];
97
+ if (tools.length > 0) {
98
+ const members = tools.map((tool) => `${docComment(tool.description, INDENT)}${INDENT}${renderToolSignature(tool)}`);
99
+ sections.push(`declare const tools: {\n${members.join("\n")}\n};`);
100
+ }
101
+ const namespaces = new Map();
102
+ for (const global of options.globals ?? []) {
103
+ const dot = global.name.indexOf(".");
104
+ if (dot === -1) {
105
+ sections.push(renderGlobal(`declare function ${global.name}`, global, ""));
106
+ continue;
107
+ }
108
+ const namespace = global.name.slice(0, dot);
109
+ const members = namespaces.get(namespace) ?? [];
110
+ if (members.length === 0)
111
+ namespaces.set(namespace, members);
112
+ members.push(renderGlobal(global.name.slice(dot + 1), global, INDENT));
113
+ }
114
+ for (const [namespace, members] of namespaces) {
115
+ sections.push(`declare const ${namespace}: {\n${members.join("\n")}\n};`);
116
+ }
117
+ return sections.join("\n\n");
118
+ }
119
+ /**
120
+ * One tool as a member of the `tools` object: `name(args: T): Promise<R>;` with the
121
+ * name as the identifier scripts use. Input types longer than `inputMaxChars` render as `unknown`.
122
+ * Tools whose output schema is an MCP `CallToolResult` render as `Promise<CallToolResult<T>>`,
123
+ * which needs {@link MCP_TYPESCRIPT_PREAMBLE}.
124
+ */
125
+ export function renderToolSignature(tool, options = {}) {
126
+ const input = tool.inputSchema === undefined
127
+ ? "unknown"
128
+ : schemaToType(tool.inputSchema, { maxChars: options.inputMaxChars ?? DEFAULT_INPUT_SCHEMA_MAX_CHARS });
129
+ return `${toCodemodeIdentifier(tool.name)}(args: ${input}): Promise<${outputType(tool.outputSchema)}>;`;
130
+ }
131
+ /**
132
+ * A tool's sample: the description followed by the tool's declaration. Used for tool
133
+ * listings and `ALL_TOOLS` entries.
134
+ */
135
+ export function renderToolSample(tool, options = {}) {
136
+ const declaration = `declare const tools: { ${renderToolSignature(tool, options)} };`;
137
+ return `${tool.description?.trim() ?? ""}\n\ncodemode tool declaration:\n\`\`\`ts\n${declaration}\n\`\`\``;
138
+ }
139
+ /**
140
+ * The `structuredContent` schema of an MCP `CallToolResult` output schema (detected by a
141
+ * `content` array of objects, boolean `isError`, and object `_meta`), `true` when it declares none, or
142
+ * `undefined` when the schema is not a `CallToolResult`.
143
+ */
144
+ export function mcpStructuredContentSchema(schema) {
145
+ if (!isObject(schema) || !isObject(schema.properties))
146
+ return undefined;
147
+ const { content, isError, _meta, structuredContent } = schema.properties;
148
+ if (!isObject(content) || content.type !== "array" || !isObject(content.items) || content.items.type !== "object") {
149
+ return undefined;
150
+ }
151
+ if (!isObject(isError) || isError.type !== "boolean" || !isObject(_meta) || _meta.type !== "object")
152
+ return undefined;
153
+ return isObject(structuredContent) || typeof structuredContent === "boolean" ? structuredContent : true;
154
+ }
155
+ function outputType(schema) {
156
+ const structured = mcpStructuredContentSchema(schema);
157
+ if (structured !== undefined) {
158
+ const type = schemaToType(structured);
159
+ return type === "unknown" ? "CallToolResult" : `CallToolResult<${type}>`;
160
+ }
161
+ return schema === undefined ? "unknown" : schemaToType(schema);
162
+ }
163
+ function renderGlobal(head, global, indent) {
164
+ if (global.signature !== undefined) {
165
+ return `${docComment(global.description, indent)}${indent}${head}${global.signature};`;
166
+ }
167
+ const input = global.inputSchema === undefined ? "unknown" : schemaToType(global.inputSchema);
168
+ const output = global.outputSchema === undefined ? "unknown" : schemaToType(global.outputSchema);
169
+ return `${docComment(global.description, indent)}${indent}${head}(args: ${input}): Promise<${output}>;`;
170
+ }
171
+ function docComment(description, indent) {
172
+ const text = description?.trim();
173
+ if (!text)
174
+ return "";
175
+ const lines = text.replaceAll("*/", "*\\/").split(/\r?\n/);
176
+ if (lines.length === 1)
177
+ return `${indent}/** ${lines[0]} */\n`;
178
+ return `${indent}/**\n${lines.map((line) => `${indent} *${line ? ` ${line}` : ""}`).join("\n")}\n${indent} */\n`;
179
+ }
180
+ function propertyKey(name) {
181
+ return IDENTIFIER.test(name) ? name : JSON.stringify(name);
182
+ }
183
+ function isObject(value) {
184
+ return typeof value === "object" && value !== null && !Array.isArray(value);
185
+ }
186
+ function union(types) {
187
+ const unique = [...new Set(types)];
188
+ if (unique.includes("unknown"))
189
+ return "unknown";
190
+ return unique.length === 0 ? "never" : unique.join(" | ");
191
+ }
192
+ /**
193
+ * Convert a JSON Schema to a TypeScript type expression: objects on one line (`{ a: string; b?: number; }`) with properties sorted by name,
194
+ * or one property per line with `//` comments when a property has a description; `Array<T>` for
195
+ * arrays. Local references (`#/$defs/...`, `#/definitions/...`) resolve against `schema`;
196
+ * recursive and remote references render as `unknown`. A result longer than `maxChars` renders as
197
+ * `unknown`.
198
+ */
199
+ export function schemaToType(schema, options = {}) {
200
+ const type = toType(schema, { root: schema, resolving: new Set(), expansions: 0 });
201
+ return options.maxChars !== undefined && type.length > options.maxChars ? "unknown" : type;
202
+ }
203
+ function resolveRef(ref, root) {
204
+ if (ref !== "#" && !ref.startsWith("#/"))
205
+ return undefined;
206
+ let current = root;
207
+ for (const segment of ref.slice(2).split("/").filter(Boolean)) {
208
+ const key = decodeURIComponent(segment).replaceAll("~1", "/").replaceAll("~0", "~");
209
+ if (!isObject(current) || !(key in current))
210
+ return undefined;
211
+ current = current[key];
212
+ }
213
+ return typeof current === "boolean" || isObject(current) ? current : undefined;
214
+ }
215
+ function toType(schema, context) {
216
+ if (schema === true)
217
+ return "unknown";
218
+ if (schema === false)
219
+ return "never";
220
+ if (!isObject(schema))
221
+ return "unknown";
222
+ if (typeof schema.$ref === "string") {
223
+ const ref = schema.$ref;
224
+ if (context.resolving.has(ref) || context.expansions >= MAX_REF_EXPANSIONS)
225
+ return "unknown";
226
+ const target = resolveRef(ref, context.root);
227
+ if (target === undefined)
228
+ return "unknown";
229
+ context.expansions++;
230
+ context.resolving.add(ref);
231
+ try {
232
+ return toType(target, context);
233
+ }
234
+ finally {
235
+ context.resolving.delete(ref);
236
+ }
237
+ }
238
+ if ("const" in schema)
239
+ return JSON.stringify(schema.const) ?? "unknown";
240
+ if (Array.isArray(schema.enum))
241
+ return union(schema.enum.map((value) => JSON.stringify(value) ?? "unknown"));
242
+ const variants = Array.isArray(schema.anyOf) ? schema.anyOf : Array.isArray(schema.oneOf) ? schema.oneOf : undefined;
243
+ if (variants)
244
+ return union(variants.map((variant) => toType(variant, context)));
245
+ if (Array.isArray(schema.allOf)) {
246
+ const parts = schema.allOf
247
+ .map((part) => toType(part, context))
248
+ .filter((part) => part !== "unknown");
249
+ return parts.length === 0
250
+ ? "unknown"
251
+ : parts.map((part) => (part.includes(" | ") ? `(${part})` : part)).join(" & ");
252
+ }
253
+ const type = schema.type;
254
+ if (Array.isArray(type))
255
+ return union(type.map((entry) => toType({ ...schema, type: entry }, context)));
256
+ switch (type) {
257
+ case "string":
258
+ return "string";
259
+ case "number":
260
+ case "integer":
261
+ return "number";
262
+ case "boolean":
263
+ return "boolean";
264
+ case "null":
265
+ return "null";
266
+ case "array":
267
+ return arrayType(schema, context);
268
+ case "object":
269
+ return objectType(schema, context);
270
+ case undefined:
271
+ if ("properties" in schema || "additionalProperties" in schema || "required" in schema) {
272
+ return objectType(schema, context);
273
+ }
274
+ if ("items" in schema || "prefixItems" in schema)
275
+ return arrayType(schema, context);
276
+ return "unknown";
277
+ default:
278
+ return "unknown";
279
+ }
280
+ }
281
+ function arrayType(schema, context) {
282
+ if (schema.items !== undefined && !Array.isArray(schema.items)) {
283
+ return `Array<${toType(schema.items, context)}>`;
284
+ }
285
+ const tuple = Array.isArray(schema.prefixItems)
286
+ ? schema.prefixItems
287
+ : Array.isArray(schema.items)
288
+ ? schema.items
289
+ : [];
290
+ if (tuple.length > 0)
291
+ return `[${tuple.map((item) => toType(item, context)).join(", ")}]`;
292
+ return "unknown[]";
293
+ }
294
+ function descriptionOf(property) {
295
+ return isObject(property) && typeof property.description === "string" ? property.description.trim() : "";
296
+ }
297
+ function objectType(schema, context) {
298
+ const properties = isObject(schema.properties) ? schema.properties : {};
299
+ const required = new Set(Array.isArray(schema.required) ? schema.required : []);
300
+ const names = Object.keys(properties).sort();
301
+ const members = names.map((name) => {
302
+ const optional = required.has(name) ? "" : "?";
303
+ return `${propertyKey(name)}${optional}: ${toType(properties[name], context)};`;
304
+ });
305
+ const additional = schema.additionalProperties;
306
+ if (additional !== undefined && additional !== false) {
307
+ const type = additional === true ? "unknown" : toType(additional, context);
308
+ members.push(`[key: string]: ${type};`);
309
+ }
310
+ else if (additional === undefined && names.length === 0) {
311
+ members.push("[key: string]: unknown;");
312
+ }
313
+ if (members.length === 0)
314
+ return "{}";
315
+ if (!names.some((name) => descriptionOf(properties[name])))
316
+ return `{ ${members.join(" ")} }`;
317
+ const lines = ["{"];
318
+ names.forEach((name, index) => {
319
+ for (const line of descriptionOf(properties[name]).split(/\r?\n/)) {
320
+ if (line.trim())
321
+ lines.push(`${INDENT}// ${line.trim()}`);
322
+ }
323
+ lines.push(`${INDENT}${members[index].replaceAll("\n", `\n${INDENT}`)}`);
324
+ });
325
+ for (const member of members.slice(names.length))
326
+ lines.push(`${INDENT}${member}`);
327
+ lines.push("}");
328
+ return lines.join("\n");
329
+ }
330
+ //# sourceMappingURL=declarations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declarations.js","sourceRoot":"","sources":["../src/declarations.ts"],"names":[],"mappings":"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","sourcesContent":["import { toCodemodeIdentifier } from \"./identifier.ts\";\n\nexport { toCodemodeIdentifier };\n\nimport type { CodemodeJsonSchema, CodemodeTool } from \"./types.ts\";\n\nconst IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\nconst INDENT = \" \";\n/** Largest rendered input type, in characters, before it becomes `unknown`. */\nexport const DEFAULT_INPUT_SCHEMA_MAX_CHARS = 16_000;\n/** Local `$ref` expansions per rendered schema, so shared definitions cannot blow up the output. */\nconst MAX_REF_EXPANSIONS = 32;\n\n/**\n * TypeScript types for MCP results, from the MCP `CallToolResult` schema, so `CallToolResult<T>`\n * declarations can refer to them.\n */\nexport const MCP_TYPESCRIPT_PREAMBLE = `type Role = \"user\" | \"assistant\";\ntype MetaObject = Record<string, unknown>;\ntype Annotations = {\n audience?: Role[];\n priority?: number;\n lastModified?: string;\n};\ntype Icon = {\n src: string;\n mimeType?: string;\n sizes?: string[];\n theme?: \"light\" | \"dark\";\n};\ntype TextResourceContents = {\n uri: string;\n mimeType?: string;\n _meta?: MetaObject;\n text: string;\n};\ntype BlobResourceContents = {\n uri: string;\n mimeType?: string;\n _meta?: MetaObject;\n blob: string;\n};\ntype TextContent = {\n type: \"text\";\n text: string;\n annotations?: Annotations;\n _meta?: MetaObject;\n};\ntype ImageContent = {\n type: \"image\";\n data: string;\n mimeType: string;\n annotations?: Annotations;\n _meta?: MetaObject;\n};\ntype AudioContent = {\n type: \"audio\";\n data: string;\n mimeType: string;\n annotations?: Annotations;\n _meta?: MetaObject;\n};\ntype ResourceLink = {\n icons?: Icon[];\n name: string;\n title?: string;\n uri: string;\n description?: string;\n mimeType?: string;\n annotations?: Annotations;\n size?: number;\n _meta?: MetaObject;\n type: \"resource_link\";\n};\ntype EmbeddedResource = {\n type: \"resource\";\n resource: TextResourceContents | BlobResourceContents;\n annotations?: Annotations;\n _meta?: MetaObject;\n};\ntype ContentBlock =\n | TextContent\n | ImageContent\n | AudioContent\n | ResourceLink\n | EmbeddedResource;\ntype CallToolResult<TStructured = { [key: string]: unknown }> = {\n _meta?: MetaObject;\n content: ContentBlock[];\n isError?: boolean;\n structuredContent?: TStructured;\n [key: string]: unknown;\n};`;\n\nexport interface RenderDeclarationsOptions {\n\ttools?: readonly CodemodeTool[];\n\tglobals?: readonly CodemodeTool[];\n}\n\n/**\n * Render TypeScript declarations for the script-visible API. Tools become members of\n * `declare const tools`, globals become `declare function` statements, and `ns.member` globals\n * members of `declare const ns`. Descriptions become doc comments; schemas become types.\n */\nexport function renderDeclarations(options: RenderDeclarationsOptions): string {\n\tconst sections: string[] = [];\n\tconst tools = options.tools ?? [];\n\tif (tools.length > 0) {\n\t\tconst members = tools.map(\n\t\t\t(tool) => `${docComment(tool.description, INDENT)}${INDENT}${renderToolSignature(tool)}`,\n\t\t);\n\t\tsections.push(`declare const tools: {\\n${members.join(\"\\n\")}\\n};`);\n\t}\n\tconst namespaces = new Map<string, string[]>();\n\tfor (const global of options.globals ?? []) {\n\t\tconst dot = global.name.indexOf(\".\");\n\t\tif (dot === -1) {\n\t\t\tsections.push(renderGlobal(`declare function ${global.name}`, global, \"\"));\n\t\t\tcontinue;\n\t\t}\n\t\tconst namespace = global.name.slice(0, dot);\n\t\tconst members = namespaces.get(namespace) ?? [];\n\t\tif (members.length === 0) namespaces.set(namespace, members);\n\t\tmembers.push(renderGlobal(global.name.slice(dot + 1), global, INDENT));\n\t}\n\tfor (const [namespace, members] of namespaces) {\n\t\tsections.push(`declare const ${namespace}: {\\n${members.join(\"\\n\")}\\n};`);\n\t}\n\treturn sections.join(\"\\n\\n\");\n}\n\n/**\n * One tool as a member of the `tools` object: `name(args: T): Promise<R>;` with the\n * name as the identifier scripts use. Input types longer than `inputMaxChars` render as `unknown`.\n * Tools whose output schema is an MCP `CallToolResult` render as `Promise<CallToolResult<T>>`,\n * which needs {@link MCP_TYPESCRIPT_PREAMBLE}.\n */\nexport function renderToolSignature(\n\ttool: Pick<CodemodeTool, \"name\" | \"inputSchema\" | \"outputSchema\">,\n\toptions: { inputMaxChars?: number } = {},\n): string {\n\tconst input =\n\t\ttool.inputSchema === undefined\n\t\t\t? \"unknown\"\n\t\t\t: schemaToType(tool.inputSchema, { maxChars: options.inputMaxChars ?? DEFAULT_INPUT_SCHEMA_MAX_CHARS });\n\treturn `${toCodemodeIdentifier(tool.name)}(args: ${input}): Promise<${outputType(tool.outputSchema)}>;`;\n}\n\n/**\n * A tool's sample: the description followed by the tool's declaration. Used for tool\n * listings and `ALL_TOOLS` entries.\n */\nexport function renderToolSample(\n\ttool: Pick<CodemodeTool, \"name\" | \"description\" | \"inputSchema\" | \"outputSchema\">,\n\toptions: { inputMaxChars?: number } = {},\n): string {\n\tconst declaration = `declare const tools: { ${renderToolSignature(tool, options)} };`;\n\treturn `${tool.description?.trim() ?? \"\"}\\n\\ncodemode tool declaration:\\n\\`\\`\\`ts\\n${declaration}\\n\\`\\`\\``;\n}\n\n/**\n * The `structuredContent` schema of an MCP `CallToolResult` output schema (detected by a\n * `content` array of objects, boolean `isError`, and object `_meta`), `true` when it declares none, or\n * `undefined` when the schema is not a `CallToolResult`.\n */\nexport function mcpStructuredContentSchema(schema: CodemodeJsonSchema | undefined): CodemodeJsonSchema | undefined {\n\tif (!isObject(schema) || !isObject(schema.properties)) return undefined;\n\tconst { content, isError, _meta, structuredContent } = schema.properties;\n\tif (!isObject(content) || content.type !== \"array\" || !isObject(content.items) || content.items.type !== \"object\") {\n\t\treturn undefined;\n\t}\n\tif (!isObject(isError) || isError.type !== \"boolean\" || !isObject(_meta) || _meta.type !== \"object\")\n\t\treturn undefined;\n\treturn isObject(structuredContent) || typeof structuredContent === \"boolean\" ? structuredContent : true;\n}\n\nfunction outputType(schema: CodemodeJsonSchema | undefined): string {\n\tconst structured = mcpStructuredContentSchema(schema);\n\tif (structured !== undefined) {\n\t\tconst type = schemaToType(structured);\n\t\treturn type === \"unknown\" ? \"CallToolResult\" : `CallToolResult<${type}>`;\n\t}\n\treturn schema === undefined ? \"unknown\" : schemaToType(schema);\n}\n\nfunction renderGlobal(head: string, global: CodemodeTool, indent: string): string {\n\tif (global.signature !== undefined) {\n\t\treturn `${docComment(global.description, indent)}${indent}${head}${global.signature};`;\n\t}\n\tconst input = global.inputSchema === undefined ? \"unknown\" : schemaToType(global.inputSchema);\n\tconst output = global.outputSchema === undefined ? \"unknown\" : schemaToType(global.outputSchema);\n\treturn `${docComment(global.description, indent)}${indent}${head}(args: ${input}): Promise<${output}>;`;\n}\n\nfunction docComment(description: string | undefined, indent: string): string {\n\tconst text = description?.trim();\n\tif (!text) return \"\";\n\tconst lines = text.replaceAll(\"*/\", \"*\\\\/\").split(/\\r?\\n/);\n\tif (lines.length === 1) return `${indent}/** ${lines[0]} */\\n`;\n\treturn `${indent}/**\\n${lines.map((line) => `${indent} *${line ? ` ${line}` : \"\"}`).join(\"\\n\")}\\n${indent} */\\n`;\n}\n\nfunction propertyKey(name: string): string {\n\treturn IDENTIFIER.test(name) ? name : JSON.stringify(name);\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction union(types: string[]): string {\n\tconst unique = [...new Set(types)];\n\tif (unique.includes(\"unknown\")) return \"unknown\";\n\treturn unique.length === 0 ? \"never\" : unique.join(\" | \");\n}\n\n/**\n * Convert a JSON Schema to a TypeScript type expression: objects on one line (`{ a: string; b?: number; }`) with properties sorted by name,\n * or one property per line with `//` comments when a property has a description; `Array<T>` for\n * arrays. Local references (`#/$defs/...`, `#/definitions/...`) resolve against `schema`;\n * recursive and remote references render as `unknown`. A result longer than `maxChars` renders as\n * `unknown`.\n */\nexport function schemaToType(schema: CodemodeJsonSchema, options: { maxChars?: number } = {}): string {\n\tconst type = toType(schema, { root: schema, resolving: new Set(), expansions: 0 });\n\treturn options.maxChars !== undefined && type.length > options.maxChars ? \"unknown\" : type;\n}\n\ninterface SchemaContext {\n\troot: CodemodeJsonSchema;\n\t/** References being expanded on the current path, to stop at recursive types. */\n\tresolving: Set<string>;\n\texpansions: number;\n}\n\nfunction resolveRef(ref: string, root: CodemodeJsonSchema): CodemodeJsonSchema | undefined {\n\tif (ref !== \"#\" && !ref.startsWith(\"#/\")) return undefined;\n\tlet current: unknown = root;\n\tfor (const segment of ref.slice(2).split(\"/\").filter(Boolean)) {\n\t\tconst key = decodeURIComponent(segment).replaceAll(\"~1\", \"/\").replaceAll(\"~0\", \"~\");\n\t\tif (!isObject(current) || !(key in current)) return undefined;\n\t\tcurrent = current[key];\n\t}\n\treturn typeof current === \"boolean\" || isObject(current) ? current : undefined;\n}\n\nfunction toType(schema: CodemodeJsonSchema, context: SchemaContext): string {\n\tif (schema === true) return \"unknown\";\n\tif (schema === false) return \"never\";\n\tif (!isObject(schema)) return \"unknown\";\n\tif (typeof schema.$ref === \"string\") {\n\t\tconst ref = schema.$ref;\n\t\tif (context.resolving.has(ref) || context.expansions >= MAX_REF_EXPANSIONS) return \"unknown\";\n\t\tconst target = resolveRef(ref, context.root);\n\t\tif (target === undefined) return \"unknown\";\n\t\tcontext.expansions++;\n\t\tcontext.resolving.add(ref);\n\t\ttry {\n\t\t\treturn toType(target, context);\n\t\t} finally {\n\t\t\tcontext.resolving.delete(ref);\n\t\t}\n\t}\n\n\tif (\"const\" in schema) return JSON.stringify(schema.const) ?? \"unknown\";\n\tif (Array.isArray(schema.enum)) return union(schema.enum.map((value) => JSON.stringify(value) ?? \"unknown\"));\n\n\tconst variants = Array.isArray(schema.anyOf) ? schema.anyOf : Array.isArray(schema.oneOf) ? schema.oneOf : undefined;\n\tif (variants) return union(variants.map((variant) => toType(variant as CodemodeJsonSchema, context)));\n\tif (Array.isArray(schema.allOf)) {\n\t\tconst parts = schema.allOf\n\t\t\t.map((part) => toType(part as CodemodeJsonSchema, context))\n\t\t\t.filter((part) => part !== \"unknown\");\n\t\treturn parts.length === 0\n\t\t\t? \"unknown\"\n\t\t\t: parts.map((part) => (part.includes(\" | \") ? `(${part})` : part)).join(\" & \");\n\t}\n\n\tconst type = schema.type;\n\tif (Array.isArray(type)) return union(type.map((entry) => toType({ ...schema, type: entry }, context)));\n\tswitch (type) {\n\t\tcase \"string\":\n\t\t\treturn \"string\";\n\t\tcase \"number\":\n\t\tcase \"integer\":\n\t\t\treturn \"number\";\n\t\tcase \"boolean\":\n\t\t\treturn \"boolean\";\n\t\tcase \"null\":\n\t\t\treturn \"null\";\n\t\tcase \"array\":\n\t\t\treturn arrayType(schema, context);\n\t\tcase \"object\":\n\t\t\treturn objectType(schema, context);\n\t\tcase undefined:\n\t\t\tif (\"properties\" in schema || \"additionalProperties\" in schema || \"required\" in schema) {\n\t\t\t\treturn objectType(schema, context);\n\t\t\t}\n\t\t\tif (\"items\" in schema || \"prefixItems\" in schema) return arrayType(schema, context);\n\t\t\treturn \"unknown\";\n\t\tdefault:\n\t\t\treturn \"unknown\";\n\t}\n}\n\nfunction arrayType(schema: Record<string, unknown>, context: SchemaContext): string {\n\tif (schema.items !== undefined && !Array.isArray(schema.items)) {\n\t\treturn `Array<${toType(schema.items as CodemodeJsonSchema, context)}>`;\n\t}\n\tconst tuple = Array.isArray(schema.prefixItems)\n\t\t? schema.prefixItems\n\t\t: Array.isArray(schema.items)\n\t\t\t? schema.items\n\t\t\t: [];\n\tif (tuple.length > 0) return `[${tuple.map((item) => toType(item as CodemodeJsonSchema, context)).join(\", \")}]`;\n\treturn \"unknown[]\";\n}\n\nfunction descriptionOf(property: unknown): string {\n\treturn isObject(property) && typeof property.description === \"string\" ? property.description.trim() : \"\";\n}\n\nfunction objectType(schema: Record<string, unknown>, context: SchemaContext): string {\n\tconst properties = isObject(schema.properties) ? schema.properties : {};\n\tconst required = new Set(Array.isArray(schema.required) ? schema.required : []);\n\tconst names = Object.keys(properties).sort();\n\tconst members = names.map((name) => {\n\t\tconst optional = required.has(name) ? \"\" : \"?\";\n\t\treturn `${propertyKey(name)}${optional}: ${toType(properties[name] as CodemodeJsonSchema, context)};`;\n\t});\n\tconst additional = schema.additionalProperties;\n\tif (additional !== undefined && additional !== false) {\n\t\tconst type = additional === true ? \"unknown\" : toType(additional as CodemodeJsonSchema, context);\n\t\tmembers.push(`[key: string]: ${type};`);\n\t} else if (additional === undefined && names.length === 0) {\n\t\tmembers.push(\"[key: string]: unknown;\");\n\t}\n\tif (members.length === 0) return \"{}\";\n\tif (!names.some((name) => descriptionOf(properties[name]))) return `{ ${members.join(\" \")} }`;\n\n\tconst lines = [\"{\"];\n\tnames.forEach((name, index) => {\n\t\tfor (const line of descriptionOf(properties[name]).split(/\\r?\\n/)) {\n\t\t\tif (line.trim()) lines.push(`${INDENT}// ${line.trim()}`);\n\t\t}\n\t\tlines.push(`${INDENT}${members[index].replaceAll(\"\\n\", `\\n${INDENT}`)}`);\n\t});\n\tfor (const member of members.slice(names.length)) lines.push(`${INDENT}${member}`);\n\tlines.push(\"}\");\n\treturn lines.join(\"\\n\");\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The identifier a script uses for a tool: characters that are not valid in a JavaScript
3
+ * identifier become `_`. `mcp__docs__search` stays as is, `my-tool` becomes `my_tool`.
4
+ */
5
+ export declare function toCodemodeIdentifier(name: string): string;
6
+ //# sourceMappingURL=identifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifier.d.ts","sourceRoot":"","sources":["../src/identifier.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOzD"}