@soleri/core 2.4.0 → 2.5.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 (300) hide show
  1. package/dist/brain/brain.d.ts +7 -0
  2. package/dist/brain/brain.d.ts.map +1 -1
  3. package/dist/brain/brain.js +56 -9
  4. package/dist/brain/brain.js.map +1 -1
  5. package/dist/brain/types.d.ts +2 -2
  6. package/dist/brain/types.d.ts.map +1 -1
  7. package/dist/cognee/client.d.ts +3 -0
  8. package/dist/cognee/client.d.ts.map +1 -1
  9. package/dist/cognee/client.js +17 -0
  10. package/dist/cognee/client.js.map +1 -1
  11. package/dist/cognee/sync-manager.d.ts +94 -0
  12. package/dist/cognee/sync-manager.d.ts.map +1 -0
  13. package/dist/cognee/sync-manager.js +293 -0
  14. package/dist/cognee/sync-manager.js.map +1 -0
  15. package/dist/curator/curator.d.ts +8 -1
  16. package/dist/curator/curator.d.ts.map +1 -1
  17. package/dist/curator/curator.js +64 -1
  18. package/dist/curator/curator.js.map +1 -1
  19. package/dist/errors/classify.d.ts +13 -0
  20. package/dist/errors/classify.d.ts.map +1 -0
  21. package/dist/errors/classify.js +97 -0
  22. package/dist/errors/classify.js.map +1 -0
  23. package/dist/errors/index.d.ts +6 -0
  24. package/dist/errors/index.d.ts.map +1 -0
  25. package/dist/errors/index.js +4 -0
  26. package/dist/errors/index.js.map +1 -0
  27. package/dist/errors/retry.d.ts +40 -0
  28. package/dist/errors/retry.d.ts.map +1 -0
  29. package/dist/errors/retry.js +97 -0
  30. package/dist/errors/retry.js.map +1 -0
  31. package/dist/errors/types.d.ts +48 -0
  32. package/dist/errors/types.d.ts.map +1 -0
  33. package/dist/errors/types.js +59 -0
  34. package/dist/errors/types.js.map +1 -0
  35. package/dist/index.d.ts +25 -5
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +21 -3
  38. package/dist/index.js.map +1 -1
  39. package/dist/intake/content-classifier.d.ts +14 -0
  40. package/dist/intake/content-classifier.d.ts.map +1 -0
  41. package/dist/intake/content-classifier.js +125 -0
  42. package/dist/intake/content-classifier.js.map +1 -0
  43. package/dist/intake/dedup-gate.d.ts +17 -0
  44. package/dist/intake/dedup-gate.d.ts.map +1 -0
  45. package/dist/intake/dedup-gate.js +66 -0
  46. package/dist/intake/dedup-gate.js.map +1 -0
  47. package/dist/intake/intake-pipeline.d.ts +63 -0
  48. package/dist/intake/intake-pipeline.d.ts.map +1 -0
  49. package/dist/intake/intake-pipeline.js +373 -0
  50. package/dist/intake/intake-pipeline.js.map +1 -0
  51. package/dist/intake/types.d.ts +65 -0
  52. package/dist/intake/types.d.ts.map +1 -0
  53. package/dist/intake/types.js +3 -0
  54. package/dist/intake/types.js.map +1 -0
  55. package/dist/intelligence/loader.js +1 -1
  56. package/dist/intelligence/loader.js.map +1 -1
  57. package/dist/intelligence/types.d.ts +3 -1
  58. package/dist/intelligence/types.d.ts.map +1 -1
  59. package/dist/loop/loop-manager.d.ts +58 -7
  60. package/dist/loop/loop-manager.d.ts.map +1 -1
  61. package/dist/loop/loop-manager.js +280 -6
  62. package/dist/loop/loop-manager.js.map +1 -1
  63. package/dist/loop/types.d.ts +69 -1
  64. package/dist/loop/types.d.ts.map +1 -1
  65. package/dist/loop/types.js +4 -1
  66. package/dist/loop/types.js.map +1 -1
  67. package/dist/persistence/index.d.ts +3 -0
  68. package/dist/persistence/index.d.ts.map +1 -0
  69. package/dist/persistence/index.js +2 -0
  70. package/dist/persistence/index.js.map +1 -0
  71. package/dist/persistence/sqlite-provider.d.ts +25 -0
  72. package/dist/persistence/sqlite-provider.d.ts.map +1 -0
  73. package/dist/persistence/sqlite-provider.js +59 -0
  74. package/dist/persistence/sqlite-provider.js.map +1 -0
  75. package/dist/persistence/types.d.ts +36 -0
  76. package/dist/persistence/types.d.ts.map +1 -0
  77. package/dist/persistence/types.js +8 -0
  78. package/dist/persistence/types.js.map +1 -0
  79. package/dist/planning/gap-analysis.d.ts +47 -4
  80. package/dist/planning/gap-analysis.d.ts.map +1 -1
  81. package/dist/planning/gap-analysis.js +190 -13
  82. package/dist/planning/gap-analysis.js.map +1 -1
  83. package/dist/planning/gap-types.d.ts +1 -1
  84. package/dist/planning/gap-types.d.ts.map +1 -1
  85. package/dist/planning/gap-types.js.map +1 -1
  86. package/dist/planning/planner.d.ts +277 -9
  87. package/dist/planning/planner.d.ts.map +1 -1
  88. package/dist/planning/planner.js +611 -46
  89. package/dist/planning/planner.js.map +1 -1
  90. package/dist/playbooks/generic/brainstorming.d.ts +9 -0
  91. package/dist/playbooks/generic/brainstorming.d.ts.map +1 -0
  92. package/dist/playbooks/generic/brainstorming.js +105 -0
  93. package/dist/playbooks/generic/brainstorming.js.map +1 -0
  94. package/dist/playbooks/generic/code-review.d.ts +11 -0
  95. package/dist/playbooks/generic/code-review.d.ts.map +1 -0
  96. package/dist/playbooks/generic/code-review.js +176 -0
  97. package/dist/playbooks/generic/code-review.js.map +1 -0
  98. package/dist/playbooks/generic/subagent-execution.d.ts +9 -0
  99. package/dist/playbooks/generic/subagent-execution.d.ts.map +1 -0
  100. package/dist/playbooks/generic/subagent-execution.js +68 -0
  101. package/dist/playbooks/generic/subagent-execution.js.map +1 -0
  102. package/dist/playbooks/generic/systematic-debugging.d.ts +9 -0
  103. package/dist/playbooks/generic/systematic-debugging.d.ts.map +1 -0
  104. package/dist/playbooks/generic/systematic-debugging.js +87 -0
  105. package/dist/playbooks/generic/systematic-debugging.js.map +1 -0
  106. package/dist/playbooks/generic/tdd.d.ts +9 -0
  107. package/dist/playbooks/generic/tdd.d.ts.map +1 -0
  108. package/dist/playbooks/generic/tdd.js +70 -0
  109. package/dist/playbooks/generic/tdd.js.map +1 -0
  110. package/dist/playbooks/generic/verification.d.ts +9 -0
  111. package/dist/playbooks/generic/verification.d.ts.map +1 -0
  112. package/dist/playbooks/generic/verification.js +74 -0
  113. package/dist/playbooks/generic/verification.js.map +1 -0
  114. package/dist/playbooks/index.d.ts +4 -0
  115. package/dist/playbooks/index.d.ts.map +1 -0
  116. package/dist/playbooks/index.js +5 -0
  117. package/dist/playbooks/index.js.map +1 -0
  118. package/dist/playbooks/playbook-registry.d.ts +42 -0
  119. package/dist/playbooks/playbook-registry.d.ts.map +1 -0
  120. package/dist/playbooks/playbook-registry.js +227 -0
  121. package/dist/playbooks/playbook-registry.js.map +1 -0
  122. package/dist/playbooks/playbook-seeder.d.ts +47 -0
  123. package/dist/playbooks/playbook-seeder.d.ts.map +1 -0
  124. package/dist/playbooks/playbook-seeder.js +104 -0
  125. package/dist/playbooks/playbook-seeder.js.map +1 -0
  126. package/dist/playbooks/playbook-types.d.ts +132 -0
  127. package/dist/playbooks/playbook-types.d.ts.map +1 -0
  128. package/dist/playbooks/playbook-types.js +12 -0
  129. package/dist/playbooks/playbook-types.js.map +1 -0
  130. package/dist/project/project-registry.d.ts.map +1 -1
  131. package/dist/project/project-registry.js +9 -11
  132. package/dist/project/project-registry.js.map +1 -1
  133. package/dist/prompts/index.d.ts +4 -0
  134. package/dist/prompts/index.d.ts.map +1 -0
  135. package/dist/prompts/index.js +3 -0
  136. package/dist/prompts/index.js.map +1 -0
  137. package/dist/prompts/parser.d.ts +17 -0
  138. package/dist/prompts/parser.d.ts.map +1 -0
  139. package/dist/prompts/parser.js +47 -0
  140. package/dist/prompts/parser.js.map +1 -0
  141. package/dist/prompts/template-manager.d.ts +25 -0
  142. package/dist/prompts/template-manager.d.ts.map +1 -0
  143. package/dist/prompts/template-manager.js +71 -0
  144. package/dist/prompts/template-manager.js.map +1 -0
  145. package/dist/prompts/types.d.ts +26 -0
  146. package/dist/prompts/types.d.ts.map +1 -0
  147. package/dist/prompts/types.js +5 -0
  148. package/dist/prompts/types.js.map +1 -0
  149. package/dist/runtime/admin-extra-ops.d.ts +5 -3
  150. package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
  151. package/dist/runtime/admin-extra-ops.js +322 -11
  152. package/dist/runtime/admin-extra-ops.js.map +1 -1
  153. package/dist/runtime/admin-ops.d.ts.map +1 -1
  154. package/dist/runtime/admin-ops.js +10 -3
  155. package/dist/runtime/admin-ops.js.map +1 -1
  156. package/dist/runtime/capture-ops.d.ts.map +1 -1
  157. package/dist/runtime/capture-ops.js +20 -2
  158. package/dist/runtime/capture-ops.js.map +1 -1
  159. package/dist/runtime/cognee-sync-ops.d.ts +12 -0
  160. package/dist/runtime/cognee-sync-ops.d.ts.map +1 -0
  161. package/dist/runtime/cognee-sync-ops.js +55 -0
  162. package/dist/runtime/cognee-sync-ops.js.map +1 -0
  163. package/dist/runtime/core-ops.d.ts +8 -6
  164. package/dist/runtime/core-ops.d.ts.map +1 -1
  165. package/dist/runtime/core-ops.js +226 -9
  166. package/dist/runtime/core-ops.js.map +1 -1
  167. package/dist/runtime/curator-extra-ops.d.ts +2 -2
  168. package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
  169. package/dist/runtime/curator-extra-ops.js +15 -3
  170. package/dist/runtime/curator-extra-ops.js.map +1 -1
  171. package/dist/runtime/domain-ops.js +2 -2
  172. package/dist/runtime/domain-ops.js.map +1 -1
  173. package/dist/runtime/grading-ops.d.ts.map +1 -1
  174. package/dist/runtime/grading-ops.js.map +1 -1
  175. package/dist/runtime/intake-ops.d.ts +14 -0
  176. package/dist/runtime/intake-ops.d.ts.map +1 -0
  177. package/dist/runtime/intake-ops.js +110 -0
  178. package/dist/runtime/intake-ops.js.map +1 -0
  179. package/dist/runtime/loop-ops.d.ts +5 -4
  180. package/dist/runtime/loop-ops.d.ts.map +1 -1
  181. package/dist/runtime/loop-ops.js +84 -12
  182. package/dist/runtime/loop-ops.js.map +1 -1
  183. package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -1
  184. package/dist/runtime/memory-cross-project-ops.js.map +1 -1
  185. package/dist/runtime/memory-extra-ops.js +5 -5
  186. package/dist/runtime/memory-extra-ops.js.map +1 -1
  187. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  188. package/dist/runtime/orchestrate-ops.js +8 -2
  189. package/dist/runtime/orchestrate-ops.js.map +1 -1
  190. package/dist/runtime/planning-extra-ops.d.ts +13 -5
  191. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  192. package/dist/runtime/planning-extra-ops.js +381 -18
  193. package/dist/runtime/planning-extra-ops.js.map +1 -1
  194. package/dist/runtime/playbook-ops.d.ts +14 -0
  195. package/dist/runtime/playbook-ops.d.ts.map +1 -0
  196. package/dist/runtime/playbook-ops.js +141 -0
  197. package/dist/runtime/playbook-ops.js.map +1 -0
  198. package/dist/runtime/project-ops.d.ts.map +1 -1
  199. package/dist/runtime/project-ops.js +7 -2
  200. package/dist/runtime/project-ops.js.map +1 -1
  201. package/dist/runtime/runtime.d.ts.map +1 -1
  202. package/dist/runtime/runtime.js +27 -8
  203. package/dist/runtime/runtime.js.map +1 -1
  204. package/dist/runtime/types.d.ts +8 -0
  205. package/dist/runtime/types.d.ts.map +1 -1
  206. package/dist/runtime/vault-extra-ops.d.ts +3 -2
  207. package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
  208. package/dist/runtime/vault-extra-ops.js +345 -4
  209. package/dist/runtime/vault-extra-ops.js.map +1 -1
  210. package/dist/vault/playbook.d.ts +34 -0
  211. package/dist/vault/playbook.d.ts.map +1 -0
  212. package/dist/vault/playbook.js +60 -0
  213. package/dist/vault/playbook.js.map +1 -0
  214. package/dist/vault/vault.d.ts +31 -32
  215. package/dist/vault/vault.d.ts.map +1 -1
  216. package/dist/vault/vault.js +201 -181
  217. package/dist/vault/vault.js.map +1 -1
  218. package/package.json +7 -3
  219. package/src/__tests__/admin-extra-ops.test.ts +62 -15
  220. package/src/__tests__/admin-ops.test.ts +2 -2
  221. package/src/__tests__/brain.test.ts +3 -3
  222. package/src/__tests__/cognee-integration.test.ts +80 -0
  223. package/src/__tests__/cognee-sync-manager.test.ts +103 -0
  224. package/src/__tests__/core-ops.test.ts +30 -4
  225. package/src/__tests__/curator-extra-ops.test.ts +24 -2
  226. package/src/__tests__/errors.test.ts +388 -0
  227. package/src/__tests__/grading-ops.test.ts +28 -7
  228. package/src/__tests__/intake-pipeline.test.ts +162 -0
  229. package/src/__tests__/loop-ops.test.ts +74 -3
  230. package/src/__tests__/memory-cross-project-ops.test.ts +3 -1
  231. package/src/__tests__/orchestrate-ops.test.ts +8 -3
  232. package/src/__tests__/persistence.test.ts +225 -0
  233. package/src/__tests__/planner.test.ts +99 -21
  234. package/src/__tests__/planning-extra-ops.test.ts +168 -10
  235. package/src/__tests__/playbook-registry.test.ts +326 -0
  236. package/src/__tests__/playbook-seeder.test.ts +163 -0
  237. package/src/__tests__/playbook.test.ts +389 -0
  238. package/src/__tests__/project-ops.test.ts +18 -4
  239. package/src/__tests__/template-manager.test.ts +222 -0
  240. package/src/__tests__/vault-extra-ops.test.ts +82 -7
  241. package/src/brain/brain.ts +71 -9
  242. package/src/brain/types.ts +2 -2
  243. package/src/cognee/client.ts +18 -0
  244. package/src/cognee/sync-manager.ts +389 -0
  245. package/src/curator/curator.ts +88 -7
  246. package/src/errors/classify.ts +102 -0
  247. package/src/errors/index.ts +5 -0
  248. package/src/errors/retry.ts +132 -0
  249. package/src/errors/types.ts +81 -0
  250. package/src/index.ts +114 -3
  251. package/src/intake/content-classifier.ts +146 -0
  252. package/src/intake/dedup-gate.ts +92 -0
  253. package/src/intake/intake-pipeline.ts +503 -0
  254. package/src/intake/types.ts +69 -0
  255. package/src/intelligence/loader.ts +1 -1
  256. package/src/intelligence/types.ts +3 -1
  257. package/src/loop/loop-manager.ts +325 -7
  258. package/src/loop/types.ts +72 -1
  259. package/src/persistence/index.ts +7 -0
  260. package/src/persistence/sqlite-provider.ts +62 -0
  261. package/src/persistence/types.ts +44 -0
  262. package/src/planning/gap-analysis.ts +286 -17
  263. package/src/planning/gap-types.ts +4 -1
  264. package/src/planning/planner.ts +828 -55
  265. package/src/playbooks/generic/brainstorming.ts +110 -0
  266. package/src/playbooks/generic/code-review.ts +181 -0
  267. package/src/playbooks/generic/subagent-execution.ts +74 -0
  268. package/src/playbooks/generic/systematic-debugging.ts +92 -0
  269. package/src/playbooks/generic/tdd.ts +75 -0
  270. package/src/playbooks/generic/verification.ts +79 -0
  271. package/src/playbooks/index.ts +27 -0
  272. package/src/playbooks/playbook-registry.ts +284 -0
  273. package/src/playbooks/playbook-seeder.ts +119 -0
  274. package/src/playbooks/playbook-types.ts +162 -0
  275. package/src/project/project-registry.ts +29 -17
  276. package/src/prompts/index.ts +3 -0
  277. package/src/prompts/parser.ts +59 -0
  278. package/src/prompts/template-manager.ts +77 -0
  279. package/src/prompts/types.ts +28 -0
  280. package/src/runtime/admin-extra-ops.ts +358 -13
  281. package/src/runtime/admin-ops.ts +17 -6
  282. package/src/runtime/capture-ops.ts +25 -6
  283. package/src/runtime/cognee-sync-ops.ts +63 -0
  284. package/src/runtime/core-ops.ts +258 -8
  285. package/src/runtime/curator-extra-ops.ts +17 -3
  286. package/src/runtime/domain-ops.ts +2 -2
  287. package/src/runtime/grading-ops.ts +11 -2
  288. package/src/runtime/intake-ops.ts +126 -0
  289. package/src/runtime/loop-ops.ts +96 -13
  290. package/src/runtime/memory-cross-project-ops.ts +1 -2
  291. package/src/runtime/memory-extra-ops.ts +5 -5
  292. package/src/runtime/orchestrate-ops.ts +8 -2
  293. package/src/runtime/planning-extra-ops.ts +414 -23
  294. package/src/runtime/playbook-ops.ts +169 -0
  295. package/src/runtime/project-ops.ts +9 -3
  296. package/src/runtime/runtime.ts +35 -9
  297. package/src/runtime/types.ts +8 -0
  298. package/src/runtime/vault-extra-ops.ts +385 -4
  299. package/src/vault/playbook.ts +87 -0
  300. package/src/vault/vault.ts +301 -235
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Playbook Registry
3
+ *
4
+ * Matches playbooks to plan context (intent + keywords) and merges
5
+ * generic + domain tiers into a single MergedPlaybook.
6
+ *
7
+ * Resolution order: vault first (user overrides), built-in fallback.
8
+ */
9
+
10
+ import type {
11
+ PlaybookDefinition,
12
+ PlaybookMatchResult,
13
+ MergedPlaybook,
14
+ PlaybookGate,
15
+ PlaybookTaskTemplate,
16
+ PlaybookIntent,
17
+ } from './playbook-types.js';
18
+
19
+ // Built-in generic playbook definitions
20
+ import { tddPlaybook } from './generic/tdd.js';
21
+ import { brainstormingPlaybook } from './generic/brainstorming.js';
22
+ import { codeReviewPlaybook } from './generic/code-review.js';
23
+ import { subagentExecutionPlaybook } from './generic/subagent-execution.js';
24
+ import { systematicDebuggingPlaybook } from './generic/systematic-debugging.js';
25
+ import { verificationPlaybook } from './generic/verification.js';
26
+
27
+ // =============================================================================
28
+ // SCORING WEIGHTS
29
+ // =============================================================================
30
+
31
+ /** Points awarded when the plan intent matches a playbook's matchIntents. */
32
+ const INTENT_MATCH_SCORE = 10;
33
+
34
+ /** Points awarded per keyword hit in the plan text. */
35
+ const KEYWORD_MATCH_SCORE = 5;
36
+
37
+ /** Minimum total score for a playbook to be considered a match. */
38
+ const MIN_MATCH_SCORE = 5;
39
+
40
+ // =============================================================================
41
+ // BUILT-IN REGISTRY
42
+ // =============================================================================
43
+
44
+ const BUILTIN_PLAYBOOKS: PlaybookDefinition[] = [
45
+ // Generic tier
46
+ tddPlaybook,
47
+ brainstormingPlaybook,
48
+ codeReviewPlaybook,
49
+ subagentExecutionPlaybook,
50
+ systematicDebuggingPlaybook,
51
+ verificationPlaybook,
52
+ ];
53
+
54
+ /**
55
+ * Get a built-in playbook by ID.
56
+ */
57
+ export function getBuiltinPlaybook(id: string): PlaybookDefinition | undefined {
58
+ return BUILTIN_PLAYBOOKS.find((p) => p.id === id);
59
+ }
60
+
61
+ /**
62
+ * Get all built-in playbooks.
63
+ */
64
+ export function getAllBuiltinPlaybooks(): readonly PlaybookDefinition[] {
65
+ return BUILTIN_PLAYBOOKS;
66
+ }
67
+
68
+ // =============================================================================
69
+ // MATCHING
70
+ // =============================================================================
71
+
72
+ /**
73
+ * Score a playbook against the given intent and text.
74
+ * Returns 0 if no match, positive score if matched.
75
+ */
76
+ export function scorePlaybook(
77
+ playbook: PlaybookDefinition,
78
+ intent: PlaybookIntent | undefined,
79
+ text: string,
80
+ ): number {
81
+ let score = 0;
82
+ const lowerText = text.toLowerCase();
83
+
84
+ // Intent match
85
+ if (intent && playbook.matchIntents.includes(intent)) {
86
+ score += INTENT_MATCH_SCORE;
87
+ }
88
+
89
+ // Keyword match
90
+ for (const keyword of playbook.matchKeywords) {
91
+ if (lowerText.includes(keyword.toLowerCase())) {
92
+ score += KEYWORD_MATCH_SCORE;
93
+ }
94
+ }
95
+
96
+ return score;
97
+ }
98
+
99
+ /**
100
+ * Find the best matching playbook from a list for the given context.
101
+ * Returns the highest-scoring playbook above the threshold, or undefined.
102
+ */
103
+ function findBestMatch(
104
+ playbooks: PlaybookDefinition[],
105
+ intent: PlaybookIntent | undefined,
106
+ text: string,
107
+ minScore: number = MIN_MATCH_SCORE,
108
+ ): { playbook: PlaybookDefinition; score: number } | undefined {
109
+ let best: { playbook: PlaybookDefinition; score: number } | undefined;
110
+
111
+ for (const playbook of playbooks) {
112
+ const score = scorePlaybook(playbook, intent, text);
113
+ if (score >= minScore && (!best || score > best.score)) {
114
+ best = { playbook, score };
115
+ }
116
+ }
117
+
118
+ return best;
119
+ }
120
+
121
+ // =============================================================================
122
+ // MERGING
123
+ // =============================================================================
124
+
125
+ /**
126
+ * Merge gates from generic and domain playbooks.
127
+ * Domain gates come after generic gates. No deduplication — they serve different purposes.
128
+ */
129
+ function mergeGates(generic?: PlaybookDefinition, domain?: PlaybookDefinition): PlaybookGate[] {
130
+ const gates: PlaybookGate[] = [];
131
+ if (generic) gates.push(...generic.gates);
132
+ if (domain) gates.push(...domain.gates);
133
+ return gates;
134
+ }
135
+
136
+ /**
137
+ * Merge task templates from generic and domain playbooks.
138
+ * Domain templates with the same order position override generic ones of the same taskType.
139
+ */
140
+ function mergeTaskTemplates(
141
+ generic?: PlaybookDefinition,
142
+ domain?: PlaybookDefinition,
143
+ ): PlaybookTaskTemplate[] {
144
+ const genericTemplates = generic?.taskTemplates ?? [];
145
+ const domainTemplates = domain?.taskTemplates ?? [];
146
+
147
+ // Start with generic templates
148
+ const merged = [...genericTemplates];
149
+
150
+ for (const domainTemplate of domainTemplates) {
151
+ // Check if domain overrides a generic template at the same order + taskType
152
+ const overrideIndex = merged.findIndex(
153
+ (g) => g.order === domainTemplate.order && g.taskType === domainTemplate.taskType,
154
+ );
155
+
156
+ if (overrideIndex >= 0) {
157
+ // Domain overrides generic
158
+ merged[overrideIndex] = domainTemplate;
159
+ } else {
160
+ // Domain adds a new template
161
+ merged.push(domainTemplate);
162
+ }
163
+ }
164
+
165
+ return merged;
166
+ }
167
+
168
+ /**
169
+ * Merge tool injections from generic and domain playbooks.
170
+ * Deduplicated — each tool appears only once.
171
+ */
172
+ function mergeTools(generic?: PlaybookDefinition, domain?: PlaybookDefinition): string[] {
173
+ const tools = new Set<string>();
174
+ if (generic) {
175
+ for (const tool of generic.toolInjections) tools.add(tool);
176
+ }
177
+ if (domain) {
178
+ for (const tool of domain.toolInjections) tools.add(tool);
179
+ }
180
+ return Array.from(tools);
181
+ }
182
+
183
+ /**
184
+ * Merge verification criteria from generic and domain playbooks.
185
+ * All criteria from both tiers — domain typically adds domain-specific checks.
186
+ */
187
+ function mergeVerification(generic?: PlaybookDefinition, domain?: PlaybookDefinition): string[] {
188
+ const criteria: string[] = [];
189
+ if (generic) criteria.push(...generic.verificationCriteria);
190
+ if (domain) criteria.push(...domain.verificationCriteria);
191
+ // Deduplicate exact matches
192
+ return [...new Set(criteria)];
193
+ }
194
+
195
+ /**
196
+ * Build a human-readable label for the merged playbook.
197
+ */
198
+ function buildLabel(generic?: PlaybookDefinition, domain?: PlaybookDefinition): string {
199
+ if (generic && domain) {
200
+ return `${domain.title} (extends ${generic.title})`;
201
+ }
202
+ if (domain) return domain.title;
203
+ if (generic) return generic.title;
204
+ return 'Unknown';
205
+ }
206
+
207
+ /**
208
+ * Create a MergedPlaybook from a generic and/or domain match.
209
+ */
210
+ export function mergePlaybooks(
211
+ generic?: PlaybookDefinition,
212
+ domain?: PlaybookDefinition,
213
+ ): MergedPlaybook {
214
+ return {
215
+ generic,
216
+ domain,
217
+ mergedGates: mergeGates(generic, domain),
218
+ mergedTasks: mergeTaskTemplates(generic, domain),
219
+ mergedTools: mergeTools(generic, domain),
220
+ mergedVerification: mergeVerification(generic, domain),
221
+ label: buildLabel(generic, domain),
222
+ };
223
+ }
224
+
225
+ // =============================================================================
226
+ // MAIN API
227
+ // =============================================================================
228
+
229
+ /**
230
+ * Match playbooks for a plan based on intent and objective/scope text.
231
+ *
232
+ * Resolution:
233
+ * 1. Search built-in playbooks (vault search is wired separately by the caller)
234
+ * 2. Find best generic match
235
+ * 3. Find best domain match
236
+ * 4. If domain has `extends`, resolve the generic it extends
237
+ * 5. Merge into MergedPlaybook
238
+ *
239
+ * @param intent - The detected intent (BUILD, FIX, REVIEW, etc.)
240
+ * @param text - The plan objective + scope text to match against
241
+ * @param vaultPlaybooks - Optional vault-stored playbooks (searched first, override builtins)
242
+ */
243
+ export function matchPlaybooks(
244
+ intent: PlaybookIntent | undefined,
245
+ text: string,
246
+ vaultPlaybooks?: PlaybookDefinition[],
247
+ ): PlaybookMatchResult {
248
+ // Combine vault (higher priority) and built-in playbooks
249
+ const allPlaybooks = [...(vaultPlaybooks ?? []), ...BUILTIN_PLAYBOOKS];
250
+
251
+ const generics = allPlaybooks.filter((p) => p.tier === 'generic');
252
+ const domains = allPlaybooks.filter((p) => p.tier === 'domain');
253
+
254
+ // Find best domain match first (domain is more specific)
255
+ const domainMatch = findBestMatch(domains, intent, text);
256
+
257
+ // Find best generic match
258
+ let genericMatch = findBestMatch(generics, intent, text);
259
+
260
+ // If domain extends a specific generic, prefer that one
261
+ if (domainMatch?.playbook.extends) {
262
+ const extendedGeneric = allPlaybooks.find((p) => p.id === domainMatch.playbook.extends);
263
+ if (extendedGeneric) {
264
+ genericMatch = { playbook: extendedGeneric, score: domainMatch.score };
265
+ }
266
+ }
267
+
268
+ // No match at all
269
+ if (!genericMatch && !domainMatch) {
270
+ return { playbook: null };
271
+ }
272
+
273
+ const merged = mergePlaybooks(genericMatch?.playbook, domainMatch?.playbook);
274
+
275
+ return {
276
+ playbook: merged,
277
+ genericMatch: genericMatch
278
+ ? { id: genericMatch.playbook.id, source: 'builtin', score: genericMatch.score }
279
+ : undefined,
280
+ domainMatch: domainMatch
281
+ ? { id: domainMatch.playbook.id, source: 'builtin', score: domainMatch.score }
282
+ : undefined,
283
+ };
284
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Playbook Seeder
3
+ *
4
+ * Seeds default playbooks into the vault on activation.
5
+ * Idempotent — only seeds playbooks that don't already exist.
6
+ * Never overwrites user-modified playbooks.
7
+ *
8
+ * Also provides conversion utilities between PlaybookDefinition and
9
+ * IntelligenceEntry for vault storage.
10
+ */
11
+
12
+ import type { IntelligenceEntry } from '../intelligence/types.js';
13
+ import type { PlaybookDefinition } from './playbook-types.js';
14
+ import { getAllBuiltinPlaybooks } from './playbook-registry.js';
15
+ import type { Vault } from '../vault/vault.js';
16
+
17
+ /**
18
+ * Convert a PlaybookDefinition to a vault IntelligenceEntry.
19
+ *
20
+ * Field mapping:
21
+ * id -> id title -> title
22
+ * 'playbook' -> type category -> domain
23
+ * 'suggestion' -> severity
24
+ * trigger -> context (prefixed with full definition JSON for reconstruction)
25
+ * description -> description
26
+ * tags -> tags
27
+ * steps -> example
28
+ * expectedOutcome -> why
29
+ */
30
+ export function playbookDefinitionToEntry(def: PlaybookDefinition): IntelligenceEntry {
31
+ // Serialize the full definition as JSON metadata so playbook_match can reconstruct it
32
+ const metadata = JSON.stringify(def);
33
+
34
+ return {
35
+ id: def.id,
36
+ type: 'playbook',
37
+ domain: def.category,
38
+ title: def.title,
39
+ severity: 'suggestion',
40
+ description: def.description,
41
+ context: `__PLAYBOOK_DEF__${metadata}__END_DEF__\n${def.trigger}`,
42
+ example: def.steps,
43
+ why: def.expectedOutcome,
44
+ tags: def.tags,
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Extract a PlaybookDefinition from a vault IntelligenceEntry.
50
+ * Returns null if the entry doesn't contain PlaybookDefinition metadata.
51
+ */
52
+ export function entryToPlaybookDefinition(entry: IntelligenceEntry): PlaybookDefinition | null {
53
+ if (entry.type !== 'playbook') return null;
54
+
55
+ const context = entry.context ?? '';
56
+ const startMarker = '__PLAYBOOK_DEF__';
57
+ const endMarker = '__END_DEF__';
58
+
59
+ const startIdx = context.indexOf(startMarker);
60
+ const endIdx = context.indexOf(endMarker);
61
+
62
+ if (startIdx < 0 || endIdx < 0) return null;
63
+
64
+ try {
65
+ const json = context.slice(startIdx + startMarker.length, endIdx);
66
+ const def = JSON.parse(json) as PlaybookDefinition;
67
+ // Validate it has the essential fields
68
+ if (!def.id || !def.tier || !def.title) return null;
69
+ return def;
70
+ } catch {
71
+ return null;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Seed default built-in playbooks into the vault.
77
+ * Idempotent — skips playbooks that already exist (never overwrites).
78
+ */
79
+ export function seedDefaultPlaybooks(vault: Vault): {
80
+ seeded: number;
81
+ skipped: number;
82
+ errors: number;
83
+ details: Array<{ id: string; action: 'seeded' | 'skipped' | 'error'; reason?: string }>;
84
+ } {
85
+ const result = {
86
+ seeded: 0,
87
+ skipped: 0,
88
+ errors: 0,
89
+ details: [] as Array<{ id: string; action: 'seeded' | 'skipped' | 'error'; reason?: string }>,
90
+ };
91
+
92
+ const playbooks = getAllBuiltinPlaybooks();
93
+
94
+ for (const playbook of playbooks) {
95
+ try {
96
+ // Check if already exists
97
+ const existing = vault.get(playbook.id);
98
+ if (existing) {
99
+ result.skipped++;
100
+ result.details.push({ id: playbook.id, action: 'skipped', reason: 'already exists' });
101
+ continue;
102
+ }
103
+
104
+ const entry = playbookDefinitionToEntry(playbook);
105
+ vault.add(entry);
106
+ result.seeded++;
107
+ result.details.push({ id: playbook.id, action: 'seeded' });
108
+ } catch (error) {
109
+ result.errors++;
110
+ result.details.push({
111
+ id: playbook.id,
112
+ action: 'error',
113
+ reason: error instanceof Error ? error.message : String(error),
114
+ });
115
+ }
116
+ }
117
+
118
+ return result;
119
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Playbook Type System
3
+ *
4
+ * Two-tier playbook architecture ported from Salvador:
5
+ * - Generic playbooks: process discipline (TDD, brainstorming, debugging, etc.)
6
+ * - Domain playbooks: agent-specific workflows that extend generics
7
+ *
8
+ * Playbooks compose at plan creation time via the `extends` relationship.
9
+ * Generic provides the rhythm, domain fills in domain-specific beats.
10
+ */
11
+
12
+ // =============================================================================
13
+ // TIERS & INTENTS
14
+ // =============================================================================
15
+
16
+ export type PlaybookTier = 'generic' | 'domain';
17
+ export type PlaybookIntent = 'BUILD' | 'FIX' | 'REVIEW' | 'PLAN' | 'IMPROVE' | 'DELIVER';
18
+
19
+ // =============================================================================
20
+ // BRAINSTORM SECTIONS
21
+ // =============================================================================
22
+
23
+ /**
24
+ * A structured brainstorming section returned by the brainstorm op.
25
+ * The LLM uses these to guide design conversation with the user.
26
+ */
27
+ export interface BrainstormSection {
28
+ /** Section title (e.g., "Component API", "Color Requirements") */
29
+ title: string;
30
+ /** What this section covers */
31
+ description: string;
32
+ /** Guiding questions for the user */
33
+ questions: string[];
34
+ }
35
+
36
+ // =============================================================================
37
+ // GATES
38
+ // =============================================================================
39
+
40
+ /**
41
+ * A gate that must be satisfied at a given lifecycle phase.
42
+ * Gates inject checkId requirements into the planning lifecycle.
43
+ */
44
+ export interface PlaybookGate {
45
+ /** Which lifecycle phase this gate applies to */
46
+ phase: 'brainstorming' | 'pre-execution' | 'post-task' | 'completion';
47
+ /** Human-readable requirement description */
48
+ requirement: string;
49
+ /** Check type to create/validate */
50
+ checkType: string;
51
+ }
52
+
53
+ // =============================================================================
54
+ // TASK TEMPLATES
55
+ // =============================================================================
56
+
57
+ /**
58
+ * A task template that the playbook injects into generated plans.
59
+ * These become PlanTask entries during task splitting.
60
+ */
61
+ export interface PlaybookTaskTemplate {
62
+ /** Task type for the generated task */
63
+ taskType: 'implementation' | 'test' | 'story' | 'documentation' | 'verification';
64
+ /** Title template — may contain {objective} placeholder */
65
+ titleTemplate: string;
66
+ /** Acceptance criteria injected into the task */
67
+ acceptanceCriteria: string[];
68
+ /** Tools relevant to this task */
69
+ tools: string[];
70
+ /** When this task should execute relative to implementation */
71
+ order: 'before-implementation' | 'after-implementation' | 'parallel';
72
+ }
73
+
74
+ // =============================================================================
75
+ // PLAYBOOK DEFINITION
76
+ // =============================================================================
77
+
78
+ /**
79
+ * Complete playbook definition — the core data type.
80
+ * Playbooks are pure data objects with no logic.
81
+ */
82
+ export interface PlaybookDefinition {
83
+ /** Unique identifier (e.g., 'generic-tdd', 'domain-component-build') */
84
+ id: string;
85
+ /** Which tier this playbook belongs to */
86
+ tier: PlaybookTier;
87
+ /** Human-readable title */
88
+ title: string;
89
+ /** When to activate — maps to vault entry 'context' field */
90
+ trigger: string;
91
+ /** Overview of what this playbook does — maps to vault 'description' field */
92
+ description: string;
93
+ /** Step-by-step process — maps to vault 'example' field */
94
+ steps: string;
95
+ /** What success looks like — maps to vault 'why' field */
96
+ expectedOutcome: string;
97
+ /** ID of generic playbook this extends (domain playbooks only) */
98
+ extends?: string;
99
+ /** Free string category (agents define their own domains) */
100
+ category: string;
101
+ /** Searchable tags */
102
+ tags: string[];
103
+ /** Intents that trigger this playbook */
104
+ matchIntents: PlaybookIntent[];
105
+ /** Keywords in plan objective/scope that trigger this playbook */
106
+ matchKeywords: string[];
107
+
108
+ // --- What this playbook injects into plans ---
109
+
110
+ /** Brainstorming sections for design exploration (used by brainstorm op) */
111
+ brainstormSections?: BrainstormSection[];
112
+
113
+ /** Lifecycle gates to enforce */
114
+ gates: PlaybookGate[];
115
+ /** Task templates to inject during task generation */
116
+ taskTemplates: PlaybookTaskTemplate[];
117
+ /** Generic op names to auto-inject into tool chain (not agent-prefixed) */
118
+ toolInjections: string[];
119
+ /** Verification criteria for completion gate */
120
+ verificationCriteria: string[];
121
+ }
122
+
123
+ // =============================================================================
124
+ // MERGED PLAYBOOK
125
+ // =============================================================================
126
+
127
+ /**
128
+ * Result of matching and merging a generic + domain playbook pair.
129
+ * This is the shape that plan-handler receives after playbook resolution.
130
+ */
131
+ export interface MergedPlaybook {
132
+ /** The generic playbook (if matched) */
133
+ generic?: PlaybookDefinition;
134
+ /** The domain playbook (if matched) */
135
+ domain?: PlaybookDefinition;
136
+ /** Combined gates from both tiers (generic first, then domain) */
137
+ mergedGates: PlaybookGate[];
138
+ /** Combined task templates (domain overrides generic where order conflicts) */
139
+ mergedTasks: PlaybookTaskTemplate[];
140
+ /** Combined tool injections (deduplicated) */
141
+ mergedTools: string[];
142
+ /** Combined verification criteria */
143
+ mergedVerification: string[];
144
+ /** Human-readable label for the matched playbook(s) */
145
+ label: string;
146
+ }
147
+
148
+ // =============================================================================
149
+ // MATCH RESULT
150
+ // =============================================================================
151
+
152
+ /**
153
+ * Result of playbook matching — includes the source of each match.
154
+ */
155
+ export interface PlaybookMatchResult {
156
+ /** The merged playbook (null if no match) */
157
+ playbook: MergedPlaybook | null;
158
+ /** Which generic matched and why */
159
+ genericMatch?: { id: string; source: 'vault' | 'builtin'; score: number };
160
+ /** Which domain matched and why */
161
+ domainMatch?: { id: string; source: 'vault' | 'builtin'; score: number };
162
+ }
@@ -42,7 +42,11 @@ interface LinkRow {
42
42
  * Generate a deterministic project ID from a filesystem path.
43
43
  */
44
44
  function pathToId(path: string): string {
45
- return path.replace(/[^a-z0-9]/gi, '-').toLowerCase().replace(/-+/g, '-').replace(/^-|-$/g, '');
45
+ return path
46
+ .replace(/[^a-z0-9]/gi, '-')
47
+ .toLowerCase()
48
+ .replace(/-+/g, '-')
49
+ .replace(/^-|-$/g, '');
46
50
  }
47
51
 
48
52
  function rowToProject(row: ProjectRow): RegisteredProject {
@@ -124,16 +128,18 @@ export class ProjectRegistry {
124
128
  const id = pathToId(path);
125
129
  const now = Date.now();
126
130
 
127
- const existing = this.db
128
- .prepare('SELECT * FROM registered_projects WHERE id = ?')
129
- .get(id) as ProjectRow | undefined;
131
+ const existing = this.db.prepare('SELECT * FROM registered_projects WHERE id = ?').get(id) as
132
+ | ProjectRow
133
+ | undefined;
130
134
 
131
135
  if (existing) {
132
136
  // Update lastAccessedAt, and optionally name/metadata if provided
133
137
  const newName = name ?? existing.name;
134
138
  const newMetadata = metadata ? JSON.stringify(metadata) : existing.metadata;
135
139
  this.db
136
- .prepare('UPDATE registered_projects SET last_accessed_at = ?, name = ?, metadata = ? WHERE id = ?')
140
+ .prepare(
141
+ 'UPDATE registered_projects SET last_accessed_at = ?, name = ?, metadata = ? WHERE id = ?',
142
+ )
137
143
  .run(now, newName, newMetadata, id);
138
144
  return rowToProject({
139
145
  ...existing,
@@ -164,9 +170,9 @@ export class ProjectRegistry {
164
170
  * Get a project by ID.
165
171
  */
166
172
  get(projectId: string): RegisteredProject | null {
167
- const row = this.db
168
- .prepare('SELECT * FROM registered_projects WHERE id = ?')
169
- .get(projectId) as ProjectRow | undefined;
173
+ const row = this.db.prepare('SELECT * FROM registered_projects WHERE id = ?').get(projectId) as
174
+ | ProjectRow
175
+ | undefined;
170
176
  return row ? rowToProject(row) : null;
171
177
  }
172
178
 
@@ -174,9 +180,9 @@ export class ProjectRegistry {
174
180
  * Get a project by its filesystem path.
175
181
  */
176
182
  getByPath(path: string): RegisteredProject | null {
177
- const row = this.db
178
- .prepare('SELECT * FROM registered_projects WHERE path = ?')
179
- .get(path) as ProjectRow | undefined;
183
+ const row = this.db.prepare('SELECT * FROM registered_projects WHERE path = ?').get(path) as
184
+ | ProjectRow
185
+ | undefined;
180
186
  return row ? rowToProject(row) : null;
181
187
  }
182
188
 
@@ -199,7 +205,9 @@ export class ProjectRegistry {
199
205
  this.db
200
206
  .prepare('DELETE FROM project_links WHERE source_project_id = ? OR target_project_id = ?')
201
207
  .run(projectId, projectId);
202
- return this.db.prepare('DELETE FROM registered_projects WHERE id = ?').run(projectId).changes > 0;
208
+ return (
209
+ this.db.prepare('DELETE FROM registered_projects WHERE id = ?').run(projectId).changes > 0
210
+ );
203
211
  })();
204
212
  return result;
205
213
  }
@@ -245,7 +253,9 @@ export class ProjectRegistry {
245
253
  */
246
254
  getRules(projectId: string): ProjectRule[] {
247
255
  const rows = this.db
248
- .prepare('SELECT * FROM project_rules WHERE project_id = ? ORDER BY priority DESC, created_at ASC')
256
+ .prepare(
257
+ 'SELECT * FROM project_rules WHERE project_id = ? ORDER BY priority DESC, created_at ASC',
258
+ )
249
259
  .all(projectId) as RuleRow[];
250
260
  return rows.map(rowToRule);
251
261
  }
@@ -313,9 +323,7 @@ export class ProjectRegistry {
313
323
  .run(sourceId, targetId, linkType).changes;
314
324
  }
315
325
  return this.db
316
- .prepare(
317
- 'DELETE FROM project_links WHERE source_project_id = ? AND target_project_id = ?',
318
- )
326
+ .prepare('DELETE FROM project_links WHERE source_project_id = ? AND target_project_id = ?')
319
327
  .run(sourceId, targetId).changes;
320
328
  }
321
329
 
@@ -338,7 +346,11 @@ export class ProjectRegistry {
338
346
  projectId: string,
339
347
  ): Array<{ project: RegisteredProject; linkType: LinkType; direction: 'outgoing' | 'incoming' }> {
340
348
  const links = this.getLinks(projectId);
341
- const results: Array<{ project: RegisteredProject; linkType: LinkType; direction: 'outgoing' | 'incoming' }> = [];
349
+ const results: Array<{
350
+ project: RegisteredProject;
351
+ linkType: LinkType;
352
+ direction: 'outgoing' | 'incoming';
353
+ }> = [];
342
354
 
343
355
  for (const link of links) {
344
356
  const isOutgoing = link.sourceProjectId === projectId;
@@ -0,0 +1,3 @@
1
+ export { TemplateManager } from './template-manager.js';
2
+ export { parseVariables, resolveIncludes } from './parser.js';
3
+ export type { PromptTemplate, TemplateVariable, RenderOptions } from './types.js';