@tanstack/ai-code-mode-snippets 0.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +208 -0
  3. package/dist/esm/code-mode-with-snippets.d.ts +65 -0
  4. package/dist/esm/code-mode-with-snippets.js +145 -0
  5. package/dist/esm/code-mode-with-snippets.js.map +1 -0
  6. package/dist/esm/create-snippet-management-tools.d.ts +40 -0
  7. package/dist/esm/create-snippet-management-tools.js +173 -0
  8. package/dist/esm/create-snippet-management-tools.js.map +1 -0
  9. package/dist/esm/create-snippets-system-prompt.d.ts +22 -0
  10. package/dist/esm/create-snippets-system-prompt.js +234 -0
  11. package/dist/esm/create-snippets-system-prompt.js.map +1 -0
  12. package/dist/esm/generate-snippet-types.d.ts +7 -0
  13. package/dist/esm/generate-snippet-types.js +79 -0
  14. package/dist/esm/generate-snippet-types.js.map +1 -0
  15. package/dist/esm/index.d.ts +14 -0
  16. package/dist/esm/index.js +10 -0
  17. package/dist/esm/select-relevant-snippets.d.ts +29 -0
  18. package/dist/esm/select-relevant-snippets.js +56 -0
  19. package/dist/esm/select-relevant-snippets.js.map +1 -0
  20. package/dist/esm/snippets-to-bindings.d.ts +34 -0
  21. package/dist/esm/snippets-to-bindings.js +74 -0
  22. package/dist/esm/snippets-to-bindings.js.map +1 -0
  23. package/dist/esm/snippets-to-tools.d.ts +74 -0
  24. package/dist/esm/snippets-to-tools.js +147 -0
  25. package/dist/esm/snippets-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 +155 -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 +3 -0
  31. package/dist/esm/storage/memory-storage.d.ts +17 -0
  32. package/dist/esm/storage/memory-storage.js +100 -0
  33. package/dist/esm/storage/memory-storage.js.map +1 -0
  34. package/dist/esm/trust-strategies.d.ts +50 -0
  35. package/dist/esm/trust-strategies.js +73 -0
  36. package/dist/esm/trust-strategies.js.map +1 -0
  37. package/dist/esm/types.d.ts +216 -0
  38. package/package.json +92 -0
  39. package/src/code-mode-with-snippets.ts +210 -0
  40. package/src/create-snippet-management-tools.ts +298 -0
  41. package/src/create-snippets-system-prompt.ts +289 -0
  42. package/src/generate-snippet-types.ts +162 -0
  43. package/src/index.ts +60 -0
  44. package/src/select-relevant-snippets.ts +136 -0
  45. package/src/snippets-to-bindings.ts +135 -0
  46. package/src/snippets-to-tools.ts +325 -0
  47. package/src/storage/file-storage.ts +275 -0
  48. package/src/storage/index.ts +6 -0
  49. package/src/storage/memory-storage.ts +172 -0
  50. package/src/trust-strategies.ts +142 -0
  51. package/src/types.ts +289 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-storage.js","names":[],"sources":["../../../src/storage/memory-storage.ts"],"sourcesContent":["import { createDefaultTrustStrategy } from '../trust-strategies'\nimport type {\n Snippet,\n SnippetIndexEntry,\n SnippetSearchOptions,\n SnippetStorage,\n} from '../types'\nimport type { TrustStrategy } from '../trust-strategies'\n\nexport interface MemorySnippetStorageOptions {\n /**\n * Initial snippets to populate the storage with\n */\n initialSnippets?: Array<Snippet>\n\n /**\n * Trust strategy for determining snippet trust levels\n * @default createDefaultTrustStrategy()\n */\n trustStrategy?: TrustStrategy\n}\n\n/**\n * In-memory snippet storage for testing and demos\n */\nexport function createMemorySnippetStorage(\n optionsOrSnippets: MemorySnippetStorageOptions | Array<Snippet> = [],\n): SnippetStorage {\n const options = Array.isArray(optionsOrSnippets)\n ? { initialSnippets: optionsOrSnippets }\n : optionsOrSnippets\n\n const { initialSnippets = [], trustStrategy = createDefaultTrustStrategy() } =\n options\n\n // Store snippets in a Map for O(1) lookup\n const snippets = new Map<string, Snippet>()\n\n // Initialize with any provided snippets\n for (const snippet of initialSnippets) {\n snippets.set(snippet.name, snippet)\n }\n\n function loadIndex(): Promise<Array<SnippetIndexEntry>> {\n return Promise.resolve(\n Array.from(snippets.values()).map((snippet) => ({\n id: snippet.id,\n name: snippet.name,\n description: snippet.description,\n usageHints: snippet.usageHints,\n trustLevel: snippet.trustLevel,\n })),\n )\n }\n\n function loadAll(): Promise<Array<Snippet>> {\n return Promise.resolve(Array.from(snippets.values()))\n }\n\n function get(name: string): Promise<Snippet | null> {\n return Promise.resolve(snippets.get(name) ?? null)\n }\n\n function save(\n snippet: Omit<Snippet, 'createdAt' | 'updatedAt'>,\n ): Promise<Snippet> {\n const now = new Date().toISOString()\n const existing = snippets.get(snippet.name)\n\n const fullSnippet: Snippet = {\n ...snippet,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n }\n\n snippets.set(snippet.name, fullSnippet)\n return Promise.resolve(fullSnippet)\n }\n\n function deleteSnippet(name: string): Promise<boolean> {\n if (!snippets.has(name)) {\n return Promise.resolve(false)\n }\n snippets.delete(name)\n return Promise.resolve(true)\n }\n\n function search(\n query: string,\n searchOptions: SnippetSearchOptions = {},\n ): Promise<Array<SnippetIndexEntry>> {\n const { limit = 5 } = searchOptions\n\n // Simple text matching\n const queryLower = query.toLowerCase()\n const terms = queryLower.split(/\\s+/)\n\n const scored = Array.from(snippets.values()).map((snippet) => {\n let score = 0\n const searchText = [\n snippet.name,\n snippet.description,\n ...snippet.usageHints,\n ]\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 (snippet.name.toLowerCase().includes(term)) {\n score += 2\n }\n }\n\n return { snippet, score }\n })\n\n return Promise.resolve(\n 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.snippet.id,\n name: s.snippet.name,\n description: s.snippet.description,\n usageHints: s.snippet.usageHints,\n trustLevel: s.snippet.trustLevel,\n })),\n )\n }\n\n function updateStats(name: string, success: boolean): Promise<void> {\n const snippet = snippets.get(name)\n if (!snippet) return Promise.resolve()\n\n const { executions, successRate } = snippet.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 snippet.trustLevel,\n newStats,\n )\n\n snippets.set(name, {\n ...snippet,\n stats: newStats,\n trustLevel: newTrustLevel,\n updatedAt: new Date().toISOString(),\n })\n return Promise.resolve()\n }\n\n return {\n loadIndex,\n loadAll,\n get,\n save,\n delete: deleteSnippet,\n search,\n updateStats,\n trustStrategy,\n }\n}\n"],"mappings":";;;;;AAyBA,SAAgB,2BACd,oBAAkE,CAAC,GACnD;CAKhB,MAAM,EAAE,kBAAkB,CAAC,GAAG,gBAAgB,2BAA2B,MAJzD,MAAM,QAAQ,iBAAiB,IAC3C,EAAE,iBAAiB,kBAAkB,IACrC;CAMJ,MAAM,2BAAW,IAAI,IAAqB;CAG1C,KAAK,MAAM,WAAW,iBACpB,SAAS,IAAI,QAAQ,MAAM,OAAO;CAGpC,SAAS,YAA+C;EACtD,OAAO,QAAQ,QACb,MAAM,KAAK,SAAS,OAAO,CAAC,CAAC,CAAC,KAAK,aAAa;GAC9C,IAAI,QAAQ;GACZ,MAAM,QAAQ;GACd,aAAa,QAAQ;GACrB,YAAY,QAAQ;GACpB,YAAY,QAAQ;EACtB,EAAE,CACJ;CACF;CAEA,SAAS,UAAmC;EAC1C,OAAO,QAAQ,QAAQ,MAAM,KAAK,SAAS,OAAO,CAAC,CAAC;CACtD;CAEA,SAAS,IAAI,MAAuC;EAClD,OAAO,QAAQ,QAAQ,SAAS,IAAI,IAAI,KAAK,IAAI;CACnD;CAEA,SAAS,KACP,SACkB;EAClB,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,MAAM,WAAW,SAAS,IAAI,QAAQ,IAAI;EAE1C,MAAM,cAAuB;GAC3B,GAAG;GACH,WAAW,UAAU,aAAa;GAClC,WAAW;EACb;EAEA,SAAS,IAAI,QAAQ,MAAM,WAAW;EACtC,OAAO,QAAQ,QAAQ,WAAW;CACpC;CAEA,SAAS,cAAc,MAAgC;EACrD,IAAI,CAAC,SAAS,IAAI,IAAI,GACpB,OAAO,QAAQ,QAAQ,KAAK;EAE9B,SAAS,OAAO,IAAI;EACpB,OAAO,QAAQ,QAAQ,IAAI;CAC7B;CAEA,SAAS,OACP,OACA,gBAAsC,CAAC,GACJ;EACnC,MAAM,EAAE,QAAQ,MAAM;EAItB,MAAM,QADa,MAAM,YACX,CAAA,CAAW,MAAM,KAAK;EAEpC,MAAM,SAAS,MAAM,KAAK,SAAS,OAAO,CAAC,CAAC,CAAC,KAAK,YAAY;GAC5D,IAAI,QAAQ;GACZ,MAAM,aAAa;IACjB,QAAQ;IACR,QAAQ;IACR,GAAG,QAAQ;GACb,CAAC,CACE,KAAK,GAAG,CAAC,CACT,YAAY;GAEf,KAAK,MAAM,QAAQ,OAAO;IACxB,IAAI,WAAW,SAAS,IAAI,GAC1B,SAAS;IAGX,IAAI,QAAQ,KAAK,YAAY,CAAC,CAAC,SAAS,IAAI,GAC1C,SAAS;GAEb;GAEA,OAAO;IAAE;IAAS;GAAM;EAC1B,CAAC;EAED,OAAO,QAAQ,QACb,OACG,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC,CAC1B,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CACjC,MAAM,GAAG,KAAK,CAAC,CACf,KAAK,OAAO;GACX,IAAI,EAAE,QAAQ;GACd,MAAM,EAAE,QAAQ;GAChB,aAAa,EAAE,QAAQ;GACvB,YAAY,EAAE,QAAQ;GACtB,YAAY,EAAE,QAAQ;EACxB,EAAE,CACN;CACF;CAEA,SAAS,YAAY,MAAc,SAAiC;EAClE,MAAM,UAAU,SAAS,IAAI,IAAI;EACjC,IAAI,CAAC,SAAS,OAAO,QAAQ,QAAQ;EAErC,MAAM,EAAE,YAAY,gBAAgB,QAAQ;EAC5C,MAAM,gBAAgB,aAAa;EAInC,MAAM,WAAW;GAAE,YAAY;GAAe,cAF3C,cAAc,cAAc,UAAU,IAAI,MAAM;EAEuB;EAG1E,MAAM,gBAAgB,cAAc,oBAClC,QAAQ,YACR,QACF;EAEA,SAAS,IAAI,MAAM;GACjB,GAAG;GACH,OAAO;GACP,YAAY;GACZ,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC,CAAC;EACD,OAAO,QAAQ,QAAQ;CACzB;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;CACF;AACF"}
@@ -0,0 +1,50 @@
1
+ import { SnippetStats, TrustLevel } from './types.js';
2
+ /**
3
+ * Strategy for determining snippet trust levels
4
+ */
5
+ export interface TrustStrategy {
6
+ /**
7
+ * Get the initial trust level for a newly created snippet
8
+ */
9
+ getInitialTrustLevel: () => TrustLevel;
10
+ /**
11
+ * Calculate the new trust level based on execution stats
12
+ */
13
+ calculateTrustLevel: (currentLevel: TrustLevel, stats: SnippetStats) => TrustLevel;
14
+ }
15
+ /**
16
+ * Default trust strategy - snippets must earn trust through successful executions
17
+ *
18
+ * - untrusted: New snippet (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 - snippets 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 snippet (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,73 @@
1
+ //#region src/trust-strategies.ts
2
+ /**
3
+ * Default trust strategy - snippets must earn trust through successful executions
4
+ *
5
+ * - untrusted: New snippet (0 executions)
6
+ * - provisional: 10+ executions with ≥90% success rate
7
+ * - trusted: 100+ executions with ≥95% success rate
8
+ */
9
+ function createDefaultTrustStrategy() {
10
+ return {
11
+ getInitialTrustLevel: () => "untrusted",
12
+ calculateTrustLevel: (currentLevel, stats) => {
13
+ const { executions, successRate } = stats;
14
+ if (currentLevel === "untrusted" && executions >= 10 && successRate >= .9) return "provisional";
15
+ if (currentLevel === "provisional" && executions >= 100 && successRate >= .95) return "trusted";
16
+ return currentLevel;
17
+ }
18
+ };
19
+ }
20
+ /**
21
+ * Always trusted strategy - snippets are immediately trusted upon creation
22
+ *
23
+ * Use this for development/testing or when you trust the LLM's code generation
24
+ */
25
+ function createAlwaysTrustedStrategy() {
26
+ return {
27
+ getInitialTrustLevel: () => "trusted",
28
+ calculateTrustLevel: () => "trusted"
29
+ };
30
+ }
31
+ /**
32
+ * Relaxed trust strategy - faster trust promotion for development
33
+ *
34
+ * - untrusted: New snippet (0 executions)
35
+ * - provisional: 3+ executions with ≥80% success rate
36
+ * - trusted: 10+ executions with ≥90% success rate
37
+ */
38
+ function createRelaxedTrustStrategy() {
39
+ return {
40
+ getInitialTrustLevel: () => "untrusted",
41
+ calculateTrustLevel: (currentLevel, stats) => {
42
+ const { executions, successRate } = stats;
43
+ if (currentLevel === "untrusted" && executions >= 3 && successRate >= .8) return "provisional";
44
+ if (currentLevel === "provisional" && executions >= 10 && successRate >= .9) return "trusted";
45
+ return currentLevel;
46
+ }
47
+ };
48
+ }
49
+ /**
50
+ * Custom trust strategy with configurable thresholds
51
+ */
52
+ function createCustomTrustStrategy(config) {
53
+ const { initialLevel = "untrusted", provisionalThreshold = {
54
+ executions: 10,
55
+ successRate: .9
56
+ }, trustedThreshold = {
57
+ executions: 100,
58
+ successRate: .95
59
+ } } = config;
60
+ return {
61
+ getInitialTrustLevel: () => initialLevel,
62
+ calculateTrustLevel: (currentLevel, stats) => {
63
+ const { executions, successRate } = stats;
64
+ if (currentLevel === "untrusted" && executions >= provisionalThreshold.executions && successRate >= provisionalThreshold.successRate) return "provisional";
65
+ if (currentLevel === "provisional" && executions >= trustedThreshold.executions && successRate >= trustedThreshold.successRate) return "trusted";
66
+ return currentLevel;
67
+ }
68
+ };
69
+ }
70
+ //#endregion
71
+ export { createAlwaysTrustedStrategy, createCustomTrustStrategy, createDefaultTrustStrategy, createRelaxedTrustStrategy };
72
+
73
+ //# sourceMappingURL=trust-strategies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-strategies.js","names":[],"sources":["../../src/trust-strategies.ts"],"sourcesContent":["import type { SnippetStats, TrustLevel } from './types'\n\n/**\n * Strategy for determining snippet trust levels\n */\nexport interface TrustStrategy {\n /**\n * Get the initial trust level for a newly created snippet\n */\n getInitialTrustLevel: () => TrustLevel\n\n /**\n * Calculate the new trust level based on execution stats\n */\n calculateTrustLevel: (\n currentLevel: TrustLevel,\n stats: SnippetStats,\n ) => TrustLevel\n}\n\n/**\n * Default trust strategy - snippets must earn trust through successful executions\n *\n * - untrusted: New snippet (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 - snippets 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 snippet (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"],"mappings":";;;;;;;;AA2BA,SAAgB,6BAA4C;CAC1D,OAAO;EACL,4BAA4B;EAE5B,sBAAsB,cAAc,UAAU;GAC5C,MAAM,EAAE,YAAY,gBAAgB;GAEpC,IACE,iBAAiB,eACjB,cAAc,MACd,eAAe,IAEf,OAAO;GAGT,IACE,iBAAiB,iBACjB,cAAc,OACd,eAAe,KAEf,OAAO;GAGT,OAAO;EACT;CACF;AACF;;;;;;AAOA,SAAgB,8BAA6C;CAC3D,OAAO;EACL,4BAA4B;EAC5B,2BAA2B;CAC7B;AACF;;;;;;;;AASA,SAAgB,6BAA4C;CAC1D,OAAO;EACL,4BAA4B;EAE5B,sBAAsB,cAAc,UAAU;GAC5C,MAAM,EAAE,YAAY,gBAAgB;GAEpC,IACE,iBAAiB,eACjB,cAAc,KACd,eAAe,IAEf,OAAO;GAGT,IACE,iBAAiB,iBACjB,cAAc,MACd,eAAe,IAEf,OAAO;GAGT,OAAO;EACT;CACF;AACF;;;;AAKA,SAAgB,0BAA0B,QAIxB;CAChB,MAAM,EACJ,eAAe,aACf,uBAAuB;EAAE,YAAY;EAAI,aAAa;CAAI,GAC1D,mBAAmB;EAAE,YAAY;EAAK,aAAa;CAAK,MACtD;CAEJ,OAAO;EACL,4BAA4B;EAE5B,sBAAsB,cAAc,UAAU;GAC5C,MAAM,EAAE,YAAY,gBAAgB;GAEpC,IACE,iBAAiB,eACjB,cAAc,qBAAqB,cACnC,eAAe,qBAAqB,aAEpC,OAAO;GAGT,IACE,iBAAiB,iBACjB,cAAc,iBAAiB,cAC/B,eAAe,iBAAiB,aAEhC,OAAO;GAGT,OAAO;EACT;CACF;AACF"}
@@ -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 snippet
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 snippet
13
+ */
14
+ export interface SnippetStats {
15
+ /**
16
+ * Total number of times this snippet 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 snippet that can be executed in the Code Mode sandbox
26
+ */
27
+ export interface Snippet {
28
+ /**
29
+ * Unique identifier for the snippet
30
+ */
31
+ id: string;
32
+ /**
33
+ * Unique name in snake_case (e.g., 'fetch_github_stats')
34
+ * This becomes the function name with snippet_ prefix in the sandbox
35
+ */
36
+ name: string;
37
+ /**
38
+ * Human-readable description of what the snippet does
39
+ */
40
+ description: string;
41
+ /**
42
+ * TypeScript code that implements the snippet
43
+ * The code receives `input` as a variable and can call:
44
+ * - external_* functions (tools)
45
+ * - other snippet_* functions (snippets)
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 snippet
59
+ * e.g., "Use when comparing NPM package popularity"
60
+ */
61
+ usageHints: Array<string>;
62
+ /**
63
+ * Names of other snippets this snippet 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: SnippetStats;
74
+ /**
75
+ * ISO timestamp when the snippet was created
76
+ */
77
+ createdAt: string;
78
+ /**
79
+ * ISO timestamp when the snippet was last updated
80
+ */
81
+ updatedAt: string;
82
+ }
83
+ /**
84
+ * Lightweight snippet entry for the index (metadata only, no code)
85
+ * Used for fast loading and snippet selection
86
+ */
87
+ export type SnippetIndexEntry = Pick<Snippet, 'id' | 'name' | 'description' | 'usageHints' | 'trustLevel'>;
88
+ /**
89
+ * Options for searching snippets
90
+ */
91
+ export interface SnippetSearchOptions {
92
+ /**
93
+ * Maximum number of results to return
94
+ * @default 5
95
+ */
96
+ limit?: number;
97
+ }
98
+ /**
99
+ * Interface for snippet storage implementations
100
+ */
101
+ export interface SnippetStorage {
102
+ /**
103
+ * Load the snippet index (lightweight metadata for all snippets)
104
+ */
105
+ loadIndex: () => Promise<Array<SnippetIndexEntry>>;
106
+ /**
107
+ * Load all snippets with full details (including code)
108
+ */
109
+ loadAll: () => Promise<Array<Snippet>>;
110
+ /**
111
+ * Get a snippet by name
112
+ */
113
+ get: (name: string) => Promise<Snippet | null>;
114
+ /**
115
+ * Save a snippet (create or update)
116
+ */
117
+ save: (snippet: Omit<Snippet, 'createdAt' | 'updatedAt'>) => Promise<Snippet>;
118
+ /**
119
+ * Delete a snippet by name
120
+ */
121
+ delete: (name: string) => Promise<boolean>;
122
+ /**
123
+ * Search for snippets by query
124
+ */
125
+ search: (query: string, options?: SnippetSearchOptions) => Promise<Array<SnippetIndexEntry>>;
126
+ /**
127
+ * Update execution statistics for a snippet
128
+ */
129
+ updateStats: (name: string, success: boolean) => Promise<void>;
130
+ /**
131
+ * Trust strategy used by this storage (optional, for creating new snippets)
132
+ */
133
+ trustStrategy?: TrustStrategy;
134
+ }
135
+ /**
136
+ * Configuration for the snippets system
137
+ */
138
+ export interface SnippetsConfig {
139
+ /**
140
+ * Storage implementation for snippets
141
+ */
142
+ storage: SnippetStorage;
143
+ /**
144
+ * Maximum number of snippets to load into context per request
145
+ * @default 5
146
+ */
147
+ maxSnippetsInContext?: number;
148
+ /**
149
+ * Trust strategy for determining snippet trust levels
150
+ * @default createDefaultTrustStrategy()
151
+ */
152
+ trustStrategy?: TrustStrategy;
153
+ }
154
+ /**
155
+ * Options for codeModeWithSnippets
156
+ */
157
+ export interface CodeModeWithSnippetsOptions {
158
+ /**
159
+ * Code Mode tool configuration (driver, tools, timeout, memoryLimit)
160
+ */
161
+ config: CodeModeToolConfig;
162
+ /**
163
+ * Text adapter for snippet selection (should be a cheap/fast model)
164
+ */
165
+ adapter: AnyTextAdapter;
166
+ /**
167
+ * Snippets configuration
168
+ */
169
+ snippets: SnippetsConfig;
170
+ /**
171
+ * Current conversation messages (used for context-aware snippet selection)
172
+ */
173
+ messages: Array<ModelMessage>;
174
+ /**
175
+ * Whether to include snippets as direct tools (not just sandbox bindings).
176
+ * When true, snippets become first-class tools the LLM can call directly.
177
+ * @default true
178
+ */
179
+ snippetsAsTools?: boolean;
180
+ }
181
+ /**
182
+ * Result from codeModeWithSnippets
183
+ */
184
+ export interface CodeModeWithSnippetsResult {
185
+ /**
186
+ * Tool registry for dynamic tool management.
187
+ * Pass this to chat() via the toolRegistry option.
188
+ * Snippets registered mid-stream will be added to this registry.
189
+ */
190
+ toolsRegistry: ToolRegistry;
191
+ /**
192
+ * System prompt documenting available snippets and external functions
193
+ */
194
+ systemPrompt: string;
195
+ /**
196
+ * Snippets that were selected for this request
197
+ */
198
+ selectedSnippets: Array<Snippet>;
199
+ }
200
+ /**
201
+ * A snippet transformed into a format suitable for sandbox injection
202
+ */
203
+ export interface SnippetBinding {
204
+ /**
205
+ * Function name with snippet_ prefix
206
+ */
207
+ name: string;
208
+ /**
209
+ * The snippet this binding wraps
210
+ */
211
+ snippet: Snippet;
212
+ /**
213
+ * Execute function that runs the snippet code
214
+ */
215
+ execute: (input: unknown) => Promise<unknown>;
216
+ }
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@tanstack/ai-code-mode-snippets",
3
+ "version": "0.3.14",
4
+ "description": "Persistent runtime snippet library for TanStack AI Code Mode agents and sandboxed tool orchestration.",
5
+ "author": "Tanner Linsley",
6
+ "license": "MIT",
7
+ "homepage": "https://tanstack.com/ai",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/TanStack/ai.git",
11
+ "directory": "packages/ai-code-mode-snippets"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/TanStack/ai/issues"
15
+ },
16
+ "funding": {
17
+ "type": "github",
18
+ "url": "https://github.com/sponsors/tannerlinsley"
19
+ },
20
+ "type": "module",
21
+ "module": "./dist/esm/index.js",
22
+ "types": "./dist/esm/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/esm/index.d.ts",
26
+ "import": "./dist/esm/index.js"
27
+ },
28
+ "./storage": {
29
+ "types": "./dist/esm/storage/index.d.ts",
30
+ "import": "./dist/esm/storage/index.js"
31
+ }
32
+ },
33
+ "sideEffects": false,
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "src"
40
+ ],
41
+ "keywords": [
42
+ "ai",
43
+ "ai-sdk",
44
+ "typescript",
45
+ "tanstack",
46
+ "code-mode",
47
+ "snippets",
48
+ "agents",
49
+ "llm",
50
+ "sandbox",
51
+ "tool-calling",
52
+ "tanstack-intent"
53
+ ],
54
+ "dependencies": {
55
+ "@tanstack/ai": "^0.44.1",
56
+ "@tanstack/ai-code-mode": "^0.3.11"
57
+ },
58
+ "peerDependencies": {
59
+ "zod": "^3.0.0 || ^4.0.0",
60
+ "@tanstack/ai": "^0.44.1",
61
+ "@tanstack/ai-code-mode": "^0.3.11"
62
+ },
63
+ "devDependencies": {
64
+ "@vitest/coverage-v8": "4.1.10",
65
+ "commander": "^13.1.0",
66
+ "dotenv": "^17.2.3",
67
+ "tsx": "^4.23.12",
68
+ "zod": "^4.2.0",
69
+ "@tanstack/ai-gemini": "0.23.0",
70
+ "@tanstack/ai-anthropic": "0.16.5",
71
+ "@tanstack/ai-openai": "0.19.0",
72
+ "@tanstack/ai-isolate-node": "0.1.50"
73
+ },
74
+ "scripts": {
75
+ "build": "vite build",
76
+ "clean": "premove ./build ./dist",
77
+ "lint:fix": "oxlint src --type-aware --fix",
78
+ "test:build": "publint --strict",
79
+ "test:oxlint": "oxlint src --type-aware",
80
+ "test:lib": "vitest --passWithNoTests",
81
+ "test:lib:dev": "pnpm test:lib --watch",
82
+ "test:types": "tsc",
83
+ "test:cli": "tsx test-cli/cli.ts",
84
+ "test:cli:run": "tsx test-cli/cli.ts run",
85
+ "test:cli:run:all": "tsx test-cli/cli.ts run --adapters openai,anthropic,gemini",
86
+ "test:cli:list": "tsx test-cli/cli.ts list",
87
+ "test:cli:simulated": "tsx test-cli/cli.ts simulated",
88
+ "test:cli:registry": "tsx test-cli/cli.ts registry",
89
+ "test:cli:live": "tsx test-cli/cli.ts live",
90
+ "test:cli:structured": "tsx test-cli/cli.ts structured"
91
+ }
92
+ }
@@ -0,0 +1,210 @@
1
+ import {
2
+ createCodeModeSystemPrompt,
3
+ createCodeModeTool,
4
+ toolsToBindings,
5
+ } from '@tanstack/ai-code-mode'
6
+ import { createToolRegistry } from '@tanstack/ai'
7
+ import { selectRelevantSnippets } from './select-relevant-snippets'
8
+ import { createSnippetManagementTools } from './create-snippet-management-tools'
9
+ import { createSnippetsSystemPrompt } from './create-snippets-system-prompt'
10
+ import { snippetsToTools } from './snippets-to-tools'
11
+ import type {
12
+ CodeModeWithSnippetsOptions,
13
+ CodeModeWithSnippetsResult,
14
+ Snippet,
15
+ } from './types'
16
+ import type { ToolBinding } from '@tanstack/ai-code-mode'
17
+
18
+ export type { CodeModeWithSnippetsOptions, CodeModeWithSnippetsResult }
19
+
20
+ /**
21
+ * Create Code Mode tools and system prompt with snippets integration.
22
+ *
23
+ * This function:
24
+ * 1. Loads the snippet index from storage
25
+ * 2. Uses a cheap/fast LLM to select relevant snippets based on conversation context
26
+ * 3. Creates the execute_typescript tool with dynamic snippet bindings
27
+ * 4. Creates snippet management tools (search, get, register)
28
+ * 5. Generates system prompts documenting available snippets
29
+ * 6. Returns a ToolRegistry that allows dynamic snippet additions mid-stream
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * // Node-only file storage lives behind the `/storage` subpath:
34
+ * import { createFileSnippetStorage } from '@tanstack/ai-code-mode-snippets/storage'
35
+ *
36
+ * const { toolsRegistry, systemPrompt, selectedSnippets } = await codeModeWithSnippets({
37
+ * config: {
38
+ * driver: createNodeIsolateDriver(),
39
+ * tools: allTools,
40
+ * timeout: 60000,
41
+ * },
42
+ * adapter: openaiText('gpt-4o-mini'), // Cheap model for selection
43
+ * snippets: {
44
+ * storage: createFileSnippetStorage('./.snippets'),
45
+ * maxSnippetsInContext: 5,
46
+ * },
47
+ * messages,
48
+ * });
49
+ *
50
+ * const stream = chat({
51
+ * adapter: openaiText('gpt-4o'), // Main model
52
+ * toolRegistry: toolsRegistry, // Dynamic tool registry
53
+ * messages,
54
+ * systemPrompts: [BASE_PROMPT, systemPrompt],
55
+ * });
56
+ * ```
57
+ */
58
+ export async function codeModeWithSnippets({
59
+ config,
60
+ adapter,
61
+ snippets,
62
+ messages,
63
+ snippetsAsTools = true,
64
+ }: CodeModeWithSnippetsOptions): Promise<CodeModeWithSnippetsResult> {
65
+ const { storage, maxSnippetsInContext = 5 } = snippets
66
+
67
+ // 1. Load the snippet index (lightweight metadata only)
68
+ const snippetIndex = await storage.loadIndex()
69
+
70
+ // 2. Use adapter to select relevant snippets based on transcript
71
+ const selectedSnippets = await selectRelevantSnippets({
72
+ adapter,
73
+ messages,
74
+ snippetIndex,
75
+ maxSnippets: maxSnippetsInContext,
76
+ storage,
77
+ })
78
+
79
+ // Pre-compute bindings from base tools (shared across snippet executions)
80
+ const baseBindings = toolsToBindings(config.tools, 'external_')
81
+
82
+ // 3. Create the execute_typescript tool with dynamic snippet bindings
83
+ const codeModeTool = createCodeModeTool({
84
+ ...config,
85
+ // Dynamic snippet bindings - fetched at execution time
86
+ getSnippetBindings: async () => {
87
+ // Get all snippets from storage (includes newly registered ones)
88
+ const allSnippets = await storage.loadAll()
89
+ // Convert to bindings with snippet_ prefix
90
+ const snippetBindings: Record<string, ToolBinding> = {}
91
+ for (const snippet of allSnippets) {
92
+ // Create a simple binding that executes the snippet code
93
+ snippetBindings[`snippet_${snippet.name}`] = {
94
+ name: `snippet_${snippet.name}`,
95
+ description: snippet.description,
96
+ inputSchema: snippet.inputSchema,
97
+ outputSchema: snippet.outputSchema,
98
+ execute: async (input: unknown) => {
99
+ // This is a simplified execution - the full snippetToTool handles events
100
+ const wrappedCode = `const input = ${JSON.stringify(input)};\n${snippet.code}`
101
+ const { stripTypeScript, createEventAwareBindings } =
102
+ await import('@tanstack/ai-code-mode')
103
+ const strippedCode = await stripTypeScript(wrappedCode)
104
+ const context = await config.driver.createContext({
105
+ bindings: createEventAwareBindings(baseBindings, () => {}),
106
+ timeout: config.timeout,
107
+ ...(config.memoryLimit !== undefined && {
108
+ memoryLimit: config.memoryLimit,
109
+ }),
110
+ })
111
+ try {
112
+ const result = await context.execute(strippedCode)
113
+ if (!result.success) {
114
+ throw new Error(
115
+ result.error?.message || 'Snippet execution failed',
116
+ )
117
+ }
118
+ return result.value
119
+ } finally {
120
+ await context.dispose()
121
+ }
122
+ },
123
+ }
124
+ }
125
+ return snippetBindings
126
+ },
127
+ })
128
+
129
+ // 4. Create a mutable tool registry
130
+ const registry = createToolRegistry()
131
+
132
+ // 5. Add the execute_typescript tool to the registry
133
+ registry.add(codeModeTool)
134
+
135
+ // 6. Create snippet management tools (they need access to the registry)
136
+ const snippetManagementTools = createSnippetManagementTools({
137
+ storage,
138
+ registry,
139
+ config,
140
+ baseBindings,
141
+ })
142
+
143
+ for (const tool of snippetManagementTools) {
144
+ registry.add(tool)
145
+ }
146
+
147
+ // 7. Convert selected snippets to direct tools and add to registry (if enabled)
148
+ if (snippetsAsTools && selectedSnippets.length > 0) {
149
+ const snippetToolsList = snippetsToTools({
150
+ snippets: selectedSnippets,
151
+ driver: config.driver,
152
+ tools: config.tools,
153
+ storage,
154
+ timeout: config.timeout,
155
+ memoryLimit: config.memoryLimit,
156
+ })
157
+
158
+ for (const snippetTool of snippetToolsList) {
159
+ registry.add(snippetTool)
160
+ }
161
+ }
162
+
163
+ // 8. Generate combined system prompt
164
+ const basePrompt = createCodeModeSystemPrompt(config)
165
+ const snippetsPrompt = createSnippetsSystemPrompt({
166
+ selectedSnippets,
167
+ totalSnippetCount: snippetIndex.length,
168
+ snippetsAsTools,
169
+ })
170
+ const systemPrompt = basePrompt + '\n\n' + snippetsPrompt
171
+
172
+ return {
173
+ toolsRegistry: registry,
174
+ systemPrompt,
175
+ selectedSnippets,
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Create a Code Mode tool configuration extended with snippets.
181
+ * This is an alternative to codeModeWithSnippets that returns
182
+ * a config object instead of directly creating tools.
183
+ *
184
+ * Useful when you want more control over the tool creation process.
185
+ */
186
+ export function createCodeModeWithSnippetsConfig({
187
+ config,
188
+ selectedSnippets,
189
+ storage,
190
+ }: {
191
+ config: CodeModeWithSnippetsOptions['config']
192
+ selectedSnippets: Array<Snippet>
193
+ storage: CodeModeWithSnippetsOptions['snippets']['storage']
194
+ }) {
195
+ // Create snippet tools for direct calling
196
+ const snippetToolsList = snippetsToTools({
197
+ snippets: selectedSnippets,
198
+ driver: config.driver,
199
+ tools: config.tools,
200
+ storage,
201
+ timeout: config.timeout,
202
+ memoryLimit: config.memoryLimit,
203
+ })
204
+
205
+ return {
206
+ ...config,
207
+ snippetTools: snippetToolsList,
208
+ selectedSnippets,
209
+ }
210
+ }