@tanstack/ai-code-mode-snippets 0.3.14

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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +208 -0
  3. package/dist/esm/code-mode-with-snippets.d.ts +65 -0
  4. package/dist/esm/code-mode-with-snippets.js +145 -0
  5. package/dist/esm/code-mode-with-snippets.js.map +1 -0
  6. package/dist/esm/create-snippet-management-tools.d.ts +40 -0
  7. package/dist/esm/create-snippet-management-tools.js +173 -0
  8. package/dist/esm/create-snippet-management-tools.js.map +1 -0
  9. package/dist/esm/create-snippets-system-prompt.d.ts +22 -0
  10. package/dist/esm/create-snippets-system-prompt.js +234 -0
  11. package/dist/esm/create-snippets-system-prompt.js.map +1 -0
  12. package/dist/esm/generate-snippet-types.d.ts +7 -0
  13. package/dist/esm/generate-snippet-types.js +79 -0
  14. package/dist/esm/generate-snippet-types.js.map +1 -0
  15. package/dist/esm/index.d.ts +14 -0
  16. package/dist/esm/index.js +10 -0
  17. package/dist/esm/select-relevant-snippets.d.ts +29 -0
  18. package/dist/esm/select-relevant-snippets.js +56 -0
  19. package/dist/esm/select-relevant-snippets.js.map +1 -0
  20. package/dist/esm/snippets-to-bindings.d.ts +34 -0
  21. package/dist/esm/snippets-to-bindings.js +74 -0
  22. package/dist/esm/snippets-to-bindings.js.map +1 -0
  23. package/dist/esm/snippets-to-tools.d.ts +74 -0
  24. package/dist/esm/snippets-to-tools.js +147 -0
  25. package/dist/esm/snippets-to-tools.js.map +1 -0
  26. package/dist/esm/storage/file-storage.d.ts +27 -0
  27. package/dist/esm/storage/file-storage.js +155 -0
  28. package/dist/esm/storage/file-storage.js.map +1 -0
  29. package/dist/esm/storage/index.d.ts +3 -0
  30. package/dist/esm/storage/index.js +3 -0
  31. package/dist/esm/storage/memory-storage.d.ts +17 -0
  32. package/dist/esm/storage/memory-storage.js +100 -0
  33. package/dist/esm/storage/memory-storage.js.map +1 -0
  34. package/dist/esm/trust-strategies.d.ts +50 -0
  35. package/dist/esm/trust-strategies.js +73 -0
  36. package/dist/esm/trust-strategies.js.map +1 -0
  37. package/dist/esm/types.d.ts +216 -0
  38. package/package.json +92 -0
  39. package/src/code-mode-with-snippets.ts +210 -0
  40. package/src/create-snippet-management-tools.ts +298 -0
  41. package/src/create-snippets-system-prompt.ts +289 -0
  42. package/src/generate-snippet-types.ts +162 -0
  43. package/src/index.ts +60 -0
  44. package/src/select-relevant-snippets.ts +136 -0
  45. package/src/snippets-to-bindings.ts +135 -0
  46. package/src/snippets-to-tools.ts +325 -0
  47. package/src/storage/file-storage.ts +275 -0
  48. package/src/storage/index.ts +6 -0
  49. package/src/storage/memory-storage.ts +172 -0
  50. package/src/trust-strategies.ts +142 -0
  51. package/src/types.ts +289 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-snippet-management-tools.js","names":[],"sources":["../../src/create-snippet-management-tools.ts"],"sourcesContent":["import { toolDefinition } from '@tanstack/ai'\nimport { toolsToBindings } from '@tanstack/ai-code-mode'\nimport { z } from 'zod'\nimport { createDefaultTrustStrategy } from './trust-strategies'\nimport { snippetToTool } from './snippets-to-tools'\nimport type { SchemaInput, ServerTool, ToolRegistry } from '@tanstack/ai'\nimport type { CodeModeToolConfig, ToolBinding } from '@tanstack/ai-code-mode'\nimport type { SnippetStorage } from './types'\nimport type { TrustStrategy } from './trust-strategies'\n\ninterface CreateSnippetManagementToolsOptions {\n /**\n * Storage implementation for snippets\n */\n storage: SnippetStorage\n\n /**\n * Trust strategy for determining initial trust level.\n * If not provided, uses the storage's trustStrategy or falls back to default.\n */\n trustStrategy?: TrustStrategy\n\n /**\n * Tool registry for adding newly registered snippets immediately.\n * When provided, register_snippet will add the new snippet to this registry\n * so it's available as a direct tool in the current chat session.\n */\n registry?: ToolRegistry\n\n /**\n * Code mode config for creating snippet tools.\n * Required when registry is provided.\n */\n config?: CodeModeToolConfig\n\n /**\n * Pre-computed bindings for external_* functions.\n * Required when registry is provided.\n */\n baseBindings?: Record<string, ToolBinding>\n}\n\n/**\n * Create tools for searching, retrieving, and registering snippets.\n * These tools allow the LLM to interact with the snippet library at runtime.\n *\n * When registry, config, and baseBindings are provided, newly registered snippets\n * will be immediately added to the registry and available as direct tools.\n */\nexport function createSnippetManagementTools({\n storage,\n trustStrategy,\n registry,\n config,\n baseBindings,\n}: CreateSnippetManagementToolsOptions): Array<\n ServerTool<SchemaInput, SchemaInput, string>\n> {\n // Use provided strategy, or storage's strategy, or default\n const strategy =\n trustStrategy ?? storage.trustStrategy ?? createDefaultTrustStrategy()\n\n // Compute bindings if not provided but config is available\n const bindings =\n baseBindings ?? (config ? toolsToBindings(config.tools, 'external_') : {})\n return [\n // Search for snippets\n toolDefinition({\n name: 'search_snippets',\n description:\n 'Search the snippet library for reusable snippets. Use this to find snippets that can help accomplish a task. Returns matching snippets with their descriptions.',\n inputSchema: z.object({\n query: z\n .string()\n .describe('Search query describing what you want to accomplish'),\n limit: z\n .number()\n .optional()\n .default(5)\n .describe('Maximum number of results (default: 5)'),\n }),\n outputSchema: z.array(\n z.object({\n name: z.string(),\n description: z.string(),\n usageHints: z.array(z.string()),\n trustLevel: z.enum(['untrusted', 'provisional', 'trusted']),\n }),\n ),\n }).server(async ({ query, limit }) => {\n const results = await storage.search(query, { limit: limit ?? 5 })\n return results.map((s) => ({\n name: s.name,\n description: s.description,\n usageHints: s.usageHints,\n trustLevel: s.trustLevel,\n }))\n }),\n\n // Get full snippet details\n toolDefinition({\n name: 'get_snippet',\n description:\n 'Get the full implementation details of a snippet, including its code. Use this after search_snippets to see how a snippet works before using it.',\n inputSchema: z.object({\n name: z.string().describe('The snippet name (without snippet_ prefix)'),\n }),\n outputSchema: z.object({\n name: z.string().optional(),\n description: z.string().optional(),\n code: z.string().optional(),\n inputSchema: z.string().optional().describe('JSON Schema as string'),\n outputSchema: z.string().optional().describe('JSON Schema as string'),\n usageHints: z.array(z.string()).optional(),\n dependsOn: z.array(z.string()).optional(),\n trustLevel: z.enum(['untrusted', 'provisional', 'trusted']).optional(),\n stats: z\n .object({\n executions: z.number(),\n successRate: z.number(),\n })\n .optional(),\n error: z.string().optional(),\n }),\n }).server(async ({ name }) => {\n const snippet = await storage.get(name)\n if (!snippet) {\n return { error: `Snippet '${name}' not found` }\n }\n return {\n name: snippet.name,\n description: snippet.description,\n code: snippet.code,\n inputSchema: JSON.stringify(snippet.inputSchema),\n outputSchema: JSON.stringify(snippet.outputSchema),\n usageHints: snippet.usageHints,\n dependsOn: snippet.dependsOn,\n trustLevel: snippet.trustLevel,\n stats: snippet.stats,\n }\n }),\n\n // Register a new snippet\n toolDefinition({\n name: 'register_snippet',\n description:\n 'Save working TypeScript code as a reusable snippet for future use. Only register code that has been tested and works correctly. The snippet becomes available as a callable tool immediately.',\n inputSchema: z.object({\n name: z\n .string()\n .regex(\n /^[a-z][a-z0-9_]*$/,\n 'Must be snake_case starting with a letter',\n )\n .describe(\n 'Unique snippet name in snake_case (e.g., fetch_github_stats)',\n ),\n description: z\n .string()\n .describe('Clear description of what the snippet does'),\n code: z\n .string()\n .describe(\n 'The TypeScript code. Receives `input` variable, can call external_* and snippet_* functions, should return a value.',\n ),\n inputSchema: z\n .string()\n .describe(\n 'JSON Schema as a JSON string describing the input parameter, e.g. {\"type\":\"object\",\"properties\":{\"a\":{\"type\":\"number\"}},\"required\":[\"a\"]}',\n ),\n outputSchema: z\n .string()\n .describe(\n 'JSON Schema as a JSON string describing the return value, e.g. {\"type\":\"object\",\"properties\":{\"result\":{\"type\":\"number\"}}}',\n ),\n usageHints: z\n .array(z.string())\n .describe(\n 'Hints about when to use this snippet, e.g. \"Use when user asks about...\"',\n ),\n dependsOn: z\n .array(z.string())\n .optional()\n .default([])\n .describe('Names of other snippets this snippet calls'),\n }),\n outputSchema: z.object({\n success: z.boolean().optional(),\n snippetId: z.string().optional(),\n name: z.string().optional(),\n message: z.string().optional(),\n error: z.string().optional(),\n }),\n }).server(async (rawSnippetDef, context) => {\n // Parse the JSON string schemas\n let inputSchema: Record<string, unknown>\n let outputSchema: Record<string, unknown>\n try {\n inputSchema = JSON.parse(rawSnippetDef.inputSchema) as Record<\n string,\n unknown\n >\n } catch {\n return { error: 'inputSchema must be a valid JSON string' }\n }\n try {\n outputSchema = JSON.parse(rawSnippetDef.outputSchema) as Record<\n string,\n unknown\n >\n } catch {\n return { error: 'outputSchema must be a valid JSON string' }\n }\n\n const snippetDef = {\n ...rawSnippetDef,\n inputSchema,\n outputSchema,\n }\n try {\n // Validate the snippet name isn't reserved\n if (snippetDef.name.startsWith('external_')) {\n return { error: \"Snippet names cannot start with 'external_'\" }\n }\n if (snippetDef.name.startsWith('snippet_')) {\n return {\n error:\n \"Snippet names should not include the 'snippet_' prefix - it will be added automatically\",\n }\n }\n\n // Check if snippet already exists\n const existing = await storage.get(snippetDef.name)\n if (existing) {\n return {\n error: `Snippet '${snippetDef.name}' already exists. Use a different name or update the existing snippet.`,\n }\n }\n\n // Generate a unique ID\n const id = crypto.randomUUID()\n\n // Get initial trust level from strategy\n const initialTrustLevel = strategy.getInitialTrustLevel()\n\n // Save the snippet\n const snippet = await storage.save({\n id,\n name: snippetDef.name,\n description: snippetDef.description,\n code: snippetDef.code,\n inputSchema: snippetDef.inputSchema,\n outputSchema: snippetDef.outputSchema,\n usageHints: snippetDef.usageHints,\n dependsOn: snippetDef.dependsOn ?? [],\n trustLevel: initialTrustLevel,\n stats: { executions: 0, successRate: 0 },\n })\n\n // If registry and config are available, add the snippet as a tool immediately\n if (registry && config) {\n const snippetTool = snippetToTool({\n snippet,\n driver: config.driver,\n bindings,\n storage,\n timeout: config.timeout,\n memoryLimit: config.memoryLimit,\n })\n registry.add(snippetTool)\n console.log(\n `[register_snippet] Added snippet '${snippet.name}' to registry immediately`,\n )\n }\n\n // Emit event for UI notification\n context?.emitCustomEvent('snippet:registered', {\n id: snippet.id,\n name: snippet.name,\n description: snippet.description,\n timestamp: Date.now(),\n })\n\n return {\n success: true,\n snippetId: snippet.id,\n name: snippet.name,\n message: `Snippet '${snippet.name}' registered successfully and is now available as the '${snippet.name}' tool.`,\n }\n } catch (error) {\n console.error('[register_snippet] Error:', error)\n return {\n error: `Failed to register snippet: ${error instanceof Error ? error.message : String(error)}`,\n }\n }\n }),\n ]\n}\n"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { Snippet } from './types.js';
2
+ interface CreateSnippetsSystemPromptOptions {
3
+ /**
4
+ * Snippets that were selected for this request
5
+ */
6
+ selectedSnippets: Array<Snippet>;
7
+ /**
8
+ * Total number of snippets in the library
9
+ */
10
+ totalSnippetCount: number;
11
+ /**
12
+ * Whether snippets are exposed as direct tools (not just sandbox bindings)
13
+ * @default true
14
+ */
15
+ snippetsAsTools?: boolean;
16
+ }
17
+ /**
18
+ * Create system prompt documentation for the snippet library.
19
+ * This is appended to the Code Mode system prompt.
20
+ */
21
+ export declare function createSnippetsSystemPrompt({ selectedSnippets, totalSnippetCount, snippetsAsTools, }: CreateSnippetsSystemPromptOptions): string;
22
+ export {};
@@ -0,0 +1,234 @@
1
+ import { generateSnippetTypes } from "./generate-snippet-types.js";
2
+ //#region src/create-snippets-system-prompt.ts
3
+ /**
4
+ * Generate example input from a JSON Schema
5
+ */
6
+ function generateExampleFromSchema(schema) {
7
+ if (schema.type === "object" && schema.properties) {
8
+ const props = schema.properties;
9
+ const example = {};
10
+ for (const [key, value] of Object.entries(props)) if (value.type === "string") example[key] = `'example_${key}'`;
11
+ else if (value.type === "number") example[key] = 0;
12
+ else if (value.type === "boolean") example[key] = true;
13
+ else if (value.type === "array") example[key] = [];
14
+ else example[key] = null;
15
+ return JSON.stringify(example).replace(/"/g, "");
16
+ }
17
+ return "{}";
18
+ }
19
+ /**
20
+ * Create system prompt documentation for the snippet library.
21
+ * This is appended to the Code Mode system prompt.
22
+ */
23
+ function createSnippetsSystemPrompt({ selectedSnippets, totalSnippetCount, snippetsAsTools = true }) {
24
+ if (totalSnippetCount === 0) return `## Snippet Library
25
+
26
+ You have access to a snippet library for storing reusable code. The library is currently empty.
27
+
28
+ ### Snippet Management Tools
29
+
30
+ - \`search_snippets(query, limit?)\` - Search for snippets (currently empty)
31
+ - \`get_snippet(name)\` - Get full snippet details including code
32
+ - \`register_snippet(...)\` - Save working code as a reusable snippet
33
+
34
+ When you write useful, reusable code, consider registering it as a snippet for future use.
35
+
36
+ **Important**: Newly registered snippets become available as tools on the **next message**, not immediately in the current conversation turn.
37
+ `;
38
+ if (selectedSnippets.length === 0) return `## Snippet Library
39
+
40
+ You have access to a persistent snippet library with ${totalSnippetCount} snippet${totalSnippetCount === 1 ? "" : "s"}. No snippets were pre-loaded for this conversation based on context.
41
+
42
+ ### Snippet Management Tools
43
+
44
+ - \`search_snippets(query, limit?)\` - Search for relevant snippets
45
+ - \`get_snippet(name)\` - Get full snippet details including code
46
+ - \`register_snippet(...)\` - Save working code as a reusable snippet
47
+
48
+ When you write useful, reusable code, consider registering it as a snippet for future use.
49
+
50
+ **Important**: Newly registered snippets become available as tools on the **next message**, not immediately in the current conversation turn.
51
+ `;
52
+ if (snippetsAsTools) {
53
+ const snippetToolDocs = selectedSnippets.map((snippet) => {
54
+ const inputExample = generateExampleFromSchema(snippet.inputSchema);
55
+ const trustBadge = snippet.trustLevel === "trusted" ? "✓ trusted" : snippet.trustLevel === "provisional" ? "◐ provisional" : "○ untrusted";
56
+ return `
57
+ ### ${snippet.name} [${trustBadge}]
58
+
59
+ ${snippet.description}
60
+
61
+ ${snippet.usageHints.map((h) => `- ${h}`).join("\n")}
62
+
63
+ **Input Schema:**
64
+ \`\`\`json
65
+ ${JSON.stringify(snippet.inputSchema, null, 2)}
66
+ \`\`\`
67
+
68
+ **Output Schema:**
69
+ \`\`\`json
70
+ ${JSON.stringify(snippet.outputSchema, null, 2)}
71
+ \`\`\`
72
+
73
+ **Example:**
74
+ Call the \`${snippet.name}\` tool with: ${inputExample}
75
+ `;
76
+ }).join("\n---\n");
77
+ return `## Snippet Library
78
+
79
+ ${selectedSnippets.length} snippet${selectedSnippets.length === 1 ? "" : "s"} pre-loaded for this conversation (${totalSnippetCount} total in library).
80
+
81
+ ### Available Snippet Tools
82
+
83
+ These snippets are available as **direct tools** you can call (marked with [SNIPPET] in description):
84
+
85
+ ${snippetToolDocs}
86
+
87
+ ### Snippet Management Tools
88
+
89
+ - \`search_snippets(query, limit?)\` - Find additional snippets not pre-loaded
90
+ - \`get_snippet(name)\` - Get full details of any snippet
91
+ - \`register_snippet(...)\` - Save working code as a new snippet
92
+
93
+ ### Using Snippets
94
+
95
+ Snippets are **regular tools** - call them directly like any other tool. No need to use \`execute_typescript\`.
96
+
97
+ ### Creating New Snippets
98
+
99
+ When you write useful, reusable code with \`execute_typescript\`, register it:
100
+
101
+ \`\`\`typescript
102
+ // After verifying code works, call the register_snippet tool
103
+ register_snippet({
104
+ name: 'compare_npm_packages',
105
+ description: 'Compare download counts for multiple NPM packages',
106
+ code: \`
107
+ const { packages } = input;
108
+ const results = await Promise.all(
109
+ packages.map(pkg => external_getNpmDownloads({ package: pkg }))
110
+ );
111
+ return packages.map((pkg, i) => ({ package: pkg, downloads: results[i].downloads }))
112
+ .sort((a, b) => b.downloads - a.downloads);
113
+ \`,
114
+ inputSchema: {
115
+ type: 'object',
116
+ properties: { packages: { type: 'array', items: { type: 'string' } } },
117
+ required: ['packages']
118
+ },
119
+ outputSchema: {
120
+ type: 'array',
121
+ items: { type: 'object', properties: { package: { type: 'string' }, downloads: { type: 'number' } } }
122
+ },
123
+ usageHints: ['Use when comparing popularity of NPM packages'],
124
+ dependsOn: [],
125
+ });
126
+ \`\`\`
127
+
128
+ **Important**: Newly registered snippets become available as tools on the **next message**, not immediately in the current conversation turn.
129
+ `;
130
+ }
131
+ const snippetDocs = selectedSnippets.map((snippet) => {
132
+ const inputExample = generateExampleFromSchema(snippet.inputSchema);
133
+ const trustBadge = snippet.trustLevel === "trusted" ? "✓ trusted" : snippet.trustLevel === "provisional" ? "◐ provisional" : "○ untrusted";
134
+ return `
135
+ ### snippet_${snippet.name} [${trustBadge}]
136
+
137
+ ${snippet.description}
138
+
139
+ ${snippet.usageHints.map((h) => `- ${h}`).join("\n")}
140
+
141
+ **Input Schema:**
142
+ \`\`\`json
143
+ ${JSON.stringify(snippet.inputSchema, null, 2)}
144
+ \`\`\`
145
+
146
+ **Output Schema:**
147
+ \`\`\`json
148
+ ${JSON.stringify(snippet.outputSchema, null, 2)}
149
+ \`\`\`
150
+
151
+ **Example:**
152
+ \`\`\`typescript
153
+ const result = await snippet_${snippet.name}(${inputExample});
154
+ \`\`\`
155
+ `;
156
+ }).join("\n---\n");
157
+ const typeStubs = generateSnippetTypes(selectedSnippets);
158
+ return `## Snippet Library
159
+
160
+ ${selectedSnippets.length} snippet${selectedSnippets.length === 1 ? "" : "s"} pre-loaded for this conversation (${totalSnippetCount} total in library).
161
+
162
+ ### Pre-loaded Snippets
163
+
164
+ These are available as \`snippet_*\` functions in your TypeScript code:
165
+
166
+ ${snippetDocs}
167
+
168
+ ### Type Definitions
169
+
170
+ \`\`\`typescript
171
+ ${typeStubs}
172
+ \`\`\`
173
+
174
+ ### Snippet Management Tools
175
+
176
+ - \`search_snippets(query, limit?)\` - Find additional snippets not pre-loaded
177
+ - \`get_snippet(name)\` - Get full details of any snippet
178
+ - \`register_snippet(...)\` - Save working code as a new snippet
179
+
180
+ ### Using Snippets
181
+
182
+ Snippets work just like \`external_*\` functions inside \`execute_typescript\`:
183
+
184
+ \`\`\`typescript
185
+ // Call a pre-loaded snippet
186
+ const stats = await snippet_fetch_github_stats({ owner: 'tanstack', repo: 'query' });
187
+
188
+ // Compose snippets with external tools
189
+ const repos = await external_searchRepositories({ query: 'react state' });
190
+ const detailed = await Promise.all(
191
+ repos.items.slice(0, 5).map(r =>
192
+ snippet_fetch_github_stats({ owner: r.owner.login, repo: r.name })
193
+ )
194
+ );
195
+ \`\`\`
196
+
197
+ ### Creating New Snippets
198
+
199
+ When you write useful, reusable code, register it:
200
+
201
+ \`\`\`typescript
202
+ // After verifying code works, call the register_snippet tool
203
+ register_snippet({
204
+ name: 'compare_npm_packages',
205
+ description: 'Compare download counts for multiple NPM packages',
206
+ code: \`
207
+ const { packages } = input;
208
+ const results = await Promise.all(
209
+ packages.map(pkg => external_getNpmDownloads({ package: pkg }))
210
+ );
211
+ return packages.map((pkg, i) => ({ package: pkg, downloads: results[i].downloads }))
212
+ .sort((a, b) => b.downloads - a.downloads);
213
+ \`,
214
+ inputSchema: {
215
+ type: 'object',
216
+ properties: { packages: { type: 'array', items: { type: 'string' } } },
217
+ required: ['packages']
218
+ },
219
+ outputSchema: {
220
+ type: 'array',
221
+ items: { type: 'object', properties: { package: { type: 'string' }, downloads: { type: 'number' } } }
222
+ },
223
+ usageHints: ['Use when comparing popularity of NPM packages'],
224
+ dependsOn: [],
225
+ });
226
+ \`\`\`
227
+
228
+ **Important**: Newly registered snippets become available as tools on the **next message**, not immediately in the current conversation turn.
229
+ `;
230
+ }
231
+ //#endregion
232
+ export { createSnippetsSystemPrompt };
233
+
234
+ //# sourceMappingURL=create-snippets-system-prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-snippets-system-prompt.js","names":[],"sources":["../../src/create-snippets-system-prompt.ts"],"sourcesContent":["import { generateSnippetTypes } from './generate-snippet-types'\nimport type { Snippet } from './types'\n\ninterface CreateSnippetsSystemPromptOptions {\n /**\n * Snippets that were selected for this request\n */\n selectedSnippets: Array<Snippet>\n\n /**\n * Total number of snippets in the library\n */\n totalSnippetCount: number\n\n /**\n * Whether snippets are exposed as direct tools (not just sandbox bindings)\n * @default true\n */\n snippetsAsTools?: boolean\n}\n\n/**\n * Generate example input from a JSON Schema\n */\nfunction generateExampleFromSchema(schema: Record<string, unknown>): string {\n if (schema.type === 'object' && schema.properties) {\n const props = schema.properties as Record<string, { type: string }>\n const example: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(props)) {\n if (value.type === 'string') example[key] = `'example_${key}'`\n else if (value.type === 'number') example[key] = 0\n else if (value.type === 'boolean') example[key] = true\n else if (value.type === 'array') example[key] = []\n else example[key] = null\n }\n\n return JSON.stringify(example).replace(/\"/g, '')\n }\n return '{}'\n}\n\n/**\n * Create system prompt documentation for the snippet library.\n * This is appended to the Code Mode system prompt.\n */\nexport function createSnippetsSystemPrompt({\n selectedSnippets,\n totalSnippetCount,\n snippetsAsTools = true,\n}: CreateSnippetsSystemPromptOptions): string {\n // No snippets in library\n if (totalSnippetCount === 0) {\n return `## Snippet Library\n\nYou have access to a snippet library for storing reusable code. The library is currently empty.\n\n### Snippet Management Tools\n\n- \\`search_snippets(query, limit?)\\` - Search for snippets (currently empty)\n- \\`get_snippet(name)\\` - Get full snippet details including code\n- \\`register_snippet(...)\\` - Save working code as a reusable snippet\n\nWhen you write useful, reusable code, consider registering it as a snippet for future use.\n\n**Important**: Newly registered snippets become available as tools on the **next message**, not immediately in the current conversation turn.\n`\n }\n\n // No snippets selected for this conversation\n if (selectedSnippets.length === 0) {\n return `## Snippet Library\n\nYou have access to a persistent snippet library with ${totalSnippetCount} snippet${totalSnippetCount === 1 ? '' : 's'}. No snippets were pre-loaded for this conversation based on context.\n\n### Snippet Management Tools\n\n- \\`search_snippets(query, limit?)\\` - Search for relevant snippets\n- \\`get_snippet(name)\\` - Get full snippet details including code\n- \\`register_snippet(...)\\` - Save working code as a reusable snippet\n\nWhen you write useful, reusable code, consider registering it as a snippet for future use.\n\n**Important**: Newly registered snippets become available as tools on the **next message**, not immediately in the current conversation turn.\n`\n }\n\n if (snippetsAsTools) {\n // Snippets are available as direct tools\n const snippetToolDocs = selectedSnippets\n .map((snippet) => {\n const inputExample = generateExampleFromSchema(snippet.inputSchema)\n const trustBadge =\n snippet.trustLevel === 'trusted'\n ? '✓ trusted'\n : snippet.trustLevel === 'provisional'\n ? '◐ provisional'\n : '○ untrusted'\n\n return `\n### ${snippet.name} [${trustBadge}]\n\n${snippet.description}\n\n${snippet.usageHints.map((h) => `- ${h}`).join('\\n')}\n\n**Input Schema:**\n\\`\\`\\`json\n${JSON.stringify(snippet.inputSchema, null, 2)}\n\\`\\`\\`\n\n**Output Schema:**\n\\`\\`\\`json\n${JSON.stringify(snippet.outputSchema, null, 2)}\n\\`\\`\\`\n\n**Example:**\nCall the \\`${snippet.name}\\` tool with: ${inputExample}\n`\n })\n .join('\\n---\\n')\n\n return `## Snippet Library\n\n${selectedSnippets.length} snippet${selectedSnippets.length === 1 ? '' : 's'} pre-loaded for this conversation (${totalSnippetCount} total in library).\n\n### Available Snippet Tools\n\nThese snippets are available as **direct tools** you can call (marked with [SNIPPET] in description):\n\n${snippetToolDocs}\n\n### Snippet Management Tools\n\n- \\`search_snippets(query, limit?)\\` - Find additional snippets not pre-loaded\n- \\`get_snippet(name)\\` - Get full details of any snippet\n- \\`register_snippet(...)\\` - Save working code as a new snippet\n\n### Using Snippets\n\nSnippets are **regular tools** - call them directly like any other tool. No need to use \\`execute_typescript\\`.\n\n### Creating New Snippets\n\nWhen you write useful, reusable code with \\`execute_typescript\\`, register it:\n\n\\`\\`\\`typescript\n// After verifying code works, call the register_snippet tool\nregister_snippet({\n name: 'compare_npm_packages',\n description: 'Compare download counts for multiple NPM packages',\n code: \\`\n const { packages } = input;\n const results = await Promise.all(\n packages.map(pkg => external_getNpmDownloads({ package: pkg }))\n );\n return packages.map((pkg, i) => ({ package: pkg, downloads: results[i].downloads }))\n .sort((a, b) => b.downloads - a.downloads);\n \\`,\n inputSchema: { \n type: 'object', \n properties: { packages: { type: 'array', items: { type: 'string' } } },\n required: ['packages']\n },\n outputSchema: {\n type: 'array',\n items: { type: 'object', properties: { package: { type: 'string' }, downloads: { type: 'number' } } }\n },\n usageHints: ['Use when comparing popularity of NPM packages'],\n dependsOn: [],\n});\n\\`\\`\\`\n\n**Important**: Newly registered snippets become available as tools on the **next message**, not immediately in the current conversation turn.\n`\n }\n\n // Snippets as sandbox bindings (legacy mode)\n const snippetDocs = selectedSnippets\n .map((snippet) => {\n const inputExample = generateExampleFromSchema(snippet.inputSchema)\n const trustBadge =\n snippet.trustLevel === 'trusted'\n ? '✓ trusted'\n : snippet.trustLevel === 'provisional'\n ? '◐ provisional'\n : '○ untrusted'\n\n return `\n### snippet_${snippet.name} [${trustBadge}]\n\n${snippet.description}\n\n${snippet.usageHints.map((h) => `- ${h}`).join('\\n')}\n\n**Input Schema:**\n\\`\\`\\`json\n${JSON.stringify(snippet.inputSchema, null, 2)}\n\\`\\`\\`\n\n**Output Schema:**\n\\`\\`\\`json\n${JSON.stringify(snippet.outputSchema, null, 2)}\n\\`\\`\\`\n\n**Example:**\n\\`\\`\\`typescript\nconst result = await snippet_${snippet.name}(${inputExample});\n\\`\\`\\`\n`\n })\n .join('\\n---\\n')\n\n // Generate type stubs for selected snippets\n const typeStubs = generateSnippetTypes(selectedSnippets)\n\n return `## Snippet Library\n\n${selectedSnippets.length} snippet${selectedSnippets.length === 1 ? '' : 's'} pre-loaded for this conversation (${totalSnippetCount} total in library).\n\n### Pre-loaded Snippets\n\nThese are available as \\`snippet_*\\` functions in your TypeScript code:\n\n${snippetDocs}\n\n### Type Definitions\n\n\\`\\`\\`typescript\n${typeStubs}\n\\`\\`\\`\n\n### Snippet Management Tools\n\n- \\`search_snippets(query, limit?)\\` - Find additional snippets not pre-loaded\n- \\`get_snippet(name)\\` - Get full details of any snippet\n- \\`register_snippet(...)\\` - Save working code as a new snippet\n\n### Using Snippets\n\nSnippets work just like \\`external_*\\` functions inside \\`execute_typescript\\`:\n\n\\`\\`\\`typescript\n// Call a pre-loaded snippet\nconst stats = await snippet_fetch_github_stats({ owner: 'tanstack', repo: 'query' });\n\n// Compose snippets with external tools\nconst repos = await external_searchRepositories({ query: 'react state' });\nconst detailed = await Promise.all(\n repos.items.slice(0, 5).map(r => \n snippet_fetch_github_stats({ owner: r.owner.login, repo: r.name })\n )\n);\n\\`\\`\\`\n\n### Creating New Snippets\n\nWhen you write useful, reusable code, register it:\n\n\\`\\`\\`typescript\n// After verifying code works, call the register_snippet tool\nregister_snippet({\n name: 'compare_npm_packages',\n description: 'Compare download counts for multiple NPM packages',\n code: \\`\n const { packages } = input;\n const results = await Promise.all(\n packages.map(pkg => external_getNpmDownloads({ package: pkg }))\n );\n return packages.map((pkg, i) => ({ package: pkg, downloads: results[i].downloads }))\n .sort((a, b) => b.downloads - a.downloads);\n \\`,\n inputSchema: { \n type: 'object', \n properties: { packages: { type: 'array', items: { type: 'string' } } },\n required: ['packages']\n },\n outputSchema: {\n type: 'array',\n items: { type: 'object', properties: { package: { type: 'string' }, downloads: { type: 'number' } } }\n },\n usageHints: ['Use when comparing popularity of NPM packages'],\n dependsOn: [],\n});\n\\`\\`\\`\n\n**Important**: Newly registered snippets become available as tools on the **next message**, not immediately in the current conversation turn.\n`\n}\n"],"mappings":";;;;;AAwBA,SAAS,0BAA0B,QAAyC;CAC1E,IAAI,OAAO,SAAS,YAAY,OAAO,YAAY;EACjD,MAAM,QAAQ,OAAO;EACrB,MAAM,UAAmC,CAAC;EAE1C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC7C,IAAI,MAAM,SAAS,UAAU,QAAQ,OAAO,YAAY,IAAI;OACvD,IAAI,MAAM,SAAS,UAAU,QAAQ,OAAO;OAC5C,IAAI,MAAM,SAAS,WAAW,QAAQ,OAAO;OAC7C,IAAI,MAAM,SAAS,SAAS,QAAQ,OAAO,CAAC;OAC5C,QAAQ,OAAO;EAGtB,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC,QAAQ,MAAM,EAAE;CACjD;CACA,OAAO;AACT;;;;;AAMA,SAAgB,2BAA2B,EACzC,kBACA,mBACA,kBAAkB,QAC0B;CAE5C,IAAI,sBAAsB,GACxB,OAAO;;;;;;;;;;;;;;CAiBT,IAAI,iBAAiB,WAAW,GAC9B,OAAO;;uDAE4C,kBAAkB,UAAU,sBAAsB,IAAI,KAAK,IAAI;;;;;;;;;;;;CAcpH,IAAI,iBAAiB;EAEnB,MAAM,kBAAkB,iBACrB,KAAK,YAAY;GAChB,MAAM,eAAe,0BAA0B,QAAQ,WAAW;GAClE,MAAM,aACJ,QAAQ,eAAe,YACnB,cACA,QAAQ,eAAe,gBACrB,kBACA;GAER,OAAO;MACT,QAAQ,KAAK,IAAI,WAAW;;EAEhC,QAAQ,YAAY;;EAEpB,QAAQ,WAAW,KAAK,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE;;;;EAInD,KAAK,UAAU,QAAQ,aAAa,MAAM,CAAC,EAAE;;;;;EAK7C,KAAK,UAAU,QAAQ,cAAc,MAAM,CAAC,EAAE;;;;aAInC,QAAQ,KAAK,gBAAgB,aAAa;;EAEjD,CAAC,CAAC,CACD,KAAK,SAAS;EAEjB,OAAO;;EAET,iBAAiB,OAAO,UAAU,iBAAiB,WAAW,IAAI,KAAK,IAAI,qCAAqC,kBAAkB;;;;;;EAMlI,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6ChB;CAGA,MAAM,cAAc,iBACjB,KAAK,YAAY;EAChB,MAAM,eAAe,0BAA0B,QAAQ,WAAW;EAClE,MAAM,aACJ,QAAQ,eAAe,YACnB,cACA,QAAQ,eAAe,gBACrB,kBACA;EAER,OAAO;cACC,QAAQ,KAAK,IAAI,WAAW;;EAExC,QAAQ,YAAY;;EAEpB,QAAQ,WAAW,KAAK,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE;;;;EAInD,KAAK,UAAU,QAAQ,aAAa,MAAM,CAAC,EAAE;;;;;EAK7C,KAAK,UAAU,QAAQ,cAAc,MAAM,CAAC,EAAE;;;;;+BAKjB,QAAQ,KAAK,GAAG,aAAa;;;CAGxD,CAAC,CAAC,CACD,KAAK,SAAS;CAGjB,MAAM,YAAY,qBAAqB,gBAAgB;CAEvD,OAAO;;EAEP,iBAAiB,OAAO,UAAU,iBAAiB,WAAW,IAAI,KAAK,IAAI,qCAAqC,kBAAkB;;;;;;EAMlI,YAAY;;;;;EAKZ,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2DZ"}
@@ -0,0 +1,7 @@
1
+ import { Snippet } from './types.js';
2
+ /**
3
+ * Generate TypeScript type stubs for snippets.
4
+ * These are included in the system prompt so the LLM knows
5
+ * the exact type signatures of available snippets.
6
+ */
7
+ export declare function generateSnippetTypes(snippets: Array<Snippet>): string;
@@ -0,0 +1,79 @@
1
+ //#region src/generate-snippet-types.ts
2
+ /**
3
+ * Convert a JSON Schema to a TypeScript type string
4
+ */
5
+ function schemaToType(schema) {
6
+ if (typeof schema !== "object") return "unknown";
7
+ const schemaType = schema.type;
8
+ if (schemaType === "string") return "string";
9
+ if (schemaType === "number" || schemaType === "integer") return "number";
10
+ if (schemaType === "boolean") return "boolean";
11
+ if (schemaType === "null") return "null";
12
+ if (schemaType === "array") {
13
+ const items = schema.items;
14
+ return `Array<${items ? schemaToType(items) : "unknown"}>`;
15
+ }
16
+ if (schemaType === "object" && schema.properties) {
17
+ const properties = schema.properties;
18
+ const required = new Set(schema.required ?? []);
19
+ return `{\n${Object.entries(properties).map(([key, propSchema]) => {
20
+ const optional = required.has(key) ? "" : "?";
21
+ const propType = schemaToType(propSchema);
22
+ return ` ${/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `"${key}"`}${optional}: ${propType};`;
23
+ }).join("\n")}\n}`;
24
+ }
25
+ if (schema.enum) return schema.enum.map((v) => JSON.stringify(v)).join(" | ");
26
+ if (schema.anyOf || schema.oneOf) return (schema.anyOf || schema.oneOf).map((v) => schemaToType(v)).join(" | ");
27
+ if (Array.isArray(schemaType)) return schemaType.map((t) => {
28
+ if (t === "string") return "string";
29
+ if (t === "number" || t === "integer") return "number";
30
+ if (t === "boolean") return "boolean";
31
+ if (t === "null") return "null";
32
+ if (t === "array") return "Array<unknown>";
33
+ if (t === "object") return "object";
34
+ return "unknown";
35
+ }).join(" | ");
36
+ return "unknown";
37
+ }
38
+ /**
39
+ * Capitalize the first letter of a string
40
+ */
41
+ function capitalize(str) {
42
+ return str.charAt(0).toUpperCase() + str.slice(1);
43
+ }
44
+ /**
45
+ * Convert snake_case to PascalCase
46
+ */
47
+ function toPascalCase(str) {
48
+ return str.split("_").map((part) => capitalize(part)).join("");
49
+ }
50
+ /**
51
+ * Generate TypeScript type stubs for snippets.
52
+ * These are included in the system prompt so the LLM knows
53
+ * the exact type signatures of available snippets.
54
+ */
55
+ function generateSnippetTypes(snippets) {
56
+ const declarations = [];
57
+ for (const snippet of snippets) {
58
+ const baseName = toPascalCase(snippet.name);
59
+ const inputTypeName = `Snippet${baseName}Input`;
60
+ const outputTypeName = `Snippet${baseName}Output`;
61
+ const inputType = schemaToType(snippet.inputSchema);
62
+ if (snippet.inputSchema.type === "object" && snippet.inputSchema.properties && Object.keys(snippet.inputSchema.properties).length > 0) declarations.push(`interface ${inputTypeName} ${inputType}`);
63
+ const outputType = schemaToType(snippet.outputSchema);
64
+ if (snippet.outputSchema.type === "object" && snippet.outputSchema.properties && Object.keys(snippet.outputSchema.properties).length > 0) declarations.push(`interface ${outputTypeName} ${outputType}`);
65
+ const inputRef = snippet.inputSchema.type === "object" && snippet.inputSchema.properties && Object.keys(snippet.inputSchema.properties).length > 0 ? inputTypeName : inputType;
66
+ const outputRef = snippet.outputSchema.type === "object" && snippet.outputSchema.properties && Object.keys(snippet.outputSchema.properties).length > 0 ? outputTypeName : outputType;
67
+ const hintsDoc = snippet.usageHints.map((h) => ` * @hint ${h}`).join("\n");
68
+ declarations.push(`/**
69
+ * ${snippet.description}
70
+ ${hintsDoc}
71
+ */
72
+ declare function snippet_${snippet.name}(input: ${inputRef}): Promise<${outputRef}>;`);
73
+ }
74
+ return declarations.join("\n\n");
75
+ }
76
+ //#endregion
77
+ export { generateSnippetTypes };
78
+
79
+ //# sourceMappingURL=generate-snippet-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-snippet-types.js","names":[],"sources":["../../src/generate-snippet-types.ts"],"sourcesContent":["import type { Snippet } from './types'\n\n/**\n * Convert a JSON Schema to a TypeScript type string\n */\nfunction schemaToType(schema: Record<string, unknown>): string {\n if (typeof schema !== 'object') {\n return 'unknown'\n }\n\n const schemaType = schema.type\n\n // Handle basic types\n if (schemaType === 'string') return 'string'\n if (schemaType === 'number' || schemaType === 'integer') return 'number'\n if (schemaType === 'boolean') return 'boolean'\n if (schemaType === 'null') return 'null'\n\n // Handle arrays\n if (schemaType === 'array') {\n const items = schema.items as Record<string, unknown> | undefined\n const itemType = items ? schemaToType(items) : 'unknown'\n return `Array<${itemType}>`\n }\n\n // Handle objects with properties\n if (schemaType === 'object' && schema.properties) {\n const properties = schema.properties as Record<\n string,\n Record<string, unknown>\n >\n const required = new Set(\n (schema.required as Array<string> | undefined) ?? [],\n )\n\n const props = Object.entries(properties)\n .map(([key, propSchema]) => {\n const optional = required.has(key) ? '' : '?'\n const propType = schemaToType(propSchema)\n // Handle property names that need quoting\n const safeName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key)\n ? key\n : `\"${key}\"`\n return ` ${safeName}${optional}: ${propType};`\n })\n .join('\\n')\n\n return `{\\n${props}\\n}`\n }\n\n // Handle enums\n if (schema.enum) {\n const enumValues = schema.enum as Array<unknown>\n return enumValues.map((v) => JSON.stringify(v)).join(' | ')\n }\n\n // Handle union types (anyOf, oneOf)\n if (schema.anyOf || schema.oneOf) {\n const variants = (schema.anyOf || schema.oneOf) as Array<\n Record<string, unknown>\n >\n return variants.map((v) => schemaToType(v)).join(' | ')\n }\n\n // Handle type arrays (e.g., [\"string\", \"null\"])\n if (Array.isArray(schemaType)) {\n return schemaType\n .map((t) => {\n if (t === 'string') return 'string'\n if (t === 'number' || t === 'integer') return 'number'\n if (t === 'boolean') return 'boolean'\n if (t === 'null') return 'null'\n if (t === 'array') return 'Array<unknown>'\n if (t === 'object') return 'object'\n return 'unknown'\n })\n .join(' | ')\n }\n\n // Fallback for unknown schemas\n return 'unknown'\n}\n\n/**\n * Capitalize the first letter of a string\n */\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * Convert snake_case to PascalCase\n */\nfunction toPascalCase(str: string): string {\n return str\n .split('_')\n .map((part) => capitalize(part))\n .join('')\n}\n\n/**\n * Generate TypeScript type stubs for snippets.\n * These are included in the system prompt so the LLM knows\n * the exact type signatures of available snippets.\n */\nexport function generateSnippetTypes(snippets: Array<Snippet>): string {\n const declarations: Array<string> = []\n\n for (const snippet of snippets) {\n const baseName = toPascalCase(snippet.name)\n const inputTypeName = `Snippet${baseName}Input`\n const outputTypeName = `Snippet${baseName}Output`\n\n // Generate input type\n const inputType = schemaToType(snippet.inputSchema)\n if (\n snippet.inputSchema.type === 'object' &&\n snippet.inputSchema.properties &&\n Object.keys(snippet.inputSchema.properties).length > 0\n ) {\n declarations.push(`interface ${inputTypeName} ${inputType}`)\n }\n\n // Generate output type\n const outputType = schemaToType(snippet.outputSchema)\n if (\n snippet.outputSchema.type === 'object' &&\n snippet.outputSchema.properties &&\n Object.keys(snippet.outputSchema.properties).length > 0\n ) {\n declarations.push(`interface ${outputTypeName} ${outputType}`)\n }\n\n // Determine type references\n const inputRef =\n snippet.inputSchema.type === 'object' &&\n snippet.inputSchema.properties &&\n Object.keys(snippet.inputSchema.properties).length > 0\n ? inputTypeName\n : inputType\n\n const outputRef =\n snippet.outputSchema.type === 'object' &&\n snippet.outputSchema.properties &&\n Object.keys(snippet.outputSchema.properties).length > 0\n ? outputTypeName\n : outputType\n\n // Generate function declaration with JSDoc\n const hintsDoc = snippet.usageHints.map((h) => ` * @hint ${h}`).join('\\n')\n\n declarations.push(\n `/**\n * ${snippet.description}\n${hintsDoc}\n */\ndeclare function snippet_${snippet.name}(input: ${inputRef}): Promise<${outputRef}>;`,\n )\n }\n\n return declarations.join('\\n\\n')\n}\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export { codeModeWithSnippets, createCodeModeWithSnippetsConfig, } from './code-mode-with-snippets.js';
2
+ export type { CodeModeWithSnippetsOptions, CodeModeWithSnippetsResult, } from './code-mode-with-snippets.js';
3
+ export { createDefaultTrustStrategy, createAlwaysTrustedStrategy, createRelaxedTrustStrategy, createCustomTrustStrategy, } from './trust-strategies.js';
4
+ export type { TrustStrategy } from './trust-strategies.js';
5
+ export { selectRelevantSnippets } from './select-relevant-snippets.js';
6
+ export { snippetsToTools, snippetToTool } from './snippets-to-tools.js';
7
+ export type { SnippetToToolOptions } from './snippets-to-tools.js';
8
+ export { snippetsToBindings, snippetsToSimpleBindings, } from './snippets-to-bindings.js';
9
+ export { createSnippetManagementTools } from './create-snippet-management-tools.js';
10
+ export { createSnippetsSystemPrompt } from './create-snippets-system-prompt.js';
11
+ export { generateSnippetTypes } from './generate-snippet-types.js';
12
+ export { createMemorySnippetStorage } from './storage/memory-storage.js';
13
+ export type { MemorySnippetStorageOptions } from './storage/memory-storage.js';
14
+ export type { Snippet, SnippetIndexEntry, SnippetStorage, SnippetsConfig, SnippetStats, TrustLevel, SnippetBinding, } from './types.js';
@@ -0,0 +1,10 @@
1
+ import { selectRelevantSnippets } from "./select-relevant-snippets.js";
2
+ import { createAlwaysTrustedStrategy, createCustomTrustStrategy, createDefaultTrustStrategy, createRelaxedTrustStrategy } from "./trust-strategies.js";
3
+ import { snippetToTool, snippetsToTools } from "./snippets-to-tools.js";
4
+ import { createSnippetManagementTools } from "./create-snippet-management-tools.js";
5
+ import { generateSnippetTypes } from "./generate-snippet-types.js";
6
+ import { createSnippetsSystemPrompt } from "./create-snippets-system-prompt.js";
7
+ import { codeModeWithSnippets, createCodeModeWithSnippetsConfig } from "./code-mode-with-snippets.js";
8
+ import { snippetsToBindings, snippetsToSimpleBindings } from "./snippets-to-bindings.js";
9
+ import { createMemorySnippetStorage } from "./storage/memory-storage.js";
10
+ export { codeModeWithSnippets, createAlwaysTrustedStrategy, createCodeModeWithSnippetsConfig, createCustomTrustStrategy, createDefaultTrustStrategy, createMemorySnippetStorage, createRelaxedTrustStrategy, createSnippetManagementTools, createSnippetsSystemPrompt, generateSnippetTypes, selectRelevantSnippets, snippetToTool, snippetsToBindings, snippetsToSimpleBindings, snippetsToTools };
@@ -0,0 +1,29 @@
1
+ import { AnyTextAdapter, ModelMessage } from '@tanstack/ai';
2
+ import { Snippet, SnippetIndexEntry, SnippetStorage } from './types.js';
3
+ interface SelectRelevantSnippetsOptions {
4
+ /**
5
+ * Text adapter for snippet selection (should be a cheap/fast model)
6
+ */
7
+ adapter: AnyTextAdapter;
8
+ /**
9
+ * Current conversation messages
10
+ */
11
+ messages: Array<ModelMessage>;
12
+ /**
13
+ * Snippet index (lightweight metadata)
14
+ */
15
+ snippetIndex: Array<SnippetIndexEntry>;
16
+ /**
17
+ * Maximum number of snippets to select
18
+ */
19
+ maxSnippets: number;
20
+ /**
21
+ * Storage to load full snippet data
22
+ */
23
+ storage: SnippetStorage;
24
+ }
25
+ /**
26
+ * Use a cheap/fast LLM to select which snippets are relevant for the current conversation
27
+ */
28
+ export declare function selectRelevantSnippets({ adapter, messages, snippetIndex, maxSnippets, storage, }: SelectRelevantSnippetsOptions): Promise<Array<Snippet>>;
29
+ export {};
@@ -0,0 +1,56 @@
1
+ import { chat } from "@tanstack/ai";
2
+ //#region src/select-relevant-snippets.ts
3
+ /**
4
+ * Use a cheap/fast LLM to select which snippets are relevant for the current conversation
5
+ */
6
+ async function selectRelevantSnippets({ adapter, messages, snippetIndex, maxSnippets, storage }) {
7
+ if (snippetIndex.length === 0) return [];
8
+ if (messages.length === 0) return [];
9
+ const selectionPrompt = `Given this conversation context:
10
+ ---
11
+ ${messages.slice(-5).map((m) => {
12
+ let content;
13
+ if (typeof m.content === "string") content = m.content;
14
+ else if (Array.isArray(m.content)) content = m.content.map((part) => {
15
+ if (typeof part === "string") return part;
16
+ if (part && typeof part === "object" && "text" in part) return part.text;
17
+ return "[non-text content]";
18
+ }).join(" ");
19
+ else content = "[complex content]";
20
+ return `${m.role}: ${content}`;
21
+ }).join("\n")}
22
+ ---
23
+
24
+ Which of these snippets (if any) would be useful for the next response? Return a JSON array of snippet names, max ${maxSnippets}. Return [] if none are relevant.
25
+
26
+ Available snippets:
27
+ ${snippetIndex.map((s) => {
28
+ const hints = s.usageHints.length > 0 ? ` (${s.usageHints[0]})` : "";
29
+ return `- ${s.name}: ${s.description}${hints}`;
30
+ }).join("\n")}
31
+
32
+ Respond with only the JSON array, no explanation. Example: ["snippet_name_1", "snippet_name_2"]`;
33
+ try {
34
+ const stream = chat({
35
+ adapter,
36
+ messages: [{
37
+ role: "user",
38
+ content: selectionPrompt
39
+ }]
40
+ });
41
+ let responseText = "";
42
+ for await (const chunk of stream) if (chunk.type === "TEXT_MESSAGE_CONTENT") responseText += chunk.delta;
43
+ let jsonText = responseText.trim();
44
+ if (jsonText.startsWith("```")) jsonText = jsonText.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "");
45
+ const selectedNames = JSON.parse(jsonText);
46
+ if (!Array.isArray(selectedNames)) return [];
47
+ return (await Promise.all(selectedNames.slice(0, maxSnippets).map((name) => storage.get(name)))).filter((s) => s !== null);
48
+ } catch (error) {
49
+ console.warn("Snippet selection failed, returning empty selection:", error);
50
+ return [];
51
+ }
52
+ }
53
+ //#endregion
54
+ export { selectRelevantSnippets };
55
+
56
+ //# sourceMappingURL=select-relevant-snippets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-relevant-snippets.js","names":[],"sources":["../../src/select-relevant-snippets.ts"],"sourcesContent":["import { chat } from '@tanstack/ai'\nimport type { AnyTextAdapter, ModelMessage, StreamChunk } from '@tanstack/ai'\nimport type { Snippet, SnippetIndexEntry, SnippetStorage } from './types'\n\ninterface SelectRelevantSnippetsOptions {\n /**\n * Text adapter for snippet selection (should be a cheap/fast model)\n */\n adapter: AnyTextAdapter\n\n /**\n * Current conversation messages\n */\n messages: Array<ModelMessage>\n\n /**\n * Snippet index (lightweight metadata)\n */\n snippetIndex: Array<SnippetIndexEntry>\n\n /**\n * Maximum number of snippets to select\n */\n maxSnippets: number\n\n /**\n * Storage to load full snippet data\n */\n storage: SnippetStorage\n}\n\n/**\n * Use a cheap/fast LLM to select which snippets are relevant for the current conversation\n */\nexport async function selectRelevantSnippets({\n adapter,\n messages,\n snippetIndex,\n maxSnippets,\n storage,\n}: SelectRelevantSnippetsOptions): Promise<Array<Snippet>> {\n // Early exit conditions\n if (snippetIndex.length === 0) return []\n if (messages.length === 0) return []\n\n // Build context from recent messages (last 5)\n const recentMessages = messages.slice(-5)\n const recentContext = recentMessages\n .map((m) => {\n let content: string\n if (typeof m.content === 'string') {\n content = m.content\n } else if (Array.isArray(m.content)) {\n // Handle content parts (text, images, etc.)\n content = m.content\n .map((part: unknown) => {\n if (typeof part === 'string') return part\n if (part && typeof part === 'object' && 'text' in part)\n return (part as { text: string }).text\n return '[non-text content]'\n })\n .join(' ')\n } else {\n content = '[complex content]'\n }\n return `${m.role}: ${content}`\n })\n .join('\\n')\n\n // Build snippet catalog for selection prompt\n const snippetCatalog = snippetIndex\n .map((s) => {\n const hints = s.usageHints.length > 0 ? ` (${s.usageHints[0]})` : ''\n return `- ${s.name}: ${s.description}${hints}`\n })\n .join('\\n')\n\n // Ask cheap model to select relevant snippets\n const selectionPrompt = `Given this conversation context:\n---\n${recentContext}\n---\n\nWhich of these snippets (if any) would be useful for the next response? Return a JSON array of snippet names, max ${maxSnippets}. Return [] if none are relevant.\n\nAvailable snippets:\n${snippetCatalog}\n\nRespond with only the JSON array, no explanation. Example: [\"snippet_name_1\", \"snippet_name_2\"]`\n\n try {\n // Use chat to get the selection\n const stream = chat({\n adapter,\n messages: [\n {\n role: 'user',\n content: selectionPrompt,\n },\n ],\n })\n\n // Collect the full response\n let responseText = ''\n for await (const chunk of stream as AsyncIterable<StreamChunk>) {\n if (chunk.type === 'TEXT_MESSAGE_CONTENT') {\n responseText += chunk.delta\n }\n }\n\n // Parse the JSON response\n // Handle potential markdown code blocks\n let jsonText = responseText.trim()\n if (jsonText.startsWith('```')) {\n // Remove markdown code block\n jsonText = jsonText.replace(/^```(?:json)?\\n?/, '').replace(/\\n?```$/, '')\n }\n\n const selectedNames: Array<string> = JSON.parse(jsonText)\n\n if (!Array.isArray(selectedNames)) {\n return []\n }\n\n // Load full snippet data for selected snippets\n const selectedSnippets = await Promise.all(\n selectedNames.slice(0, maxSnippets).map((name) => storage.get(name)),\n )\n\n return selectedSnippets.filter((s): s is Snippet => s !== null)\n } catch (error) {\n // If parsing fails or any error occurs, return empty (safe fallback)\n console.warn('Snippet selection failed, returning empty selection:', error)\n return []\n }\n}\n"],"mappings":";;;;;AAkCA,eAAsB,uBAAuB,EAC3C,SACA,UACA,cACA,aACA,WACyD;CAEzD,IAAI,aAAa,WAAW,GAAG,OAAO,CAAC;CACvC,IAAI,SAAS,WAAW,GAAG,OAAO,CAAC;CAmCnC,MAAM,kBAAkB;;EAhCD,SAAS,MAAM,EAChB,CAAA,CACnB,KAAK,MAAM;EACV,IAAI;EACJ,IAAI,OAAO,EAAE,YAAY,UACvB,UAAU,EAAE;OACP,IAAI,MAAM,QAAQ,EAAE,OAAO,GAEhC,UAAU,EAAE,QACT,KAAK,SAAkB;GACtB,IAAI,OAAO,SAAS,UAAU,OAAO;GACrC,IAAI,QAAQ,OAAO,SAAS,YAAY,UAAU,MAChD,OAAQ,KAA0B;GACpC,OAAO;EACT,CAAC,CAAC,CACD,KAAK,GAAG;OAEX,UAAU;EAEZ,OAAO,GAAG,EAAE,KAAK,IAAI;CACvB,CAAC,CAAC,CACD,KAAK,IAaR,EAAc;;;oHAGoG,YAAY;;;EAbvG,aACpB,KAAK,MAAM;EACV,MAAM,QAAQ,EAAE,WAAW,SAAS,IAAI,KAAK,EAAE,WAAW,GAAG,KAAK;EAClE,OAAO,KAAK,EAAE,KAAK,IAAI,EAAE,cAAc;CACzC,CAAC,CAAC,CACD,KAAK,IAWR,EAAe;;;CAIf,IAAI;EAEF,MAAM,SAAS,KAAK;GAClB;GACA,UAAU,CACR;IACE,MAAM;IACN,SAAS;GACX,CACF;EACF,CAAC;EAGD,IAAI,eAAe;EACnB,WAAW,MAAM,SAAS,QACxB,IAAI,MAAM,SAAS,wBACjB,gBAAgB,MAAM;EAM1B,IAAI,WAAW,aAAa,KAAK;EACjC,IAAI,SAAS,WAAW,KAAK,GAE3B,WAAW,SAAS,QAAQ,oBAAoB,EAAE,CAAC,CAAC,QAAQ,WAAW,EAAE;EAG3E,MAAM,gBAA+B,KAAK,MAAM,QAAQ;EAExD,IAAI,CAAC,MAAM,QAAQ,aAAa,GAC9B,OAAO,CAAC;EAQV,QAAO,MAJwB,QAAQ,IACrC,cAAc,MAAM,GAAG,WAAW,CAAC,CAAC,KAAK,SAAS,QAAQ,IAAI,IAAI,CAAC,CACrE,EAAA,CAEwB,QAAQ,MAAoB,MAAM,IAAI;CAChE,SAAS,OAAO;EAEd,QAAQ,KAAK,wDAAwD,KAAK;EAC1E,OAAO,CAAC;CACV;AACF"}
@@ -0,0 +1,34 @@
1
+ import { ToolExecutionContext } from '@tanstack/ai';
2
+ import { ToolBinding } from '@tanstack/ai-code-mode';
3
+ import { Snippet, SnippetStorage } from './types.js';
4
+ interface SnippetsToBindingsOptions {
5
+ /**
6
+ * Snippets to convert to bindings
7
+ */
8
+ snippets: Array<Snippet>;
9
+ /**
10
+ * Tool execution context for emitting custom events
11
+ */
12
+ context?: ToolExecutionContext;
13
+ /**
14
+ * Function to execute snippet code in the sandbox
15
+ * The snippet code receives `input` as a variable
16
+ */
17
+ executeInSandbox: (code: string, input: unknown) => Promise<unknown>;
18
+ /**
19
+ * Storage for updating execution stats
20
+ */
21
+ storage: SnippetStorage;
22
+ }
23
+ /**
24
+ * Convert snippets to sandbox bindings with the snippet_ prefix.
25
+ * Snippets become callable functions inside the sandbox.
26
+ */
27
+ export declare function snippetsToBindings({ snippets, context, executeInSandbox, storage, }: SnippetsToBindingsOptions): Record<string, ToolBinding>;
28
+ /**
29
+ * Create a simple binding record for snippets without full sandbox execution.
30
+ * This is used when snippets are being documented in the system prompt
31
+ * but not yet being executed.
32
+ */
33
+ export declare function snippetsToSimpleBindings(snippets: Array<Snippet>): Record<string, ToolBinding>;
34
+ export {};