@tanstack/ai-code-mode-skills 0.1.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 (52) hide show
  1. package/README.md +199 -0
  2. package/dist/esm/code-mode-with-skills.d.ts +58 -0
  3. package/dist/esm/code-mode-with-skills.js +124 -0
  4. package/dist/esm/code-mode-with-skills.js.map +1 -0
  5. package/dist/esm/create-skill-management-tools.d.ts +40 -0
  6. package/dist/esm/create-skill-management-tools.js +198 -0
  7. package/dist/esm/create-skill-management-tools.js.map +1 -0
  8. package/dist/esm/create-skills-system-prompt.d.ts +22 -0
  9. package/dist/esm/create-skills-system-prompt.js +236 -0
  10. package/dist/esm/create-skills-system-prompt.js.map +1 -0
  11. package/dist/esm/generate-skill-types.d.ts +7 -0
  12. package/dist/esm/generate-skill-types.js +87 -0
  13. package/dist/esm/generate-skill-types.js.map +1 -0
  14. package/dist/esm/index.d.ts +13 -0
  15. package/dist/esm/index.js +29 -0
  16. package/dist/esm/index.js.map +1 -0
  17. package/dist/esm/select-relevant-skills.d.ts +29 -0
  18. package/dist/esm/select-relevant-skills.js +79 -0
  19. package/dist/esm/select-relevant-skills.js.map +1 -0
  20. package/dist/esm/skills-to-bindings.d.ts +34 -0
  21. package/dist/esm/skills-to-bindings.js +77 -0
  22. package/dist/esm/skills-to-bindings.js.map +1 -0
  23. package/dist/esm/skills-to-tools.d.ts +74 -0
  24. package/dist/esm/skills-to-tools.js +189 -0
  25. package/dist/esm/skills-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 +149 -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 +7 -0
  31. package/dist/esm/storage/index.js.map +1 -0
  32. package/dist/esm/storage/memory-storage.d.ts +17 -0
  33. package/dist/esm/storage/memory-storage.js +99 -0
  34. package/dist/esm/storage/memory-storage.js.map +1 -0
  35. package/dist/esm/trust-strategies.d.ts +50 -0
  36. package/dist/esm/trust-strategies.js +63 -0
  37. package/dist/esm/trust-strategies.js.map +1 -0
  38. package/dist/esm/types.d.ts +216 -0
  39. package/package.json +82 -0
  40. package/src/code-mode-with-skills.ts +204 -0
  41. package/src/create-skill-management-tools.ts +296 -0
  42. package/src/create-skills-system-prompt.ts +289 -0
  43. package/src/generate-skill-types.ts +162 -0
  44. package/src/index.ts +51 -0
  45. package/src/select-relevant-skills.ts +136 -0
  46. package/src/skills-to-bindings.ts +134 -0
  47. package/src/skills-to-tools.ts +319 -0
  48. package/src/storage/file-storage.ts +243 -0
  49. package/src/storage/index.ts +6 -0
  50. package/src/storage/memory-storage.ts +163 -0
  51. package/src/trust-strategies.ts +142 -0
  52. package/src/types.ts +289 -0
package/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # @tanstack/ai-code-mode-skills
2
+
3
+ Persistent skill library for TanStack AI Code Mode - LLM-created reusable code snippets.
4
+
5
+ ## Overview
6
+
7
+ The Skills System extends Code Mode with persistent, LLM-creatable reusable code snippets. Skills are TypeScript functions that the LLM can create, catalog, and invoke across sessions—enabling compounding capability over time.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pnpm add @tanstack/ai-code-mode-skills
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```typescript
18
+ import {
19
+ codeModeWithSkills,
20
+ createFileSkillStorage,
21
+ createAlwaysTrustedStrategy,
22
+ } from '@tanstack/ai-code-mode-skills'
23
+ import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'
24
+
25
+ // Create skill storage
26
+ const skillStorage = createFileSkillStorage({
27
+ directory: './.skills',
28
+ trustStrategy: createAlwaysTrustedStrategy(),
29
+ })
30
+
31
+ // Create code mode config
32
+ const codeModeConfig = {
33
+ driver: createNodeIsolateDriver(),
34
+ tools: allTools, // Your external tools
35
+ timeout: 60000,
36
+ memoryLimit: 128,
37
+ }
38
+
39
+ // Build a dynamic registry and system prompt with skills
40
+ const { registry, systemPrompt, selectedSkills } = await codeModeWithSkills({
41
+ config: codeModeConfig,
42
+ adapter: anthropic('claude-3-haiku'), // Cheap model for skill selection
43
+ skills: {
44
+ storage: skillStorage,
45
+ maxSkillsInContext: 5,
46
+ },
47
+ messages,
48
+ })
49
+
50
+ // Use in chat
51
+ const stream = chat({
52
+ adapter: anthropic('claude-sonnet-4-20250514'), // Main model
53
+ toolRegistry: registry,
54
+ messages,
55
+ systemPrompts: [basePrompt, systemPrompt],
56
+ })
57
+ ```
58
+
59
+ ## Testing
60
+
61
+ This package includes a CLI for testing the skills system. The tests verify the complete skills lifecycle:
62
+
63
+ 1. **First run (Skill Creation)**: LLM uses `execute_typescript` to solve a problem and registers a reusable skill
64
+ 2. **Second run (Skill Reuse)**: LLM calls the saved skill directly without needing `execute_typescript`
65
+
66
+ ### Running the Simulated Test
67
+
68
+ The simulated test uses a mock adapter with predetermined responses for fully deterministic testing. No API key required.
69
+
70
+ ```bash
71
+ # From the package directory
72
+ cd packages/typescript/ai-code-mode-skills
73
+
74
+ # Run the simulated test
75
+ pnpm test:cli:simulated
76
+ ```
77
+
78
+ ### Running the Live Test
79
+
80
+ The live test uses a real LLM (OpenAI or Anthropic) to verify the skills flow with actual LLM responses.
81
+
82
+ #### Setup
83
+
84
+ 1. Copy the environment example file:
85
+
86
+ ```bash
87
+ cp test-cli/env.example test-cli/.env.local
88
+ ```
89
+
90
+ 2. Edit `test-cli/.env.local` and add your API key:
91
+ ```
92
+ OPENAI_API_KEY=sk-...
93
+ # or
94
+ ANTHROPIC_API_KEY=sk-ant-...
95
+ ```
96
+
97
+ #### Run the test
98
+
99
+ ```bash
100
+ # Run with OpenAI (default)
101
+ pnpm test:cli:live
102
+
103
+ # Run with Anthropic
104
+ pnpm test:cli:live --provider anthropic
105
+
106
+ # Run with a specific model
107
+ pnpm test:cli:live --model gpt-4o-mini
108
+
109
+ # Run with verbose output
110
+ pnpm test:cli:live -v
111
+ ```
112
+
113
+ ### CLI Commands
114
+
115
+ ```bash
116
+ # Show help
117
+ pnpm test:cli --help
118
+
119
+ # Run simulated test (deterministic, no API key)
120
+ pnpm test:cli simulated
121
+
122
+ # Run live test (requires API key)
123
+ pnpm test:cli live [options]
124
+
125
+ Options:
126
+ --provider <provider> LLM provider: openai or anthropic (default: openai)
127
+ --model <model> Model to use (default depends on provider)
128
+ -v, --verbose Enable verbose output
129
+ ```
130
+
131
+ ## API Reference
132
+
133
+ ### `codeModeWithSkills(options)`
134
+
135
+ Creates Code Mode tools and system prompt with skills integration.
136
+
137
+ **Options:**
138
+
139
+ - `config` - Code Mode tool configuration (driver, tools, timeout, memoryLimit)
140
+ - `adapter` - Text adapter for skill selection (should be a cheap/fast model)
141
+ - `skills.storage` - Skill storage implementation
142
+ - `skills.maxSkillsInContext` - Maximum skills to load into context (default: 5)
143
+ - `messages` - Current conversation messages
144
+ - `skillsAsTools` - Whether to include skills as direct tools (default: true)
145
+
146
+ **Returns:**
147
+
148
+ - `registry` - Mutable `ToolRegistry` containing `execute_typescript`, skill management tools, and selected skill tools
149
+ - `systemPrompt` - System prompt documenting available skills and external functions
150
+ - `selectedSkills` - Skills that were selected for this request
151
+
152
+ ### Storage
153
+
154
+ Storage is available from both the root export and the explicit storage subpath:
155
+
156
+ ```typescript
157
+ import { createFileSkillStorage } from '@tanstack/ai-code-mode-skills'
158
+ // or
159
+ import { createFileSkillStorage } from '@tanstack/ai-code-mode-skills/storage'
160
+ ```
161
+
162
+ #### `createFileSkillStorage(options)`
163
+
164
+ Git-friendly file-based storage:
165
+
166
+ ```
167
+ .skills/
168
+ ├── compare_react_state_libraries/
169
+ │ ├── meta.json # Metadata, schemas, stats
170
+ │ └── code.ts # TypeScript implementation
171
+ └── fetch_github_stats/
172
+ ├── meta.json
173
+ └── code.ts
174
+ ```
175
+
176
+ #### `createMemorySkillStorage(options)`
177
+
178
+ In-memory storage for testing.
179
+
180
+ ### Trust Strategies
181
+
182
+ Skills track execution success and promote trust levels over time:
183
+
184
+ | Trust Level | Description |
185
+ | ------------- | --------------------------------- |
186
+ | `untrusted` | Newly created, not yet proven |
187
+ | `provisional` | 10+ executions with ≥90% success |
188
+ | `trusted` | 100+ executions with ≥95% success |
189
+
190
+ Available strategies:
191
+
192
+ - `createDefaultTrustStrategy()` - Earn trust through successful executions
193
+ - `createAlwaysTrustedStrategy()` - Trust immediately (dev/testing)
194
+ - `createRelaxedTrustStrategy()` - Faster promotion
195
+ - `createCustomTrustStrategy(options)` - Custom thresholds
196
+
197
+ ## License
198
+
199
+ MIT
@@ -0,0 +1,58 @@
1
+ import { CodeModeWithSkillsOptions, CodeModeWithSkillsResult, Skill } from './types.js';
2
+ export type { CodeModeWithSkillsOptions, CodeModeWithSkillsResult };
3
+ /**
4
+ * Create Code Mode tools and system prompt with skills integration.
5
+ *
6
+ * This function:
7
+ * 1. Loads the skill index from storage
8
+ * 2. Uses a cheap/fast LLM to select relevant skills based on conversation context
9
+ * 3. Creates the execute_typescript tool with dynamic skill bindings
10
+ * 4. Creates skill management tools (search, get, register)
11
+ * 5. Generates system prompts documenting available skills
12
+ * 6. Returns a ToolRegistry that allows dynamic skill additions mid-stream
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const { toolsRegistry, systemPrompt, selectedSkills } = await codeModeWithSkills({
17
+ * config: {
18
+ * driver: createNodeIsolateDriver(),
19
+ * tools: allTools,
20
+ * timeout: 60000,
21
+ * },
22
+ * adapter: openaiText('gpt-4o-mini'), // Cheap model for selection
23
+ * skills: {
24
+ * storage: createFileSkillStorage('./.skills'),
25
+ * maxSkillsInContext: 5,
26
+ * },
27
+ * messages,
28
+ * });
29
+ *
30
+ * const stream = chat({
31
+ * adapter: openaiText('gpt-4o'), // Main model
32
+ * toolRegistry: toolsRegistry, // Dynamic tool registry
33
+ * messages,
34
+ * systemPrompts: [BASE_PROMPT, systemPrompt],
35
+ * });
36
+ * ```
37
+ */
38
+ export declare function codeModeWithSkills({ config, adapter, skills, messages, skillsAsTools, }: CodeModeWithSkillsOptions): Promise<CodeModeWithSkillsResult>;
39
+ /**
40
+ * Create a Code Mode tool configuration extended with skills.
41
+ * This is an alternative to codeModeWithSkills that returns
42
+ * a config object instead of directly creating tools.
43
+ *
44
+ * Useful when you want more control over the tool creation process.
45
+ */
46
+ export declare function createCodeModeWithSkillsConfig({ config, selectedSkills, storage, }: {
47
+ config: CodeModeWithSkillsOptions['config'];
48
+ selectedSkills: Array<Skill>;
49
+ storage: CodeModeWithSkillsOptions['skills']['storage'];
50
+ }): {
51
+ skillTools: import('@tanstack/ai').ServerTool<any, any, any>[];
52
+ selectedSkills: Skill[];
53
+ driver: import('@tanstack/ai-code-mode').IsolateDriver;
54
+ tools: Array<import('@tanstack/ai-code-mode').CodeModeTool>;
55
+ timeout?: number;
56
+ memoryLimit?: number;
57
+ getSkillBindings?: () => Promise<Record<string, import('@tanstack/ai-code-mode').ToolBinding>>;
58
+ };
@@ -0,0 +1,124 @@
1
+ import { toolsToBindings, createCodeModeTool, createCodeModeSystemPrompt } from "@tanstack/ai-code-mode";
2
+ import { createToolRegistry } from "@tanstack/ai";
3
+ import { selectRelevantSkills } from "./select-relevant-skills.js";
4
+ import { createSkillManagementTools } from "./create-skill-management-tools.js";
5
+ import { createSkillsSystemPrompt } from "./create-skills-system-prompt.js";
6
+ import { skillsToTools } from "./skills-to-tools.js";
7
+ async function codeModeWithSkills({
8
+ config,
9
+ adapter,
10
+ skills,
11
+ messages,
12
+ skillsAsTools = true
13
+ }) {
14
+ const { storage, maxSkillsInContext = 5 } = skills;
15
+ const skillIndex = await storage.loadIndex();
16
+ const selectedSkills = await selectRelevantSkills({
17
+ adapter,
18
+ messages,
19
+ skillIndex,
20
+ maxSkills: maxSkillsInContext,
21
+ storage
22
+ });
23
+ const baseBindings = toolsToBindings(config.tools, "external_");
24
+ const codeModeTool = createCodeModeTool({
25
+ ...config,
26
+ // Dynamic skill bindings - fetched at execution time
27
+ getSkillBindings: async () => {
28
+ const allSkills = await storage.loadAll();
29
+ const skillBindings = {};
30
+ for (const skill of allSkills) {
31
+ skillBindings[`skill_${skill.name}`] = {
32
+ name: `skill_${skill.name}`,
33
+ description: skill.description,
34
+ inputSchema: skill.inputSchema,
35
+ outputSchema: skill.outputSchema,
36
+ execute: async (input) => {
37
+ const wrappedCode = `const input = ${JSON.stringify(input)};
38
+ ${skill.code}`;
39
+ const { stripTypeScript, createEventAwareBindings } = await import("@tanstack/ai-code-mode");
40
+ const strippedCode = await stripTypeScript(wrappedCode);
41
+ const context = await config.driver.createContext({
42
+ bindings: createEventAwareBindings(baseBindings, () => {
43
+ }),
44
+ timeout: config.timeout,
45
+ memoryLimit: config.memoryLimit
46
+ });
47
+ try {
48
+ const result = await context.execute(strippedCode);
49
+ if (!result.success) {
50
+ throw new Error(
51
+ result.error?.message || "Skill execution failed"
52
+ );
53
+ }
54
+ return result.value;
55
+ } finally {
56
+ await context.dispose();
57
+ }
58
+ }
59
+ };
60
+ }
61
+ return skillBindings;
62
+ }
63
+ });
64
+ const registry = createToolRegistry();
65
+ registry.add(codeModeTool);
66
+ const skillManagementTools = createSkillManagementTools({
67
+ storage,
68
+ registry,
69
+ config,
70
+ baseBindings
71
+ });
72
+ for (const tool of skillManagementTools) {
73
+ registry.add(tool);
74
+ }
75
+ if (skillsAsTools && selectedSkills.length > 0) {
76
+ const skillToolsList = skillsToTools({
77
+ skills: selectedSkills,
78
+ driver: config.driver,
79
+ tools: config.tools,
80
+ storage,
81
+ timeout: config.timeout,
82
+ memoryLimit: config.memoryLimit
83
+ });
84
+ for (const skillTool of skillToolsList) {
85
+ registry.add(skillTool);
86
+ }
87
+ }
88
+ const basePrompt = createCodeModeSystemPrompt(config);
89
+ const skillsPrompt = createSkillsSystemPrompt({
90
+ selectedSkills,
91
+ totalSkillCount: skillIndex.length,
92
+ skillsAsTools
93
+ });
94
+ const systemPrompt = basePrompt + "\n\n" + skillsPrompt;
95
+ return {
96
+ toolsRegistry: registry,
97
+ systemPrompt,
98
+ selectedSkills
99
+ };
100
+ }
101
+ function createCodeModeWithSkillsConfig({
102
+ config,
103
+ selectedSkills,
104
+ storage
105
+ }) {
106
+ const skillToolsList = skillsToTools({
107
+ skills: selectedSkills,
108
+ driver: config.driver,
109
+ tools: config.tools,
110
+ storage,
111
+ timeout: config.timeout,
112
+ memoryLimit: config.memoryLimit
113
+ });
114
+ return {
115
+ ...config,
116
+ skillTools: skillToolsList,
117
+ selectedSkills
118
+ };
119
+ }
120
+ export {
121
+ codeModeWithSkills,
122
+ createCodeModeWithSkillsConfig
123
+ };
124
+ //# sourceMappingURL=code-mode-with-skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-mode-with-skills.js","sources":["../../src/code-mode-with-skills.ts"],"sourcesContent":["import {\n createCodeModeSystemPrompt,\n createCodeModeTool,\n toolsToBindings,\n} from '@tanstack/ai-code-mode'\nimport { createToolRegistry } from '@tanstack/ai'\nimport { selectRelevantSkills } from './select-relevant-skills'\nimport { createSkillManagementTools } from './create-skill-management-tools'\nimport { createSkillsSystemPrompt } from './create-skills-system-prompt'\nimport { skillsToTools } from './skills-to-tools'\nimport type {\n CodeModeWithSkillsOptions,\n CodeModeWithSkillsResult,\n Skill,\n} from './types'\n\nexport type { CodeModeWithSkillsOptions, CodeModeWithSkillsResult }\n\n/**\n * Create Code Mode tools and system prompt with skills integration.\n *\n * This function:\n * 1. Loads the skill index from storage\n * 2. Uses a cheap/fast LLM to select relevant skills based on conversation context\n * 3. Creates the execute_typescript tool with dynamic skill bindings\n * 4. Creates skill management tools (search, get, register)\n * 5. Generates system prompts documenting available skills\n * 6. Returns a ToolRegistry that allows dynamic skill additions mid-stream\n *\n * @example\n * ```typescript\n * const { toolsRegistry, systemPrompt, selectedSkills } = await codeModeWithSkills({\n * config: {\n * driver: createNodeIsolateDriver(),\n * tools: allTools,\n * timeout: 60000,\n * },\n * adapter: openaiText('gpt-4o-mini'), // Cheap model for selection\n * skills: {\n * storage: createFileSkillStorage('./.skills'),\n * maxSkillsInContext: 5,\n * },\n * messages,\n * });\n *\n * const stream = chat({\n * adapter: openaiText('gpt-4o'), // Main model\n * toolRegistry: toolsRegistry, // Dynamic tool registry\n * messages,\n * systemPrompts: [BASE_PROMPT, systemPrompt],\n * });\n * ```\n */\nexport async function codeModeWithSkills({\n config,\n adapter,\n skills,\n messages,\n skillsAsTools = true,\n}: CodeModeWithSkillsOptions): Promise<CodeModeWithSkillsResult> {\n const { storage, maxSkillsInContext = 5 } = skills\n\n // 1. Load the skill index (lightweight metadata only)\n const skillIndex = await storage.loadIndex()\n\n // 2. Use adapter to select relevant skills based on transcript\n const selectedSkills = await selectRelevantSkills({\n adapter,\n messages,\n skillIndex,\n maxSkills: maxSkillsInContext,\n storage,\n })\n\n // Pre-compute bindings from base tools (shared across skill executions)\n const baseBindings = toolsToBindings(config.tools, 'external_')\n\n // 3. Create the execute_typescript tool with dynamic skill bindings\n const codeModeTool = createCodeModeTool({\n ...config,\n // Dynamic skill bindings - fetched at execution time\n getSkillBindings: async () => {\n // Get all skills from storage (includes newly registered ones)\n const allSkills = await storage.loadAll()\n // Convert to bindings with skill_ prefix\n const skillBindings: Record<string, any> = {}\n for (const skill of allSkills) {\n // Create a simple binding that executes the skill code\n skillBindings[`skill_${skill.name}`] = {\n name: `skill_${skill.name}`,\n description: skill.description,\n inputSchema: skill.inputSchema,\n outputSchema: skill.outputSchema,\n execute: async (input: unknown) => {\n // This is a simplified execution - the full skillToTool handles events\n const wrappedCode = `const input = ${JSON.stringify(input)};\\n${skill.code}`\n const { stripTypeScript, createEventAwareBindings } =\n await import('@tanstack/ai-code-mode')\n const strippedCode = await stripTypeScript(wrappedCode)\n const context = await config.driver.createContext({\n bindings: createEventAwareBindings(baseBindings, () => {}),\n timeout: config.timeout,\n memoryLimit: config.memoryLimit,\n })\n try {\n const result = await context.execute(strippedCode)\n if (!result.success) {\n throw new Error(\n result.error?.message || 'Skill execution failed',\n )\n }\n return result.value\n } finally {\n await context.dispose()\n }\n },\n }\n }\n return skillBindings\n },\n })\n\n // 4. Create a mutable tool registry\n const registry = createToolRegistry()\n\n // 5. Add the execute_typescript tool to the registry\n registry.add(codeModeTool)\n\n // 6. Create skill management tools (they need access to the registry)\n const skillManagementTools = createSkillManagementTools({\n storage,\n registry,\n config,\n baseBindings,\n })\n\n for (const tool of skillManagementTools) {\n registry.add(tool)\n }\n\n // 7. Convert selected skills to direct tools and add to registry (if enabled)\n if (skillsAsTools && selectedSkills.length > 0) {\n const skillToolsList = skillsToTools({\n skills: selectedSkills,\n driver: config.driver,\n tools: config.tools,\n storage,\n timeout: config.timeout,\n memoryLimit: config.memoryLimit,\n })\n\n for (const skillTool of skillToolsList) {\n registry.add(skillTool)\n }\n }\n\n // 8. Generate combined system prompt\n const basePrompt = createCodeModeSystemPrompt(config)\n const skillsPrompt = createSkillsSystemPrompt({\n selectedSkills,\n totalSkillCount: skillIndex.length,\n skillsAsTools,\n })\n const systemPrompt = basePrompt + '\\n\\n' + skillsPrompt\n\n return {\n toolsRegistry: registry,\n systemPrompt,\n selectedSkills,\n }\n}\n\n/**\n * Create a Code Mode tool configuration extended with skills.\n * This is an alternative to codeModeWithSkills that returns\n * a config object instead of directly creating tools.\n *\n * Useful when you want more control over the tool creation process.\n */\nexport function createCodeModeWithSkillsConfig({\n config,\n selectedSkills,\n storage,\n}: {\n config: CodeModeWithSkillsOptions['config']\n selectedSkills: Array<Skill>\n storage: CodeModeWithSkillsOptions['skills']['storage']\n}) {\n // Create skill tools for direct calling\n const skillToolsList = skillsToTools({\n skills: selectedSkills,\n driver: config.driver,\n tools: config.tools,\n storage,\n timeout: config.timeout,\n memoryLimit: config.memoryLimit,\n })\n\n return {\n ...config,\n skillTools: skillToolsList,\n selectedSkills,\n }\n}\n"],"names":[],"mappings":";;;;;;AAqDA,eAAsB,mBAAmB;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAClB,GAAiE;AAC/D,QAAM,EAAE,SAAS,qBAAqB,EAAA,IAAM;AAG5C,QAAM,aAAa,MAAM,QAAQ,UAAA;AAGjC,QAAM,iBAAiB,MAAM,qBAAqB;AAAA,IAChD;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,EAAA,CACD;AAGD,QAAM,eAAe,gBAAgB,OAAO,OAAO,WAAW;AAG9D,QAAM,eAAe,mBAAmB;AAAA,IACtC,GAAG;AAAA;AAAA,IAEH,kBAAkB,YAAY;AAE5B,YAAM,YAAY,MAAM,QAAQ,QAAA;AAEhC,YAAM,gBAAqC,CAAA;AAC3C,iBAAW,SAAS,WAAW;AAE7B,sBAAc,SAAS,MAAM,IAAI,EAAE,IAAI;AAAA,UACrC,MAAM,SAAS,MAAM,IAAI;AAAA,UACzB,aAAa,MAAM;AAAA,UACnB,aAAa,MAAM;AAAA,UACnB,cAAc,MAAM;AAAA,UACpB,SAAS,OAAO,UAAmB;AAEjC,kBAAM,cAAc,iBAAiB,KAAK,UAAU,KAAK,CAAC;AAAA,EAAM,MAAM,IAAI;AAC1E,kBAAM,EAAE,iBAAiB,6BACvB,MAAM,OAAO,wBAAwB;AACvC,kBAAM,eAAe,MAAM,gBAAgB,WAAW;AACtD,kBAAM,UAAU,MAAM,OAAO,OAAO,cAAc;AAAA,cAChD,UAAU,yBAAyB,cAAc,MAAM;AAAA,cAAC,CAAC;AAAA,cACzD,SAAS,OAAO;AAAA,cAChB,aAAa,OAAO;AAAA,YAAA,CACrB;AACD,gBAAI;AACF,oBAAM,SAAS,MAAM,QAAQ,QAAQ,YAAY;AACjD,kBAAI,CAAC,OAAO,SAAS;AACnB,sBAAM,IAAI;AAAA,kBACR,OAAO,OAAO,WAAW;AAAA,gBAAA;AAAA,cAE7B;AACA,qBAAO,OAAO;AAAA,YAChB,UAAA;AACE,oBAAM,QAAQ,QAAA;AAAA,YAChB;AAAA,UACF;AAAA,QAAA;AAAA,MAEJ;AACA,aAAO;AAAA,IACT;AAAA,EAAA,CACD;AAGD,QAAM,WAAW,mBAAA;AAGjB,WAAS,IAAI,YAAY;AAGzB,QAAM,uBAAuB,2BAA2B;AAAA,IACtD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AAED,aAAW,QAAQ,sBAAsB;AACvC,aAAS,IAAI,IAAI;AAAA,EACnB;AAGA,MAAI,iBAAiB,eAAe,SAAS,GAAG;AAC9C,UAAM,iBAAiB,cAAc;AAAA,MACnC,QAAQ;AAAA,MACR,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO;AAAA,MACd;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,aAAa,OAAO;AAAA,IAAA,CACrB;AAED,eAAW,aAAa,gBAAgB;AACtC,eAAS,IAAI,SAAS;AAAA,IACxB;AAAA,EACF;AAGA,QAAM,aAAa,2BAA2B,MAAM;AACpD,QAAM,eAAe,yBAAyB;AAAA,IAC5C;AAAA,IACA,iBAAiB,WAAW;AAAA,IAC5B;AAAA,EAAA,CACD;AACD,QAAM,eAAe,aAAa,SAAS;AAE3C,SAAO;AAAA,IACL,eAAe;AAAA,IACf;AAAA,IACA;AAAA,EAAA;AAEJ;AASO,SAAS,+BAA+B;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AACF,GAIG;AAED,QAAM,iBAAiB,cAAc;AAAA,IACnC,QAAQ;AAAA,IACR,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO;AAAA,IACd;AAAA,IACA,SAAS,OAAO;AAAA,IAChB,aAAa,OAAO;AAAA,EAAA,CACrB;AAED,SAAO;AAAA,IACL,GAAG;AAAA,IACH,YAAY;AAAA,IACZ;AAAA,EAAA;AAEJ;"}
@@ -0,0 +1,40 @@
1
+ import { ServerTool, ToolRegistry } from '@tanstack/ai';
2
+ import { CodeModeToolConfig, ToolBinding } from '@tanstack/ai-code-mode';
3
+ import { SkillStorage } from './types.js';
4
+ import { TrustStrategy } from './trust-strategies.js';
5
+ interface CreateSkillManagementToolsOptions {
6
+ /**
7
+ * Storage implementation for skills
8
+ */
9
+ storage: SkillStorage;
10
+ /**
11
+ * Trust strategy for determining initial trust level.
12
+ * If not provided, uses the storage's trustStrategy or falls back to default.
13
+ */
14
+ trustStrategy?: TrustStrategy;
15
+ /**
16
+ * Tool registry for adding newly registered skills immediately.
17
+ * When provided, register_skill will add the new skill to this registry
18
+ * so it's available as a direct tool in the current chat session.
19
+ */
20
+ registry?: ToolRegistry;
21
+ /**
22
+ * Code mode config for creating skill tools.
23
+ * Required when registry is provided.
24
+ */
25
+ config?: CodeModeToolConfig;
26
+ /**
27
+ * Pre-computed bindings for external_* functions.
28
+ * Required when registry is provided.
29
+ */
30
+ baseBindings?: Record<string, ToolBinding>;
31
+ }
32
+ /**
33
+ * Create tools for searching, retrieving, and registering skills.
34
+ * These tools allow the LLM to interact with the skill library at runtime.
35
+ *
36
+ * When registry, config, and baseBindings are provided, newly registered skills
37
+ * will be immediately added to the registry and available as direct tools.
38
+ */
39
+ export declare function createSkillManagementTools({ storage, trustStrategy, registry, config, baseBindings, }: CreateSkillManagementToolsOptions): Array<ServerTool<any, any, any>>;
40
+ export {};
@@ -0,0 +1,198 @@
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.js";
5
+ import { skillToTool } from "./skills-to-tools.js";
6
+ function createSkillManagementTools({
7
+ storage,
8
+ trustStrategy,
9
+ registry,
10
+ config,
11
+ baseBindings
12
+ }) {
13
+ const strategy = trustStrategy ?? storage.trustStrategy ?? createDefaultTrustStrategy();
14
+ const bindings = baseBindings ?? (config ? toolsToBindings(config.tools, "external_") : {});
15
+ return [
16
+ // Search for skills
17
+ toolDefinition({
18
+ name: "search_skills",
19
+ description: "Search the skill library for reusable skills. Use this to find skills that can help accomplish a task. Returns matching skills with their descriptions.",
20
+ inputSchema: z.object({
21
+ query: z.string().describe("Search query describing what you want to accomplish"),
22
+ limit: z.number().optional().default(5).describe("Maximum number of results (default: 5)")
23
+ }),
24
+ outputSchema: z.array(
25
+ z.object({
26
+ name: z.string(),
27
+ description: z.string(),
28
+ usageHints: z.array(z.string()),
29
+ trustLevel: z.enum(["untrusted", "provisional", "trusted"])
30
+ })
31
+ )
32
+ }).server(async ({ query, limit }) => {
33
+ const results = await storage.search(query, { limit: limit ?? 5 });
34
+ return results.map((s) => ({
35
+ name: s.name,
36
+ description: s.description,
37
+ usageHints: s.usageHints,
38
+ trustLevel: s.trustLevel
39
+ }));
40
+ }),
41
+ // Get full skill details
42
+ toolDefinition({
43
+ name: "get_skill",
44
+ description: "Get the full implementation details of a skill, including its code. Use this after search_skills to see how a skill works before using it.",
45
+ inputSchema: z.object({
46
+ name: z.string().describe("The skill name (without skill_ prefix)")
47
+ }),
48
+ outputSchema: z.object({
49
+ name: z.string().optional(),
50
+ description: z.string().optional(),
51
+ code: z.string().optional(),
52
+ inputSchema: z.string().optional().describe("JSON Schema as string"),
53
+ outputSchema: z.string().optional().describe("JSON Schema as string"),
54
+ usageHints: z.array(z.string()).optional(),
55
+ dependsOn: z.array(z.string()).optional(),
56
+ trustLevel: z.enum(["untrusted", "provisional", "trusted"]).optional(),
57
+ stats: z.object({
58
+ executions: z.number(),
59
+ successRate: z.number()
60
+ }).optional(),
61
+ error: z.string().optional()
62
+ })
63
+ }).server(async ({ name }) => {
64
+ const skill = await storage.get(name);
65
+ if (!skill) {
66
+ return { error: `Skill '${name}' not found` };
67
+ }
68
+ return {
69
+ name: skill.name,
70
+ description: skill.description,
71
+ code: skill.code,
72
+ inputSchema: JSON.stringify(skill.inputSchema),
73
+ outputSchema: JSON.stringify(skill.outputSchema),
74
+ usageHints: skill.usageHints,
75
+ dependsOn: skill.dependsOn,
76
+ trustLevel: skill.trustLevel,
77
+ stats: skill.stats
78
+ };
79
+ }),
80
+ // Register a new skill
81
+ toolDefinition({
82
+ name: "register_skill",
83
+ description: "Save working TypeScript code as a reusable skill for future use. Only register code that has been tested and works correctly. The skill becomes available as a callable tool immediately.",
84
+ inputSchema: z.object({
85
+ name: z.string().regex(
86
+ /^[a-z][a-z0-9_]*$/,
87
+ "Must be snake_case starting with a letter"
88
+ ).describe(
89
+ "Unique skill name in snake_case (e.g., fetch_github_stats)"
90
+ ),
91
+ description: z.string().describe("Clear description of what the skill does"),
92
+ code: z.string().describe(
93
+ "The TypeScript code. Receives `input` variable, can call external_* and skill_* functions, should return a value."
94
+ ),
95
+ inputSchema: z.string().describe(
96
+ 'JSON Schema as a JSON string describing the input parameter, e.g. {"type":"object","properties":{"a":{"type":"number"}},"required":["a"]}'
97
+ ),
98
+ outputSchema: z.string().describe(
99
+ 'JSON Schema as a JSON string describing the return value, e.g. {"type":"object","properties":{"result":{"type":"number"}}}'
100
+ ),
101
+ usageHints: z.array(z.string()).describe(
102
+ 'Hints about when to use this skill, e.g. "Use when user asks about..."'
103
+ ),
104
+ dependsOn: z.array(z.string()).optional().default([]).describe("Names of other skills this skill calls")
105
+ }),
106
+ outputSchema: z.object({
107
+ success: z.boolean().optional(),
108
+ skillId: z.string().optional(),
109
+ name: z.string().optional(),
110
+ message: z.string().optional(),
111
+ error: z.string().optional()
112
+ })
113
+ }).server(async (rawSkillDef, context) => {
114
+ let inputSchema;
115
+ let outputSchema;
116
+ try {
117
+ inputSchema = JSON.parse(rawSkillDef.inputSchema);
118
+ } catch {
119
+ return { error: "inputSchema must be a valid JSON string" };
120
+ }
121
+ try {
122
+ outputSchema = JSON.parse(rawSkillDef.outputSchema);
123
+ } catch {
124
+ return { error: "outputSchema must be a valid JSON string" };
125
+ }
126
+ const skillDef = {
127
+ ...rawSkillDef,
128
+ inputSchema,
129
+ outputSchema
130
+ };
131
+ try {
132
+ if (skillDef.name.startsWith("external_")) {
133
+ return { error: "Skill names cannot start with 'external_'" };
134
+ }
135
+ if (skillDef.name.startsWith("skill_")) {
136
+ return {
137
+ error: "Skill names should not include the 'skill_' prefix - it will be added automatically"
138
+ };
139
+ }
140
+ const existing = await storage.get(skillDef.name);
141
+ if (existing) {
142
+ return {
143
+ error: `Skill '${skillDef.name}' already exists. Use a different name or update the existing skill.`
144
+ };
145
+ }
146
+ const id = crypto.randomUUID();
147
+ const initialTrustLevel = strategy.getInitialTrustLevel();
148
+ const skill = await storage.save({
149
+ id,
150
+ name: skillDef.name,
151
+ description: skillDef.description,
152
+ code: skillDef.code,
153
+ inputSchema: skillDef.inputSchema,
154
+ outputSchema: skillDef.outputSchema,
155
+ usageHints: skillDef.usageHints,
156
+ dependsOn: skillDef.dependsOn ?? [],
157
+ trustLevel: initialTrustLevel,
158
+ stats: { executions: 0, successRate: 0 }
159
+ });
160
+ if (registry && config) {
161
+ const skillTool = skillToTool({
162
+ skill,
163
+ driver: config.driver,
164
+ bindings,
165
+ storage,
166
+ timeout: config.timeout,
167
+ memoryLimit: config.memoryLimit
168
+ });
169
+ registry.add(skillTool);
170
+ console.log(
171
+ `[register_skill] Added skill '${skill.name}' to registry immediately`
172
+ );
173
+ }
174
+ context?.emitCustomEvent("skill:registered", {
175
+ id: skill.id,
176
+ name: skill.name,
177
+ description: skill.description,
178
+ timestamp: Date.now()
179
+ });
180
+ return {
181
+ success: true,
182
+ skillId: skill.id,
183
+ name: skill.name,
184
+ message: `Skill '${skill.name}' registered successfully and is now available as the '${skill.name}' tool.`
185
+ };
186
+ } catch (error) {
187
+ console.error("[register_skill] Error:", error);
188
+ return {
189
+ error: `Failed to register skill: ${error instanceof Error ? error.message : String(error)}`
190
+ };
191
+ }
192
+ })
193
+ ];
194
+ }
195
+ export {
196
+ createSkillManagementTools
197
+ };
198
+ //# sourceMappingURL=create-skill-management-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-skill-management-tools.js","sources":["../../src/create-skill-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 { skillToTool } from './skills-to-tools'\nimport type { ServerTool, ToolRegistry } from '@tanstack/ai'\nimport type { CodeModeToolConfig, ToolBinding } from '@tanstack/ai-code-mode'\nimport type { SkillStorage } from './types'\nimport type { TrustStrategy } from './trust-strategies'\n\ninterface CreateSkillManagementToolsOptions {\n /**\n * Storage implementation for skills\n */\n storage: SkillStorage\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 skills immediately.\n * When provided, register_skill will add the new skill 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 skill 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 skills.\n * These tools allow the LLM to interact with the skill library at runtime.\n *\n * When registry, config, and baseBindings are provided, newly registered skills\n * will be immediately added to the registry and available as direct tools.\n */\nexport function createSkillManagementTools({\n storage,\n trustStrategy,\n registry,\n config,\n baseBindings,\n}: CreateSkillManagementToolsOptions): Array<ServerTool<any, any, any>> {\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 skills\n toolDefinition({\n name: 'search_skills',\n description:\n 'Search the skill library for reusable skills. Use this to find skills that can help accomplish a task. Returns matching skills 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 skill details\n toolDefinition({\n name: 'get_skill',\n description:\n 'Get the full implementation details of a skill, including its code. Use this after search_skills to see how a skill works before using it.',\n inputSchema: z.object({\n name: z.string().describe('The skill name (without skill_ 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 skill = await storage.get(name)\n if (!skill) {\n return { error: `Skill '${name}' not found` }\n }\n return {\n name: skill.name,\n description: skill.description,\n code: skill.code,\n inputSchema: JSON.stringify(skill.inputSchema),\n outputSchema: JSON.stringify(skill.outputSchema),\n usageHints: skill.usageHints,\n dependsOn: skill.dependsOn,\n trustLevel: skill.trustLevel,\n stats: skill.stats,\n }\n }),\n\n // Register a new skill\n toolDefinition({\n name: 'register_skill',\n description:\n 'Save working TypeScript code as a reusable skill for future use. Only register code that has been tested and works correctly. The skill 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 skill name in snake_case (e.g., fetch_github_stats)',\n ),\n description: z\n .string()\n .describe('Clear description of what the skill does'),\n code: z\n .string()\n .describe(\n 'The TypeScript code. Receives `input` variable, can call external_* and skill_* 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 skill, e.g. \"Use when user asks about...\"',\n ),\n dependsOn: z\n .array(z.string())\n .optional()\n .default([])\n .describe('Names of other skills this skill calls'),\n }),\n outputSchema: z.object({\n success: z.boolean().optional(),\n skillId: z.string().optional(),\n name: z.string().optional(),\n message: z.string().optional(),\n error: z.string().optional(),\n }),\n }).server(async (rawSkillDef, 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(rawSkillDef.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(rawSkillDef.outputSchema) as Record<\n string,\n unknown\n >\n } catch {\n return { error: 'outputSchema must be a valid JSON string' }\n }\n\n const skillDef = {\n ...rawSkillDef,\n inputSchema,\n outputSchema,\n }\n try {\n // Validate the skill name isn't reserved\n if (skillDef.name.startsWith('external_')) {\n return { error: \"Skill names cannot start with 'external_'\" }\n }\n if (skillDef.name.startsWith('skill_')) {\n return {\n error:\n \"Skill names should not include the 'skill_' prefix - it will be added automatically\",\n }\n }\n\n // Check if skill already exists\n const existing = await storage.get(skillDef.name)\n if (existing) {\n return {\n error: `Skill '${skillDef.name}' already exists. Use a different name or update the existing skill.`,\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 skill\n const skill = await storage.save({\n id,\n name: skillDef.name,\n description: skillDef.description,\n code: skillDef.code,\n inputSchema: skillDef.inputSchema,\n outputSchema: skillDef.outputSchema,\n usageHints: skillDef.usageHints,\n dependsOn: skillDef.dependsOn ?? [],\n trustLevel: initialTrustLevel,\n stats: { executions: 0, successRate: 0 },\n })\n\n // If registry and config are available, add the skill as a tool immediately\n if (registry && config) {\n const skillTool = skillToTool({\n skill,\n driver: config.driver,\n bindings,\n storage,\n timeout: config.timeout,\n memoryLimit: config.memoryLimit,\n })\n registry.add(skillTool)\n console.log(\n `[register_skill] Added skill '${skill.name}' to registry immediately`,\n )\n }\n\n // Emit event for UI notification\n context?.emitCustomEvent('skill:registered', {\n id: skill.id,\n name: skill.name,\n description: skill.description,\n timestamp: Date.now(),\n })\n\n return {\n success: true,\n skillId: skill.id,\n name: skill.name,\n message: `Skill '${skill.name}' registered successfully and is now available as the '${skill.name}' tool.`,\n }\n } catch (error) {\n console.error('[register_skill] Error:', error)\n return {\n error: `Failed to register skill: ${error instanceof Error ? error.message : String(error)}`,\n }\n }\n }),\n ]\n}\n"],"names":[],"mappings":"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"}