@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
@@ -0,0 +1,163 @@
1
+ import { createDefaultTrustStrategy } from '../trust-strategies'
2
+ import type {
3
+ Skill,
4
+ SkillIndexEntry,
5
+ SkillSearchOptions,
6
+ SkillStorage,
7
+ } from '../types'
8
+ import type { TrustStrategy } from '../trust-strategies'
9
+
10
+ export interface MemorySkillStorageOptions {
11
+ /**
12
+ * Initial skills to populate the storage with
13
+ */
14
+ initialSkills?: Array<Skill>
15
+
16
+ /**
17
+ * Trust strategy for determining skill trust levels
18
+ * @default createDefaultTrustStrategy()
19
+ */
20
+ trustStrategy?: TrustStrategy
21
+ }
22
+
23
+ /**
24
+ * In-memory skill storage for testing and demos
25
+ */
26
+ export function createMemorySkillStorage(
27
+ optionsOrSkills: MemorySkillStorageOptions | Array<Skill> = [],
28
+ ): SkillStorage {
29
+ const options = Array.isArray(optionsOrSkills)
30
+ ? { initialSkills: optionsOrSkills }
31
+ : optionsOrSkills
32
+
33
+ const { initialSkills = [], trustStrategy = createDefaultTrustStrategy() } =
34
+ options
35
+
36
+ // Store skills in a Map for O(1) lookup
37
+ const skills = new Map<string, Skill>()
38
+
39
+ // Initialize with any provided skills
40
+ for (const skill of initialSkills) {
41
+ skills.set(skill.name, skill)
42
+ }
43
+
44
+ async function loadIndex(): Promise<Array<SkillIndexEntry>> {
45
+ return Array.from(skills.values()).map((skill) => ({
46
+ id: skill.id,
47
+ name: skill.name,
48
+ description: skill.description,
49
+ usageHints: skill.usageHints,
50
+ trustLevel: skill.trustLevel,
51
+ }))
52
+ }
53
+
54
+ async function loadAll(): Promise<Array<Skill>> {
55
+ return Array.from(skills.values())
56
+ }
57
+
58
+ async function get(name: string): Promise<Skill | null> {
59
+ return skills.get(name) ?? null
60
+ }
61
+
62
+ async function save(
63
+ skill: Omit<Skill, 'createdAt' | 'updatedAt'>,
64
+ ): Promise<Skill> {
65
+ const now = new Date().toISOString()
66
+ const existing = skills.get(skill.name)
67
+
68
+ const fullSkill: Skill = {
69
+ ...skill,
70
+ createdAt: existing?.createdAt ?? now,
71
+ updatedAt: now,
72
+ }
73
+
74
+ skills.set(skill.name, fullSkill)
75
+ return fullSkill
76
+ }
77
+
78
+ async function deleteSkill(name: string): Promise<boolean> {
79
+ if (!skills.has(name)) {
80
+ return false
81
+ }
82
+ skills.delete(name)
83
+ return true
84
+ }
85
+
86
+ async function search(
87
+ query: string,
88
+ options: SkillSearchOptions = {},
89
+ ): Promise<Array<SkillIndexEntry>> {
90
+ const { limit = 5 } = options
91
+
92
+ // Simple text matching
93
+ const queryLower = query.toLowerCase()
94
+ const terms = queryLower.split(/\s+/)
95
+
96
+ const scored = Array.from(skills.values()).map((skill) => {
97
+ let score = 0
98
+ const searchText = [skill.name, skill.description, ...skill.usageHints]
99
+ .join(' ')
100
+ .toLowerCase()
101
+
102
+ for (const term of terms) {
103
+ if (searchText.includes(term)) {
104
+ score += 1
105
+ }
106
+ // Boost exact name matches
107
+ if (skill.name.toLowerCase().includes(term)) {
108
+ score += 2
109
+ }
110
+ }
111
+
112
+ return { skill, score }
113
+ })
114
+
115
+ return scored
116
+ .filter((s) => s.score > 0)
117
+ .sort((a, b) => b.score - a.score)
118
+ .slice(0, limit)
119
+ .map((s) => ({
120
+ id: s.skill.id,
121
+ name: s.skill.name,
122
+ description: s.skill.description,
123
+ usageHints: s.skill.usageHints,
124
+ trustLevel: s.skill.trustLevel,
125
+ }))
126
+ }
127
+
128
+ async function updateStats(name: string, success: boolean): Promise<void> {
129
+ const skill = skills.get(name)
130
+ if (!skill) return
131
+
132
+ const { executions, successRate } = skill.stats
133
+ const newExecutions = executions + 1
134
+ const newSuccessRate =
135
+ (successRate * executions + (success ? 1 : 0)) / newExecutions
136
+
137
+ const newStats = { executions: newExecutions, successRate: newSuccessRate }
138
+
139
+ // Use trust strategy to calculate new trust level
140
+ const newTrustLevel = trustStrategy.calculateTrustLevel(
141
+ skill.trustLevel,
142
+ newStats,
143
+ )
144
+
145
+ skills.set(name, {
146
+ ...skill,
147
+ stats: newStats,
148
+ trustLevel: newTrustLevel,
149
+ updatedAt: new Date().toISOString(),
150
+ })
151
+ }
152
+
153
+ return {
154
+ loadIndex,
155
+ loadAll,
156
+ get,
157
+ save,
158
+ delete: deleteSkill,
159
+ search,
160
+ updateStats,
161
+ trustStrategy,
162
+ }
163
+ }
@@ -0,0 +1,142 @@
1
+ import type { SkillStats, TrustLevel } from './types'
2
+
3
+ /**
4
+ * Strategy for determining skill trust levels
5
+ */
6
+ export interface TrustStrategy {
7
+ /**
8
+ * Get the initial trust level for a newly created skill
9
+ */
10
+ getInitialTrustLevel: () => TrustLevel
11
+
12
+ /**
13
+ * Calculate the new trust level based on execution stats
14
+ */
15
+ calculateTrustLevel: (
16
+ currentLevel: TrustLevel,
17
+ stats: SkillStats,
18
+ ) => TrustLevel
19
+ }
20
+
21
+ /**
22
+ * Default trust strategy - skills must earn trust through successful executions
23
+ *
24
+ * - untrusted: New skill (0 executions)
25
+ * - provisional: 10+ executions with ≥90% success rate
26
+ * - trusted: 100+ executions with ≥95% success rate
27
+ */
28
+ export function createDefaultTrustStrategy(): TrustStrategy {
29
+ return {
30
+ getInitialTrustLevel: () => 'untrusted',
31
+
32
+ calculateTrustLevel: (currentLevel, stats) => {
33
+ const { executions, successRate } = stats
34
+
35
+ if (
36
+ currentLevel === 'untrusted' &&
37
+ executions >= 10 &&
38
+ successRate >= 0.9
39
+ ) {
40
+ return 'provisional'
41
+ }
42
+
43
+ if (
44
+ currentLevel === 'provisional' &&
45
+ executions >= 100 &&
46
+ successRate >= 0.95
47
+ ) {
48
+ return 'trusted'
49
+ }
50
+
51
+ return currentLevel
52
+ },
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Always trusted strategy - skills are immediately trusted upon creation
58
+ *
59
+ * Use this for development/testing or when you trust the LLM's code generation
60
+ */
61
+ export function createAlwaysTrustedStrategy(): TrustStrategy {
62
+ return {
63
+ getInitialTrustLevel: () => 'trusted',
64
+ calculateTrustLevel: () => 'trusted',
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Relaxed trust strategy - faster trust promotion for development
70
+ *
71
+ * - untrusted: New skill (0 executions)
72
+ * - provisional: 3+ executions with ≥80% success rate
73
+ * - trusted: 10+ executions with ≥90% success rate
74
+ */
75
+ export function createRelaxedTrustStrategy(): TrustStrategy {
76
+ return {
77
+ getInitialTrustLevel: () => 'untrusted',
78
+
79
+ calculateTrustLevel: (currentLevel, stats) => {
80
+ const { executions, successRate } = stats
81
+
82
+ if (
83
+ currentLevel === 'untrusted' &&
84
+ executions >= 3 &&
85
+ successRate >= 0.8
86
+ ) {
87
+ return 'provisional'
88
+ }
89
+
90
+ if (
91
+ currentLevel === 'provisional' &&
92
+ executions >= 10 &&
93
+ successRate >= 0.9
94
+ ) {
95
+ return 'trusted'
96
+ }
97
+
98
+ return currentLevel
99
+ },
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Custom trust strategy with configurable thresholds
105
+ */
106
+ export function createCustomTrustStrategy(config: {
107
+ initialLevel?: TrustLevel
108
+ provisionalThreshold?: { executions: number; successRate: number }
109
+ trustedThreshold?: { executions: number; successRate: number }
110
+ }): TrustStrategy {
111
+ const {
112
+ initialLevel = 'untrusted',
113
+ provisionalThreshold = { executions: 10, successRate: 0.9 },
114
+ trustedThreshold = { executions: 100, successRate: 0.95 },
115
+ } = config
116
+
117
+ return {
118
+ getInitialTrustLevel: () => initialLevel,
119
+
120
+ calculateTrustLevel: (currentLevel, stats) => {
121
+ const { executions, successRate } = stats
122
+
123
+ if (
124
+ currentLevel === 'untrusted' &&
125
+ executions >= provisionalThreshold.executions &&
126
+ successRate >= provisionalThreshold.successRate
127
+ ) {
128
+ return 'provisional'
129
+ }
130
+
131
+ if (
132
+ currentLevel === 'provisional' &&
133
+ executions >= trustedThreshold.executions &&
134
+ successRate >= trustedThreshold.successRate
135
+ ) {
136
+ return 'trusted'
137
+ }
138
+
139
+ return currentLevel
140
+ },
141
+ }
142
+ }
package/src/types.ts ADDED
@@ -0,0 +1,289 @@
1
+ import type { AnyTextAdapter, ModelMessage, ToolRegistry } from '@tanstack/ai'
2
+ import type { CodeModeToolConfig } from '@tanstack/ai-code-mode'
3
+ import type { TrustStrategy } from './trust-strategies'
4
+
5
+ // ============================================================================
6
+ // Trust Levels
7
+ // ============================================================================
8
+
9
+ /**
10
+ * Trust level for a skill
11
+ * - untrusted: Newly created, not yet proven
12
+ * - provisional: Has been successfully executed 10+ times with 90%+ success
13
+ * - trusted: Has been successfully executed 100+ times with 95%+ success
14
+ */
15
+ export type TrustLevel = 'untrusted' | 'provisional' | 'trusted'
16
+
17
+ // ============================================================================
18
+ // Skill Statistics
19
+ // ============================================================================
20
+
21
+ /**
22
+ * Execution statistics for a skill
23
+ */
24
+ export interface SkillStats {
25
+ /**
26
+ * Total number of times this skill has been executed
27
+ */
28
+ executions: number
29
+
30
+ /**
31
+ * Success rate (0-1) based on execution history
32
+ */
33
+ successRate: number
34
+ }
35
+
36
+ // ============================================================================
37
+ // Skill Types
38
+ // ============================================================================
39
+
40
+ /**
41
+ * A reusable skill that can be executed in the Code Mode sandbox
42
+ */
43
+ export interface Skill {
44
+ /**
45
+ * Unique identifier for the skill
46
+ */
47
+ id: string
48
+
49
+ /**
50
+ * Unique name in snake_case (e.g., 'fetch_github_stats')
51
+ * This becomes the function name with skill_ prefix in the sandbox
52
+ */
53
+ name: string
54
+
55
+ /**
56
+ * Human-readable description of what the skill does
57
+ */
58
+ description: string
59
+
60
+ /**
61
+ * TypeScript code that implements the skill
62
+ * The code receives `input` as a variable and can call:
63
+ * - external_* functions (tools)
64
+ * - other skill_* functions (skills)
65
+ * Should return a value
66
+ */
67
+ code: string
68
+
69
+ /**
70
+ * JSON Schema describing the input parameter
71
+ */
72
+ inputSchema: Record<string, unknown>
73
+
74
+ /**
75
+ * JSON Schema describing the return value
76
+ */
77
+ outputSchema: Record<string, unknown>
78
+
79
+ /**
80
+ * Hints about when to use this skill
81
+ * e.g., "Use when comparing NPM package popularity"
82
+ */
83
+ usageHints: Array<string>
84
+
85
+ /**
86
+ * Names of other skills this skill depends on/calls
87
+ */
88
+ dependsOn: Array<string>
89
+
90
+ /**
91
+ * Trust level based on execution history
92
+ */
93
+ trustLevel: TrustLevel
94
+
95
+ /**
96
+ * Execution statistics
97
+ */
98
+ stats: SkillStats
99
+
100
+ /**
101
+ * ISO timestamp when the skill was created
102
+ */
103
+ createdAt: string
104
+
105
+ /**
106
+ * ISO timestamp when the skill was last updated
107
+ */
108
+ updatedAt: string
109
+ }
110
+
111
+ // ============================================================================
112
+ // Skill Index Types
113
+ // ============================================================================
114
+
115
+ /**
116
+ * Lightweight skill entry for the index (metadata only, no code)
117
+ * Used for fast loading and skill selection
118
+ */
119
+ export type SkillIndexEntry = Pick<
120
+ Skill,
121
+ 'id' | 'name' | 'description' | 'usageHints' | 'trustLevel'
122
+ >
123
+
124
+ // ============================================================================
125
+ // Storage Interface
126
+ // ============================================================================
127
+
128
+ /**
129
+ * Options for searching skills
130
+ */
131
+ export interface SkillSearchOptions {
132
+ /**
133
+ * Maximum number of results to return
134
+ * @default 5
135
+ */
136
+ limit?: number
137
+ }
138
+
139
+ /**
140
+ * Interface for skill storage implementations
141
+ */
142
+ export interface SkillStorage {
143
+ /**
144
+ * Load the skill index (lightweight metadata for all skills)
145
+ */
146
+ loadIndex: () => Promise<Array<SkillIndexEntry>>
147
+
148
+ /**
149
+ * Load all skills with full details (including code)
150
+ */
151
+ loadAll: () => Promise<Array<Skill>>
152
+
153
+ /**
154
+ * Get a skill by name
155
+ */
156
+ get: (name: string) => Promise<Skill | null>
157
+
158
+ /**
159
+ * Save a skill (create or update)
160
+ */
161
+ save: (skill: Omit<Skill, 'createdAt' | 'updatedAt'>) => Promise<Skill>
162
+
163
+ /**
164
+ * Delete a skill by name
165
+ */
166
+ delete: (name: string) => Promise<boolean>
167
+
168
+ /**
169
+ * Search for skills by query
170
+ */
171
+ search: (
172
+ query: string,
173
+ options?: SkillSearchOptions,
174
+ ) => Promise<Array<SkillIndexEntry>>
175
+
176
+ /**
177
+ * Update execution statistics for a skill
178
+ */
179
+ updateStats: (name: string, success: boolean) => Promise<void>
180
+
181
+ /**
182
+ * Trust strategy used by this storage (optional, for creating new skills)
183
+ */
184
+ trustStrategy?: TrustStrategy
185
+ }
186
+
187
+ // ============================================================================
188
+ // Configuration Types
189
+ // ============================================================================
190
+
191
+ /**
192
+ * Configuration for the skills system
193
+ */
194
+ export interface SkillsConfig {
195
+ /**
196
+ * Storage implementation for skills
197
+ */
198
+ storage: SkillStorage
199
+
200
+ /**
201
+ * Maximum number of skills to load into context per request
202
+ * @default 5
203
+ */
204
+ maxSkillsInContext?: number
205
+
206
+ /**
207
+ * Trust strategy for determining skill trust levels
208
+ * @default createDefaultTrustStrategy()
209
+ */
210
+ trustStrategy?: TrustStrategy
211
+ }
212
+
213
+ /**
214
+ * Options for codeModeWithSkills
215
+ */
216
+ export interface CodeModeWithSkillsOptions {
217
+ /**
218
+ * Code Mode tool configuration (driver, tools, timeout, memoryLimit)
219
+ */
220
+ config: CodeModeToolConfig
221
+
222
+ /**
223
+ * Text adapter for skill selection (should be a cheap/fast model)
224
+ */
225
+ adapter: AnyTextAdapter
226
+
227
+ /**
228
+ * Skills configuration
229
+ */
230
+ skills: SkillsConfig
231
+
232
+ /**
233
+ * Current conversation messages (used for context-aware skill selection)
234
+ */
235
+ messages: Array<ModelMessage>
236
+
237
+ /**
238
+ * Whether to include skills as direct tools (not just sandbox bindings).
239
+ * When true, skills become first-class tools the LLM can call directly.
240
+ * @default true
241
+ */
242
+ skillsAsTools?: boolean
243
+ }
244
+
245
+ /**
246
+ * Result from codeModeWithSkills
247
+ */
248
+ export interface CodeModeWithSkillsResult {
249
+ /**
250
+ * Tool registry for dynamic tool management.
251
+ * Pass this to chat() via the toolRegistry option.
252
+ * Skills registered mid-stream will be added to this registry.
253
+ */
254
+ toolsRegistry: ToolRegistry
255
+
256
+ /**
257
+ * System prompt documenting available skills and external functions
258
+ */
259
+ systemPrompt: string
260
+
261
+ /**
262
+ * Skills that were selected for this request
263
+ */
264
+ selectedSkills: Array<Skill>
265
+ }
266
+
267
+ // ============================================================================
268
+ // Skill Binding Types (internal)
269
+ // ============================================================================
270
+
271
+ /**
272
+ * A skill transformed into a format suitable for sandbox injection
273
+ */
274
+ export interface SkillBinding {
275
+ /**
276
+ * Function name with skill_ prefix
277
+ */
278
+ name: string
279
+
280
+ /**
281
+ * The skill this binding wraps
282
+ */
283
+ skill: Skill
284
+
285
+ /**
286
+ * Execute function that runs the skill code
287
+ */
288
+ execute: (input: unknown) => Promise<unknown>
289
+ }