@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
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { createDefaultTrustStrategy } from "../trust-strategies.js";
|
|
2
|
+
function createMemorySkillStorage(optionsOrSkills = []) {
|
|
3
|
+
const options = Array.isArray(optionsOrSkills) ? { initialSkills: optionsOrSkills } : optionsOrSkills;
|
|
4
|
+
const { initialSkills = [], trustStrategy = createDefaultTrustStrategy() } = options;
|
|
5
|
+
const skills = /* @__PURE__ */ new Map();
|
|
6
|
+
for (const skill of initialSkills) {
|
|
7
|
+
skills.set(skill.name, skill);
|
|
8
|
+
}
|
|
9
|
+
async function loadIndex() {
|
|
10
|
+
return Array.from(skills.values()).map((skill) => ({
|
|
11
|
+
id: skill.id,
|
|
12
|
+
name: skill.name,
|
|
13
|
+
description: skill.description,
|
|
14
|
+
usageHints: skill.usageHints,
|
|
15
|
+
trustLevel: skill.trustLevel
|
|
16
|
+
}));
|
|
17
|
+
}
|
|
18
|
+
async function loadAll() {
|
|
19
|
+
return Array.from(skills.values());
|
|
20
|
+
}
|
|
21
|
+
async function get(name) {
|
|
22
|
+
return skills.get(name) ?? null;
|
|
23
|
+
}
|
|
24
|
+
async function save(skill) {
|
|
25
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
26
|
+
const existing = skills.get(skill.name);
|
|
27
|
+
const fullSkill = {
|
|
28
|
+
...skill,
|
|
29
|
+
createdAt: existing?.createdAt ?? now,
|
|
30
|
+
updatedAt: now
|
|
31
|
+
};
|
|
32
|
+
skills.set(skill.name, fullSkill);
|
|
33
|
+
return fullSkill;
|
|
34
|
+
}
|
|
35
|
+
async function deleteSkill(name) {
|
|
36
|
+
if (!skills.has(name)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
skills.delete(name);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
async function search(query, options2 = {}) {
|
|
43
|
+
const { limit = 5 } = options2;
|
|
44
|
+
const queryLower = query.toLowerCase();
|
|
45
|
+
const terms = queryLower.split(/\s+/);
|
|
46
|
+
const scored = Array.from(skills.values()).map((skill) => {
|
|
47
|
+
let score = 0;
|
|
48
|
+
const searchText = [skill.name, skill.description, ...skill.usageHints].join(" ").toLowerCase();
|
|
49
|
+
for (const term of terms) {
|
|
50
|
+
if (searchText.includes(term)) {
|
|
51
|
+
score += 1;
|
|
52
|
+
}
|
|
53
|
+
if (skill.name.toLowerCase().includes(term)) {
|
|
54
|
+
score += 2;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { skill, score };
|
|
58
|
+
});
|
|
59
|
+
return scored.filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit).map((s) => ({
|
|
60
|
+
id: s.skill.id,
|
|
61
|
+
name: s.skill.name,
|
|
62
|
+
description: s.skill.description,
|
|
63
|
+
usageHints: s.skill.usageHints,
|
|
64
|
+
trustLevel: s.skill.trustLevel
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
async function updateStats(name, success) {
|
|
68
|
+
const skill = skills.get(name);
|
|
69
|
+
if (!skill) return;
|
|
70
|
+
const { executions, successRate } = skill.stats;
|
|
71
|
+
const newExecutions = executions + 1;
|
|
72
|
+
const newSuccessRate = (successRate * executions + (success ? 1 : 0)) / newExecutions;
|
|
73
|
+
const newStats = { executions: newExecutions, successRate: newSuccessRate };
|
|
74
|
+
const newTrustLevel = trustStrategy.calculateTrustLevel(
|
|
75
|
+
skill.trustLevel,
|
|
76
|
+
newStats
|
|
77
|
+
);
|
|
78
|
+
skills.set(name, {
|
|
79
|
+
...skill,
|
|
80
|
+
stats: newStats,
|
|
81
|
+
trustLevel: newTrustLevel,
|
|
82
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
loadIndex,
|
|
87
|
+
loadAll,
|
|
88
|
+
get,
|
|
89
|
+
save,
|
|
90
|
+
delete: deleteSkill,
|
|
91
|
+
search,
|
|
92
|
+
updateStats,
|
|
93
|
+
trustStrategy
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export {
|
|
97
|
+
createMemorySkillStorage
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=memory-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-storage.js","sources":["../../../src/storage/memory-storage.ts"],"sourcesContent":["import { createDefaultTrustStrategy } from '../trust-strategies'\nimport type {\n Skill,\n SkillIndexEntry,\n SkillSearchOptions,\n SkillStorage,\n} from '../types'\nimport type { TrustStrategy } from '../trust-strategies'\n\nexport interface MemorySkillStorageOptions {\n /**\n * Initial skills to populate the storage with\n */\n initialSkills?: Array<Skill>\n\n /**\n * Trust strategy for determining skill trust levels\n * @default createDefaultTrustStrategy()\n */\n trustStrategy?: TrustStrategy\n}\n\n/**\n * In-memory skill storage for testing and demos\n */\nexport function createMemorySkillStorage(\n optionsOrSkills: MemorySkillStorageOptions | Array<Skill> = [],\n): SkillStorage {\n const options = Array.isArray(optionsOrSkills)\n ? { initialSkills: optionsOrSkills }\n : optionsOrSkills\n\n const { initialSkills = [], trustStrategy = createDefaultTrustStrategy() } =\n options\n\n // Store skills in a Map for O(1) lookup\n const skills = new Map<string, Skill>()\n\n // Initialize with any provided skills\n for (const skill of initialSkills) {\n skills.set(skill.name, skill)\n }\n\n async function loadIndex(): Promise<Array<SkillIndexEntry>> {\n return Array.from(skills.values()).map((skill) => ({\n id: skill.id,\n name: skill.name,\n description: skill.description,\n usageHints: skill.usageHints,\n trustLevel: skill.trustLevel,\n }))\n }\n\n async function loadAll(): Promise<Array<Skill>> {\n return Array.from(skills.values())\n }\n\n async function get(name: string): Promise<Skill | null> {\n return skills.get(name) ?? null\n }\n\n async function save(\n skill: Omit<Skill, 'createdAt' | 'updatedAt'>,\n ): Promise<Skill> {\n const now = new Date().toISOString()\n const existing = skills.get(skill.name)\n\n const fullSkill: Skill = {\n ...skill,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n }\n\n skills.set(skill.name, fullSkill)\n return fullSkill\n }\n\n async function deleteSkill(name: string): Promise<boolean> {\n if (!skills.has(name)) {\n return false\n }\n skills.delete(name)\n return true\n }\n\n async function search(\n query: string,\n options: SkillSearchOptions = {},\n ): Promise<Array<SkillIndexEntry>> {\n const { limit = 5 } = options\n\n // Simple text matching\n const queryLower = query.toLowerCase()\n const terms = queryLower.split(/\\s+/)\n\n const scored = Array.from(skills.values()).map((skill) => {\n let score = 0\n const searchText = [skill.name, skill.description, ...skill.usageHints]\n .join(' ')\n .toLowerCase()\n\n for (const term of terms) {\n if (searchText.includes(term)) {\n score += 1\n }\n // Boost exact name matches\n if (skill.name.toLowerCase().includes(term)) {\n score += 2\n }\n }\n\n return { skill, score }\n })\n\n return scored\n .filter((s) => s.score > 0)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit)\n .map((s) => ({\n id: s.skill.id,\n name: s.skill.name,\n description: s.skill.description,\n usageHints: s.skill.usageHints,\n trustLevel: s.skill.trustLevel,\n }))\n }\n\n async function updateStats(name: string, success: boolean): Promise<void> {\n const skill = skills.get(name)\n if (!skill) return\n\n const { executions, successRate } = skill.stats\n const newExecutions = executions + 1\n const newSuccessRate =\n (successRate * executions + (success ? 1 : 0)) / newExecutions\n\n const newStats = { executions: newExecutions, successRate: newSuccessRate }\n\n // Use trust strategy to calculate new trust level\n const newTrustLevel = trustStrategy.calculateTrustLevel(\n skill.trustLevel,\n newStats,\n )\n\n skills.set(name, {\n ...skill,\n stats: newStats,\n trustLevel: newTrustLevel,\n updatedAt: new Date().toISOString(),\n })\n }\n\n return {\n loadIndex,\n loadAll,\n get,\n save,\n delete: deleteSkill,\n search,\n updateStats,\n trustStrategy,\n }\n}\n"],"names":["options"],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { SkillStats, TrustLevel } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Strategy for determining skill trust levels
|
|
4
|
+
*/
|
|
5
|
+
export interface TrustStrategy {
|
|
6
|
+
/**
|
|
7
|
+
* Get the initial trust level for a newly created skill
|
|
8
|
+
*/
|
|
9
|
+
getInitialTrustLevel: () => TrustLevel;
|
|
10
|
+
/**
|
|
11
|
+
* Calculate the new trust level based on execution stats
|
|
12
|
+
*/
|
|
13
|
+
calculateTrustLevel: (currentLevel: TrustLevel, stats: SkillStats) => TrustLevel;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Default trust strategy - skills must earn trust through successful executions
|
|
17
|
+
*
|
|
18
|
+
* - untrusted: New skill (0 executions)
|
|
19
|
+
* - provisional: 10+ executions with ≥90% success rate
|
|
20
|
+
* - trusted: 100+ executions with ≥95% success rate
|
|
21
|
+
*/
|
|
22
|
+
export declare function createDefaultTrustStrategy(): TrustStrategy;
|
|
23
|
+
/**
|
|
24
|
+
* Always trusted strategy - skills are immediately trusted upon creation
|
|
25
|
+
*
|
|
26
|
+
* Use this for development/testing or when you trust the LLM's code generation
|
|
27
|
+
*/
|
|
28
|
+
export declare function createAlwaysTrustedStrategy(): TrustStrategy;
|
|
29
|
+
/**
|
|
30
|
+
* Relaxed trust strategy - faster trust promotion for development
|
|
31
|
+
*
|
|
32
|
+
* - untrusted: New skill (0 executions)
|
|
33
|
+
* - provisional: 3+ executions with ≥80% success rate
|
|
34
|
+
* - trusted: 10+ executions with ≥90% success rate
|
|
35
|
+
*/
|
|
36
|
+
export declare function createRelaxedTrustStrategy(): TrustStrategy;
|
|
37
|
+
/**
|
|
38
|
+
* Custom trust strategy with configurable thresholds
|
|
39
|
+
*/
|
|
40
|
+
export declare function createCustomTrustStrategy(config: {
|
|
41
|
+
initialLevel?: TrustLevel;
|
|
42
|
+
provisionalThreshold?: {
|
|
43
|
+
executions: number;
|
|
44
|
+
successRate: number;
|
|
45
|
+
};
|
|
46
|
+
trustedThreshold?: {
|
|
47
|
+
executions: number;
|
|
48
|
+
successRate: number;
|
|
49
|
+
};
|
|
50
|
+
}): TrustStrategy;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
function createDefaultTrustStrategy() {
|
|
2
|
+
return {
|
|
3
|
+
getInitialTrustLevel: () => "untrusted",
|
|
4
|
+
calculateTrustLevel: (currentLevel, stats) => {
|
|
5
|
+
const { executions, successRate } = stats;
|
|
6
|
+
if (currentLevel === "untrusted" && executions >= 10 && successRate >= 0.9) {
|
|
7
|
+
return "provisional";
|
|
8
|
+
}
|
|
9
|
+
if (currentLevel === "provisional" && executions >= 100 && successRate >= 0.95) {
|
|
10
|
+
return "trusted";
|
|
11
|
+
}
|
|
12
|
+
return currentLevel;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function createAlwaysTrustedStrategy() {
|
|
17
|
+
return {
|
|
18
|
+
getInitialTrustLevel: () => "trusted",
|
|
19
|
+
calculateTrustLevel: () => "trusted"
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function createRelaxedTrustStrategy() {
|
|
23
|
+
return {
|
|
24
|
+
getInitialTrustLevel: () => "untrusted",
|
|
25
|
+
calculateTrustLevel: (currentLevel, stats) => {
|
|
26
|
+
const { executions, successRate } = stats;
|
|
27
|
+
if (currentLevel === "untrusted" && executions >= 3 && successRate >= 0.8) {
|
|
28
|
+
return "provisional";
|
|
29
|
+
}
|
|
30
|
+
if (currentLevel === "provisional" && executions >= 10 && successRate >= 0.9) {
|
|
31
|
+
return "trusted";
|
|
32
|
+
}
|
|
33
|
+
return currentLevel;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function createCustomTrustStrategy(config) {
|
|
38
|
+
const {
|
|
39
|
+
initialLevel = "untrusted",
|
|
40
|
+
provisionalThreshold = { executions: 10, successRate: 0.9 },
|
|
41
|
+
trustedThreshold = { executions: 100, successRate: 0.95 }
|
|
42
|
+
} = config;
|
|
43
|
+
return {
|
|
44
|
+
getInitialTrustLevel: () => initialLevel,
|
|
45
|
+
calculateTrustLevel: (currentLevel, stats) => {
|
|
46
|
+
const { executions, successRate } = stats;
|
|
47
|
+
if (currentLevel === "untrusted" && executions >= provisionalThreshold.executions && successRate >= provisionalThreshold.successRate) {
|
|
48
|
+
return "provisional";
|
|
49
|
+
}
|
|
50
|
+
if (currentLevel === "provisional" && executions >= trustedThreshold.executions && successRate >= trustedThreshold.successRate) {
|
|
51
|
+
return "trusted";
|
|
52
|
+
}
|
|
53
|
+
return currentLevel;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
createAlwaysTrustedStrategy,
|
|
59
|
+
createCustomTrustStrategy,
|
|
60
|
+
createDefaultTrustStrategy,
|
|
61
|
+
createRelaxedTrustStrategy
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=trust-strategies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trust-strategies.js","sources":["../../src/trust-strategies.ts"],"sourcesContent":["import type { SkillStats, TrustLevel } from './types'\n\n/**\n * Strategy for determining skill trust levels\n */\nexport interface TrustStrategy {\n /**\n * Get the initial trust level for a newly created skill\n */\n getInitialTrustLevel: () => TrustLevel\n\n /**\n * Calculate the new trust level based on execution stats\n */\n calculateTrustLevel: (\n currentLevel: TrustLevel,\n stats: SkillStats,\n ) => TrustLevel\n}\n\n/**\n * Default trust strategy - skills must earn trust through successful executions\n *\n * - untrusted: New skill (0 executions)\n * - provisional: 10+ executions with ≥90% success rate\n * - trusted: 100+ executions with ≥95% success rate\n */\nexport function createDefaultTrustStrategy(): TrustStrategy {\n return {\n getInitialTrustLevel: () => 'untrusted',\n\n calculateTrustLevel: (currentLevel, stats) => {\n const { executions, successRate } = stats\n\n if (\n currentLevel === 'untrusted' &&\n executions >= 10 &&\n successRate >= 0.9\n ) {\n return 'provisional'\n }\n\n if (\n currentLevel === 'provisional' &&\n executions >= 100 &&\n successRate >= 0.95\n ) {\n return 'trusted'\n }\n\n return currentLevel\n },\n }\n}\n\n/**\n * Always trusted strategy - skills are immediately trusted upon creation\n *\n * Use this for development/testing or when you trust the LLM's code generation\n */\nexport function createAlwaysTrustedStrategy(): TrustStrategy {\n return {\n getInitialTrustLevel: () => 'trusted',\n calculateTrustLevel: () => 'trusted',\n }\n}\n\n/**\n * Relaxed trust strategy - faster trust promotion for development\n *\n * - untrusted: New skill (0 executions)\n * - provisional: 3+ executions with ≥80% success rate\n * - trusted: 10+ executions with ≥90% success rate\n */\nexport function createRelaxedTrustStrategy(): TrustStrategy {\n return {\n getInitialTrustLevel: () => 'untrusted',\n\n calculateTrustLevel: (currentLevel, stats) => {\n const { executions, successRate } = stats\n\n if (\n currentLevel === 'untrusted' &&\n executions >= 3 &&\n successRate >= 0.8\n ) {\n return 'provisional'\n }\n\n if (\n currentLevel === 'provisional' &&\n executions >= 10 &&\n successRate >= 0.9\n ) {\n return 'trusted'\n }\n\n return currentLevel\n },\n }\n}\n\n/**\n * Custom trust strategy with configurable thresholds\n */\nexport function createCustomTrustStrategy(config: {\n initialLevel?: TrustLevel\n provisionalThreshold?: { executions: number; successRate: number }\n trustedThreshold?: { executions: number; successRate: number }\n}): TrustStrategy {\n const {\n initialLevel = 'untrusted',\n provisionalThreshold = { executions: 10, successRate: 0.9 },\n trustedThreshold = { executions: 100, successRate: 0.95 },\n } = config\n\n return {\n getInitialTrustLevel: () => initialLevel,\n\n calculateTrustLevel: (currentLevel, stats) => {\n const { executions, successRate } = stats\n\n if (\n currentLevel === 'untrusted' &&\n executions >= provisionalThreshold.executions &&\n successRate >= provisionalThreshold.successRate\n ) {\n return 'provisional'\n }\n\n if (\n currentLevel === 'provisional' &&\n executions >= trustedThreshold.executions &&\n successRate >= trustedThreshold.successRate\n ) {\n return 'trusted'\n }\n\n return currentLevel\n },\n }\n}\n"],"names":[],"mappings":"AA2BO,SAAS,6BAA4C;AAC1D,SAAO;AAAA,IACL,sBAAsB,MAAM;AAAA,IAE5B,qBAAqB,CAAC,cAAc,UAAU;AAC5C,YAAM,EAAE,YAAY,YAAA,IAAgB;AAEpC,UACE,iBAAiB,eACjB,cAAc,MACd,eAAe,KACf;AACA,eAAO;AAAA,MACT;AAEA,UACE,iBAAiB,iBACjB,cAAc,OACd,eAAe,MACf;AACA,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT;AAAA,EAAA;AAEJ;AAOO,SAAS,8BAA6C;AAC3D,SAAO;AAAA,IACL,sBAAsB,MAAM;AAAA,IAC5B,qBAAqB,MAAM;AAAA,EAAA;AAE/B;AASO,SAAS,6BAA4C;AAC1D,SAAO;AAAA,IACL,sBAAsB,MAAM;AAAA,IAE5B,qBAAqB,CAAC,cAAc,UAAU;AAC5C,YAAM,EAAE,YAAY,YAAA,IAAgB;AAEpC,UACE,iBAAiB,eACjB,cAAc,KACd,eAAe,KACf;AACA,eAAO;AAAA,MACT;AAEA,UACE,iBAAiB,iBACjB,cAAc,MACd,eAAe,KACf;AACA,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT;AAAA,EAAA;AAEJ;AAKO,SAAS,0BAA0B,QAIxB;AAChB,QAAM;AAAA,IACJ,eAAe;AAAA,IACf,uBAAuB,EAAE,YAAY,IAAI,aAAa,IAAA;AAAA,IACtD,mBAAmB,EAAE,YAAY,KAAK,aAAa,KAAA;AAAA,EAAK,IACtD;AAEJ,SAAO;AAAA,IACL,sBAAsB,MAAM;AAAA,IAE5B,qBAAqB,CAAC,cAAc,UAAU;AAC5C,YAAM,EAAE,YAAY,YAAA,IAAgB;AAEpC,UACE,iBAAiB,eACjB,cAAc,qBAAqB,cACnC,eAAe,qBAAqB,aACpC;AACA,eAAO;AAAA,MACT;AAEA,UACE,iBAAiB,iBACjB,cAAc,iBAAiB,cAC/B,eAAe,iBAAiB,aAChC;AACA,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT;AAAA,EAAA;AAEJ;"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { AnyTextAdapter, ModelMessage, ToolRegistry } from '@tanstack/ai';
|
|
2
|
+
import { CodeModeToolConfig } from '@tanstack/ai-code-mode';
|
|
3
|
+
import { TrustStrategy } from './trust-strategies.js';
|
|
4
|
+
/**
|
|
5
|
+
* Trust level for a skill
|
|
6
|
+
* - untrusted: Newly created, not yet proven
|
|
7
|
+
* - provisional: Has been successfully executed 10+ times with 90%+ success
|
|
8
|
+
* - trusted: Has been successfully executed 100+ times with 95%+ success
|
|
9
|
+
*/
|
|
10
|
+
export type TrustLevel = 'untrusted' | 'provisional' | 'trusted';
|
|
11
|
+
/**
|
|
12
|
+
* Execution statistics for a skill
|
|
13
|
+
*/
|
|
14
|
+
export interface SkillStats {
|
|
15
|
+
/**
|
|
16
|
+
* Total number of times this skill has been executed
|
|
17
|
+
*/
|
|
18
|
+
executions: number;
|
|
19
|
+
/**
|
|
20
|
+
* Success rate (0-1) based on execution history
|
|
21
|
+
*/
|
|
22
|
+
successRate: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A reusable skill that can be executed in the Code Mode sandbox
|
|
26
|
+
*/
|
|
27
|
+
export interface Skill {
|
|
28
|
+
/**
|
|
29
|
+
* Unique identifier for the skill
|
|
30
|
+
*/
|
|
31
|
+
id: string;
|
|
32
|
+
/**
|
|
33
|
+
* Unique name in snake_case (e.g., 'fetch_github_stats')
|
|
34
|
+
* This becomes the function name with skill_ prefix in the sandbox
|
|
35
|
+
*/
|
|
36
|
+
name: string;
|
|
37
|
+
/**
|
|
38
|
+
* Human-readable description of what the skill does
|
|
39
|
+
*/
|
|
40
|
+
description: string;
|
|
41
|
+
/**
|
|
42
|
+
* TypeScript code that implements the skill
|
|
43
|
+
* The code receives `input` as a variable and can call:
|
|
44
|
+
* - external_* functions (tools)
|
|
45
|
+
* - other skill_* functions (skills)
|
|
46
|
+
* Should return a value
|
|
47
|
+
*/
|
|
48
|
+
code: string;
|
|
49
|
+
/**
|
|
50
|
+
* JSON Schema describing the input parameter
|
|
51
|
+
*/
|
|
52
|
+
inputSchema: Record<string, unknown>;
|
|
53
|
+
/**
|
|
54
|
+
* JSON Schema describing the return value
|
|
55
|
+
*/
|
|
56
|
+
outputSchema: Record<string, unknown>;
|
|
57
|
+
/**
|
|
58
|
+
* Hints about when to use this skill
|
|
59
|
+
* e.g., "Use when comparing NPM package popularity"
|
|
60
|
+
*/
|
|
61
|
+
usageHints: Array<string>;
|
|
62
|
+
/**
|
|
63
|
+
* Names of other skills this skill depends on/calls
|
|
64
|
+
*/
|
|
65
|
+
dependsOn: Array<string>;
|
|
66
|
+
/**
|
|
67
|
+
* Trust level based on execution history
|
|
68
|
+
*/
|
|
69
|
+
trustLevel: TrustLevel;
|
|
70
|
+
/**
|
|
71
|
+
* Execution statistics
|
|
72
|
+
*/
|
|
73
|
+
stats: SkillStats;
|
|
74
|
+
/**
|
|
75
|
+
* ISO timestamp when the skill was created
|
|
76
|
+
*/
|
|
77
|
+
createdAt: string;
|
|
78
|
+
/**
|
|
79
|
+
* ISO timestamp when the skill was last updated
|
|
80
|
+
*/
|
|
81
|
+
updatedAt: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Lightweight skill entry for the index (metadata only, no code)
|
|
85
|
+
* Used for fast loading and skill selection
|
|
86
|
+
*/
|
|
87
|
+
export type SkillIndexEntry = Pick<Skill, 'id' | 'name' | 'description' | 'usageHints' | 'trustLevel'>;
|
|
88
|
+
/**
|
|
89
|
+
* Options for searching skills
|
|
90
|
+
*/
|
|
91
|
+
export interface SkillSearchOptions {
|
|
92
|
+
/**
|
|
93
|
+
* Maximum number of results to return
|
|
94
|
+
* @default 5
|
|
95
|
+
*/
|
|
96
|
+
limit?: number;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Interface for skill storage implementations
|
|
100
|
+
*/
|
|
101
|
+
export interface SkillStorage {
|
|
102
|
+
/**
|
|
103
|
+
* Load the skill index (lightweight metadata for all skills)
|
|
104
|
+
*/
|
|
105
|
+
loadIndex: () => Promise<Array<SkillIndexEntry>>;
|
|
106
|
+
/**
|
|
107
|
+
* Load all skills with full details (including code)
|
|
108
|
+
*/
|
|
109
|
+
loadAll: () => Promise<Array<Skill>>;
|
|
110
|
+
/**
|
|
111
|
+
* Get a skill by name
|
|
112
|
+
*/
|
|
113
|
+
get: (name: string) => Promise<Skill | null>;
|
|
114
|
+
/**
|
|
115
|
+
* Save a skill (create or update)
|
|
116
|
+
*/
|
|
117
|
+
save: (skill: Omit<Skill, 'createdAt' | 'updatedAt'>) => Promise<Skill>;
|
|
118
|
+
/**
|
|
119
|
+
* Delete a skill by name
|
|
120
|
+
*/
|
|
121
|
+
delete: (name: string) => Promise<boolean>;
|
|
122
|
+
/**
|
|
123
|
+
* Search for skills by query
|
|
124
|
+
*/
|
|
125
|
+
search: (query: string, options?: SkillSearchOptions) => Promise<Array<SkillIndexEntry>>;
|
|
126
|
+
/**
|
|
127
|
+
* Update execution statistics for a skill
|
|
128
|
+
*/
|
|
129
|
+
updateStats: (name: string, success: boolean) => Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Trust strategy used by this storage (optional, for creating new skills)
|
|
132
|
+
*/
|
|
133
|
+
trustStrategy?: TrustStrategy;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Configuration for the skills system
|
|
137
|
+
*/
|
|
138
|
+
export interface SkillsConfig {
|
|
139
|
+
/**
|
|
140
|
+
* Storage implementation for skills
|
|
141
|
+
*/
|
|
142
|
+
storage: SkillStorage;
|
|
143
|
+
/**
|
|
144
|
+
* Maximum number of skills to load into context per request
|
|
145
|
+
* @default 5
|
|
146
|
+
*/
|
|
147
|
+
maxSkillsInContext?: number;
|
|
148
|
+
/**
|
|
149
|
+
* Trust strategy for determining skill trust levels
|
|
150
|
+
* @default createDefaultTrustStrategy()
|
|
151
|
+
*/
|
|
152
|
+
trustStrategy?: TrustStrategy;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Options for codeModeWithSkills
|
|
156
|
+
*/
|
|
157
|
+
export interface CodeModeWithSkillsOptions {
|
|
158
|
+
/**
|
|
159
|
+
* Code Mode tool configuration (driver, tools, timeout, memoryLimit)
|
|
160
|
+
*/
|
|
161
|
+
config: CodeModeToolConfig;
|
|
162
|
+
/**
|
|
163
|
+
* Text adapter for skill selection (should be a cheap/fast model)
|
|
164
|
+
*/
|
|
165
|
+
adapter: AnyTextAdapter;
|
|
166
|
+
/**
|
|
167
|
+
* Skills configuration
|
|
168
|
+
*/
|
|
169
|
+
skills: SkillsConfig;
|
|
170
|
+
/**
|
|
171
|
+
* Current conversation messages (used for context-aware skill selection)
|
|
172
|
+
*/
|
|
173
|
+
messages: Array<ModelMessage>;
|
|
174
|
+
/**
|
|
175
|
+
* Whether to include skills as direct tools (not just sandbox bindings).
|
|
176
|
+
* When true, skills become first-class tools the LLM can call directly.
|
|
177
|
+
* @default true
|
|
178
|
+
*/
|
|
179
|
+
skillsAsTools?: boolean;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Result from codeModeWithSkills
|
|
183
|
+
*/
|
|
184
|
+
export interface CodeModeWithSkillsResult {
|
|
185
|
+
/**
|
|
186
|
+
* Tool registry for dynamic tool management.
|
|
187
|
+
* Pass this to chat() via the toolRegistry option.
|
|
188
|
+
* Skills registered mid-stream will be added to this registry.
|
|
189
|
+
*/
|
|
190
|
+
toolsRegistry: ToolRegistry;
|
|
191
|
+
/**
|
|
192
|
+
* System prompt documenting available skills and external functions
|
|
193
|
+
*/
|
|
194
|
+
systemPrompt: string;
|
|
195
|
+
/**
|
|
196
|
+
* Skills that were selected for this request
|
|
197
|
+
*/
|
|
198
|
+
selectedSkills: Array<Skill>;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* A skill transformed into a format suitable for sandbox injection
|
|
202
|
+
*/
|
|
203
|
+
export interface SkillBinding {
|
|
204
|
+
/**
|
|
205
|
+
* Function name with skill_ prefix
|
|
206
|
+
*/
|
|
207
|
+
name: string;
|
|
208
|
+
/**
|
|
209
|
+
* The skill this binding wraps
|
|
210
|
+
*/
|
|
211
|
+
skill: Skill;
|
|
212
|
+
/**
|
|
213
|
+
* Execute function that runs the skill code
|
|
214
|
+
*/
|
|
215
|
+
execute: (input: unknown) => Promise<unknown>;
|
|
216
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tanstack/ai-code-mode-skills",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Persistent skill library for TanStack AI Code Mode - LLM-created reusable code snippets",
|
|
5
|
+
"author": "",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/TanStack/ai.git",
|
|
13
|
+
"directory": "packages/typescript/ai-code-mode-skills"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"module": "./dist/esm/index.js",
|
|
17
|
+
"types": "./dist/esm/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/esm/index.d.ts",
|
|
21
|
+
"import": "./dist/esm/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./storage": {
|
|
24
|
+
"types": "./dist/esm/storage/index.d.ts",
|
|
25
|
+
"import": "./dist/esm/storage/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"src"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "vite build",
|
|
38
|
+
"clean": "premove ./build ./dist",
|
|
39
|
+
"lint:fix": "eslint ./src --fix",
|
|
40
|
+
"test:build": "publint --strict",
|
|
41
|
+
"test:eslint": "eslint ./src",
|
|
42
|
+
"test:lib": "vitest --passWithNoTests",
|
|
43
|
+
"test:lib:dev": "pnpm test:lib --watch",
|
|
44
|
+
"test:types": "tsc",
|
|
45
|
+
"test:cli": "tsx test-cli/cli.ts",
|
|
46
|
+
"test:cli:run": "tsx test-cli/cli.ts run",
|
|
47
|
+
"test:cli:run:all": "tsx test-cli/cli.ts run --adapters openai,anthropic,gemini",
|
|
48
|
+
"test:cli:list": "tsx test-cli/cli.ts list",
|
|
49
|
+
"test:cli:simulated": "tsx test-cli/cli.ts simulated",
|
|
50
|
+
"test:cli:registry": "tsx test-cli/cli.ts registry",
|
|
51
|
+
"test:cli:live": "tsx test-cli/cli.ts live",
|
|
52
|
+
"test:cli:structured": "tsx test-cli/cli.ts structured"
|
|
53
|
+
},
|
|
54
|
+
"keywords": [
|
|
55
|
+
"ai",
|
|
56
|
+
"tanstack",
|
|
57
|
+
"code-mode",
|
|
58
|
+
"skills",
|
|
59
|
+
"llm",
|
|
60
|
+
"sandbox"
|
|
61
|
+
],
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"@tanstack/ai": "workspace:*",
|
|
64
|
+
"@tanstack/ai-code-mode": "workspace:*"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"@tanstack/ai": "workspace:*",
|
|
68
|
+
"@tanstack/ai-code-mode": "workspace:*",
|
|
69
|
+
"zod": "^3.0.0 || ^4.0.0"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@tanstack/ai-anthropic": "workspace:*",
|
|
73
|
+
"@tanstack/ai-gemini": "workspace:*",
|
|
74
|
+
"@tanstack/ai-isolate-node": "workspace:*",
|
|
75
|
+
"@tanstack/ai-openai": "workspace:*",
|
|
76
|
+
"@vitest/coverage-v8": "4.0.14",
|
|
77
|
+
"commander": "^13.1.0",
|
|
78
|
+
"dotenv": "^17.2.3",
|
|
79
|
+
"tsx": "^4.21.0",
|
|
80
|
+
"zod": "^4.2.0"
|
|
81
|
+
}
|
|
82
|
+
}
|