@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.
- package/LICENSE +21 -0
- package/README.md +208 -0
- package/dist/esm/code-mode-with-snippets.d.ts +65 -0
- package/dist/esm/code-mode-with-snippets.js +145 -0
- package/dist/esm/code-mode-with-snippets.js.map +1 -0
- package/dist/esm/create-snippet-management-tools.d.ts +40 -0
- package/dist/esm/create-snippet-management-tools.js +173 -0
- package/dist/esm/create-snippet-management-tools.js.map +1 -0
- package/dist/esm/create-snippets-system-prompt.d.ts +22 -0
- package/dist/esm/create-snippets-system-prompt.js +234 -0
- package/dist/esm/create-snippets-system-prompt.js.map +1 -0
- package/dist/esm/generate-snippet-types.d.ts +7 -0
- package/dist/esm/generate-snippet-types.js +79 -0
- package/dist/esm/generate-snippet-types.js.map +1 -0
- package/dist/esm/index.d.ts +14 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/select-relevant-snippets.d.ts +29 -0
- package/dist/esm/select-relevant-snippets.js +56 -0
- package/dist/esm/select-relevant-snippets.js.map +1 -0
- package/dist/esm/snippets-to-bindings.d.ts +34 -0
- package/dist/esm/snippets-to-bindings.js +74 -0
- package/dist/esm/snippets-to-bindings.js.map +1 -0
- package/dist/esm/snippets-to-tools.d.ts +74 -0
- package/dist/esm/snippets-to-tools.js +147 -0
- package/dist/esm/snippets-to-tools.js.map +1 -0
- package/dist/esm/storage/file-storage.d.ts +27 -0
- package/dist/esm/storage/file-storage.js +155 -0
- package/dist/esm/storage/file-storage.js.map +1 -0
- package/dist/esm/storage/index.d.ts +3 -0
- package/dist/esm/storage/index.js +3 -0
- package/dist/esm/storage/memory-storage.d.ts +17 -0
- package/dist/esm/storage/memory-storage.js +100 -0
- package/dist/esm/storage/memory-storage.js.map +1 -0
- package/dist/esm/trust-strategies.d.ts +50 -0
- package/dist/esm/trust-strategies.js +73 -0
- package/dist/esm/trust-strategies.js.map +1 -0
- package/dist/esm/types.d.ts +216 -0
- package/package.json +92 -0
- package/src/code-mode-with-snippets.ts +210 -0
- package/src/create-snippet-management-tools.ts +298 -0
- package/src/create-snippets-system-prompt.ts +289 -0
- package/src/generate-snippet-types.ts +162 -0
- package/src/index.ts +60 -0
- package/src/select-relevant-snippets.ts +136 -0
- package/src/snippets-to-bindings.ts +135 -0
- package/src/snippets-to-tools.ts +325 -0
- package/src/storage/file-storage.ts +275 -0
- package/src/storage/index.ts +6 -0
- package/src/storage/memory-storage.ts +172 -0
- package/src/trust-strategies.ts +142 -0
- package/src/types.ts +289 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { toolDefinition } from '@tanstack/ai'
|
|
2
|
+
import { toolsToBindings } from '@tanstack/ai-code-mode'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
import { createDefaultTrustStrategy } from './trust-strategies'
|
|
5
|
+
import { snippetToTool } from './snippets-to-tools'
|
|
6
|
+
import type { SchemaInput, ServerTool, ToolRegistry } from '@tanstack/ai'
|
|
7
|
+
import type { CodeModeToolConfig, ToolBinding } from '@tanstack/ai-code-mode'
|
|
8
|
+
import type { SnippetStorage } from './types'
|
|
9
|
+
import type { TrustStrategy } from './trust-strategies'
|
|
10
|
+
|
|
11
|
+
interface CreateSnippetManagementToolsOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Storage implementation for snippets
|
|
14
|
+
*/
|
|
15
|
+
storage: SnippetStorage
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Trust strategy for determining initial trust level.
|
|
19
|
+
* If not provided, uses the storage's trustStrategy or falls back to default.
|
|
20
|
+
*/
|
|
21
|
+
trustStrategy?: TrustStrategy
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Tool registry for adding newly registered snippets immediately.
|
|
25
|
+
* When provided, register_snippet will add the new snippet to this registry
|
|
26
|
+
* so it's available as a direct tool in the current chat session.
|
|
27
|
+
*/
|
|
28
|
+
registry?: ToolRegistry
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Code mode config for creating snippet tools.
|
|
32
|
+
* Required when registry is provided.
|
|
33
|
+
*/
|
|
34
|
+
config?: CodeModeToolConfig
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Pre-computed bindings for external_* functions.
|
|
38
|
+
* Required when registry is provided.
|
|
39
|
+
*/
|
|
40
|
+
baseBindings?: Record<string, ToolBinding>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create tools for searching, retrieving, and registering snippets.
|
|
45
|
+
* These tools allow the LLM to interact with the snippet library at runtime.
|
|
46
|
+
*
|
|
47
|
+
* When registry, config, and baseBindings are provided, newly registered snippets
|
|
48
|
+
* will be immediately added to the registry and available as direct tools.
|
|
49
|
+
*/
|
|
50
|
+
export function createSnippetManagementTools({
|
|
51
|
+
storage,
|
|
52
|
+
trustStrategy,
|
|
53
|
+
registry,
|
|
54
|
+
config,
|
|
55
|
+
baseBindings,
|
|
56
|
+
}: CreateSnippetManagementToolsOptions): Array<
|
|
57
|
+
ServerTool<SchemaInput, SchemaInput, string>
|
|
58
|
+
> {
|
|
59
|
+
// Use provided strategy, or storage's strategy, or default
|
|
60
|
+
const strategy =
|
|
61
|
+
trustStrategy ?? storage.trustStrategy ?? createDefaultTrustStrategy()
|
|
62
|
+
|
|
63
|
+
// Compute bindings if not provided but config is available
|
|
64
|
+
const bindings =
|
|
65
|
+
baseBindings ?? (config ? toolsToBindings(config.tools, 'external_') : {})
|
|
66
|
+
return [
|
|
67
|
+
// Search for snippets
|
|
68
|
+
toolDefinition({
|
|
69
|
+
name: 'search_snippets',
|
|
70
|
+
description:
|
|
71
|
+
'Search the snippet library for reusable snippets. Use this to find snippets that can help accomplish a task. Returns matching snippets with their descriptions.',
|
|
72
|
+
inputSchema: z.object({
|
|
73
|
+
query: z
|
|
74
|
+
.string()
|
|
75
|
+
.describe('Search query describing what you want to accomplish'),
|
|
76
|
+
limit: z
|
|
77
|
+
.number()
|
|
78
|
+
.optional()
|
|
79
|
+
.default(5)
|
|
80
|
+
.describe('Maximum number of results (default: 5)'),
|
|
81
|
+
}),
|
|
82
|
+
outputSchema: z.array(
|
|
83
|
+
z.object({
|
|
84
|
+
name: z.string(),
|
|
85
|
+
description: z.string(),
|
|
86
|
+
usageHints: z.array(z.string()),
|
|
87
|
+
trustLevel: z.enum(['untrusted', 'provisional', 'trusted']),
|
|
88
|
+
}),
|
|
89
|
+
),
|
|
90
|
+
}).server(async ({ query, limit }) => {
|
|
91
|
+
const results = await storage.search(query, { limit: limit ?? 5 })
|
|
92
|
+
return results.map((s) => ({
|
|
93
|
+
name: s.name,
|
|
94
|
+
description: s.description,
|
|
95
|
+
usageHints: s.usageHints,
|
|
96
|
+
trustLevel: s.trustLevel,
|
|
97
|
+
}))
|
|
98
|
+
}),
|
|
99
|
+
|
|
100
|
+
// Get full snippet details
|
|
101
|
+
toolDefinition({
|
|
102
|
+
name: 'get_snippet',
|
|
103
|
+
description:
|
|
104
|
+
'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.',
|
|
105
|
+
inputSchema: z.object({
|
|
106
|
+
name: z.string().describe('The snippet name (without snippet_ prefix)'),
|
|
107
|
+
}),
|
|
108
|
+
outputSchema: z.object({
|
|
109
|
+
name: z.string().optional(),
|
|
110
|
+
description: z.string().optional(),
|
|
111
|
+
code: z.string().optional(),
|
|
112
|
+
inputSchema: z.string().optional().describe('JSON Schema as string'),
|
|
113
|
+
outputSchema: z.string().optional().describe('JSON Schema as string'),
|
|
114
|
+
usageHints: z.array(z.string()).optional(),
|
|
115
|
+
dependsOn: z.array(z.string()).optional(),
|
|
116
|
+
trustLevel: z.enum(['untrusted', 'provisional', 'trusted']).optional(),
|
|
117
|
+
stats: z
|
|
118
|
+
.object({
|
|
119
|
+
executions: z.number(),
|
|
120
|
+
successRate: z.number(),
|
|
121
|
+
})
|
|
122
|
+
.optional(),
|
|
123
|
+
error: z.string().optional(),
|
|
124
|
+
}),
|
|
125
|
+
}).server(async ({ name }) => {
|
|
126
|
+
const snippet = await storage.get(name)
|
|
127
|
+
if (!snippet) {
|
|
128
|
+
return { error: `Snippet '${name}' not found` }
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
name: snippet.name,
|
|
132
|
+
description: snippet.description,
|
|
133
|
+
code: snippet.code,
|
|
134
|
+
inputSchema: JSON.stringify(snippet.inputSchema),
|
|
135
|
+
outputSchema: JSON.stringify(snippet.outputSchema),
|
|
136
|
+
usageHints: snippet.usageHints,
|
|
137
|
+
dependsOn: snippet.dependsOn,
|
|
138
|
+
trustLevel: snippet.trustLevel,
|
|
139
|
+
stats: snippet.stats,
|
|
140
|
+
}
|
|
141
|
+
}),
|
|
142
|
+
|
|
143
|
+
// Register a new snippet
|
|
144
|
+
toolDefinition({
|
|
145
|
+
name: 'register_snippet',
|
|
146
|
+
description:
|
|
147
|
+
'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.',
|
|
148
|
+
inputSchema: z.object({
|
|
149
|
+
name: z
|
|
150
|
+
.string()
|
|
151
|
+
.regex(
|
|
152
|
+
/^[a-z][a-z0-9_]*$/,
|
|
153
|
+
'Must be snake_case starting with a letter',
|
|
154
|
+
)
|
|
155
|
+
.describe(
|
|
156
|
+
'Unique snippet name in snake_case (e.g., fetch_github_stats)',
|
|
157
|
+
),
|
|
158
|
+
description: z
|
|
159
|
+
.string()
|
|
160
|
+
.describe('Clear description of what the snippet does'),
|
|
161
|
+
code: z
|
|
162
|
+
.string()
|
|
163
|
+
.describe(
|
|
164
|
+
'The TypeScript code. Receives `input` variable, can call external_* and snippet_* functions, should return a value.',
|
|
165
|
+
),
|
|
166
|
+
inputSchema: z
|
|
167
|
+
.string()
|
|
168
|
+
.describe(
|
|
169
|
+
'JSON Schema as a JSON string describing the input parameter, e.g. {"type":"object","properties":{"a":{"type":"number"}},"required":["a"]}',
|
|
170
|
+
),
|
|
171
|
+
outputSchema: z
|
|
172
|
+
.string()
|
|
173
|
+
.describe(
|
|
174
|
+
'JSON Schema as a JSON string describing the return value, e.g. {"type":"object","properties":{"result":{"type":"number"}}}',
|
|
175
|
+
),
|
|
176
|
+
usageHints: z
|
|
177
|
+
.array(z.string())
|
|
178
|
+
.describe(
|
|
179
|
+
'Hints about when to use this snippet, e.g. "Use when user asks about..."',
|
|
180
|
+
),
|
|
181
|
+
dependsOn: z
|
|
182
|
+
.array(z.string())
|
|
183
|
+
.optional()
|
|
184
|
+
.default([])
|
|
185
|
+
.describe('Names of other snippets this snippet calls'),
|
|
186
|
+
}),
|
|
187
|
+
outputSchema: z.object({
|
|
188
|
+
success: z.boolean().optional(),
|
|
189
|
+
snippetId: z.string().optional(),
|
|
190
|
+
name: z.string().optional(),
|
|
191
|
+
message: z.string().optional(),
|
|
192
|
+
error: z.string().optional(),
|
|
193
|
+
}),
|
|
194
|
+
}).server(async (rawSnippetDef, context) => {
|
|
195
|
+
// Parse the JSON string schemas
|
|
196
|
+
let inputSchema: Record<string, unknown>
|
|
197
|
+
let outputSchema: Record<string, unknown>
|
|
198
|
+
try {
|
|
199
|
+
inputSchema = JSON.parse(rawSnippetDef.inputSchema) as Record<
|
|
200
|
+
string,
|
|
201
|
+
unknown
|
|
202
|
+
>
|
|
203
|
+
} catch {
|
|
204
|
+
return { error: 'inputSchema must be a valid JSON string' }
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
outputSchema = JSON.parse(rawSnippetDef.outputSchema) as Record<
|
|
208
|
+
string,
|
|
209
|
+
unknown
|
|
210
|
+
>
|
|
211
|
+
} catch {
|
|
212
|
+
return { error: 'outputSchema must be a valid JSON string' }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const snippetDef = {
|
|
216
|
+
...rawSnippetDef,
|
|
217
|
+
inputSchema,
|
|
218
|
+
outputSchema,
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
// Validate the snippet name isn't reserved
|
|
222
|
+
if (snippetDef.name.startsWith('external_')) {
|
|
223
|
+
return { error: "Snippet names cannot start with 'external_'" }
|
|
224
|
+
}
|
|
225
|
+
if (snippetDef.name.startsWith('snippet_')) {
|
|
226
|
+
return {
|
|
227
|
+
error:
|
|
228
|
+
"Snippet names should not include the 'snippet_' prefix - it will be added automatically",
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Check if snippet already exists
|
|
233
|
+
const existing = await storage.get(snippetDef.name)
|
|
234
|
+
if (existing) {
|
|
235
|
+
return {
|
|
236
|
+
error: `Snippet '${snippetDef.name}' already exists. Use a different name or update the existing snippet.`,
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Generate a unique ID
|
|
241
|
+
const id = crypto.randomUUID()
|
|
242
|
+
|
|
243
|
+
// Get initial trust level from strategy
|
|
244
|
+
const initialTrustLevel = strategy.getInitialTrustLevel()
|
|
245
|
+
|
|
246
|
+
// Save the snippet
|
|
247
|
+
const snippet = await storage.save({
|
|
248
|
+
id,
|
|
249
|
+
name: snippetDef.name,
|
|
250
|
+
description: snippetDef.description,
|
|
251
|
+
code: snippetDef.code,
|
|
252
|
+
inputSchema: snippetDef.inputSchema,
|
|
253
|
+
outputSchema: snippetDef.outputSchema,
|
|
254
|
+
usageHints: snippetDef.usageHints,
|
|
255
|
+
dependsOn: snippetDef.dependsOn ?? [],
|
|
256
|
+
trustLevel: initialTrustLevel,
|
|
257
|
+
stats: { executions: 0, successRate: 0 },
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
// If registry and config are available, add the snippet as a tool immediately
|
|
261
|
+
if (registry && config) {
|
|
262
|
+
const snippetTool = snippetToTool({
|
|
263
|
+
snippet,
|
|
264
|
+
driver: config.driver,
|
|
265
|
+
bindings,
|
|
266
|
+
storage,
|
|
267
|
+
timeout: config.timeout,
|
|
268
|
+
memoryLimit: config.memoryLimit,
|
|
269
|
+
})
|
|
270
|
+
registry.add(snippetTool)
|
|
271
|
+
console.log(
|
|
272
|
+
`[register_snippet] Added snippet '${snippet.name}' to registry immediately`,
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Emit event for UI notification
|
|
277
|
+
context?.emitCustomEvent('snippet:registered', {
|
|
278
|
+
id: snippet.id,
|
|
279
|
+
name: snippet.name,
|
|
280
|
+
description: snippet.description,
|
|
281
|
+
timestamp: Date.now(),
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
success: true,
|
|
286
|
+
snippetId: snippet.id,
|
|
287
|
+
name: snippet.name,
|
|
288
|
+
message: `Snippet '${snippet.name}' registered successfully and is now available as the '${snippet.name}' tool.`,
|
|
289
|
+
}
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error('[register_snippet] Error:', error)
|
|
292
|
+
return {
|
|
293
|
+
error: `Failed to register snippet: ${error instanceof Error ? error.message : String(error)}`,
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}),
|
|
297
|
+
]
|
|
298
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { generateSnippetTypes } from './generate-snippet-types'
|
|
2
|
+
import type { Snippet } from './types'
|
|
3
|
+
|
|
4
|
+
interface CreateSnippetsSystemPromptOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Snippets that were selected for this request
|
|
7
|
+
*/
|
|
8
|
+
selectedSnippets: Array<Snippet>
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Total number of snippets in the library
|
|
12
|
+
*/
|
|
13
|
+
totalSnippetCount: number
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Whether snippets are exposed as direct tools (not just sandbox bindings)
|
|
17
|
+
* @default true
|
|
18
|
+
*/
|
|
19
|
+
snippetsAsTools?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Generate example input from a JSON Schema
|
|
24
|
+
*/
|
|
25
|
+
function generateExampleFromSchema(schema: Record<string, unknown>): string {
|
|
26
|
+
if (schema.type === 'object' && schema.properties) {
|
|
27
|
+
const props = schema.properties as Record<string, { type: string }>
|
|
28
|
+
const example: Record<string, unknown> = {}
|
|
29
|
+
|
|
30
|
+
for (const [key, value] of Object.entries(props)) {
|
|
31
|
+
if (value.type === 'string') example[key] = `'example_${key}'`
|
|
32
|
+
else if (value.type === 'number') example[key] = 0
|
|
33
|
+
else if (value.type === 'boolean') example[key] = true
|
|
34
|
+
else if (value.type === 'array') example[key] = []
|
|
35
|
+
else example[key] = null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return JSON.stringify(example).replace(/"/g, '')
|
|
39
|
+
}
|
|
40
|
+
return '{}'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create system prompt documentation for the snippet library.
|
|
45
|
+
* This is appended to the Code Mode system prompt.
|
|
46
|
+
*/
|
|
47
|
+
export function createSnippetsSystemPrompt({
|
|
48
|
+
selectedSnippets,
|
|
49
|
+
totalSnippetCount,
|
|
50
|
+
snippetsAsTools = true,
|
|
51
|
+
}: CreateSnippetsSystemPromptOptions): string {
|
|
52
|
+
// No snippets in library
|
|
53
|
+
if (totalSnippetCount === 0) {
|
|
54
|
+
return `## Snippet Library
|
|
55
|
+
|
|
56
|
+
You have access to a snippet library for storing reusable code. The library is currently empty.
|
|
57
|
+
|
|
58
|
+
### Snippet Management Tools
|
|
59
|
+
|
|
60
|
+
- \`search_snippets(query, limit?)\` - Search for snippets (currently empty)
|
|
61
|
+
- \`get_snippet(name)\` - Get full snippet details including code
|
|
62
|
+
- \`register_snippet(...)\` - Save working code as a reusable snippet
|
|
63
|
+
|
|
64
|
+
When you write useful, reusable code, consider registering it as a snippet for future use.
|
|
65
|
+
|
|
66
|
+
**Important**: Newly registered snippets become available as tools on the **next message**, not immediately in the current conversation turn.
|
|
67
|
+
`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// No snippets selected for this conversation
|
|
71
|
+
if (selectedSnippets.length === 0) {
|
|
72
|
+
return `## Snippet Library
|
|
73
|
+
|
|
74
|
+
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.
|
|
75
|
+
|
|
76
|
+
### Snippet Management Tools
|
|
77
|
+
|
|
78
|
+
- \`search_snippets(query, limit?)\` - Search for relevant snippets
|
|
79
|
+
- \`get_snippet(name)\` - Get full snippet details including code
|
|
80
|
+
- \`register_snippet(...)\` - Save working code as a reusable snippet
|
|
81
|
+
|
|
82
|
+
When you write useful, reusable code, consider registering it as a snippet for future use.
|
|
83
|
+
|
|
84
|
+
**Important**: Newly registered snippets become available as tools on the **next message**, not immediately in the current conversation turn.
|
|
85
|
+
`
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (snippetsAsTools) {
|
|
89
|
+
// Snippets are available as direct tools
|
|
90
|
+
const snippetToolDocs = selectedSnippets
|
|
91
|
+
.map((snippet) => {
|
|
92
|
+
const inputExample = generateExampleFromSchema(snippet.inputSchema)
|
|
93
|
+
const trustBadge =
|
|
94
|
+
snippet.trustLevel === 'trusted'
|
|
95
|
+
? '✓ trusted'
|
|
96
|
+
: snippet.trustLevel === 'provisional'
|
|
97
|
+
? '◐ provisional'
|
|
98
|
+
: '○ untrusted'
|
|
99
|
+
|
|
100
|
+
return `
|
|
101
|
+
### ${snippet.name} [${trustBadge}]
|
|
102
|
+
|
|
103
|
+
${snippet.description}
|
|
104
|
+
|
|
105
|
+
${snippet.usageHints.map((h) => `- ${h}`).join('\n')}
|
|
106
|
+
|
|
107
|
+
**Input Schema:**
|
|
108
|
+
\`\`\`json
|
|
109
|
+
${JSON.stringify(snippet.inputSchema, null, 2)}
|
|
110
|
+
\`\`\`
|
|
111
|
+
|
|
112
|
+
**Output Schema:**
|
|
113
|
+
\`\`\`json
|
|
114
|
+
${JSON.stringify(snippet.outputSchema, null, 2)}
|
|
115
|
+
\`\`\`
|
|
116
|
+
|
|
117
|
+
**Example:**
|
|
118
|
+
Call the \`${snippet.name}\` tool with: ${inputExample}
|
|
119
|
+
`
|
|
120
|
+
})
|
|
121
|
+
.join('\n---\n')
|
|
122
|
+
|
|
123
|
+
return `## Snippet Library
|
|
124
|
+
|
|
125
|
+
${selectedSnippets.length} snippet${selectedSnippets.length === 1 ? '' : 's'} pre-loaded for this conversation (${totalSnippetCount} total in library).
|
|
126
|
+
|
|
127
|
+
### Available Snippet Tools
|
|
128
|
+
|
|
129
|
+
These snippets are available as **direct tools** you can call (marked with [SNIPPET] in description):
|
|
130
|
+
|
|
131
|
+
${snippetToolDocs}
|
|
132
|
+
|
|
133
|
+
### Snippet Management Tools
|
|
134
|
+
|
|
135
|
+
- \`search_snippets(query, limit?)\` - Find additional snippets not pre-loaded
|
|
136
|
+
- \`get_snippet(name)\` - Get full details of any snippet
|
|
137
|
+
- \`register_snippet(...)\` - Save working code as a new snippet
|
|
138
|
+
|
|
139
|
+
### Using Snippets
|
|
140
|
+
|
|
141
|
+
Snippets are **regular tools** - call them directly like any other tool. No need to use \`execute_typescript\`.
|
|
142
|
+
|
|
143
|
+
### Creating New Snippets
|
|
144
|
+
|
|
145
|
+
When you write useful, reusable code with \`execute_typescript\`, register it:
|
|
146
|
+
|
|
147
|
+
\`\`\`typescript
|
|
148
|
+
// After verifying code works, call the register_snippet tool
|
|
149
|
+
register_snippet({
|
|
150
|
+
name: 'compare_npm_packages',
|
|
151
|
+
description: 'Compare download counts for multiple NPM packages',
|
|
152
|
+
code: \`
|
|
153
|
+
const { packages } = input;
|
|
154
|
+
const results = await Promise.all(
|
|
155
|
+
packages.map(pkg => external_getNpmDownloads({ package: pkg }))
|
|
156
|
+
);
|
|
157
|
+
return packages.map((pkg, i) => ({ package: pkg, downloads: results[i].downloads }))
|
|
158
|
+
.sort((a, b) => b.downloads - a.downloads);
|
|
159
|
+
\`,
|
|
160
|
+
inputSchema: {
|
|
161
|
+
type: 'object',
|
|
162
|
+
properties: { packages: { type: 'array', items: { type: 'string' } } },
|
|
163
|
+
required: ['packages']
|
|
164
|
+
},
|
|
165
|
+
outputSchema: {
|
|
166
|
+
type: 'array',
|
|
167
|
+
items: { type: 'object', properties: { package: { type: 'string' }, downloads: { type: 'number' } } }
|
|
168
|
+
},
|
|
169
|
+
usageHints: ['Use when comparing popularity of NPM packages'],
|
|
170
|
+
dependsOn: [],
|
|
171
|
+
});
|
|
172
|
+
\`\`\`
|
|
173
|
+
|
|
174
|
+
**Important**: Newly registered snippets become available as tools on the **next message**, not immediately in the current conversation turn.
|
|
175
|
+
`
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Snippets as sandbox bindings (legacy mode)
|
|
179
|
+
const snippetDocs = selectedSnippets
|
|
180
|
+
.map((snippet) => {
|
|
181
|
+
const inputExample = generateExampleFromSchema(snippet.inputSchema)
|
|
182
|
+
const trustBadge =
|
|
183
|
+
snippet.trustLevel === 'trusted'
|
|
184
|
+
? '✓ trusted'
|
|
185
|
+
: snippet.trustLevel === 'provisional'
|
|
186
|
+
? '◐ provisional'
|
|
187
|
+
: '○ untrusted'
|
|
188
|
+
|
|
189
|
+
return `
|
|
190
|
+
### snippet_${snippet.name} [${trustBadge}]
|
|
191
|
+
|
|
192
|
+
${snippet.description}
|
|
193
|
+
|
|
194
|
+
${snippet.usageHints.map((h) => `- ${h}`).join('\n')}
|
|
195
|
+
|
|
196
|
+
**Input Schema:**
|
|
197
|
+
\`\`\`json
|
|
198
|
+
${JSON.stringify(snippet.inputSchema, null, 2)}
|
|
199
|
+
\`\`\`
|
|
200
|
+
|
|
201
|
+
**Output Schema:**
|
|
202
|
+
\`\`\`json
|
|
203
|
+
${JSON.stringify(snippet.outputSchema, null, 2)}
|
|
204
|
+
\`\`\`
|
|
205
|
+
|
|
206
|
+
**Example:**
|
|
207
|
+
\`\`\`typescript
|
|
208
|
+
const result = await snippet_${snippet.name}(${inputExample});
|
|
209
|
+
\`\`\`
|
|
210
|
+
`
|
|
211
|
+
})
|
|
212
|
+
.join('\n---\n')
|
|
213
|
+
|
|
214
|
+
// Generate type stubs for selected snippets
|
|
215
|
+
const typeStubs = generateSnippetTypes(selectedSnippets)
|
|
216
|
+
|
|
217
|
+
return `## Snippet Library
|
|
218
|
+
|
|
219
|
+
${selectedSnippets.length} snippet${selectedSnippets.length === 1 ? '' : 's'} pre-loaded for this conversation (${totalSnippetCount} total in library).
|
|
220
|
+
|
|
221
|
+
### Pre-loaded Snippets
|
|
222
|
+
|
|
223
|
+
These are available as \`snippet_*\` functions in your TypeScript code:
|
|
224
|
+
|
|
225
|
+
${snippetDocs}
|
|
226
|
+
|
|
227
|
+
### Type Definitions
|
|
228
|
+
|
|
229
|
+
\`\`\`typescript
|
|
230
|
+
${typeStubs}
|
|
231
|
+
\`\`\`
|
|
232
|
+
|
|
233
|
+
### Snippet Management Tools
|
|
234
|
+
|
|
235
|
+
- \`search_snippets(query, limit?)\` - Find additional snippets not pre-loaded
|
|
236
|
+
- \`get_snippet(name)\` - Get full details of any snippet
|
|
237
|
+
- \`register_snippet(...)\` - Save working code as a new snippet
|
|
238
|
+
|
|
239
|
+
### Using Snippets
|
|
240
|
+
|
|
241
|
+
Snippets work just like \`external_*\` functions inside \`execute_typescript\`:
|
|
242
|
+
|
|
243
|
+
\`\`\`typescript
|
|
244
|
+
// Call a pre-loaded snippet
|
|
245
|
+
const stats = await snippet_fetch_github_stats({ owner: 'tanstack', repo: 'query' });
|
|
246
|
+
|
|
247
|
+
// Compose snippets with external tools
|
|
248
|
+
const repos = await external_searchRepositories({ query: 'react state' });
|
|
249
|
+
const detailed = await Promise.all(
|
|
250
|
+
repos.items.slice(0, 5).map(r =>
|
|
251
|
+
snippet_fetch_github_stats({ owner: r.owner.login, repo: r.name })
|
|
252
|
+
)
|
|
253
|
+
);
|
|
254
|
+
\`\`\`
|
|
255
|
+
|
|
256
|
+
### Creating New Snippets
|
|
257
|
+
|
|
258
|
+
When you write useful, reusable code, register it:
|
|
259
|
+
|
|
260
|
+
\`\`\`typescript
|
|
261
|
+
// After verifying code works, call the register_snippet tool
|
|
262
|
+
register_snippet({
|
|
263
|
+
name: 'compare_npm_packages',
|
|
264
|
+
description: 'Compare download counts for multiple NPM packages',
|
|
265
|
+
code: \`
|
|
266
|
+
const { packages } = input;
|
|
267
|
+
const results = await Promise.all(
|
|
268
|
+
packages.map(pkg => external_getNpmDownloads({ package: pkg }))
|
|
269
|
+
);
|
|
270
|
+
return packages.map((pkg, i) => ({ package: pkg, downloads: results[i].downloads }))
|
|
271
|
+
.sort((a, b) => b.downloads - a.downloads);
|
|
272
|
+
\`,
|
|
273
|
+
inputSchema: {
|
|
274
|
+
type: 'object',
|
|
275
|
+
properties: { packages: { type: 'array', items: { type: 'string' } } },
|
|
276
|
+
required: ['packages']
|
|
277
|
+
},
|
|
278
|
+
outputSchema: {
|
|
279
|
+
type: 'array',
|
|
280
|
+
items: { type: 'object', properties: { package: { type: 'string' }, downloads: { type: 'number' } } }
|
|
281
|
+
},
|
|
282
|
+
usageHints: ['Use when comparing popularity of NPM packages'],
|
|
283
|
+
dependsOn: [],
|
|
284
|
+
});
|
|
285
|
+
\`\`\`
|
|
286
|
+
|
|
287
|
+
**Important**: Newly registered snippets become available as tools on the **next message**, not immediately in the current conversation turn.
|
|
288
|
+
`
|
|
289
|
+
}
|