@directive-run/knowledge 1.16.0 → 1.17.1

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/ai/ai-mcp-rag.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  Model Context Protocol (MCP) server integration and Retrieval-Augmented Generation (RAG) enrichment for Directive AI agents. Import from `@directive-run/ai/mcp` for the MCP adapter (the main barrel re-exports it with `@deprecated` notices for v2 removal), and from `@directive-run/ai` for the RAG enricher + embedder utilities.
6
6
 
7
+ > **Client vs. server, two different packages.** This page covers the MCP **client** side — Directive AI agents calling out to external MCP servers. For the **server** side — exposing Directive itself (knowledge files, code examples, Claude Code skills) so other AI clients can consume it — that's `@directive-run/mcp`. Same protocol, opposite arrows.
8
+
7
9
  ## Decision tree
8
10
 
9
11
  ```
@@ -0,0 +1,216 @@
1
+ {
2
+ "$schema": "./compositions.schema.json",
3
+ "version": 1,
4
+ "edges": [
5
+ {
6
+ "from": "@directive-run/query",
7
+ "to": "@directive-run/mutator",
8
+ "reason": "discriminated-union mutators pair with query's invalidation tags for end-to-end optimistic writes"
9
+ },
10
+ {
11
+ "from": "@directive-run/query",
12
+ "to": "@directive-run/optimistic",
13
+ "reason": "withOptimistic / withOptimisticHandlers add client-side rollback when a mutation fails"
14
+ },
15
+ {
16
+ "from": "@directive-run/query",
17
+ "to": "@directive-run/timeline",
18
+ "reason": "timeline captures every cache invalidation + refetch so flaky tests reproduce byte-for-byte"
19
+ },
20
+ {
21
+ "from": "@directive-run/mutator",
22
+ "to": "@directive-run/query",
23
+ "reason": "pair mutators with query's invalidation tags so a successful mutation refetches the right resources"
24
+ },
25
+ {
26
+ "from": "@directive-run/mutator",
27
+ "to": "@directive-run/optimistic",
28
+ "reason": "withOptimisticHandlers wraps a defineMutator handler with snapshot + rollback in one call"
29
+ },
30
+ {
31
+ "from": "@directive-run/mutator",
32
+ "to": "@directive-run/timeline",
33
+ "reason": "every mutation cycle records into the timeline for replay-based test reproduction"
34
+ },
35
+ {
36
+ "from": "@directive-run/optimistic",
37
+ "to": "@directive-run/mutator",
38
+ "reason": "defineMutator handlers wrapped with withOptimisticHandlers get rollback for free"
39
+ },
40
+ {
41
+ "from": "@directive-run/optimistic",
42
+ "to": "@directive-run/query",
43
+ "reason": "an optimistic mutation that invalidates a query tag also reverts the invalidation on rollback"
44
+ },
45
+ {
46
+ "from": "@directive-run/optimistic",
47
+ "to": "@directive-run/timeline",
48
+ "reason": "optimistic writes AND their rollbacks both record into the timeline"
49
+ },
50
+ {
51
+ "from": "@directive-run/timeline",
52
+ "to": "@directive-run/mutator",
53
+ "reason": "every mutation cycle records into the timeline; replay a failed test's exact mutation sequence"
54
+ },
55
+ {
56
+ "from": "@directive-run/timeline",
57
+ "to": "@directive-run/optimistic",
58
+ "reason": "captures optimistic writes AND rollbacks for byte-for-byte test reproduction"
59
+ },
60
+ {
61
+ "from": "@directive-run/timeline",
62
+ "to": "@directive-run/query",
63
+ "reason": "every query invalidation + refetch records into the timeline"
64
+ },
65
+ {
66
+ "from": "@directive-run/timeline",
67
+ "to": "@directive-run/core",
68
+ "reason": "core's reconciliation cycles are the source events the timeline captures"
69
+ },
70
+ {
71
+ "from": "@directive-run/core",
72
+ "to": "@directive-run/react",
73
+ "reason": "useFact / useDerived / useEvents hooks subscribe to the core system from React components"
74
+ },
75
+ {
76
+ "from": "@directive-run/core",
77
+ "to": "@directive-run/vue",
78
+ "reason": "useFact / useDerived composables expose the core system to Vue components"
79
+ },
80
+ {
81
+ "from": "@directive-run/core",
82
+ "to": "@directive-run/svelte",
83
+ "reason": "Svelte store adapters wrap the core system for component subscription"
84
+ },
85
+ {
86
+ "from": "@directive-run/core",
87
+ "to": "@directive-run/solid",
88
+ "reason": "Solid signal adapters wrap the core system"
89
+ },
90
+ {
91
+ "from": "@directive-run/core",
92
+ "to": "@directive-run/lit",
93
+ "reason": "Lit controllers wrap the core system for Web Component subscription"
94
+ },
95
+ {
96
+ "from": "@directive-run/core",
97
+ "to": "@directive-run/el",
98
+ "reason": "framework-free DOM bindings — bind, bindText, mount thread schema typing into vanilla DOM updaters"
99
+ },
100
+ {
101
+ "from": "@directive-run/core",
102
+ "to": "@directive-run/ai",
103
+ "reason": "AI orchestrator modules ARE Directive modules — constraints trigger agent runs, resolvers execute them"
104
+ },
105
+ {
106
+ "from": "@directive-run/ai",
107
+ "to": "@directive-run/core",
108
+ "reason": "agent orchestrators are built on core's constraint-resolver loop with custom resolvers that call LLMs"
109
+ },
110
+ {
111
+ "from": "@directive-run/ai",
112
+ "to": "@directive-run/knowledge",
113
+ "reason": "knowledge package is the source of truth for the rules + examples that ai/mcp's RAG enricher serves"
114
+ },
115
+ {
116
+ "from": "@directive-run/ai",
117
+ "to": "@directive-run/mcp",
118
+ "reason": "ai/mcp is the CLIENT side (createMCPAdapter); @directive-run/mcp is the SERVER side — opposite arrows, same protocol"
119
+ },
120
+ {
121
+ "from": "@directive-run/mcp",
122
+ "to": "@directive-run/knowledge",
123
+ "reason": "MCP server tools (list_knowledge, get_knowledge, search_knowledge) front the knowledge package's data APIs"
124
+ },
125
+ {
126
+ "from": "@directive-run/mcp",
127
+ "to": "@directive-run/claude-plugin",
128
+ "reason": "MCP server tools (list_skills, get_skill) expose the Claude Code skill bundles to non-Claude MCP clients"
129
+ },
130
+ {
131
+ "from": "@directive-run/mcp",
132
+ "to": "@directive-run/scaffold",
133
+ "reason": "generate_module MCP tool wraps the scaffold package's pure generators and returns source as a string"
134
+ },
135
+ {
136
+ "from": "@directive-run/mcp",
137
+ "to": "@directive-run/lint",
138
+ "reason": "review_source and fix_code MCP tools wrap the lint package's ts-morph rule registry"
139
+ },
140
+ {
141
+ "from": "@directive-run/cli",
142
+ "to": "@directive-run/scaffold",
143
+ "reason": "directive new <name> delegates to scaffold's generators and writes the result to disk"
144
+ },
145
+ {
146
+ "from": "@directive-run/cli",
147
+ "to": "@directive-run/knowledge",
148
+ "reason": "directive ai-rules bundles the knowledge package's content into per-assistant rule files (.cursorrules, CLAUDE.md, etc.)"
149
+ },
150
+ {
151
+ "from": "@directive-run/cli",
152
+ "to": "@directive-run/claude-plugin",
153
+ "reason": "directive ai-rules can install the Claude Code plugin bundle as one of its targets"
154
+ },
155
+ {
156
+ "from": "@directive-run/claude-plugin",
157
+ "to": "@directive-run/knowledge",
158
+ "reason": "skill bundles copy supporting files from the knowledge package at build time"
159
+ },
160
+ {
161
+ "from": "@directive-run/scaffold",
162
+ "to": "@directive-run/core",
163
+ "reason": "generated modules import createModule, t, and ModuleSchema from core"
164
+ },
165
+ {
166
+ "from": "@directive-run/lint",
167
+ "to": "@directive-run/knowledge",
168
+ "reason": "rule IDs match the anti-pattern IDs parsed from knowledge/core/anti-patterns.md"
169
+ },
170
+ {
171
+ "from": "@directive-run/react",
172
+ "to": "@directive-run/core",
173
+ "reason": "all React hooks consume systems built with @directive-run/core"
174
+ },
175
+ {
176
+ "from": "@directive-run/vue",
177
+ "to": "@directive-run/core",
178
+ "reason": "all Vue composables consume systems built with @directive-run/core"
179
+ },
180
+ {
181
+ "from": "@directive-run/svelte",
182
+ "to": "@directive-run/core",
183
+ "reason": "Svelte adapter consumes systems built with @directive-run/core"
184
+ },
185
+ {
186
+ "from": "@directive-run/solid",
187
+ "to": "@directive-run/core",
188
+ "reason": "Solid adapter consumes systems built with @directive-run/core"
189
+ },
190
+ {
191
+ "from": "@directive-run/lit",
192
+ "to": "@directive-run/core",
193
+ "reason": "Lit controllers consume systems built with @directive-run/core"
194
+ },
195
+ {
196
+ "from": "@directive-run/el",
197
+ "to": "@directive-run/core",
198
+ "reason": "framework-free DOM bindings consume systems built with @directive-run/core"
199
+ },
200
+ {
201
+ "from": "@directive-run/knowledge",
202
+ "to": "@directive-run/cli",
203
+ "reason": "knowledge ships as the data layer for directive ai-rules / directive install"
204
+ },
205
+ {
206
+ "from": "@directive-run/knowledge",
207
+ "to": "@directive-run/mcp",
208
+ "reason": "knowledge ships as the data layer for MCP tools (list_knowledge, get_knowledge, etc.)"
209
+ },
210
+ {
211
+ "from": "@directive-run/knowledge",
212
+ "to": "@directive-run/claude-plugin",
213
+ "reason": "knowledge ships as the data layer for Claude Code skill bundles"
214
+ }
215
+ ]
216
+ }
package/dist/index.cjs CHANGED
@@ -1,12 +1,16 @@
1
- 'use strict';var fs=require('fs'),path=require('path'),url=require('url');var _documentCurrentScript=typeof document!=='undefined'?document.currentScript:null;var h=path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))),g=path.resolve(h,"..");function s(n){let t=path.join(g,n);if(fs.existsSync(t))return t;let e=path.join(g,"..",n);return fs.existsSync(e)?e:t}var w=s("core"),A=s("ai"),y=s("examples"),R=s("api-skeleton.md"),r=null,i=null;function a(n,t){try{let e=fs.readdirSync(n).filter(o=>o.endsWith(".md")||o.endsWith(".ts"));for(let o of e){let m=o.replace(/\.(md|ts)$/,"");t.set(m,fs.readFileSync(path.join(n,o),"utf-8"));}}catch(e){if(e.code!=="ENOENT")throw e}}function u(){let n=new Map;a(w,n),a(A,n);try{n.set("api-skeleton",fs.readFileSync(R,"utf-8"));}catch{}return n}function f(){let n=new Map;return a(y,n),n}function _(n){return r||(r=u()),r.get(n)??""}function O(){return r||(r=u()),r}function S(n){return i||(i=f()),i.get(n)??""}function P(){return i||(i=f()),i}function T(n){return n.map(t=>_(t)).filter(Boolean).join(`
1
+ 'use strict';var fs=require('fs'),path=require('path'),url=require('url');var _documentCurrentScript=typeof document!=='undefined'?document.currentScript:null;var L=path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))),P=path.resolve(L,".."),a=null;function k(){let t=[path.join(P,"core","anti-patterns.md"),path.join(P,"..","core","anti-patterns.md")];for(let e of t)if(fs.existsSync(e))return e;return t[0]??""}function z(t){return t.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").replace(/-{2,}/g,"-")}function K(t){let e=t.toLowerCase();return /schema|builder/.test(e)?"schema":/constraint|cross-?module|require/.test(e)?"constraint":/resolver|settle|start/.test(e)?"resolver":/deriv|passthrough/.test(e)?"derivation":/effect/.test(e)?"effect":/usedirective|react|hook/.test(e)?"react":/import|bracket|deep import|name|context|abbreviat/.test(e)?"naming":("module")}function G(t){let e=t.toLowerCase();return /nonexistent|missing|no error|deep import|async logic/.test(e)?"error":"warning"}function U(t){let e=t.split(`
2
+ `),r=[],n=null,o=/^##\s+(\d+)\.\s+(.+?)\s*$/;for(let s of e){let i=o.exec(s);if(i?.[1]&&i?.[2]){n&&r.push(n),n={number:Number(i[1]),title:i[2],body:""};continue}if(n){if(/^##\s+\D/.test(s)){r.push(n),n=null;break}n.body+=`${s}
3
+ `;}}return n&&r.push(n),r}function $(t){let e=t.match(/```typescript\n([\s\S]*?)```/),r=[];for(let p of t.split(`
4
+ `)){if(p.startsWith("```"))break;r.push(p);}let n=r.join(`
5
+ `).trim();if(!e?.[1])return {explanation:n};let o=e[1],s=o.match(/\/\/\s*WRONG[^\n]*\n([\s\S]*?)(?:\/\/\s*CORRECT|$)/),i=o.match(/\/\/\s*CORRECT[^\n]*\n([\s\S]*)/);return {explanation:n,badExample:s?.[1]?.trim()??void 0,goodExample:i?.[1]?.trim()??void 0}}function h(){if(a)return a;let t=k(),e;try{e=fs.readFileSync(t,"utf-8");}catch{return a=Object.freeze([]),a}let n=U(e).map(o=>{let{explanation:s,badExample:i,goodExample:p}=$(o.body);return {id:z(o.title),number:o.number,title:o.title,severity:G(o.title),category:K(o.title),explanation:s,badExample:i,goodExample:p}});return a=Object.freeze(n),a}function D(){return h()}function B(t){return h().find(e=>e.id===t)}function W(){a=null;}var V=path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))),C=path.resolve(V,".."),R=["redux","zustand","xstate","mobx","jotai","recoil"],u=null;function Y(){let t=[path.join(C,"migration.json"),path.join(C,"..","migration.json")];for(let e of t)if(fs.existsSync(e))return e;return t[0]??""}function S(){if(u)return u;try{let t=fs.readFileSync(Y(),"utf-8"),e=JSON.parse(t);u=Object.freeze(e.sources??[]);}catch{u=Object.freeze([]);}return u}function Z(){return R}function tt(){return S()}function et(t){return S().find(e=>e.id===t)}function nt(){u=null;}var ct=path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))),E=path.resolve(ct,".."),g=null;function lt(){let t=[path.join(E,"compositions.json"),path.join(E,"..","compositions.json")];for(let e of t)if(fs.existsSync(e))return e;return t[0]??""}function m(){if(g)return g;try{let t=fs.readFileSync(lt(),"utf-8"),e=JSON.parse(t);g=Object.freeze(e.edges??[]);}catch{g=Object.freeze([]);}return g}function ut(){return m()}function gt(t){return m().filter(e=>e.from===t)}function pt(t){return m().filter(e=>e.to===t)}function ft(){g=null;}var Pt=path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))),v=path.resolve(Pt,"..");function f(t){let e=path.join(v,t);if(fs.existsSync(e))return e;let r=path.join(v,"..",t);return fs.existsSync(r)?r:e}var ht=f("core"),At=f("ai"),Ct=f("examples"),Rt=f("api-skeleton.md"),c=null,l=null;function y(t,e){try{let r=fs.readdirSync(t).filter(n=>n.endsWith(".md")||n.endsWith(".ts"));for(let n of r){let o=n.replace(/\.(md|ts)$/,"");e.set(o,fs.readFileSync(path.join(t,n),"utf-8"));}}catch(r){if(r.code!=="ENOENT")throw r}}function w(){let t=new Map;y(ht,t),y(At,t);try{t.set("api-skeleton",fs.readFileSync(Rt,"utf-8"));}catch{}return t}function _(){let t=new Map;return y(Ct,t),t}function St(t){return c||(c=w()),c.get(t)??""}function Gt(){return c||(c=w()),c}function Mt(t){return l||(l=_()),l.get(t)??""}function Ut(){return l||(l=_()),l}function $t(t){return t.map(e=>St(e)).filter(Boolean).join(`
2
6
 
3
7
  ---
4
8
 
5
- `)}function k(n){return n.map(t=>{let e=S(t);return e?`### Example: ${t}
9
+ `)}function Dt(t){return t.map(e=>{let r=Mt(e);return r?`### Example: ${e}
6
10
 
7
11
  \`\`\`typescript
8
- ${e}
12
+ ${r}
9
13
  \`\`\``:""}).filter(Boolean).join(`
10
14
 
11
- `)}function v(){r=null,i=null;}exports.clearCache=v;exports.getAllExamples=P;exports.getAllKnowledge=O;exports.getExample=S;exports.getExampleFiles=k;exports.getKnowledge=_;exports.getKnowledgeFiles=T;//# sourceMappingURL=index.cjs.map
15
+ `)}function Bt(){c=null,l=null;}exports.MIGRATION_SOURCES=R;exports.clearAntiPatternCache=W;exports.clearCache=Bt;exports.clearCompositionsCache=ft;exports.clearMigrationCache=nt;exports.getAllExamples=Ut;exports.getAllKnowledge=Gt;exports.getAntiPatternById=B;exports.getAntiPatterns=D;exports.getCompositions=ut;exports.getCompositionsFor=gt;exports.getExample=Mt;exports.getExampleFiles=Dt;exports.getKnowledge=St;exports.getKnowledgeFiles=$t;exports.getMigrationPattern=et;exports.getMigrationPatterns=tt;exports.getMigrationSources=Z;exports.getReverseCompositionsFor=pt;//# sourceMappingURL=index.cjs.map
12
16
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["__dirname","dirname","fileURLToPath","PKG_ROOT","resolve","resolveAsset","name","fromDist","join","existsSync","fromSrc","CORE_DIR","AI_DIR","EXAMPLES_DIR","API_SKELETON_PATH","knowledgeCache","exampleCache","loadDir","dir","map","files","readdirSync","f","file","readFileSync","err","loadAllKnowledge","loadAllExamples","getKnowledge","getAllKnowledge","getExample","getAllExamples","getKnowledgeFiles","names","getExampleFiles","content","clearCache"],"mappings":"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","file":"index.cjs","sourcesContent":["import { existsSync, readFileSync, readdirSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// Resolve package root: works both in src/ (dev) and dist/ (bundled)\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\n/**\n * Resolve a path relative to the package root.\n * Tries dist-relative first (bundled), then src-relative (dev).\n */\nfunction resolveAsset(name: string): string {\n const fromDist = join(PKG_ROOT, name);\n if (existsSync(fromDist)) {\n return fromDist;\n }\n\n const fromSrc = join(PKG_ROOT, \"..\", name);\n if (existsSync(fromSrc)) {\n return fromSrc;\n }\n\n return fromDist; // default, will just return empty maps\n}\n\nconst CORE_DIR = resolveAsset(\"core\");\nconst AI_DIR = resolveAsset(\"ai\");\nconst EXAMPLES_DIR = resolveAsset(\"examples\");\nconst API_SKELETON_PATH = resolveAsset(\"api-skeleton.md\");\n\nlet knowledgeCache: Map<string, string> | null = null;\nlet exampleCache: Map<string, string> | null = null;\n\nfunction loadDir(dir: string, map: Map<string, string>): void {\n try {\n const files = readdirSync(dir).filter(\n (f) => f.endsWith(\".md\") || f.endsWith(\".ts\"),\n );\n for (const file of files) {\n const name = file.replace(/\\.(md|ts)$/, \"\");\n map.set(name, readFileSync(join(dir, file), \"utf-8\"));\n }\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw err;\n }\n // directory does not exist — expected during tests or incomplete installs\n }\n}\n\nfunction loadAllKnowledge(): Map<string, string> {\n const map = new Map<string, string>();\n loadDir(CORE_DIR, map);\n loadDir(AI_DIR, map);\n\n // Include api-skeleton\n try {\n map.set(\"api-skeleton\", readFileSync(API_SKELETON_PATH, \"utf-8\"));\n } catch {\n // may not exist yet\n }\n\n return map;\n}\n\nfunction loadAllExamples(): Map<string, string> {\n const map = new Map<string, string>();\n loadDir(EXAMPLES_DIR, map);\n\n return map;\n}\n\nexport function getKnowledge(name: string): string {\n if (!knowledgeCache) {\n knowledgeCache = loadAllKnowledge();\n }\n\n return knowledgeCache.get(name) ?? \"\";\n}\n\nexport function getAllKnowledge(): ReadonlyMap<string, string> {\n if (!knowledgeCache) {\n knowledgeCache = loadAllKnowledge();\n }\n\n return knowledgeCache;\n}\n\nexport function getExample(name: string): string {\n if (!exampleCache) {\n exampleCache = loadAllExamples();\n }\n\n return exampleCache.get(name) ?? \"\";\n}\n\nexport function getAllExamples(): ReadonlyMap<string, string> {\n if (!exampleCache) {\n exampleCache = loadAllExamples();\n }\n\n return exampleCache;\n}\n\nexport function getKnowledgeFiles(names: string[]): string {\n return names\n .map((name) => getKnowledge(name))\n .filter(Boolean)\n .join(\"\\n\\n---\\n\\n\");\n}\n\nexport function getExampleFiles(names: string[]): string {\n return names\n .map((name) => {\n const content = getExample(name);\n if (!content) {\n return \"\";\n }\n\n return `### Example: ${name}\\n\\n\\`\\`\\`typescript\\n${content}\\n\\`\\`\\``;\n })\n .filter(Boolean)\n .join(\"\\n\\n\");\n}\n\n/** Clear cached knowledge and examples. Useful for dev/watch mode. */\nexport function clearCache(): void {\n knowledgeCache = null;\n exampleCache = null;\n}\n"]}
1
+ {"version":3,"sources":["../src/parsers/anti-patterns.ts","../src/parsers/migration.ts","../src/parsers/compositions.ts","../src/index.ts"],"names":["__dirname","dirname","fileURLToPath","PKG_ROOT","resolve","cache","resolveSourcePath","candidates","join","c","existsSync","slugify","title","categorize","lower","severityFor","splitSections","md","lines","sections","current","heading","line","match","extractExamples","body","codeBlock","explanationLines","explanation","code","wrongMatch","correctMatch","loadAntiPatterns","sourcePath","readFileSync","out","section","badExample","goodExample","getAntiPatterns","getAntiPatternById","id","ap","clearAntiPatternCache","MIGRATION_SOURCES","load","raw","parsed","getMigrationSources","getMigrationPatterns","getMigrationPattern","source","p","clearMigrationCache","getCompositions","getCompositionsFor","packageName","getReverseCompositionsFor","clearCompositionsCache","resolveAsset","name","fromDist","fromSrc","CORE_DIR","AI_DIR","EXAMPLES_DIR","API_SKELETON_PATH","knowledgeCache","exampleCache","loadDir","dir","map","files","readdirSync","f","file","err","loadAllKnowledge","loadAllExamples","getKnowledge","getAllKnowledge","getExample","getAllExamples","getKnowledgeFiles","names","getExampleFiles","content","clearCache"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Parser for `packages/knowledge/core/anti-patterns.md` → structured\n * `AntiPattern[]` data. Lazy + cached so the cost is paid once per\n * process at first call.\n *\n * The source markdown is organized as numbered sections (`## N. Title`)\n * each containing a TypeScript code block with `// WRONG` and\n * `// CORRECT` examples. Some sections include prose between the\n * heading and the code block — captured as `explanation`.\n *\n * IDs are kebab-cased slugs derived from the heading text; stable\n * across releases as long as the heading isn't renamed.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\nexport type AntiPatternSeverity = \"error\" | \"warning\" | \"info\";\nexport type AntiPatternCategory =\n | \"module\"\n | \"schema\"\n | \"constraint\"\n | \"resolver\"\n | \"derivation\"\n | \"effect\"\n | \"naming\"\n | \"react\"\n | \"composition\";\n\nexport interface AntiPattern {\n /** Stable slug, derived from the heading text. */\n id: string;\n /** Section number in the source markdown (1..N). */\n number: number;\n /** Human-readable title (the heading text minus the number prefix). */\n title: string;\n /** Default severity. Heuristic; tunable per-rule later. */\n severity: AntiPatternSeverity;\n /** Heuristic category from title keywords. */\n category: AntiPatternCategory;\n /** Body prose between heading and the first code block (often empty). */\n explanation: string;\n /** The `// WRONG` example, if present. */\n badExample?: string;\n /** The `// CORRECT` example, if present. */\n goodExample?: string;\n}\n\nlet cache: ReadonlyArray<AntiPattern> | null = null;\n\nfunction resolveSourcePath(): string {\n const candidates = [\n join(PKG_ROOT, \"core\", \"anti-patterns.md\"),\n join(PKG_ROOT, \"..\", \"core\", \"anti-patterns.md\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) {\n return c;\n }\n }\n return candidates[0] ?? \"\";\n}\n\nfunction slugify(title: string): string {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .replace(/-{2,}/g, \"-\");\n}\n\nfunction categorize(title: string): AntiPatternCategory {\n const lower = title.toLowerCase();\n if (/schema|builder/.test(lower)) {\n return \"schema\";\n }\n if (/constraint|cross-?module|require/.test(lower)) {\n return \"constraint\";\n }\n if (/resolver|settle|start/.test(lower)) {\n return \"resolver\";\n }\n if (/deriv|passthrough/.test(lower)) {\n return \"derivation\";\n }\n if (/effect/.test(lower)) {\n return \"effect\";\n }\n if (/usedirective|react|hook/.test(lower)) {\n return \"react\";\n }\n if (/import|bracket|deep import|name|context|abbreviat/.test(lower)) {\n return \"naming\";\n }\n if (/init|module|config/.test(lower)) {\n return \"module\";\n }\n return \"module\";\n}\n\nfunction severityFor(title: string): AntiPatternSeverity {\n const lower = title.toLowerCase();\n if (/nonexistent|missing|no error|deep import|async logic/.test(lower)) {\n return \"error\";\n }\n return \"warning\";\n}\n\ninterface Section {\n number: number;\n title: string;\n body: string;\n}\n\nfunction splitSections(md: string): Section[] {\n const lines = md.split(\"\\n\");\n const sections: Section[] = [];\n let current: Section | null = null;\n const heading = /^##\\s+(\\d+)\\.\\s+(.+?)\\s*$/;\n for (const line of lines) {\n const match = heading.exec(line);\n if (match?.[1] && match?.[2]) {\n if (current) {\n sections.push(current);\n }\n current = {\n number: Number(match[1]),\n title: match[2],\n body: \"\",\n };\n continue;\n }\n if (current) {\n // Stop at the first non-numbered ## section (e.g. \"Quick Reference Checklist\")\n if (/^##\\s+\\D/.test(line)) {\n sections.push(current);\n current = null;\n break;\n }\n current.body += `${line}\\n`;\n }\n }\n if (current) {\n sections.push(current);\n }\n return sections;\n}\n\nfunction extractExamples(body: string): {\n explanation: string;\n badExample?: string;\n goodExample?: string;\n} {\n const codeBlock = body.match(/```typescript\\n([\\s\\S]*?)```/);\n const explanationLines: string[] = [];\n for (const line of body.split(\"\\n\")) {\n if (line.startsWith(\"```\")) {\n break;\n }\n explanationLines.push(line);\n }\n const explanation = explanationLines.join(\"\\n\").trim();\n\n if (!codeBlock?.[1]) {\n return { explanation };\n }\n\n const code = codeBlock[1];\n // Split on the WRONG/CORRECT comment markers.\n const wrongMatch = code.match(\n /\\/\\/\\s*WRONG[^\\n]*\\n([\\s\\S]*?)(?:\\/\\/\\s*CORRECT|$)/,\n );\n const correctMatch = code.match(/\\/\\/\\s*CORRECT[^\\n]*\\n([\\s\\S]*)/);\n\n return {\n explanation,\n badExample: wrongMatch?.[1]?.trim() ?? undefined,\n goodExample: correctMatch?.[1]?.trim() ?? undefined,\n };\n}\n\nfunction loadAntiPatterns(): ReadonlyArray<AntiPattern> {\n if (cache) {\n return cache;\n }\n\n const sourcePath = resolveSourcePath();\n let md: string;\n try {\n md = readFileSync(sourcePath, \"utf-8\");\n } catch {\n cache = Object.freeze([]);\n return cache;\n }\n\n const sections = splitSections(md);\n const out: AntiPattern[] = sections.map((section) => {\n const { explanation, badExample, goodExample } = extractExamples(\n section.body,\n );\n return {\n id: slugify(section.title),\n number: section.number,\n title: section.title,\n severity: severityFor(section.title),\n category: categorize(section.title),\n explanation,\n badExample,\n goodExample,\n };\n });\n\n cache = Object.freeze(out);\n return cache;\n}\n\n/** Return every parsed anti-pattern, stable order, frozen. */\nexport function getAntiPatterns(): ReadonlyArray<AntiPattern> {\n return loadAntiPatterns();\n}\n\n/** Look up one anti-pattern by its slug id. */\nexport function getAntiPatternById(id: string): AntiPattern | undefined {\n return loadAntiPatterns().find((ap) => ap.id === id);\n}\n\n/** Clear the in-memory cache. Test / watch-mode helper. */\nexport function clearAntiPatternCache(): void {\n cache = null;\n}\n","/**\n * Loader for `packages/knowledge/migration.json` → structured\n * migration patterns. Lazy + cached.\n *\n * The JSON ships in the published tarball; consumers don't need to\n * parse markdown at runtime.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\n/** Supported source-library identifiers. */\nexport const MIGRATION_SOURCES = [\n \"redux\",\n \"zustand\",\n \"xstate\",\n \"mobx\",\n \"jotai\",\n \"recoil\",\n] as const;\nexport type MigrationSourceId = (typeof MIGRATION_SOURCES)[number];\n\nexport interface MigrationConceptRow {\n from: string;\n to: string;\n note: string;\n}\n\nexport interface MigrationPattern {\n id: MigrationSourceId;\n name: string;\n conceptMap: MigrationConceptRow[];\n steps: string[];\n before: string;\n after: string;\n}\n\ninterface MigrationFile {\n version: number;\n sources: MigrationPattern[];\n}\n\nlet cache: ReadonlyArray<MigrationPattern> | null = null;\n\nfunction resolveSourcePath(): string {\n const candidates = [\n join(PKG_ROOT, \"migration.json\"),\n join(PKG_ROOT, \"..\", \"migration.json\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) {\n return c;\n }\n }\n return candidates[0] ?? \"\";\n}\n\nfunction load(): ReadonlyArray<MigrationPattern> {\n if (cache) {\n return cache;\n }\n try {\n const raw = readFileSync(resolveSourcePath(), \"utf-8\");\n const parsed = JSON.parse(raw) as MigrationFile;\n cache = Object.freeze(parsed.sources ?? []);\n } catch {\n cache = Object.freeze([]);\n }\n return cache;\n}\n\n/** All supported source library IDs. */\nexport function getMigrationSources(): ReadonlyArray<MigrationSourceId> {\n return MIGRATION_SOURCES;\n}\n\n/** All migration patterns, in registry order. */\nexport function getMigrationPatterns(): ReadonlyArray<MigrationPattern> {\n return load();\n}\n\n/** One migration pattern by its source-library id. */\nexport function getMigrationPattern(\n source: string,\n): MigrationPattern | undefined {\n return load().find((p) => p.id === source);\n}\n\n/** Test / watch-mode helper. */\nexport function clearMigrationCache(): void {\n cache = null;\n}\n","/**\n * Loader for `packages/knowledge/compositions.json` → typed\n * composition graph. Lazy + cached.\n *\n * The JSON ships in the published tarball. Adjacency list rather\n * than per-package projections so consumers can answer both\n * \"what does X compose with?\" and \"what composes with X?\" without\n * duplicate data.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n// After bundling, all parsers live in dist/index.js — one level up\n// from dist reaches the package root. In src/ during dev, the file is\n// two levels up; the fallback in `resolveSourcePath` handles that.\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\nexport interface CompositionEdge {\n from: string;\n to: string;\n reason: string;\n}\n\ninterface CompositionsFile {\n version: number;\n edges: CompositionEdge[];\n}\n\nlet cache: ReadonlyArray<CompositionEdge> | null = null;\n\nfunction resolveSourcePath(): string {\n const candidates = [\n join(PKG_ROOT, \"compositions.json\"),\n join(PKG_ROOT, \"..\", \"compositions.json\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) {\n return c;\n }\n }\n return candidates[0] ?? \"\";\n}\n\nfunction load(): ReadonlyArray<CompositionEdge> {\n if (cache) {\n return cache;\n }\n try {\n const raw = readFileSync(resolveSourcePath(), \"utf-8\");\n const parsed = JSON.parse(raw) as CompositionsFile;\n cache = Object.freeze(parsed.edges ?? []);\n } catch {\n cache = Object.freeze([]);\n }\n return cache;\n}\n\n/** All composition edges as a flat list. */\nexport function getCompositions(): ReadonlyArray<CompositionEdge> {\n return load();\n}\n\n/**\n * All edges originating at the given package. Returns an empty array\n * when the package is unknown or has no outgoing compositions.\n */\nexport function getCompositionsFor(\n packageName: string,\n): ReadonlyArray<CompositionEdge> {\n return load().filter((e) => e.from === packageName);\n}\n\n/**\n * All edges arriving at the given package — \"who composes WITH me?\"\n */\nexport function getReverseCompositionsFor(\n packageName: string,\n): ReadonlyArray<CompositionEdge> {\n return load().filter((e) => e.to === packageName);\n}\n\n/** Test / watch-mode helper. */\nexport function clearCompositionsCache(): void {\n cache = null;\n}\n","import { existsSync, readFileSync, readdirSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// Resolve package root: works both in src/ (dev) and dist/ (bundled)\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\n/**\n * Resolve a path relative to the package root.\n * Tries dist-relative first (bundled), then src-relative (dev).\n */\nfunction resolveAsset(name: string): string {\n const fromDist = join(PKG_ROOT, name);\n if (existsSync(fromDist)) {\n return fromDist;\n }\n\n const fromSrc = join(PKG_ROOT, \"..\", name);\n if (existsSync(fromSrc)) {\n return fromSrc;\n }\n\n return fromDist; // default, will just return empty maps\n}\n\nconst CORE_DIR = resolveAsset(\"core\");\nconst AI_DIR = resolveAsset(\"ai\");\nconst EXAMPLES_DIR = resolveAsset(\"examples\");\nconst API_SKELETON_PATH = resolveAsset(\"api-skeleton.md\");\n\nlet knowledgeCache: Map<string, string> | null = null;\nlet exampleCache: Map<string, string> | null = null;\n\nfunction loadDir(dir: string, map: Map<string, string>): void {\n try {\n const files = readdirSync(dir).filter(\n (f) => f.endsWith(\".md\") || f.endsWith(\".ts\"),\n );\n for (const file of files) {\n const name = file.replace(/\\.(md|ts)$/, \"\");\n map.set(name, readFileSync(join(dir, file), \"utf-8\"));\n }\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw err;\n }\n // directory does not exist — expected during tests or incomplete installs\n }\n}\n\nfunction loadAllKnowledge(): Map<string, string> {\n const map = new Map<string, string>();\n loadDir(CORE_DIR, map);\n loadDir(AI_DIR, map);\n\n // Include api-skeleton\n try {\n map.set(\"api-skeleton\", readFileSync(API_SKELETON_PATH, \"utf-8\"));\n } catch {\n // may not exist yet\n }\n\n return map;\n}\n\nfunction loadAllExamples(): Map<string, string> {\n const map = new Map<string, string>();\n loadDir(EXAMPLES_DIR, map);\n\n return map;\n}\n\nexport function getKnowledge(name: string): string {\n if (!knowledgeCache) {\n knowledgeCache = loadAllKnowledge();\n }\n\n return knowledgeCache.get(name) ?? \"\";\n}\n\nexport function getAllKnowledge(): ReadonlyMap<string, string> {\n if (!knowledgeCache) {\n knowledgeCache = loadAllKnowledge();\n }\n\n return knowledgeCache;\n}\n\nexport function getExample(name: string): string {\n if (!exampleCache) {\n exampleCache = loadAllExamples();\n }\n\n return exampleCache.get(name) ?? \"\";\n}\n\nexport function getAllExamples(): ReadonlyMap<string, string> {\n if (!exampleCache) {\n exampleCache = loadAllExamples();\n }\n\n return exampleCache;\n}\n\nexport function getKnowledgeFiles(names: string[]): string {\n return names\n .map((name) => getKnowledge(name))\n .filter(Boolean)\n .join(\"\\n\\n---\\n\\n\");\n}\n\nexport function getExampleFiles(names: string[]): string {\n return names\n .map((name) => {\n const content = getExample(name);\n if (!content) {\n return \"\";\n }\n\n return `### Example: ${name}\\n\\n\\`\\`\\`typescript\\n${content}\\n\\`\\`\\``;\n })\n .filter(Boolean)\n .join(\"\\n\\n\");\n}\n\n/** Clear cached knowledge and examples. Useful for dev/watch mode. */\nexport function clearCache(): void {\n knowledgeCache = null;\n exampleCache = null;\n}\n\n// ---------------------------------------------------------------------------\n// Structured data APIs (v1.16.0 — back the MCP `list_review_rules`,\n// `get_review_rule`, `list_migration_sources`, `get_migration_pattern`,\n// `get_composable_packages` tools).\n// ---------------------------------------------------------------------------\n\nexport {\n type AntiPattern,\n type AntiPatternCategory,\n type AntiPatternSeverity,\n clearAntiPatternCache,\n getAntiPatternById,\n getAntiPatterns,\n} from \"./parsers/anti-patterns.js\";\n\nexport {\n type MigrationConceptRow,\n type MigrationPattern,\n type MigrationSourceId,\n MIGRATION_SOURCES,\n clearMigrationCache,\n getMigrationPattern,\n getMigrationPatterns,\n getMigrationSources,\n} from \"./parsers/migration.js\";\n\nexport {\n type CompositionEdge,\n clearCompositionsCache,\n getCompositions,\n getCompositionsFor,\n getReverseCompositionsFor,\n} from \"./parsers/compositions.js\";\n"]}
package/dist/index.d.cts CHANGED
@@ -1,3 +1,103 @@
1
+ /**
2
+ * Parser for `packages/knowledge/core/anti-patterns.md` → structured
3
+ * `AntiPattern[]` data. Lazy + cached so the cost is paid once per
4
+ * process at first call.
5
+ *
6
+ * The source markdown is organized as numbered sections (`## N. Title`)
7
+ * each containing a TypeScript code block with `// WRONG` and
8
+ * `// CORRECT` examples. Some sections include prose between the
9
+ * heading and the code block — captured as `explanation`.
10
+ *
11
+ * IDs are kebab-cased slugs derived from the heading text; stable
12
+ * across releases as long as the heading isn't renamed.
13
+ */
14
+ type AntiPatternSeverity = "error" | "warning" | "info";
15
+ type AntiPatternCategory = "module" | "schema" | "constraint" | "resolver" | "derivation" | "effect" | "naming" | "react" | "composition";
16
+ interface AntiPattern {
17
+ /** Stable slug, derived from the heading text. */
18
+ id: string;
19
+ /** Section number in the source markdown (1..N). */
20
+ number: number;
21
+ /** Human-readable title (the heading text minus the number prefix). */
22
+ title: string;
23
+ /** Default severity. Heuristic; tunable per-rule later. */
24
+ severity: AntiPatternSeverity;
25
+ /** Heuristic category from title keywords. */
26
+ category: AntiPatternCategory;
27
+ /** Body prose between heading and the first code block (often empty). */
28
+ explanation: string;
29
+ /** The `// WRONG` example, if present. */
30
+ badExample?: string;
31
+ /** The `// CORRECT` example, if present. */
32
+ goodExample?: string;
33
+ }
34
+ /** Return every parsed anti-pattern, stable order, frozen. */
35
+ declare function getAntiPatterns(): ReadonlyArray<AntiPattern>;
36
+ /** Look up one anti-pattern by its slug id. */
37
+ declare function getAntiPatternById(id: string): AntiPattern | undefined;
38
+ /** Clear the in-memory cache. Test / watch-mode helper. */
39
+ declare function clearAntiPatternCache(): void;
40
+
41
+ /**
42
+ * Loader for `packages/knowledge/migration.json` → structured
43
+ * migration patterns. Lazy + cached.
44
+ *
45
+ * The JSON ships in the published tarball; consumers don't need to
46
+ * parse markdown at runtime.
47
+ */
48
+ /** Supported source-library identifiers. */
49
+ declare const MIGRATION_SOURCES: readonly ["redux", "zustand", "xstate", "mobx", "jotai", "recoil"];
50
+ type MigrationSourceId = (typeof MIGRATION_SOURCES)[number];
51
+ interface MigrationConceptRow {
52
+ from: string;
53
+ to: string;
54
+ note: string;
55
+ }
56
+ interface MigrationPattern {
57
+ id: MigrationSourceId;
58
+ name: string;
59
+ conceptMap: MigrationConceptRow[];
60
+ steps: string[];
61
+ before: string;
62
+ after: string;
63
+ }
64
+ /** All supported source library IDs. */
65
+ declare function getMigrationSources(): ReadonlyArray<MigrationSourceId>;
66
+ /** All migration patterns, in registry order. */
67
+ declare function getMigrationPatterns(): ReadonlyArray<MigrationPattern>;
68
+ /** One migration pattern by its source-library id. */
69
+ declare function getMigrationPattern(source: string): MigrationPattern | undefined;
70
+ /** Test / watch-mode helper. */
71
+ declare function clearMigrationCache(): void;
72
+
73
+ /**
74
+ * Loader for `packages/knowledge/compositions.json` → typed
75
+ * composition graph. Lazy + cached.
76
+ *
77
+ * The JSON ships in the published tarball. Adjacency list rather
78
+ * than per-package projections so consumers can answer both
79
+ * "what does X compose with?" and "what composes with X?" without
80
+ * duplicate data.
81
+ */
82
+ interface CompositionEdge {
83
+ from: string;
84
+ to: string;
85
+ reason: string;
86
+ }
87
+ /** All composition edges as a flat list. */
88
+ declare function getCompositions(): ReadonlyArray<CompositionEdge>;
89
+ /**
90
+ * All edges originating at the given package. Returns an empty array
91
+ * when the package is unknown or has no outgoing compositions.
92
+ */
93
+ declare function getCompositionsFor(packageName: string): ReadonlyArray<CompositionEdge>;
94
+ /**
95
+ * All edges arriving at the given package — "who composes WITH me?"
96
+ */
97
+ declare function getReverseCompositionsFor(packageName: string): ReadonlyArray<CompositionEdge>;
98
+ /** Test / watch-mode helper. */
99
+ declare function clearCompositionsCache(): void;
100
+
1
101
  declare function getKnowledge(name: string): string;
2
102
  declare function getAllKnowledge(): ReadonlyMap<string, string>;
3
103
  declare function getExample(name: string): string;
@@ -7,4 +107,4 @@ declare function getExampleFiles(names: string[]): string;
7
107
  /** Clear cached knowledge and examples. Useful for dev/watch mode. */
8
108
  declare function clearCache(): void;
9
109
 
10
- export { clearCache, getAllExamples, getAllKnowledge, getExample, getExampleFiles, getKnowledge, getKnowledgeFiles };
110
+ export { type AntiPattern, type AntiPatternCategory, type AntiPatternSeverity, type CompositionEdge, MIGRATION_SOURCES, type MigrationConceptRow, type MigrationPattern, type MigrationSourceId, clearAntiPatternCache, clearCache, clearCompositionsCache, clearMigrationCache, getAllExamples, getAllKnowledge, getAntiPatternById, getAntiPatterns, getCompositions, getCompositionsFor, getExample, getExampleFiles, getKnowledge, getKnowledgeFiles, getMigrationPattern, getMigrationPatterns, getMigrationSources, getReverseCompositionsFor };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,103 @@
1
+ /**
2
+ * Parser for `packages/knowledge/core/anti-patterns.md` → structured
3
+ * `AntiPattern[]` data. Lazy + cached so the cost is paid once per
4
+ * process at first call.
5
+ *
6
+ * The source markdown is organized as numbered sections (`## N. Title`)
7
+ * each containing a TypeScript code block with `// WRONG` and
8
+ * `// CORRECT` examples. Some sections include prose between the
9
+ * heading and the code block — captured as `explanation`.
10
+ *
11
+ * IDs are kebab-cased slugs derived from the heading text; stable
12
+ * across releases as long as the heading isn't renamed.
13
+ */
14
+ type AntiPatternSeverity = "error" | "warning" | "info";
15
+ type AntiPatternCategory = "module" | "schema" | "constraint" | "resolver" | "derivation" | "effect" | "naming" | "react" | "composition";
16
+ interface AntiPattern {
17
+ /** Stable slug, derived from the heading text. */
18
+ id: string;
19
+ /** Section number in the source markdown (1..N). */
20
+ number: number;
21
+ /** Human-readable title (the heading text minus the number prefix). */
22
+ title: string;
23
+ /** Default severity. Heuristic; tunable per-rule later. */
24
+ severity: AntiPatternSeverity;
25
+ /** Heuristic category from title keywords. */
26
+ category: AntiPatternCategory;
27
+ /** Body prose between heading and the first code block (often empty). */
28
+ explanation: string;
29
+ /** The `// WRONG` example, if present. */
30
+ badExample?: string;
31
+ /** The `// CORRECT` example, if present. */
32
+ goodExample?: string;
33
+ }
34
+ /** Return every parsed anti-pattern, stable order, frozen. */
35
+ declare function getAntiPatterns(): ReadonlyArray<AntiPattern>;
36
+ /** Look up one anti-pattern by its slug id. */
37
+ declare function getAntiPatternById(id: string): AntiPattern | undefined;
38
+ /** Clear the in-memory cache. Test / watch-mode helper. */
39
+ declare function clearAntiPatternCache(): void;
40
+
41
+ /**
42
+ * Loader for `packages/knowledge/migration.json` → structured
43
+ * migration patterns. Lazy + cached.
44
+ *
45
+ * The JSON ships in the published tarball; consumers don't need to
46
+ * parse markdown at runtime.
47
+ */
48
+ /** Supported source-library identifiers. */
49
+ declare const MIGRATION_SOURCES: readonly ["redux", "zustand", "xstate", "mobx", "jotai", "recoil"];
50
+ type MigrationSourceId = (typeof MIGRATION_SOURCES)[number];
51
+ interface MigrationConceptRow {
52
+ from: string;
53
+ to: string;
54
+ note: string;
55
+ }
56
+ interface MigrationPattern {
57
+ id: MigrationSourceId;
58
+ name: string;
59
+ conceptMap: MigrationConceptRow[];
60
+ steps: string[];
61
+ before: string;
62
+ after: string;
63
+ }
64
+ /** All supported source library IDs. */
65
+ declare function getMigrationSources(): ReadonlyArray<MigrationSourceId>;
66
+ /** All migration patterns, in registry order. */
67
+ declare function getMigrationPatterns(): ReadonlyArray<MigrationPattern>;
68
+ /** One migration pattern by its source-library id. */
69
+ declare function getMigrationPattern(source: string): MigrationPattern | undefined;
70
+ /** Test / watch-mode helper. */
71
+ declare function clearMigrationCache(): void;
72
+
73
+ /**
74
+ * Loader for `packages/knowledge/compositions.json` → typed
75
+ * composition graph. Lazy + cached.
76
+ *
77
+ * The JSON ships in the published tarball. Adjacency list rather
78
+ * than per-package projections so consumers can answer both
79
+ * "what does X compose with?" and "what composes with X?" without
80
+ * duplicate data.
81
+ */
82
+ interface CompositionEdge {
83
+ from: string;
84
+ to: string;
85
+ reason: string;
86
+ }
87
+ /** All composition edges as a flat list. */
88
+ declare function getCompositions(): ReadonlyArray<CompositionEdge>;
89
+ /**
90
+ * All edges originating at the given package. Returns an empty array
91
+ * when the package is unknown or has no outgoing compositions.
92
+ */
93
+ declare function getCompositionsFor(packageName: string): ReadonlyArray<CompositionEdge>;
94
+ /**
95
+ * All edges arriving at the given package — "who composes WITH me?"
96
+ */
97
+ declare function getReverseCompositionsFor(packageName: string): ReadonlyArray<CompositionEdge>;
98
+ /** Test / watch-mode helper. */
99
+ declare function clearCompositionsCache(): void;
100
+
1
101
  declare function getKnowledge(name: string): string;
2
102
  declare function getAllKnowledge(): ReadonlyMap<string, string>;
3
103
  declare function getExample(name: string): string;
@@ -7,4 +107,4 @@ declare function getExampleFiles(names: string[]): string;
7
107
  /** Clear cached knowledge and examples. Useful for dev/watch mode. */
8
108
  declare function clearCache(): void;
9
109
 
10
- export { clearCache, getAllExamples, getAllKnowledge, getExample, getExampleFiles, getKnowledge, getKnowledgeFiles };
110
+ export { type AntiPattern, type AntiPatternCategory, type AntiPatternSeverity, type CompositionEdge, MIGRATION_SOURCES, type MigrationConceptRow, type MigrationPattern, type MigrationSourceId, clearAntiPatternCache, clearCache, clearCompositionsCache, clearMigrationCache, getAllExamples, getAllKnowledge, getAntiPatternById, getAntiPatterns, getCompositions, getCompositionsFor, getExample, getExampleFiles, getKnowledge, getKnowledgeFiles, getMigrationPattern, getMigrationPatterns, getMigrationSources, getReverseCompositionsFor };
package/dist/index.js CHANGED
@@ -1,12 +1,16 @@
1
- import {existsSync,readFileSync,readdirSync}from'fs';import {dirname,resolve,join}from'path';import {fileURLToPath}from'url';var h=dirname(fileURLToPath(import.meta.url)),g=resolve(h,"..");function s(n){let t=join(g,n);if(existsSync(t))return t;let e=join(g,"..",n);return existsSync(e)?e:t}var w=s("core"),A=s("ai"),y=s("examples"),R=s("api-skeleton.md"),r=null,i=null;function a(n,t){try{let e=readdirSync(n).filter(o=>o.endsWith(".md")||o.endsWith(".ts"));for(let o of e){let m=o.replace(/\.(md|ts)$/,"");t.set(m,readFileSync(join(n,o),"utf-8"));}}catch(e){if(e.code!=="ENOENT")throw e}}function u(){let n=new Map;a(w,n),a(A,n);try{n.set("api-skeleton",readFileSync(R,"utf-8"));}catch{}return n}function f(){let n=new Map;return a(y,n),n}function _(n){return r||(r=u()),r.get(n)??""}function O(){return r||(r=u()),r}function S(n){return i||(i=f()),i.get(n)??""}function P(){return i||(i=f()),i}function T(n){return n.map(t=>_(t)).filter(Boolean).join(`
1
+ import {existsSync,readFileSync,readdirSync}from'fs';import {dirname,resolve,join}from'path';import {fileURLToPath}from'url';var L=dirname(fileURLToPath(import.meta.url)),P=resolve(L,".."),a=null;function k(){let t=[join(P,"core","anti-patterns.md"),join(P,"..","core","anti-patterns.md")];for(let e of t)if(existsSync(e))return e;return t[0]??""}function z(t){return t.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").replace(/-{2,}/g,"-")}function K(t){let e=t.toLowerCase();return /schema|builder/.test(e)?"schema":/constraint|cross-?module|require/.test(e)?"constraint":/resolver|settle|start/.test(e)?"resolver":/deriv|passthrough/.test(e)?"derivation":/effect/.test(e)?"effect":/usedirective|react|hook/.test(e)?"react":/import|bracket|deep import|name|context|abbreviat/.test(e)?"naming":("module")}function G(t){let e=t.toLowerCase();return /nonexistent|missing|no error|deep import|async logic/.test(e)?"error":"warning"}function U(t){let e=t.split(`
2
+ `),r=[],n=null,o=/^##\s+(\d+)\.\s+(.+?)\s*$/;for(let s of e){let i=o.exec(s);if(i?.[1]&&i?.[2]){n&&r.push(n),n={number:Number(i[1]),title:i[2],body:""};continue}if(n){if(/^##\s+\D/.test(s)){r.push(n),n=null;break}n.body+=`${s}
3
+ `;}}return n&&r.push(n),r}function $(t){let e=t.match(/```typescript\n([\s\S]*?)```/),r=[];for(let p of t.split(`
4
+ `)){if(p.startsWith("```"))break;r.push(p);}let n=r.join(`
5
+ `).trim();if(!e?.[1])return {explanation:n};let o=e[1],s=o.match(/\/\/\s*WRONG[^\n]*\n([\s\S]*?)(?:\/\/\s*CORRECT|$)/),i=o.match(/\/\/\s*CORRECT[^\n]*\n([\s\S]*)/);return {explanation:n,badExample:s?.[1]?.trim()??void 0,goodExample:i?.[1]?.trim()??void 0}}function h(){if(a)return a;let t=k(),e;try{e=readFileSync(t,"utf-8");}catch{return a=Object.freeze([]),a}let n=U(e).map(o=>{let{explanation:s,badExample:i,goodExample:p}=$(o.body);return {id:z(o.title),number:o.number,title:o.title,severity:G(o.title),category:K(o.title),explanation:s,badExample:i,goodExample:p}});return a=Object.freeze(n),a}function D(){return h()}function B(t){return h().find(e=>e.id===t)}function W(){a=null;}var V=dirname(fileURLToPath(import.meta.url)),C=resolve(V,".."),R=["redux","zustand","xstate","mobx","jotai","recoil"],u=null;function Y(){let t=[join(C,"migration.json"),join(C,"..","migration.json")];for(let e of t)if(existsSync(e))return e;return t[0]??""}function S(){if(u)return u;try{let t=readFileSync(Y(),"utf-8"),e=JSON.parse(t);u=Object.freeze(e.sources??[]);}catch{u=Object.freeze([]);}return u}function Z(){return R}function tt(){return S()}function et(t){return S().find(e=>e.id===t)}function nt(){u=null;}var ct=dirname(fileURLToPath(import.meta.url)),E=resolve(ct,".."),g=null;function lt(){let t=[join(E,"compositions.json"),join(E,"..","compositions.json")];for(let e of t)if(existsSync(e))return e;return t[0]??""}function m(){if(g)return g;try{let t=readFileSync(lt(),"utf-8"),e=JSON.parse(t);g=Object.freeze(e.edges??[]);}catch{g=Object.freeze([]);}return g}function ut(){return m()}function gt(t){return m().filter(e=>e.from===t)}function pt(t){return m().filter(e=>e.to===t)}function ft(){g=null;}var Pt=dirname(fileURLToPath(import.meta.url)),v=resolve(Pt,"..");function f(t){let e=join(v,t);if(existsSync(e))return e;let r=join(v,"..",t);return existsSync(r)?r:e}var ht=f("core"),At=f("ai"),Ct=f("examples"),Rt=f("api-skeleton.md"),c=null,l=null;function y(t,e){try{let r=readdirSync(t).filter(n=>n.endsWith(".md")||n.endsWith(".ts"));for(let n of r){let o=n.replace(/\.(md|ts)$/,"");e.set(o,readFileSync(join(t,n),"utf-8"));}}catch(r){if(r.code!=="ENOENT")throw r}}function w(){let t=new Map;y(ht,t),y(At,t);try{t.set("api-skeleton",readFileSync(Rt,"utf-8"));}catch{}return t}function _(){let t=new Map;return y(Ct,t),t}function St(t){return c||(c=w()),c.get(t)??""}function Gt(){return c||(c=w()),c}function Mt(t){return l||(l=_()),l.get(t)??""}function Ut(){return l||(l=_()),l}function $t(t){return t.map(e=>St(e)).filter(Boolean).join(`
2
6
 
3
7
  ---
4
8
 
5
- `)}function k(n){return n.map(t=>{let e=S(t);return e?`### Example: ${t}
9
+ `)}function Dt(t){return t.map(e=>{let r=Mt(e);return r?`### Example: ${e}
6
10
 
7
11
  \`\`\`typescript
8
- ${e}
12
+ ${r}
9
13
  \`\`\``:""}).filter(Boolean).join(`
10
14
 
11
- `)}function v(){r=null,i=null;}export{v as clearCache,P as getAllExamples,O as getAllKnowledge,S as getExample,k as getExampleFiles,_ as getKnowledge,T as getKnowledgeFiles};//# sourceMappingURL=index.js.map
15
+ `)}function Bt(){c=null,l=null;}export{R as MIGRATION_SOURCES,W as clearAntiPatternCache,Bt as clearCache,ft as clearCompositionsCache,nt as clearMigrationCache,Ut as getAllExamples,Gt as getAllKnowledge,B as getAntiPatternById,D as getAntiPatterns,ut as getCompositions,gt as getCompositionsFor,Mt as getExample,Dt as getExampleFiles,St as getKnowledge,$t as getKnowledgeFiles,et as getMigrationPattern,tt as getMigrationPatterns,Z as getMigrationSources,pt as getReverseCompositionsFor};//# sourceMappingURL=index.js.map
12
16
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["__dirname","dirname","fileURLToPath","PKG_ROOT","resolve","resolveAsset","name","fromDist","join","existsSync","fromSrc","CORE_DIR","AI_DIR","EXAMPLES_DIR","API_SKELETON_PATH","knowledgeCache","exampleCache","loadDir","dir","map","files","readdirSync","f","file","readFileSync","err","loadAllKnowledge","loadAllExamples","getKnowledge","getAllKnowledge","getExample","getAllExamples","getKnowledgeFiles","names","getExampleFiles","content","clearCache"],"mappings":"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","file":"index.js","sourcesContent":["import { existsSync, readFileSync, readdirSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// Resolve package root: works both in src/ (dev) and dist/ (bundled)\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\n/**\n * Resolve a path relative to the package root.\n * Tries dist-relative first (bundled), then src-relative (dev).\n */\nfunction resolveAsset(name: string): string {\n const fromDist = join(PKG_ROOT, name);\n if (existsSync(fromDist)) {\n return fromDist;\n }\n\n const fromSrc = join(PKG_ROOT, \"..\", name);\n if (existsSync(fromSrc)) {\n return fromSrc;\n }\n\n return fromDist; // default, will just return empty maps\n}\n\nconst CORE_DIR = resolveAsset(\"core\");\nconst AI_DIR = resolveAsset(\"ai\");\nconst EXAMPLES_DIR = resolveAsset(\"examples\");\nconst API_SKELETON_PATH = resolveAsset(\"api-skeleton.md\");\n\nlet knowledgeCache: Map<string, string> | null = null;\nlet exampleCache: Map<string, string> | null = null;\n\nfunction loadDir(dir: string, map: Map<string, string>): void {\n try {\n const files = readdirSync(dir).filter(\n (f) => f.endsWith(\".md\") || f.endsWith(\".ts\"),\n );\n for (const file of files) {\n const name = file.replace(/\\.(md|ts)$/, \"\");\n map.set(name, readFileSync(join(dir, file), \"utf-8\"));\n }\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw err;\n }\n // directory does not exist — expected during tests or incomplete installs\n }\n}\n\nfunction loadAllKnowledge(): Map<string, string> {\n const map = new Map<string, string>();\n loadDir(CORE_DIR, map);\n loadDir(AI_DIR, map);\n\n // Include api-skeleton\n try {\n map.set(\"api-skeleton\", readFileSync(API_SKELETON_PATH, \"utf-8\"));\n } catch {\n // may not exist yet\n }\n\n return map;\n}\n\nfunction loadAllExamples(): Map<string, string> {\n const map = new Map<string, string>();\n loadDir(EXAMPLES_DIR, map);\n\n return map;\n}\n\nexport function getKnowledge(name: string): string {\n if (!knowledgeCache) {\n knowledgeCache = loadAllKnowledge();\n }\n\n return knowledgeCache.get(name) ?? \"\";\n}\n\nexport function getAllKnowledge(): ReadonlyMap<string, string> {\n if (!knowledgeCache) {\n knowledgeCache = loadAllKnowledge();\n }\n\n return knowledgeCache;\n}\n\nexport function getExample(name: string): string {\n if (!exampleCache) {\n exampleCache = loadAllExamples();\n }\n\n return exampleCache.get(name) ?? \"\";\n}\n\nexport function getAllExamples(): ReadonlyMap<string, string> {\n if (!exampleCache) {\n exampleCache = loadAllExamples();\n }\n\n return exampleCache;\n}\n\nexport function getKnowledgeFiles(names: string[]): string {\n return names\n .map((name) => getKnowledge(name))\n .filter(Boolean)\n .join(\"\\n\\n---\\n\\n\");\n}\n\nexport function getExampleFiles(names: string[]): string {\n return names\n .map((name) => {\n const content = getExample(name);\n if (!content) {\n return \"\";\n }\n\n return `### Example: ${name}\\n\\n\\`\\`\\`typescript\\n${content}\\n\\`\\`\\``;\n })\n .filter(Boolean)\n .join(\"\\n\\n\");\n}\n\n/** Clear cached knowledge and examples. Useful for dev/watch mode. */\nexport function clearCache(): void {\n knowledgeCache = null;\n exampleCache = null;\n}\n"]}
1
+ {"version":3,"sources":["../src/parsers/anti-patterns.ts","../src/parsers/migration.ts","../src/parsers/compositions.ts","../src/index.ts"],"names":["__dirname","dirname","fileURLToPath","PKG_ROOT","resolve","cache","resolveSourcePath","candidates","join","c","existsSync","slugify","title","categorize","lower","severityFor","splitSections","md","lines","sections","current","heading","line","match","extractExamples","body","codeBlock","explanationLines","explanation","code","wrongMatch","correctMatch","loadAntiPatterns","sourcePath","readFileSync","out","section","badExample","goodExample","getAntiPatterns","getAntiPatternById","id","ap","clearAntiPatternCache","MIGRATION_SOURCES","load","raw","parsed","getMigrationSources","getMigrationPatterns","getMigrationPattern","source","p","clearMigrationCache","getCompositions","getCompositionsFor","packageName","getReverseCompositionsFor","clearCompositionsCache","resolveAsset","name","fromDist","fromSrc","CORE_DIR","AI_DIR","EXAMPLES_DIR","API_SKELETON_PATH","knowledgeCache","exampleCache","loadDir","dir","map","files","readdirSync","f","file","err","loadAllKnowledge","loadAllExamples","getKnowledge","getAllKnowledge","getExample","getAllExamples","getKnowledgeFiles","names","getExampleFiles","content","clearCache"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Parser for `packages/knowledge/core/anti-patterns.md` → structured\n * `AntiPattern[]` data. Lazy + cached so the cost is paid once per\n * process at first call.\n *\n * The source markdown is organized as numbered sections (`## N. Title`)\n * each containing a TypeScript code block with `// WRONG` and\n * `// CORRECT` examples. Some sections include prose between the\n * heading and the code block — captured as `explanation`.\n *\n * IDs are kebab-cased slugs derived from the heading text; stable\n * across releases as long as the heading isn't renamed.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\nexport type AntiPatternSeverity = \"error\" | \"warning\" | \"info\";\nexport type AntiPatternCategory =\n | \"module\"\n | \"schema\"\n | \"constraint\"\n | \"resolver\"\n | \"derivation\"\n | \"effect\"\n | \"naming\"\n | \"react\"\n | \"composition\";\n\nexport interface AntiPattern {\n /** Stable slug, derived from the heading text. */\n id: string;\n /** Section number in the source markdown (1..N). */\n number: number;\n /** Human-readable title (the heading text minus the number prefix). */\n title: string;\n /** Default severity. Heuristic; tunable per-rule later. */\n severity: AntiPatternSeverity;\n /** Heuristic category from title keywords. */\n category: AntiPatternCategory;\n /** Body prose between heading and the first code block (often empty). */\n explanation: string;\n /** The `// WRONG` example, if present. */\n badExample?: string;\n /** The `// CORRECT` example, if present. */\n goodExample?: string;\n}\n\nlet cache: ReadonlyArray<AntiPattern> | null = null;\n\nfunction resolveSourcePath(): string {\n const candidates = [\n join(PKG_ROOT, \"core\", \"anti-patterns.md\"),\n join(PKG_ROOT, \"..\", \"core\", \"anti-patterns.md\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) {\n return c;\n }\n }\n return candidates[0] ?? \"\";\n}\n\nfunction slugify(title: string): string {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .replace(/-{2,}/g, \"-\");\n}\n\nfunction categorize(title: string): AntiPatternCategory {\n const lower = title.toLowerCase();\n if (/schema|builder/.test(lower)) {\n return \"schema\";\n }\n if (/constraint|cross-?module|require/.test(lower)) {\n return \"constraint\";\n }\n if (/resolver|settle|start/.test(lower)) {\n return \"resolver\";\n }\n if (/deriv|passthrough/.test(lower)) {\n return \"derivation\";\n }\n if (/effect/.test(lower)) {\n return \"effect\";\n }\n if (/usedirective|react|hook/.test(lower)) {\n return \"react\";\n }\n if (/import|bracket|deep import|name|context|abbreviat/.test(lower)) {\n return \"naming\";\n }\n if (/init|module|config/.test(lower)) {\n return \"module\";\n }\n return \"module\";\n}\n\nfunction severityFor(title: string): AntiPatternSeverity {\n const lower = title.toLowerCase();\n if (/nonexistent|missing|no error|deep import|async logic/.test(lower)) {\n return \"error\";\n }\n return \"warning\";\n}\n\ninterface Section {\n number: number;\n title: string;\n body: string;\n}\n\nfunction splitSections(md: string): Section[] {\n const lines = md.split(\"\\n\");\n const sections: Section[] = [];\n let current: Section | null = null;\n const heading = /^##\\s+(\\d+)\\.\\s+(.+?)\\s*$/;\n for (const line of lines) {\n const match = heading.exec(line);\n if (match?.[1] && match?.[2]) {\n if (current) {\n sections.push(current);\n }\n current = {\n number: Number(match[1]),\n title: match[2],\n body: \"\",\n };\n continue;\n }\n if (current) {\n // Stop at the first non-numbered ## section (e.g. \"Quick Reference Checklist\")\n if (/^##\\s+\\D/.test(line)) {\n sections.push(current);\n current = null;\n break;\n }\n current.body += `${line}\\n`;\n }\n }\n if (current) {\n sections.push(current);\n }\n return sections;\n}\n\nfunction extractExamples(body: string): {\n explanation: string;\n badExample?: string;\n goodExample?: string;\n} {\n const codeBlock = body.match(/```typescript\\n([\\s\\S]*?)```/);\n const explanationLines: string[] = [];\n for (const line of body.split(\"\\n\")) {\n if (line.startsWith(\"```\")) {\n break;\n }\n explanationLines.push(line);\n }\n const explanation = explanationLines.join(\"\\n\").trim();\n\n if (!codeBlock?.[1]) {\n return { explanation };\n }\n\n const code = codeBlock[1];\n // Split on the WRONG/CORRECT comment markers.\n const wrongMatch = code.match(\n /\\/\\/\\s*WRONG[^\\n]*\\n([\\s\\S]*?)(?:\\/\\/\\s*CORRECT|$)/,\n );\n const correctMatch = code.match(/\\/\\/\\s*CORRECT[^\\n]*\\n([\\s\\S]*)/);\n\n return {\n explanation,\n badExample: wrongMatch?.[1]?.trim() ?? undefined,\n goodExample: correctMatch?.[1]?.trim() ?? undefined,\n };\n}\n\nfunction loadAntiPatterns(): ReadonlyArray<AntiPattern> {\n if (cache) {\n return cache;\n }\n\n const sourcePath = resolveSourcePath();\n let md: string;\n try {\n md = readFileSync(sourcePath, \"utf-8\");\n } catch {\n cache = Object.freeze([]);\n return cache;\n }\n\n const sections = splitSections(md);\n const out: AntiPattern[] = sections.map((section) => {\n const { explanation, badExample, goodExample } = extractExamples(\n section.body,\n );\n return {\n id: slugify(section.title),\n number: section.number,\n title: section.title,\n severity: severityFor(section.title),\n category: categorize(section.title),\n explanation,\n badExample,\n goodExample,\n };\n });\n\n cache = Object.freeze(out);\n return cache;\n}\n\n/** Return every parsed anti-pattern, stable order, frozen. */\nexport function getAntiPatterns(): ReadonlyArray<AntiPattern> {\n return loadAntiPatterns();\n}\n\n/** Look up one anti-pattern by its slug id. */\nexport function getAntiPatternById(id: string): AntiPattern | undefined {\n return loadAntiPatterns().find((ap) => ap.id === id);\n}\n\n/** Clear the in-memory cache. Test / watch-mode helper. */\nexport function clearAntiPatternCache(): void {\n cache = null;\n}\n","/**\n * Loader for `packages/knowledge/migration.json` → structured\n * migration patterns. Lazy + cached.\n *\n * The JSON ships in the published tarball; consumers don't need to\n * parse markdown at runtime.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\n/** Supported source-library identifiers. */\nexport const MIGRATION_SOURCES = [\n \"redux\",\n \"zustand\",\n \"xstate\",\n \"mobx\",\n \"jotai\",\n \"recoil\",\n] as const;\nexport type MigrationSourceId = (typeof MIGRATION_SOURCES)[number];\n\nexport interface MigrationConceptRow {\n from: string;\n to: string;\n note: string;\n}\n\nexport interface MigrationPattern {\n id: MigrationSourceId;\n name: string;\n conceptMap: MigrationConceptRow[];\n steps: string[];\n before: string;\n after: string;\n}\n\ninterface MigrationFile {\n version: number;\n sources: MigrationPattern[];\n}\n\nlet cache: ReadonlyArray<MigrationPattern> | null = null;\n\nfunction resolveSourcePath(): string {\n const candidates = [\n join(PKG_ROOT, \"migration.json\"),\n join(PKG_ROOT, \"..\", \"migration.json\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) {\n return c;\n }\n }\n return candidates[0] ?? \"\";\n}\n\nfunction load(): ReadonlyArray<MigrationPattern> {\n if (cache) {\n return cache;\n }\n try {\n const raw = readFileSync(resolveSourcePath(), \"utf-8\");\n const parsed = JSON.parse(raw) as MigrationFile;\n cache = Object.freeze(parsed.sources ?? []);\n } catch {\n cache = Object.freeze([]);\n }\n return cache;\n}\n\n/** All supported source library IDs. */\nexport function getMigrationSources(): ReadonlyArray<MigrationSourceId> {\n return MIGRATION_SOURCES;\n}\n\n/** All migration patterns, in registry order. */\nexport function getMigrationPatterns(): ReadonlyArray<MigrationPattern> {\n return load();\n}\n\n/** One migration pattern by its source-library id. */\nexport function getMigrationPattern(\n source: string,\n): MigrationPattern | undefined {\n return load().find((p) => p.id === source);\n}\n\n/** Test / watch-mode helper. */\nexport function clearMigrationCache(): void {\n cache = null;\n}\n","/**\n * Loader for `packages/knowledge/compositions.json` → typed\n * composition graph. Lazy + cached.\n *\n * The JSON ships in the published tarball. Adjacency list rather\n * than per-package projections so consumers can answer both\n * \"what does X compose with?\" and \"what composes with X?\" without\n * duplicate data.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n// After bundling, all parsers live in dist/index.js — one level up\n// from dist reaches the package root. In src/ during dev, the file is\n// two levels up; the fallback in `resolveSourcePath` handles that.\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\nexport interface CompositionEdge {\n from: string;\n to: string;\n reason: string;\n}\n\ninterface CompositionsFile {\n version: number;\n edges: CompositionEdge[];\n}\n\nlet cache: ReadonlyArray<CompositionEdge> | null = null;\n\nfunction resolveSourcePath(): string {\n const candidates = [\n join(PKG_ROOT, \"compositions.json\"),\n join(PKG_ROOT, \"..\", \"compositions.json\"),\n ];\n for (const c of candidates) {\n if (existsSync(c)) {\n return c;\n }\n }\n return candidates[0] ?? \"\";\n}\n\nfunction load(): ReadonlyArray<CompositionEdge> {\n if (cache) {\n return cache;\n }\n try {\n const raw = readFileSync(resolveSourcePath(), \"utf-8\");\n const parsed = JSON.parse(raw) as CompositionsFile;\n cache = Object.freeze(parsed.edges ?? []);\n } catch {\n cache = Object.freeze([]);\n }\n return cache;\n}\n\n/** All composition edges as a flat list. */\nexport function getCompositions(): ReadonlyArray<CompositionEdge> {\n return load();\n}\n\n/**\n * All edges originating at the given package. Returns an empty array\n * when the package is unknown or has no outgoing compositions.\n */\nexport function getCompositionsFor(\n packageName: string,\n): ReadonlyArray<CompositionEdge> {\n return load().filter((e) => e.from === packageName);\n}\n\n/**\n * All edges arriving at the given package — \"who composes WITH me?\"\n */\nexport function getReverseCompositionsFor(\n packageName: string,\n): ReadonlyArray<CompositionEdge> {\n return load().filter((e) => e.to === packageName);\n}\n\n/** Test / watch-mode helper. */\nexport function clearCompositionsCache(): void {\n cache = null;\n}\n","import { existsSync, readFileSync, readdirSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// Resolve package root: works both in src/ (dev) and dist/ (bundled)\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = resolve(__dirname, \"..\");\n\n/**\n * Resolve a path relative to the package root.\n * Tries dist-relative first (bundled), then src-relative (dev).\n */\nfunction resolveAsset(name: string): string {\n const fromDist = join(PKG_ROOT, name);\n if (existsSync(fromDist)) {\n return fromDist;\n }\n\n const fromSrc = join(PKG_ROOT, \"..\", name);\n if (existsSync(fromSrc)) {\n return fromSrc;\n }\n\n return fromDist; // default, will just return empty maps\n}\n\nconst CORE_DIR = resolveAsset(\"core\");\nconst AI_DIR = resolveAsset(\"ai\");\nconst EXAMPLES_DIR = resolveAsset(\"examples\");\nconst API_SKELETON_PATH = resolveAsset(\"api-skeleton.md\");\n\nlet knowledgeCache: Map<string, string> | null = null;\nlet exampleCache: Map<string, string> | null = null;\n\nfunction loadDir(dir: string, map: Map<string, string>): void {\n try {\n const files = readdirSync(dir).filter(\n (f) => f.endsWith(\".md\") || f.endsWith(\".ts\"),\n );\n for (const file of files) {\n const name = file.replace(/\\.(md|ts)$/, \"\");\n map.set(name, readFileSync(join(dir, file), \"utf-8\"));\n }\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n throw err;\n }\n // directory does not exist — expected during tests or incomplete installs\n }\n}\n\nfunction loadAllKnowledge(): Map<string, string> {\n const map = new Map<string, string>();\n loadDir(CORE_DIR, map);\n loadDir(AI_DIR, map);\n\n // Include api-skeleton\n try {\n map.set(\"api-skeleton\", readFileSync(API_SKELETON_PATH, \"utf-8\"));\n } catch {\n // may not exist yet\n }\n\n return map;\n}\n\nfunction loadAllExamples(): Map<string, string> {\n const map = new Map<string, string>();\n loadDir(EXAMPLES_DIR, map);\n\n return map;\n}\n\nexport function getKnowledge(name: string): string {\n if (!knowledgeCache) {\n knowledgeCache = loadAllKnowledge();\n }\n\n return knowledgeCache.get(name) ?? \"\";\n}\n\nexport function getAllKnowledge(): ReadonlyMap<string, string> {\n if (!knowledgeCache) {\n knowledgeCache = loadAllKnowledge();\n }\n\n return knowledgeCache;\n}\n\nexport function getExample(name: string): string {\n if (!exampleCache) {\n exampleCache = loadAllExamples();\n }\n\n return exampleCache.get(name) ?? \"\";\n}\n\nexport function getAllExamples(): ReadonlyMap<string, string> {\n if (!exampleCache) {\n exampleCache = loadAllExamples();\n }\n\n return exampleCache;\n}\n\nexport function getKnowledgeFiles(names: string[]): string {\n return names\n .map((name) => getKnowledge(name))\n .filter(Boolean)\n .join(\"\\n\\n---\\n\\n\");\n}\n\nexport function getExampleFiles(names: string[]): string {\n return names\n .map((name) => {\n const content = getExample(name);\n if (!content) {\n return \"\";\n }\n\n return `### Example: ${name}\\n\\n\\`\\`\\`typescript\\n${content}\\n\\`\\`\\``;\n })\n .filter(Boolean)\n .join(\"\\n\\n\");\n}\n\n/** Clear cached knowledge and examples. Useful for dev/watch mode. */\nexport function clearCache(): void {\n knowledgeCache = null;\n exampleCache = null;\n}\n\n// ---------------------------------------------------------------------------\n// Structured data APIs (v1.16.0 — back the MCP `list_review_rules`,\n// `get_review_rule`, `list_migration_sources`, `get_migration_pattern`,\n// `get_composable_packages` tools).\n// ---------------------------------------------------------------------------\n\nexport {\n type AntiPattern,\n type AntiPatternCategory,\n type AntiPatternSeverity,\n clearAntiPatternCache,\n getAntiPatternById,\n getAntiPatterns,\n} from \"./parsers/anti-patterns.js\";\n\nexport {\n type MigrationConceptRow,\n type MigrationPattern,\n type MigrationSourceId,\n MIGRATION_SOURCES,\n clearMigrationCache,\n getMigrationPattern,\n getMigrationPatterns,\n getMigrationSources,\n} from \"./parsers/migration.js\";\n\nexport {\n type CompositionEdge,\n clearCompositionsCache,\n getCompositions,\n getCompositionsFor,\n getReverseCompositionsFor,\n} from \"./parsers/compositions.js\";\n"]}
package/migration.json ADDED
@@ -0,0 +1,277 @@
1
+ {
2
+ "$schema": "./migration.schema.json",
3
+ "version": 1,
4
+ "sources": [
5
+ {
6
+ "id": "redux",
7
+ "name": "Redux",
8
+ "conceptMap": [
9
+ {
10
+ "from": "Store",
11
+ "to": "System",
12
+ "note": "createSystem() wraps modules"
13
+ },
14
+ {
15
+ "from": "Slice / Reducer",
16
+ "to": "Module",
17
+ "note": "createModule() with schema + init"
18
+ },
19
+ {
20
+ "from": "State fields",
21
+ "to": "Facts",
22
+ "note": "Schema keys with t.*() type builders"
23
+ },
24
+ {
25
+ "from": "Selectors",
26
+ "to": "Derivations",
27
+ "note": "Auto-tracked, no createSelector needed"
28
+ },
29
+ {
30
+ "from": "Actions",
31
+ "to": "Requirements",
32
+ "note": "Typed with UPPER_SNAKE_CASE"
33
+ },
34
+ {
35
+ "from": "Thunks / Sagas",
36
+ "to": "Resolvers",
37
+ "note": "Async handlers with retry, dedup"
38
+ },
39
+ { "from": "Middleware", "to": "Plugins", "note": "Lifecycle hooks" },
40
+ {
41
+ "from": "useSelector(state => state.x)",
42
+ "to": "useFact(\"x\") / useDerived(\"y\")",
43
+ "note": "From @directive-run/react. Subscribe directly to a fact or derivation by key — no Provider, no createSelector"
44
+ },
45
+ {
46
+ "from": "dispatch(action)",
47
+ "to": "system.dispatch(event)",
48
+ "note": "Or constraint auto-triggers"
49
+ },
50
+ {
51
+ "from": "configureStore",
52
+ "to": "createSystem",
53
+ "note": "With plugins instead of middleware"
54
+ }
55
+ ],
56
+ "steps": [
57
+ "Replace createStore / configureStore with createSystem({ module })",
58
+ "Convert each slice into a createModule call with schema + init",
59
+ "Rewrite reducers as direct mutations on facts (immer-style nested writes are fine)",
60
+ "Rewrite thunks/sagas as constraint + resolver pairs",
61
+ "Replace useSelector(state.x) calls with useFact(\"x\") / useDerived(\"y\")",
62
+ "Drop the action-type constants — requirement types are colocated with the resolver"
63
+ ],
64
+ "before": "import { createSlice } from \"@reduxjs/toolkit\";\n\nconst counterSlice = createSlice({\n name: \"counter\",\n initialState: { value: 0, status: \"idle\" },\n reducers: {\n increment: (state) => { state.value += 1; },\n },\n extraReducers: (builder) => {\n builder.addCase(fetchCount.fulfilled, (state, action) => {\n state.value = action.payload;\n state.status = \"loaded\";\n });\n },\n});",
65
+ "after": "import { createModule, t } from \"@directive-run/core\";\n\nexport const counter = createModule(\"counter\", {\n schema: {\n facts: {\n value: t.number(),\n status: t.string<\"idle\" | \"loading\" | \"loaded\">(),\n },\n requirements: {\n FETCH_COUNT: {},\n },\n },\n init: (facts) => {\n facts.value = 0;\n facts.status = \"idle\";\n },\n constraints: {\n needsData: {\n when: (facts) => facts.status === \"idle\",\n require: { type: \"FETCH_COUNT\" },\n },\n },\n resolvers: {\n fetchCount: {\n requirement: \"FETCH_COUNT\",\n retry: { attempts: 3, backoff: \"exponential\" },\n resolve: async (req, context) => {\n context.facts.status = \"loading\";\n const response = await fetch(\"/api/count\");\n const data = await response.json();\n context.facts.value = data.count;\n context.facts.status = \"loaded\";\n },\n },\n },\n});"
66
+ },
67
+ {
68
+ "id": "zustand",
69
+ "name": "Zustand",
70
+ "conceptMap": [
71
+ {
72
+ "from": "create()",
73
+ "to": "createModule()",
74
+ "note": "Module is more structured"
75
+ },
76
+ {
77
+ "from": "Set callbacks",
78
+ "to": "Direct mutation in init / events",
79
+ "note": "facts.x = value"
80
+ },
81
+ {
82
+ "from": "Get callbacks",
83
+ "to": "Read from facts proxy",
84
+ "note": "facts.x in a derive function"
85
+ },
86
+ {
87
+ "from": "Slices via spread",
88
+ "to": "Multiple modules + crossModuleDeps",
89
+ "note": "Composition is structural, not spread-based"
90
+ },
91
+ {
92
+ "from": "Persist middleware",
93
+ "to": "persistencePlugin",
94
+ "note": "From @directive-run/core/plugins"
95
+ },
96
+ {
97
+ "from": "subscribe()",
98
+ "to": "system.subscribe() or useFact()",
99
+ "note": "Auto-cleanup with React hooks"
100
+ }
101
+ ],
102
+ "steps": [
103
+ "Replace create((set, get) => ({ ... })) with createModule + schema",
104
+ "Move state fields into schema.facts with t.*() builders",
105
+ "Convert setter functions into events with direct mutation",
106
+ "Replace get().x reads inside derived values with derive: { x: (facts) => facts.x }",
107
+ "Wire the system to React via useFact / useDerived"
108
+ ],
109
+ "before": "import { create } from \"zustand\";\n\nconst useCounter = create((set, get) => ({\n value: 0,\n increment: () => set({ value: get().value + 1 }),\n reset: () => set({ value: 0 }),\n}));",
110
+ "after": "import { createModule, t } from \"@directive-run/core\";\n\nexport const counter = createModule(\"counter\", {\n schema: {\n facts: { value: t.number() },\n events: {\n increment: {},\n reset: {},\n },\n },\n init: (facts) => {\n facts.value = 0;\n },\n events: {\n increment: (facts) => {\n facts.value += 1;\n },\n reset: (facts) => {\n facts.value = 0;\n },\n },\n});"
111
+ },
112
+ {
113
+ "id": "xstate",
114
+ "name": "XState",
115
+ "conceptMap": [
116
+ {
117
+ "from": "createMachine",
118
+ "to": "createModule",
119
+ "note": "States become discriminated facts"
120
+ },
121
+ {
122
+ "from": "States as nodes",
123
+ "to": "Status fact with t.string<Union>()",
124
+ "note": "Union types make the state space explicit"
125
+ },
126
+ {
127
+ "from": "Transitions on event",
128
+ "to": "events: { eventName: (facts, payload) => mutation }",
129
+ "note": "Direct mutation drives reconciliation"
130
+ },
131
+ {
132
+ "from": "Guards",
133
+ "to": "when in constraints",
134
+ "note": "Constraints are guards on requirement emission"
135
+ },
136
+ {
137
+ "from": "Actions",
138
+ "to": "Direct fact mutation or constraint-triggered resolvers",
139
+ "note": ""
140
+ },
141
+ {
142
+ "from": "Services / actors",
143
+ "to": "Resolvers",
144
+ "note": "Async work with retry, dedup, timeout"
145
+ },
146
+ {
147
+ "from": "Hierarchical states",
148
+ "to": "Multiple modules + crossModuleDeps",
149
+ "note": "Composition over nesting"
150
+ },
151
+ {
152
+ "from": "interpret(machine).start()",
153
+ "to": "system.start()",
154
+ "note": "Or auto-started via createSystem"
155
+ }
156
+ ],
157
+ "steps": [
158
+ "Identify the state union — those become a Status fact with t.string<Union>()",
159
+ "Map each state-to-state transition into an event or constraint",
160
+ "Move side-effecting services into resolvers with retry policies",
161
+ "Replace guards with constraint when functions",
162
+ "If the machine is hierarchical, split into multiple modules"
163
+ ],
164
+ "before": "import { createMachine } from \"xstate\";\n\nconst trafficLight = createMachine({\n id: \"traffic\",\n initial: \"red\",\n states: {\n red: { on: { TIMER: \"green\" } },\n yellow: { on: { TIMER: \"red\" } },\n green: { on: { TIMER: \"yellow\" } },\n },\n});",
165
+ "after": "import { createModule, t } from \"@directive-run/core\";\n\ntype Phase = \"red\" | \"yellow\" | \"green\";\nconst NEXT: Record<Phase, Phase> = { red: \"green\", green: \"yellow\", yellow: \"red\" };\n\nexport const trafficLight = createModule(\"traffic-light\", {\n schema: {\n facts: { phase: t.string<Phase>() },\n events: { timer: {} },\n },\n init: (facts) => {\n facts.phase = \"red\";\n },\n events: {\n timer: (facts) => {\n facts.phase = NEXT[facts.phase];\n },\n },\n});"
166
+ },
167
+ {
168
+ "id": "mobx",
169
+ "name": "MobX",
170
+ "conceptMap": [
171
+ {
172
+ "from": "Observable",
173
+ "to": "Fact",
174
+ "note": "All facts are reactive"
175
+ },
176
+ {
177
+ "from": "Computed",
178
+ "to": "Derivation",
179
+ "note": "Auto-tracked, no decorator needed"
180
+ },
181
+ {
182
+ "from": "Action / runInAction",
183
+ "to": "Event handler",
184
+ "note": "Mutations batch into one reconciliation"
185
+ },
186
+ {
187
+ "from": "Class store",
188
+ "to": "createModule",
189
+ "note": "No this binding"
190
+ },
191
+ {
192
+ "from": "Reaction / autorun",
193
+ "to": "Effect",
194
+ "note": "Fire-and-forget side effects"
195
+ }
196
+ ],
197
+ "steps": [
198
+ "Convert observable class fields into schema.facts",
199
+ "Convert @computed methods into derive functions",
200
+ "Convert @action methods into events with direct mutation",
201
+ "Move autorun / reaction blocks into effects"
202
+ ],
203
+ "before": "import { makeAutoObservable } from \"mobx\";\n\nclass CounterStore {\n value = 0;\n get isHigh() { return this.value > 10; }\n increment() { this.value += 1; }\n constructor() { makeAutoObservable(this); }\n}",
204
+ "after": "import { createModule, t } from \"@directive-run/core\";\n\nexport const counter = createModule(\"counter\", {\n schema: {\n facts: { value: t.number() },\n derivations: { isHigh: t.boolean() },\n events: { increment: {} },\n },\n init: (facts) => {\n facts.value = 0;\n },\n derive: {\n isHigh: (facts) => facts.value > 10,\n },\n events: {\n increment: (facts) => {\n facts.value += 1;\n },\n },\n});"
205
+ },
206
+ {
207
+ "id": "jotai",
208
+ "name": "Jotai",
209
+ "conceptMap": [
210
+ { "from": "atom()", "to": "Fact", "note": "Schema.facts entry" },
211
+ {
212
+ "from": "derivedAtom",
213
+ "to": "Derivation",
214
+ "note": "Auto-tracked computed value"
215
+ },
216
+ {
217
+ "from": "useAtom",
218
+ "to": "useFact / useDerived",
219
+ "note": "From @directive-run/react"
220
+ },
221
+ {
222
+ "from": "atomWithReducer",
223
+ "to": "Module with events",
224
+ "note": "Direct fact mutation in event handlers"
225
+ },
226
+ {
227
+ "from": "Provider",
228
+ "to": "DirectiveProvider",
229
+ "note": "From @directive-run/react"
230
+ }
231
+ ],
232
+ "steps": [
233
+ "Group related atoms into a single module's schema.facts",
234
+ "Convert derivedAtom into derive function",
235
+ "Replace useAtom() with useFact() / useDerived()",
236
+ "Wrap the React tree in DirectiveProvider"
237
+ ],
238
+ "before": "import { atom, useAtom } from \"jotai\";\n\nconst countAtom = atom(0);\nconst doubledAtom = atom((get) => get(countAtom) * 2);\n\nfunction Counter() {\n const [count, setCount] = useAtom(countAtom);\n const [doubled] = useAtom(doubledAtom);\n return <button onClick={() => setCount(count + 1)}>{count} ({doubled})</button>;\n}",
239
+ "after": "import { createModule, createSystem, t } from \"@directive-run/core\";\nimport { createDirectiveContext } from \"@directive-run/react\";\n\nconst counter = createModule(\"counter\", {\n schema: {\n facts: { count: t.number() },\n derivations: { doubled: t.number() },\n events: { increment: {} },\n },\n init: (facts) => { facts.count = 0; },\n derive: { doubled: (facts) => facts.count * 2 },\n events: { increment: (facts) => { facts.count += 1; } },\n});\n\nconst system = createSystem({ module: counter });\nconst { useFact, useDerived, useEvents } = createDirectiveContext(system);\n\nfunction Counter() {\n const count = useFact(\"count\");\n const doubled = useDerived(\"doubled\");\n const { increment } = useEvents();\n return <button onClick={() => increment({})}>{count} ({doubled})</button>;\n}"
240
+ },
241
+ {
242
+ "id": "recoil",
243
+ "name": "Recoil",
244
+ "conceptMap": [
245
+ { "from": "atom", "to": "Fact", "note": "Schema.facts entry" },
246
+ {
247
+ "from": "selector",
248
+ "to": "Derivation",
249
+ "note": "Auto-tracked, async via resolvers"
250
+ },
251
+ {
252
+ "from": "useRecoilState",
253
+ "to": "useFact + useEvents",
254
+ "note": "Mutation is event dispatch"
255
+ },
256
+ {
257
+ "from": "selectorFamily",
258
+ "to": "Parameterized derivation or constraint+resolver pair",
259
+ "note": ""
260
+ },
261
+ {
262
+ "from": "RecoilRoot",
263
+ "to": "DirectiveProvider",
264
+ "note": "From @directive-run/react"
265
+ }
266
+ ],
267
+ "steps": [
268
+ "Map every Recoil atom to a fact in a module's schema",
269
+ "Convert selectors into derive functions",
270
+ "For async selectors, model as constraint + resolver",
271
+ "Replace useRecoilState / useRecoilValue with useFact / useDerived"
272
+ ],
273
+ "before": "import { atom, selector, useRecoilState } from \"recoil\";\n\nconst countState = atom({ key: \"count\", default: 0 });\nconst doubledState = selector({\n key: \"doubled\",\n get: ({ get }) => get(countState) * 2,\n});",
274
+ "after": "import { createModule, t } from \"@directive-run/core\";\n\nexport const counter = createModule(\"counter\", {\n schema: {\n facts: { count: t.number() },\n derivations: { doubled: t.number() },\n events: { increment: {} },\n },\n init: (facts) => { facts.count = 0; },\n derive: { doubled: (facts) => facts.count * 2 },\n events: { increment: (facts) => { facts.count += 1; } },\n});"
275
+ }
276
+ ]
277
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directive-run/knowledge",
3
- "version": "1.16.0",
3
+ "version": "1.17.1",
4
4
  "description": "Knowledge files, examples, and validation for Directive — the constraint-driven TypeScript runtime.",
5
5
  "license": "(MIT OR Apache-2.0)",
6
6
  "author": "Jason Comes",
@@ -43,7 +43,9 @@
43
43
  "ai",
44
44
  "examples",
45
45
  "api-skeleton.md",
46
- "sitemap.md"
46
+ "sitemap.md",
47
+ "compositions.json",
48
+ "migration.json"
47
49
  ],
48
50
  "devDependencies": {
49
51
  "@types/node": "^25.2.0",
@@ -51,8 +53,8 @@
51
53
  "tsx": "^4.19.2",
52
54
  "typescript": "^5.7.2",
53
55
  "vitest": "^3.0.0",
54
- "@directive-run/core": "1.16.0",
55
- "@directive-run/ai": "1.16.0"
56
+ "@directive-run/core": "1.17.1",
57
+ "@directive-run/ai": "1.17.1"
56
58
  },
57
59
  "scripts": {
58
60
  "build": "tsx scripts/generate-api-skeleton.ts && tsx scripts/generate-sitemap.ts && tsx scripts/extract-examples.ts && tsup",
package/sitemap.md CHANGED
@@ -15,6 +15,12 @@ Website: https://directive.run
15
15
  - [Comparison](https://directive.run/docs/comparison)
16
16
  - [Choosing Primitives](https://directive.run/docs/choosing-primitives)
17
17
 
18
+ ### AI Tooling
19
+ - [IDE Integration](https://directive.run/docs/ide-integration)
20
+ - [Claude Code plugin](https://directive.run/docs/ide-integration#claude-code)
21
+ - [CLI ai-rules](https://directive.run/docs/ide-integration#cli-ai-rules)
22
+ - [Knowledge package](https://directive.run/docs/ide-integration#knowledge-package)
23
+
18
24
  ### Core API
19
25
  - [Overview](https://directive.run/docs/core-api)
20
26
  - [Module & System](https://directive.run/docs/module-system)