@soleri/core 2.11.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/data/flows/build.flow.yaml +128 -0
  2. package/data/flows/deliver.flow.yaml +110 -0
  3. package/data/flows/design.flow.yaml +108 -0
  4. package/data/flows/enhance.flow.yaml +90 -0
  5. package/data/flows/explore.flow.yaml +84 -0
  6. package/data/flows/fix.flow.yaml +90 -0
  7. package/data/flows/plan.flow.yaml +87 -0
  8. package/data/flows/review.flow.yaml +90 -0
  9. package/dist/brain/brain.d.ts.map +1 -1
  10. package/dist/brain/brain.js +10 -0
  11. package/dist/brain/brain.js.map +1 -1
  12. package/dist/brain/intelligence.d.ts.map +1 -1
  13. package/dist/brain/intelligence.js +16 -2
  14. package/dist/brain/intelligence.js.map +1 -1
  15. package/dist/capabilities/chain-mapping.d.ts +21 -0
  16. package/dist/capabilities/chain-mapping.d.ts.map +1 -0
  17. package/dist/capabilities/chain-mapping.js +86 -0
  18. package/dist/capabilities/chain-mapping.js.map +1 -0
  19. package/dist/capabilities/index.d.ts +10 -0
  20. package/dist/capabilities/index.d.ts.map +1 -0
  21. package/dist/capabilities/index.js +8 -0
  22. package/dist/capabilities/index.js.map +1 -0
  23. package/dist/capabilities/registry.d.ts +95 -0
  24. package/dist/capabilities/registry.d.ts.map +1 -0
  25. package/dist/capabilities/registry.js +227 -0
  26. package/dist/capabilities/registry.js.map +1 -0
  27. package/dist/capabilities/types.d.ts +106 -0
  28. package/dist/capabilities/types.d.ts.map +1 -0
  29. package/dist/capabilities/types.js +12 -0
  30. package/dist/capabilities/types.js.map +1 -0
  31. package/dist/control/intent-router.d.ts.map +1 -1
  32. package/dist/control/intent-router.js +58 -2
  33. package/dist/control/intent-router.js.map +1 -1
  34. package/dist/domain-packs/index.d.ts +8 -0
  35. package/dist/domain-packs/index.d.ts.map +1 -0
  36. package/dist/domain-packs/index.js +8 -0
  37. package/dist/domain-packs/index.js.map +1 -0
  38. package/dist/domain-packs/inject-rules.d.ts +24 -0
  39. package/dist/domain-packs/inject-rules.d.ts.map +1 -0
  40. package/dist/domain-packs/inject-rules.js +65 -0
  41. package/dist/domain-packs/inject-rules.js.map +1 -0
  42. package/dist/domain-packs/knowledge-installer.d.ts +27 -0
  43. package/dist/domain-packs/knowledge-installer.d.ts.map +1 -0
  44. package/dist/domain-packs/knowledge-installer.js +89 -0
  45. package/dist/domain-packs/knowledge-installer.js.map +1 -0
  46. package/dist/domain-packs/loader.d.ts +28 -0
  47. package/dist/domain-packs/loader.d.ts.map +1 -0
  48. package/dist/domain-packs/loader.js +105 -0
  49. package/dist/domain-packs/loader.js.map +1 -0
  50. package/dist/domain-packs/pack-runtime.d.ts +80 -0
  51. package/dist/domain-packs/pack-runtime.d.ts.map +1 -0
  52. package/dist/domain-packs/pack-runtime.js +36 -0
  53. package/dist/domain-packs/pack-runtime.js.map +1 -0
  54. package/dist/domain-packs/skills-installer.d.ts +21 -0
  55. package/dist/domain-packs/skills-installer.d.ts.map +1 -0
  56. package/dist/domain-packs/skills-installer.js +38 -0
  57. package/dist/domain-packs/skills-installer.js.map +1 -0
  58. package/dist/domain-packs/token-resolver.d.ts +37 -0
  59. package/dist/domain-packs/token-resolver.d.ts.map +1 -0
  60. package/dist/domain-packs/token-resolver.js +109 -0
  61. package/dist/domain-packs/token-resolver.js.map +1 -0
  62. package/dist/domain-packs/types.d.ts +91 -0
  63. package/dist/domain-packs/types.d.ts.map +1 -0
  64. package/dist/domain-packs/types.js +122 -0
  65. package/dist/domain-packs/types.js.map +1 -0
  66. package/dist/engine/bin/soleri-engine.d.ts +12 -0
  67. package/dist/engine/bin/soleri-engine.d.ts.map +1 -0
  68. package/dist/engine/bin/soleri-engine.js +183 -0
  69. package/dist/engine/bin/soleri-engine.js.map +1 -0
  70. package/dist/engine/core-ops.d.ts +27 -0
  71. package/dist/engine/core-ops.d.ts.map +1 -0
  72. package/dist/engine/core-ops.js +159 -0
  73. package/dist/engine/core-ops.js.map +1 -0
  74. package/dist/engine/index.d.ts +19 -0
  75. package/dist/engine/index.d.ts.map +1 -0
  76. package/dist/engine/index.js +17 -0
  77. package/dist/engine/index.js.map +1 -0
  78. package/dist/engine/register-engine.d.ts +54 -0
  79. package/dist/engine/register-engine.d.ts.map +1 -0
  80. package/dist/engine/register-engine.js +270 -0
  81. package/dist/engine/register-engine.js.map +1 -0
  82. package/dist/engine/test-helpers.d.ts +30 -0
  83. package/dist/engine/test-helpers.d.ts.map +1 -0
  84. package/dist/engine/test-helpers.js +59 -0
  85. package/dist/engine/test-helpers.js.map +1 -0
  86. package/dist/flows/context-router.d.ts +39 -0
  87. package/dist/flows/context-router.d.ts.map +1 -0
  88. package/dist/flows/context-router.js +206 -0
  89. package/dist/flows/context-router.js.map +1 -0
  90. package/dist/flows/dispatch-registry.d.ts +24 -0
  91. package/dist/flows/dispatch-registry.d.ts.map +1 -0
  92. package/dist/flows/dispatch-registry.js +70 -0
  93. package/dist/flows/dispatch-registry.js.map +1 -0
  94. package/dist/flows/epilogue.d.ts +24 -0
  95. package/dist/flows/epilogue.d.ts.map +1 -0
  96. package/dist/flows/epilogue.js +52 -0
  97. package/dist/flows/epilogue.js.map +1 -0
  98. package/dist/flows/executor.d.ts +25 -0
  99. package/dist/flows/executor.d.ts.map +1 -0
  100. package/dist/flows/executor.js +153 -0
  101. package/dist/flows/executor.js.map +1 -0
  102. package/dist/flows/gate-evaluator.d.ts +26 -0
  103. package/dist/flows/gate-evaluator.d.ts.map +1 -0
  104. package/dist/flows/gate-evaluator.js +162 -0
  105. package/dist/flows/gate-evaluator.js.map +1 -0
  106. package/dist/flows/index.d.ts +14 -0
  107. package/dist/flows/index.d.ts.map +1 -0
  108. package/dist/flows/index.js +20 -0
  109. package/dist/flows/index.js.map +1 -0
  110. package/dist/flows/loader.d.ts +17 -0
  111. package/dist/flows/loader.d.ts.map +1 -0
  112. package/dist/flows/loader.js +61 -0
  113. package/dist/flows/loader.js.map +1 -0
  114. package/dist/flows/plan-builder.d.ts +40 -0
  115. package/dist/flows/plan-builder.d.ts.map +1 -0
  116. package/dist/flows/plan-builder.js +213 -0
  117. package/dist/flows/plan-builder.js.map +1 -0
  118. package/dist/flows/probes.d.ts +11 -0
  119. package/dist/flows/probes.d.ts.map +1 -0
  120. package/dist/flows/probes.js +62 -0
  121. package/dist/flows/probes.js.map +1 -0
  122. package/dist/flows/types.d.ts +950 -0
  123. package/dist/flows/types.d.ts.map +1 -0
  124. package/dist/flows/types.js +105 -0
  125. package/dist/flows/types.js.map +1 -0
  126. package/dist/index.d.ts +11 -1
  127. package/dist/index.d.ts.map +1 -1
  128. package/dist/index.js +10 -1
  129. package/dist/index.js.map +1 -1
  130. package/dist/intelligence/loader.d.ts +19 -0
  131. package/dist/intelligence/loader.d.ts.map +1 -1
  132. package/dist/intelligence/loader.js +86 -5
  133. package/dist/intelligence/loader.js.map +1 -1
  134. package/dist/intelligence/types.d.ts +1 -0
  135. package/dist/intelligence/types.d.ts.map +1 -1
  136. package/dist/packs/types.d.ts +58 -19
  137. package/dist/packs/types.d.ts.map +1 -1
  138. package/dist/packs/types.js +14 -0
  139. package/dist/packs/types.js.map +1 -1
  140. package/dist/playbooks/generic/onboarding.d.ts +9 -0
  141. package/dist/playbooks/generic/onboarding.d.ts.map +1 -0
  142. package/dist/playbooks/generic/onboarding.js +74 -0
  143. package/dist/playbooks/generic/onboarding.js.map +1 -0
  144. package/dist/playbooks/playbook-registry.d.ts.map +1 -1
  145. package/dist/playbooks/playbook-registry.js +2 -0
  146. package/dist/playbooks/playbook-registry.js.map +1 -1
  147. package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
  148. package/dist/runtime/admin-extra-ops.js +15 -9
  149. package/dist/runtime/admin-extra-ops.js.map +1 -1
  150. package/dist/runtime/admin-ops.js +4 -4
  151. package/dist/runtime/admin-ops.js.map +1 -1
  152. package/dist/runtime/capture-ops.d.ts.map +1 -1
  153. package/dist/runtime/capture-ops.js +33 -1
  154. package/dist/runtime/capture-ops.js.map +1 -1
  155. package/dist/runtime/domain-ops.d.ts +21 -5
  156. package/dist/runtime/domain-ops.d.ts.map +1 -1
  157. package/dist/runtime/domain-ops.js +85 -8
  158. package/dist/runtime/domain-ops.js.map +1 -1
  159. package/dist/runtime/facades/cognee-facade.d.ts.map +1 -1
  160. package/dist/runtime/facades/cognee-facade.js +3 -1
  161. package/dist/runtime/facades/cognee-facade.js.map +1 -1
  162. package/dist/runtime/facades/index.d.ts.map +1 -1
  163. package/dist/runtime/facades/index.js +10 -6
  164. package/dist/runtime/facades/index.js.map +1 -1
  165. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  166. package/dist/runtime/facades/vault-facade.js +2 -0
  167. package/dist/runtime/facades/vault-facade.js.map +1 -1
  168. package/dist/runtime/orchestrate-ops.d.ts +8 -7
  169. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  170. package/dist/runtime/orchestrate-ops.js +227 -58
  171. package/dist/runtime/orchestrate-ops.js.map +1 -1
  172. package/dist/runtime/runtime.d.ts.map +1 -1
  173. package/dist/runtime/runtime.js +23 -17
  174. package/dist/runtime/runtime.js.map +1 -1
  175. package/dist/runtime/types.d.ts +6 -2
  176. package/dist/runtime/types.d.ts.map +1 -1
  177. package/dist/runtime/vault-linking-ops.d.ts +13 -0
  178. package/dist/runtime/vault-linking-ops.d.ts.map +1 -0
  179. package/dist/runtime/vault-linking-ops.js +367 -0
  180. package/dist/runtime/vault-linking-ops.js.map +1 -0
  181. package/dist/vault/linking.d.ts +46 -0
  182. package/dist/vault/linking.d.ts.map +1 -0
  183. package/dist/vault/linking.js +275 -0
  184. package/dist/vault/linking.js.map +1 -0
  185. package/dist/vault/vault-types.d.ts +37 -0
  186. package/dist/vault/vault-types.d.ts.map +1 -1
  187. package/dist/vault/vault.d.ts +12 -0
  188. package/dist/vault/vault.d.ts.map +1 -1
  189. package/dist/vault/vault.js +85 -6
  190. package/dist/vault/vault.js.map +1 -1
  191. package/package.json +4 -1
  192. package/src/__tests__/admin-extra-ops.test.ts +1 -1
  193. package/src/__tests__/admin-ops.test.ts +2 -1
  194. package/src/__tests__/cognee-client-gaps.test.ts +470 -0
  195. package/src/__tests__/cognee-hybrid-search.test.ts +478 -0
  196. package/src/__tests__/cognee-sync-manager-deep.test.ts +630 -0
  197. package/src/__tests__/cognee-sync-manager.test.ts +1 -0
  198. package/src/__tests__/core-ops.test.ts +9 -61
  199. package/src/__tests__/domain-packs.test.ts +421 -0
  200. package/src/__tests__/flows.test.ts +604 -0
  201. package/src/__tests__/playbook-registry.test.ts +2 -2
  202. package/src/__tests__/playbook-seeder.test.ts +8 -8
  203. package/src/__tests__/playbook.test.ts +5 -5
  204. package/src/__tests__/token-resolver.test.ts +79 -0
  205. package/src/brain/brain.ts +12 -0
  206. package/src/brain/intelligence.ts +21 -2
  207. package/src/capabilities/chain-mapping.ts +93 -0
  208. package/src/capabilities/index.ts +21 -0
  209. package/src/capabilities/registry.ts +290 -0
  210. package/src/capabilities/types.ts +143 -0
  211. package/src/control/intent-router.ts +46 -2
  212. package/src/domain-packs/index.ts +27 -0
  213. package/src/domain-packs/inject-rules.ts +74 -0
  214. package/src/domain-packs/knowledge-installer.ts +116 -0
  215. package/src/domain-packs/loader.ts +124 -0
  216. package/src/domain-packs/pack-runtime.ts +99 -0
  217. package/src/domain-packs/skills-installer.ts +56 -0
  218. package/src/domain-packs/token-resolver.ts +126 -0
  219. package/src/domain-packs/types.ts +229 -0
  220. package/src/engine/__tests__/register-engine.test.ts +104 -0
  221. package/src/engine/bin/soleri-engine.ts +217 -0
  222. package/src/engine/core-ops.ts +178 -0
  223. package/src/engine/index.ts +19 -0
  224. package/src/engine/register-engine.ts +385 -0
  225. package/src/engine/test-helpers.ts +83 -0
  226. package/src/flows/context-router.ts +257 -0
  227. package/src/flows/dispatch-registry.ts +80 -0
  228. package/src/flows/epilogue.ts +65 -0
  229. package/src/flows/executor.ts +182 -0
  230. package/src/flows/gate-evaluator.ts +171 -0
  231. package/src/flows/index.ts +52 -0
  232. package/src/flows/loader.ts +63 -0
  233. package/src/flows/plan-builder.ts +250 -0
  234. package/src/flows/probes.ts +70 -0
  235. package/src/flows/types.ts +217 -0
  236. package/src/index.ts +68 -1
  237. package/src/intelligence/loader.ts +96 -5
  238. package/src/intelligence/types.ts +1 -0
  239. package/src/packs/types.ts +19 -0
  240. package/src/playbooks/generic/onboarding.ts +79 -0
  241. package/src/playbooks/playbook-registry.ts +2 -0
  242. package/src/runtime/admin-extra-ops.ts +14 -8
  243. package/src/runtime/admin-ops.ts +4 -4
  244. package/src/runtime/capture-ops.ts +40 -1
  245. package/src/runtime/domain-ops.ts +92 -7
  246. package/src/runtime/facades/cognee-facade.ts +3 -1
  247. package/src/runtime/facades/index.ts +12 -6
  248. package/src/runtime/facades/vault-facade.ts +2 -0
  249. package/src/runtime/orchestrate-ops.ts +271 -62
  250. package/src/runtime/runtime.ts +27 -18
  251. package/src/runtime/types.ts +6 -2
  252. package/src/runtime/vault-linking-ops.ts +454 -0
  253. package/src/vault/linking.ts +333 -0
  254. package/src/vault/vault-types.ts +46 -0
  255. package/src/vault/vault.ts +94 -7
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Capability type system for Soleri's three-layer architecture.
3
+ *
4
+ * Capabilities are the atomic unit of agent functionality — what the agent
5
+ * CAN DO, not which tool it calls. Flows reference capabilities by intent
6
+ * (e.g., "color.validate"), and the registry resolves them to pack handlers
7
+ * at runtime.
8
+ *
9
+ * @see docs/architecture/capability-packs.md
10
+ */
11
+
12
+ import type { PackRuntime } from '../domain-packs/pack-runtime.js';
13
+ import type { IntelligenceEntry } from '../intelligence/types.js';
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Capability definition & handler
17
+ // ---------------------------------------------------------------------------
18
+
19
+ export interface CapabilityDefinition {
20
+ /** Namespaced ID: domain.action (e.g., "color.validate", "token.check") */
21
+ id: string;
22
+
23
+ /** Human-readable description of what this capability does */
24
+ description: string;
25
+
26
+ /** What this capability produces (output contract) */
27
+ provides: string[];
28
+
29
+ /** What this capability requires as input */
30
+ requires: string[];
31
+
32
+ /** Other capabilities that must be available (auto-resolved) */
33
+ depends?: string[];
34
+
35
+ /** Vault knowledge entry IDs to auto-load when this capability runs */
36
+ knowledge?: string[];
37
+ }
38
+
39
+ /**
40
+ * Capability handler — the actual implementation.
41
+ *
42
+ * Declared separately from CapabilityDefinition because:
43
+ * - Manifest declares the definition (static, serializable)
44
+ * - Pack's onActivate() registers the handler (runtime, async)
45
+ */
46
+ export type CapabilityHandler = (
47
+ params: Record<string, unknown>,
48
+ context: CapabilityContext,
49
+ ) => Promise<CapabilityResult>;
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Capability context & result
53
+ // ---------------------------------------------------------------------------
54
+
55
+ /**
56
+ * CapabilityContext — extends PackRuntime with knowledge and composition.
57
+ *
58
+ * PackRuntime already provides: vault, getProject, listProjects, createCheck,
59
+ * validateCheck, validateAndConsume. This adds:
60
+ * - knowledge: auto-loaded from pack bundle + user vault
61
+ * - brain: recommendations from the learning loop
62
+ * - invoke: call another capability (composition)
63
+ */
64
+ export interface CapabilityContext {
65
+ /** Pack runtime (vault, projects, checks) — from existing PackRuntime */
66
+ runtime: PackRuntime;
67
+
68
+ /** Auto-loaded knowledge from pack + user vault */
69
+ knowledge: KnowledgeContext;
70
+
71
+ /** Brain recommendations for this capability */
72
+ brain: BrainRecommendation[];
73
+
74
+ /** Request another capability (for composition) */
75
+ invoke: (capabilityId: string, params: Record<string, unknown>) => Promise<CapabilityResult>;
76
+ }
77
+
78
+ export interface CapabilityResult {
79
+ success: boolean;
80
+ data: Record<string, unknown>;
81
+ /** Which "provides" fields were actually produced */
82
+ produced: string[];
83
+ }
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // Knowledge & brain
87
+ // ---------------------------------------------------------------------------
88
+
89
+ export interface KnowledgeContext {
90
+ /** Entries from the pack's bundled knowledge (IntelligenceBundle) */
91
+ pack: IntelligenceEntry[];
92
+ /** Entries from user's project vault (searched by capability's knowledge[] IDs) */
93
+ vault: IntelligenceEntry[];
94
+ /** Combined, deduplicated, ranked by relevance */
95
+ merged: IntelligenceEntry[];
96
+ }
97
+
98
+ export interface BrainRecommendation {
99
+ pattern: string;
100
+ strength: number;
101
+ source: string;
102
+ }
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // Registry types
106
+ // ---------------------------------------------------------------------------
107
+
108
+ export interface RegisteredCapability {
109
+ definition: CapabilityDefinition;
110
+ providers: Array<{
111
+ packId: string;
112
+ handler: CapabilityHandler;
113
+ /** Higher = preferred (see Multi-Provider Resolution in RFC) */
114
+ priority: number;
115
+ }>;
116
+ }
117
+
118
+ export interface ResolvedCapability {
119
+ available: boolean;
120
+ capabilityId: string;
121
+ handler?: CapabilityHandler;
122
+ providers?: string[];
123
+ knowledge?: string[];
124
+ missingDependencies?: string[];
125
+ suggestion?: PackSuggestion[];
126
+ }
127
+
128
+ export interface PackSuggestion {
129
+ packId: string;
130
+ provides: string[];
131
+ }
132
+
133
+ export interface FlowValidation {
134
+ valid: boolean;
135
+ available: string[];
136
+ missing: string[];
137
+ degraded: Array<{
138
+ capability: string;
139
+ impact: 'blocking' | 'degraded' | 'optional';
140
+ suggestion: PackSuggestion[];
141
+ }>;
142
+ canRunPartially: boolean;
143
+ }
@@ -18,6 +18,30 @@ import type {
18
18
  MorphResult,
19
19
  } from './types.js';
20
20
 
21
+ // ─── Token Stemming ─────────────────────────────────────────────────
22
+ // Lightweight suffix stripping for intent matching.
23
+ // Handles: "crashes" → "crash", "deploying" → "deploy", "broken" → "broke",
24
+ // "issues" → "issue", "building" → "build", "optimizing" → "optimize"
25
+
26
+ function stemToken(token: string): string {
27
+ if (token.length < 4) return token;
28
+ // Order matters — check longer suffixes first
29
+ if (token.endsWith('ying')) return token.slice(0, -4) + 'y'; // deploying → deploy
30
+ if (token.endsWith('izing')) return token.slice(0, -3) + 'e'; // optimizing → optimize
31
+ if (token.endsWith('ating')) return token.slice(0, -3) + 'e'; // creating → create
32
+ if (token.endsWith('ting')) return token.slice(0, -4) + 't'; // getting → get (approx)
33
+ if (token.endsWith('ning')) return token.slice(0, -4) + 'n'; // planning → plan (approx)
34
+ if (token.endsWith('ing')) return token.slice(0, -3); // fixing → fix, building → build
35
+ if (token.endsWith('ies')) return token.slice(0, -3) + 'y'; // queries → query
36
+ if (token.endsWith('shes')) return token.slice(0, -2); // crashes → crash
37
+ if (token.endsWith('ches')) return token.slice(0, -2); // searches → search
38
+ if (token.endsWith('ses')) return token.slice(0, -2); // releases → releas (close enough)
39
+ if (token.endsWith('es') && token.length > 4) return token.slice(0, -1); // issues → issue
40
+ if (token.endsWith('ed') && token.length > 4) return token.slice(0, -2); // fixed → fix
41
+ if (token.endsWith('s') && !token.endsWith('ss') && token.length > 3) return token.slice(0, -1); // bugs → bug
42
+ return token;
43
+ }
44
+
21
45
  // ─── Default Mode Definitions ───────────────────────────────────────
22
46
 
23
47
  const DEFAULT_MODES: ModeConfig[] = [
@@ -33,7 +57,23 @@ const DEFAULT_MODES: ModeConfig[] = [
33
57
  intent: 'fix',
34
58
  description: 'Fixing bugs, errors, and broken behavior',
35
59
  behaviorRules: ['Identify root cause first', 'Verify fix with tests', 'Check for regressions'],
36
- keywords: ['fix', 'bug', 'broken', 'error', 'crash', 'issue', 'debug', 'repair', 'janky'],
60
+ keywords: [
61
+ 'fix',
62
+ 'bug',
63
+ 'broken',
64
+ 'error',
65
+ 'crash',
66
+ 'issue',
67
+ 'debug',
68
+ 'repair',
69
+ 'janky',
70
+ 'fail',
71
+ 'wrong',
72
+ 'stuck',
73
+ 'regression',
74
+ 'fault',
75
+ 'defect',
76
+ ],
37
77
  },
38
78
  {
39
79
  mode: 'VALIDATE-MODE',
@@ -152,7 +192,11 @@ export class IntentRouter {
152
192
  // ─── Intent Classification ──────────────────────────────────────────
153
193
 
154
194
  routeIntent(prompt: string): IntentClassification {
155
- const tokens = new Set(prompt.toLowerCase().split(/\s+/).filter(Boolean));
195
+ const rawTokens = prompt.toLowerCase().split(/\s+/).filter(Boolean);
196
+ // Stem tokens: strip common suffixes for fuzzy matching
197
+ // "crashes" → "crash", "broken" stays "broken", "deploying" → "deploy"
198
+ const stemmed = rawTokens.map((t) => stemToken(t));
199
+ const tokens = new Set([...rawTokens, ...stemmed]);
156
200
  const modes = this.getModes();
157
201
 
158
202
  let bestMode: ModeConfig | null = null;
@@ -0,0 +1,27 @@
1
+ export {
2
+ type DomainPack,
3
+ type DomainPackManifest,
4
+ type DomainPackRef,
5
+ type KnowledgeManifest,
6
+ type PackSkillDefinition,
7
+ type ValidateResult,
8
+ validateDomainPack,
9
+ SEMANTIC_FACADE_NAMES,
10
+ } from './types.js';
11
+
12
+ export { loadDomainPack, loadDomainPacksFromConfig, resolveDependencies } from './loader.js';
13
+
14
+ export { installKnowledge, type KnowledgeInstallResult } from './knowledge-installer.js';
15
+
16
+ export { installSkills, type SkillsInstallResult } from './skills-installer.js';
17
+
18
+ export { injectDomainRules, removeDomainRules } from './inject-rules.js';
19
+
20
+ export {
21
+ type PackRuntime,
22
+ type PackProjectContext,
23
+ type PackCheckContext,
24
+ createPackRuntime,
25
+ } from './pack-runtime.js';
26
+
27
+ export { resolveToken, listProjectTokens, buildReverseIndex } from './token-resolver.js';
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Domain-level CLAUDE.md injection for domain packs.
3
+ *
4
+ * Each pack can inject behavioral rules under its own marker:
5
+ * <!-- domain:packName --> ... <!-- /domain:packName -->
6
+ *
7
+ * Injection is idempotent — existing content between markers is replaced.
8
+ */
9
+
10
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
11
+
12
+ const OPEN_MARKER = (name: string) => `<!-- domain:${name} -->`;
13
+ const CLOSE_MARKER = (name: string) => `<!-- /domain:${name} -->`;
14
+
15
+ /**
16
+ * Inject domain rules into a CLAUDE.md file.
17
+ *
18
+ * @param filePath - Path to CLAUDE.md
19
+ * @param packName - Domain pack name (used in markers)
20
+ * @param rulesContent - Markdown content to inject
21
+ */
22
+ export function injectDomainRules(filePath: string, packName: string, rulesContent: string): void {
23
+ if (!rulesContent || rulesContent.trim().length === 0) return;
24
+
25
+ const open = OPEN_MARKER(packName);
26
+ const close = CLOSE_MARKER(packName);
27
+ const block = `${open}\n${rulesContent.trim()}\n${close}`;
28
+
29
+ if (!existsSync(filePath)) {
30
+ writeFileSync(filePath, block + '\n', 'utf-8');
31
+ return;
32
+ }
33
+
34
+ let content = readFileSync(filePath, 'utf-8');
35
+
36
+ // Replace existing block if present (idempotent)
37
+ const openIdx = content.indexOf(open);
38
+ const closeIdx = content.indexOf(close);
39
+
40
+ if (openIdx !== -1 && closeIdx !== -1) {
41
+ const before = content.slice(0, openIdx);
42
+ const after = content.slice(closeIdx + close.length);
43
+ content = before + block + after;
44
+ } else {
45
+ // Append at end
46
+ content = content.trimEnd() + '\n\n' + block + '\n';
47
+ }
48
+
49
+ writeFileSync(filePath, content, 'utf-8');
50
+ }
51
+
52
+ /**
53
+ * Remove domain rules from a CLAUDE.md file.
54
+ *
55
+ * @param filePath - Path to CLAUDE.md
56
+ * @param packName - Domain pack name
57
+ */
58
+ export function removeDomainRules(filePath: string, packName: string): void {
59
+ if (!existsSync(filePath)) return;
60
+
61
+ const open = OPEN_MARKER(packName);
62
+ const close = CLOSE_MARKER(packName);
63
+
64
+ let content = readFileSync(filePath, 'utf-8');
65
+ const openIdx = content.indexOf(open);
66
+ const closeIdx = content.indexOf(close);
67
+
68
+ if (openIdx !== -1 && closeIdx !== -1) {
69
+ const before = content.slice(0, openIdx);
70
+ const after = content.slice(closeIdx + close.length);
71
+ content = (before + after).replace(/\n{3,}/g, '\n\n');
72
+ writeFileSync(filePath, content, 'utf-8');
73
+ }
74
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Three-tier knowledge installer for domain packs.
3
+ *
4
+ * Respects KnowledgeManifest tiers:
5
+ * - canonical/: seed_canonical (immutable, highest authority)
6
+ * - curated/: import via vault (curator-eligible)
7
+ * - captured/: import via vault (tier=captured)
8
+ *
9
+ * All entries tagged with origin=pack and source=packName.
10
+ */
11
+
12
+ import { readdirSync, readFileSync, existsSync } from 'node:fs';
13
+ import { join, resolve } from 'node:path';
14
+ import type { AgentRuntime } from '../runtime/types.js';
15
+ import type { DomainPack } from './types.js';
16
+
17
+ export interface KnowledgeInstallResult {
18
+ canonical: number;
19
+ curated: number;
20
+ captured: number;
21
+ skipped: number;
22
+ }
23
+
24
+ /**
25
+ * Install knowledge from a domain pack into the agent's vault.
26
+ *
27
+ * @param pack - The domain pack with knowledge manifest
28
+ * @param runtime - Agent runtime with vault access
29
+ * @param rootDir - Absolute path to the pack's root directory
30
+ */
31
+ export async function installKnowledge(
32
+ pack: DomainPack,
33
+ runtime: AgentRuntime,
34
+ rootDir: string,
35
+ ): Promise<KnowledgeInstallResult> {
36
+ const result: KnowledgeInstallResult = { canonical: 0, curated: 0, captured: 0, skipped: 0 };
37
+
38
+ if (!pack.knowledge) return result;
39
+
40
+ const { vault } = runtime;
41
+ const knowledge = pack.knowledge;
42
+
43
+ // Tier 1: Canonical (immutable)
44
+ if (knowledge.canonical) {
45
+ const dir = resolve(rootDir, knowledge.canonical);
46
+ if (existsSync(dir)) {
47
+ const count = importMarkdownEntries(vault, dir, {
48
+ tier: 'canonical',
49
+ origin: 'pack',
50
+ source: pack.name,
51
+ immutable: true,
52
+ });
53
+ result.canonical = count;
54
+ }
55
+ }
56
+
57
+ // Tier 2: Curated (grooming-eligible)
58
+ if (knowledge.curated) {
59
+ const dir = resolve(rootDir, knowledge.curated);
60
+ if (existsSync(dir)) {
61
+ const count = importMarkdownEntries(vault, dir, {
62
+ tier: 'curated',
63
+ origin: 'pack',
64
+ source: pack.name,
65
+ });
66
+ result.curated = count;
67
+ }
68
+ }
69
+
70
+ // Tier 3: Captured (seed learnings)
71
+ if (knowledge.captured) {
72
+ const dir = resolve(rootDir, knowledge.captured);
73
+ if (existsSync(dir)) {
74
+ const count = importMarkdownEntries(vault, dir, {
75
+ tier: 'captured',
76
+ origin: 'pack',
77
+ source: pack.name,
78
+ });
79
+ result.captured = count;
80
+ }
81
+ }
82
+
83
+ return result;
84
+ }
85
+
86
+ /** Import markdown files from a directory into the vault. */
87
+ function importMarkdownEntries(
88
+ vault: AgentRuntime['vault'],
89
+ dir: string,
90
+ meta: { tier: string; origin: string; source: string; immutable?: boolean },
91
+ ): number {
92
+ const files = readdirSync(dir).filter((f) => f.endsWith('.md'));
93
+ let imported = 0;
94
+
95
+ for (const file of files) {
96
+ const content = readFileSync(join(dir, file), 'utf-8');
97
+ const id = `pack-${meta.source}-${file.replace(/\.md$/, '')}`;
98
+
99
+ // Skip if canonical entry already exists (immutable = never overwrite)
100
+ if (meta.immutable && vault.get(id)) continue;
101
+
102
+ vault.add({
103
+ id,
104
+ type: 'pattern',
105
+ title: file.replace(/\.md$/, '').replace(/-/g, ' '),
106
+ description: content.slice(0, 200),
107
+ severity: 'suggestion',
108
+ tags: [`pack:${meta.source}`, `tier:${meta.tier}`],
109
+ domain: meta.source,
110
+ origin: meta.origin as 'pack' | 'agent' | 'user',
111
+ });
112
+ imported++;
113
+ }
114
+
115
+ return imported;
116
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Domain Pack loader — resolves, validates, and dependency-sorts domain packs.
3
+ */
4
+
5
+ import { validateDomainPack } from './types.js';
6
+ import type { DomainPack, DomainPackManifest, DomainPackRef } from './types.js';
7
+
8
+ /**
9
+ * Load a single domain pack from an npm package.
10
+ *
11
+ * @param packageName - npm package name (e.g., '@soleri/domain-design')
12
+ * @returns Validated DomainPackManifest
13
+ * @throws If package cannot be imported or fails validation
14
+ */
15
+ export async function loadDomainPack(packageName: string): Promise<DomainPackManifest> {
16
+ let mod: Record<string, unknown>;
17
+ try {
18
+ mod = await import(packageName);
19
+ } catch (err) {
20
+ throw new Error(
21
+ `Failed to import domain pack "${packageName}": ${err instanceof Error ? err.message : String(err)}`, { cause: err },
22
+ );
23
+ }
24
+
25
+ // Support both default export and named 'pack' export
26
+ const packCandidate = mod.default ?? mod.pack;
27
+ if (!packCandidate) {
28
+ throw new Error(`Domain pack "${packageName}" has no default or named "pack" export.`);
29
+ }
30
+
31
+ const result = validateDomainPack(packCandidate);
32
+ if (!result.success) {
33
+ throw new Error(`Domain pack "${packageName}" failed validation: ${result.errors.message}`);
34
+ }
35
+
36
+ return {
37
+ ...result.data,
38
+ packageName,
39
+ };
40
+ }
41
+
42
+ /**
43
+ * Load all domain packs from AgentConfig refs.
44
+ *
45
+ * @param refs - Array of DomainPackRef from agent config
46
+ * @returns Validated and dependency-sorted packs
47
+ */
48
+ export async function loadDomainPacksFromConfig(
49
+ refs: DomainPackRef[],
50
+ ): Promise<DomainPackManifest[]> {
51
+ const packs = await Promise.all(refs.map((ref) => loadDomainPack(ref.package)));
52
+ return resolveDependencies(packs);
53
+ }
54
+
55
+ /**
56
+ * Topological sort of domain packs by their `requires` field.
57
+ *
58
+ * @param packs - Array of domain packs (validated)
59
+ * @returns Sorted array (dependencies before dependents)
60
+ * @throws On circular dependencies or missing dependencies
61
+ */
62
+ export function resolveDependencies<T extends DomainPack>(packs: T[]): T[] {
63
+ const byName = new Map<string, T>();
64
+ for (const pack of packs) {
65
+ byName.set(pack.name, pack);
66
+ }
67
+
68
+ // Check for missing dependencies
69
+ for (const pack of packs) {
70
+ if (pack.requires) {
71
+ for (const dep of pack.requires) {
72
+ if (!byName.has(dep)) {
73
+ throw new Error(
74
+ `Domain pack "${pack.name}" requires "${dep}" but it was not found in the loaded packs.`,
75
+ );
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ // Kahn's algorithm for topological sort
82
+ const inDegree = new Map<string, number>();
83
+ const adjList = new Map<string, string[]>();
84
+
85
+ for (const pack of packs) {
86
+ inDegree.set(pack.name, 0);
87
+ adjList.set(pack.name, []);
88
+ }
89
+
90
+ for (const pack of packs) {
91
+ if (pack.requires) {
92
+ for (const dep of pack.requires) {
93
+ adjList.get(dep)!.push(pack.name);
94
+ inDegree.set(pack.name, (inDegree.get(pack.name) ?? 0) + 1);
95
+ }
96
+ }
97
+ }
98
+
99
+ const queue: string[] = [];
100
+ for (const [name, degree] of inDegree) {
101
+ if (degree === 0) queue.push(name);
102
+ }
103
+
104
+ const sorted: T[] = [];
105
+ while (queue.length > 0) {
106
+ const current = queue.shift()!;
107
+ sorted.push(byName.get(current)!);
108
+
109
+ for (const dependent of adjList.get(current) ?? []) {
110
+ const newDegree = (inDegree.get(dependent) ?? 1) - 1;
111
+ inDegree.set(dependent, newDegree);
112
+ if (newDegree === 0) queue.push(dependent);
113
+ }
114
+ }
115
+
116
+ if (sorted.length !== packs.length) {
117
+ const remaining = packs
118
+ .filter((p) => !sorted.some((s) => s.name === p.name))
119
+ .map((p) => p.name);
120
+ throw new Error(`Circular dependency detected among domain packs: ${remaining.join(', ')}`);
121
+ }
122
+
123
+ return sorted;
124
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * PackRuntime — the subset of AgentRuntime available to domain packs.
3
+ *
4
+ * Domain packs receive this via onActivate(runtime). It exposes only what
5
+ * packs need: vault search, project registry, and session store.
6
+ * Packs should NOT depend on the full AgentRuntime interface.
7
+ */
8
+
9
+ import type { Vault } from '../vault/vault.js';
10
+
11
+ /**
12
+ * Minimal project context for token resolution.
13
+ * Matches the shape of ProjectRegistry entries.
14
+ */
15
+ export interface PackProjectContext {
16
+ id: string;
17
+ name: string;
18
+ path: string;
19
+ colors?: {
20
+ [scale: string]: {
21
+ scale: Record<string, string>;
22
+ base: string;
23
+ };
24
+ };
25
+ semanticTokens?: Record<string, string>;
26
+ }
27
+
28
+ /**
29
+ * Session check for tool chaining (contrast → component create).
30
+ */
31
+ export interface PackCheckContext {
32
+ id: string;
33
+ type: string;
34
+ data: Record<string, unknown>;
35
+ timestamp: number;
36
+ }
37
+
38
+ /**
39
+ * The runtime interface domain packs receive via onActivate.
40
+ *
41
+ * Designed for graceful degradation: packs work without runtime
42
+ * (simplified mode), but gain full power when connected.
43
+ */
44
+ export interface PackRuntime {
45
+ /** Vault for knowledge search and capture */
46
+ vault: Vault;
47
+
48
+ /** Get a registered project by ID (for token resolution) */
49
+ getProject(projectId: string): PackProjectContext | undefined;
50
+
51
+ /** List all registered projects */
52
+ listProjects(): Array<{ id: string; name: string; path: string }>;
53
+
54
+ /** Create a session check (for tool chaining) */
55
+ createCheck(type: string, data: Record<string, unknown>): string;
56
+
57
+ /** Validate a session check */
58
+ validateCheck(checkId: string, expectedType: string): PackCheckContext | null;
59
+
60
+ /** Validate and consume a session check (single-use) */
61
+ validateAndConsume(checkId: string, expectedType: string): PackCheckContext | null;
62
+ }
63
+
64
+ /**
65
+ * Create a PackRuntime from an AgentRuntime.
66
+ *
67
+ * This adapter extracts the subset of runtime that packs need,
68
+ * avoiding tight coupling to the full AgentRuntime interface.
69
+ */
70
+ export function createPackRuntime(runtime: {
71
+ vault: Vault;
72
+ projectRegistry: {
73
+ getProject(id: string): PackProjectContext | undefined;
74
+ listProjects(): Array<{ id: string; name: string; path: string }>;
75
+ };
76
+ sessionStore?: {
77
+ createCheck(type: string, data: Record<string, unknown>): string;
78
+ validateCheck(id: string, type: string): PackCheckContext | null;
79
+ validateAndConsume(id: string, type: string): PackCheckContext | null;
80
+ };
81
+ }): PackRuntime {
82
+ return {
83
+ vault: runtime.vault,
84
+ getProject: (id) => runtime.projectRegistry.getProject(id),
85
+ listProjects: () => runtime.projectRegistry.listProjects(),
86
+ createCheck: (type, data) => {
87
+ if (!runtime.sessionStore) throw new Error('Session store not available');
88
+ return runtime.sessionStore.createCheck(type, data);
89
+ },
90
+ validateCheck: (id, type) => {
91
+ if (!runtime.sessionStore) return null;
92
+ return runtime.sessionStore.validateCheck(id, type);
93
+ },
94
+ validateAndConsume: (id, type) => {
95
+ if (!runtime.sessionStore) return null;
96
+ return runtime.sessionStore.validateAndConsume(id, type);
97
+ },
98
+ };
99
+ }