@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.
- package/README.md +199 -0
- package/dist/esm/code-mode-with-skills.d.ts +58 -0
- package/dist/esm/code-mode-with-skills.js +124 -0
- package/dist/esm/code-mode-with-skills.js.map +1 -0
- package/dist/esm/create-skill-management-tools.d.ts +40 -0
- package/dist/esm/create-skill-management-tools.js +198 -0
- package/dist/esm/create-skill-management-tools.js.map +1 -0
- package/dist/esm/create-skills-system-prompt.d.ts +22 -0
- package/dist/esm/create-skills-system-prompt.js +236 -0
- package/dist/esm/create-skills-system-prompt.js.map +1 -0
- package/dist/esm/generate-skill-types.d.ts +7 -0
- package/dist/esm/generate-skill-types.js +87 -0
- package/dist/esm/generate-skill-types.js.map +1 -0
- package/dist/esm/index.d.ts +13 -0
- package/dist/esm/index.js +29 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/select-relevant-skills.d.ts +29 -0
- package/dist/esm/select-relevant-skills.js +79 -0
- package/dist/esm/select-relevant-skills.js.map +1 -0
- package/dist/esm/skills-to-bindings.d.ts +34 -0
- package/dist/esm/skills-to-bindings.js +77 -0
- package/dist/esm/skills-to-bindings.js.map +1 -0
- package/dist/esm/skills-to-tools.d.ts +74 -0
- package/dist/esm/skills-to-tools.js +189 -0
- package/dist/esm/skills-to-tools.js.map +1 -0
- package/dist/esm/storage/file-storage.d.ts +27 -0
- package/dist/esm/storage/file-storage.js +149 -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 +7 -0
- package/dist/esm/storage/index.js.map +1 -0
- package/dist/esm/storage/memory-storage.d.ts +17 -0
- package/dist/esm/storage/memory-storage.js +99 -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 +63 -0
- package/dist/esm/trust-strategies.js.map +1 -0
- package/dist/esm/types.d.ts +216 -0
- package/package.json +82 -0
- package/src/code-mode-with-skills.ts +204 -0
- package/src/create-skill-management-tools.ts +296 -0
- package/src/create-skills-system-prompt.ts +289 -0
- package/src/generate-skill-types.ts +162 -0
- package/src/index.ts +51 -0
- package/src/select-relevant-skills.ts +136 -0
- package/src/skills-to-bindings.ts +134 -0
- package/src/skills-to-tools.ts +319 -0
- package/src/storage/file-storage.ts +243 -0
- package/src/storage/index.ts +6 -0
- package/src/storage/memory-storage.ts +163 -0
- package/src/trust-strategies.ts +142 -0
- 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":"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"}
|
|
@@ -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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"}
|