dual-brain 0.2.30 → 0.3.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.
Files changed (312) hide show
  1. package/.dual-brain/docs/claude-code-extension-points.md +32 -0
  2. package/.dual-brain/docs/data-tools-capabilities.md +181 -0
  3. package/.dual-brain/docs/ecosystem-tools.md +91 -0
  4. package/.dual-brain/docs/panel-handoff.md +124 -0
  5. package/.dual-brain/docs/ruflo-analysis.md +48 -0
  6. package/bin/dual-brain.mjs +56 -56
  7. package/dist/mcp-server/index.d.ts +27 -0
  8. package/dist/mcp-server/index.js +359 -0
  9. package/dist/mcp-server/index.js.map +1 -0
  10. package/dist/src/agent-protocol.d.ts +163 -0
  11. package/dist/src/agent-protocol.js +368 -0
  12. package/dist/src/agent-protocol.js.map +1 -0
  13. package/dist/src/agents/registry.d.ts +52 -0
  14. package/dist/src/agents/registry.js +393 -0
  15. package/dist/src/agents/registry.js.map +1 -0
  16. package/dist/src/awareness.d.ts +93 -0
  17. package/dist/src/awareness.js +406 -0
  18. package/dist/src/awareness.js.map +1 -0
  19. package/dist/src/brief.d.ts +48 -0
  20. package/dist/src/brief.js +179 -0
  21. package/dist/src/brief.js.map +1 -0
  22. package/dist/src/calibration.d.ts +32 -0
  23. package/dist/src/calibration.js +133 -0
  24. package/dist/src/calibration.js.map +1 -0
  25. package/dist/src/checkpoint.d.ts +33 -0
  26. package/dist/src/checkpoint.js +99 -0
  27. package/dist/src/checkpoint.js.map +1 -0
  28. package/dist/src/ci-triage.d.ts +33 -0
  29. package/dist/src/ci-triage.js +193 -0
  30. package/dist/src/ci-triage.js.map +1 -0
  31. package/dist/src/cognitive-loop.d.ts +56 -0
  32. package/dist/src/cognitive-loop.js +495 -0
  33. package/dist/src/cognitive-loop.js.map +1 -0
  34. package/dist/src/collaboration.d.ts +147 -0
  35. package/dist/src/collaboration.js +438 -0
  36. package/dist/src/collaboration.js.map +1 -0
  37. package/dist/src/context-intel.d.ts +47 -0
  38. package/dist/src/context-intel.js +156 -0
  39. package/dist/src/context-intel.js.map +1 -0
  40. package/dist/src/context.d.ts +53 -0
  41. package/dist/src/context.js +332 -0
  42. package/dist/src/context.js.map +1 -0
  43. package/dist/src/continuity.d.ts +89 -0
  44. package/dist/src/continuity.js +230 -0
  45. package/dist/src/continuity.js.map +1 -0
  46. package/dist/src/cost-tracker.d.ts +47 -0
  47. package/dist/src/cost-tracker.js +170 -0
  48. package/dist/src/cost-tracker.js.map +1 -0
  49. package/dist/src/debrief.d.ts +53 -0
  50. package/dist/src/debrief.js +222 -0
  51. package/dist/src/debrief.js.map +1 -0
  52. package/dist/src/decide.d.ts +96 -0
  53. package/dist/src/decide.js +744 -0
  54. package/dist/src/decide.js.map +1 -0
  55. package/dist/src/decompose.d.ts +39 -0
  56. package/dist/src/decompose.js +218 -0
  57. package/dist/src/decompose.js.map +1 -0
  58. package/dist/src/detect.d.ts +91 -0
  59. package/dist/src/detect.js +544 -0
  60. package/dist/src/detect.js.map +1 -0
  61. package/dist/src/dispatch.d.ts +154 -0
  62. package/dist/src/dispatch.js +1306 -0
  63. package/dist/src/dispatch.js.map +1 -0
  64. package/dist/src/doctor.d.ts +421 -0
  65. package/dist/src/doctor.js +1689 -0
  66. package/dist/src/doctor.js.map +1 -0
  67. package/dist/src/engine.d.ts +70 -0
  68. package/dist/src/engine.js +155 -0
  69. package/dist/src/engine.js.map +1 -0
  70. package/dist/src/envelope.d.ts +36 -0
  71. package/dist/src/envelope.js +80 -0
  72. package/dist/src/envelope.js.map +1 -0
  73. package/dist/src/failure-memory.d.ts +55 -0
  74. package/dist/src/failure-memory.js +175 -0
  75. package/dist/src/failure-memory.js.map +1 -0
  76. package/dist/src/fx.d.ts +87 -0
  77. package/dist/src/fx.js +272 -0
  78. package/dist/src/fx.js.map +1 -0
  79. package/dist/src/governance.d.ts +93 -0
  80. package/dist/src/governance.js +261 -0
  81. package/dist/src/governance.js.map +1 -0
  82. package/dist/src/handoff.d.ts +11 -0
  83. package/dist/src/handoff.js +90 -0
  84. package/dist/src/handoff.js.map +1 -0
  85. package/dist/src/head-protocol.d.ts +76 -0
  86. package/dist/src/head-protocol.js +109 -0
  87. package/dist/src/head-protocol.js.map +1 -0
  88. package/dist/src/head.d.ts +222 -0
  89. package/dist/src/head.js +765 -0
  90. package/dist/src/head.js.map +1 -0
  91. package/dist/src/health.d.ts +132 -0
  92. package/dist/src/health.js +435 -0
  93. package/dist/src/health.js.map +1 -0
  94. package/dist/src/inbox.d.ts +70 -0
  95. package/dist/src/inbox.js +218 -0
  96. package/dist/src/inbox.js.map +1 -0
  97. package/dist/src/index.d.ts +33 -0
  98. package/dist/src/index.js +38 -0
  99. package/dist/src/index.js.map +1 -0
  100. package/dist/src/install-hooks.d.ts +13 -0
  101. package/dist/src/install-hooks.js +88 -0
  102. package/dist/src/install-hooks.js.map +1 -0
  103. package/dist/src/integrity.d.ts +59 -0
  104. package/dist/src/integrity.js +206 -0
  105. package/dist/src/integrity.js.map +1 -0
  106. package/dist/src/intelligence.d.ts +104 -0
  107. package/dist/src/intelligence.js +391 -0
  108. package/dist/src/intelligence.js.map +1 -0
  109. package/dist/src/ledger.d.ts +54 -0
  110. package/dist/src/ledger.js +179 -0
  111. package/dist/src/ledger.js.map +1 -0
  112. package/dist/src/living-docs.d.ts +14 -0
  113. package/dist/src/living-docs.js +197 -0
  114. package/dist/src/living-docs.js.map +1 -0
  115. package/dist/src/memory-tiers.d.ts +37 -0
  116. package/dist/src/memory-tiers.js +160 -0
  117. package/dist/src/memory-tiers.js.map +1 -0
  118. package/dist/src/model-profiles.d.ts +65 -0
  119. package/dist/src/model-profiles.js +568 -0
  120. package/dist/src/model-profiles.js.map +1 -0
  121. package/dist/src/models.d.ts +58 -0
  122. package/dist/src/models.js +327 -0
  123. package/dist/src/models.js.map +1 -0
  124. package/dist/src/narrative.d.ts +54 -0
  125. package/dist/src/narrative.js +163 -0
  126. package/dist/src/narrative.js.map +1 -0
  127. package/dist/src/nextstep.d.ts +16 -0
  128. package/dist/src/nextstep.js +103 -0
  129. package/dist/src/nextstep.js.map +1 -0
  130. package/dist/src/observer.d.ts +18 -0
  131. package/dist/src/observer.js +251 -0
  132. package/dist/src/observer.js.map +1 -0
  133. package/dist/src/outcome.d.ts +110 -0
  134. package/dist/src/outcome.js +377 -0
  135. package/dist/src/outcome.js.map +1 -0
  136. package/dist/src/pipeline.d.ts +167 -0
  137. package/dist/src/pipeline.js +1503 -0
  138. package/dist/src/pipeline.js.map +1 -0
  139. package/dist/src/playbook.d.ts +59 -0
  140. package/dist/src/playbook.js +238 -0
  141. package/dist/src/playbook.js.map +1 -0
  142. package/dist/src/pr-agent.d.ts +97 -0
  143. package/dist/src/pr-agent.js +195 -0
  144. package/dist/src/pr-agent.js.map +1 -0
  145. package/dist/src/predictive.d.ts +57 -0
  146. package/dist/src/predictive.js +230 -0
  147. package/dist/src/predictive.js.map +1 -0
  148. package/dist/src/profile.d.ts +294 -0
  149. package/dist/src/profile.js +1347 -0
  150. package/dist/src/profile.js.map +1 -0
  151. package/dist/src/prompt-audit.d.ts +22 -0
  152. package/dist/src/prompt-audit.js +194 -0
  153. package/dist/src/prompt-audit.js.map +1 -0
  154. package/dist/src/prompt-intel.d.ts +12 -0
  155. package/dist/src/prompt-intel.js +321 -0
  156. package/dist/src/prompt-intel.js.map +1 -0
  157. package/dist/src/provider-context.d.ts +121 -0
  158. package/dist/src/provider-context.js +222 -0
  159. package/dist/src/provider-context.js.map +1 -0
  160. package/dist/src/provider-manager.d.ts +92 -0
  161. package/dist/src/provider-manager.js +428 -0
  162. package/dist/src/provider-manager.js.map +1 -0
  163. package/dist/src/receipt.d.ts +87 -0
  164. package/dist/src/receipt.js +326 -0
  165. package/dist/src/receipt.js.map +1 -0
  166. package/dist/src/recommendations.d.ts +13 -0
  167. package/dist/src/recommendations.js +291 -0
  168. package/dist/src/recommendations.js.map +1 -0
  169. package/dist/src/redact.d.ts +15 -0
  170. package/dist/src/redact.js +129 -0
  171. package/dist/src/redact.js.map +1 -0
  172. package/dist/src/replit.d.ts +397 -0
  173. package/dist/src/replit.js +1160 -0
  174. package/dist/src/replit.js.map +1 -0
  175. package/dist/src/repo.d.ts +149 -0
  176. package/dist/src/repo.js +416 -0
  177. package/dist/src/repo.js.map +1 -0
  178. package/dist/src/revert.d.ts +30 -0
  179. package/dist/src/revert.js +166 -0
  180. package/dist/src/revert.js.map +1 -0
  181. package/dist/src/room.d.ts +102 -0
  182. package/dist/src/room.js +212 -0
  183. package/dist/src/room.js.map +1 -0
  184. package/dist/src/routing-advisor.d.ts +57 -0
  185. package/dist/src/routing-advisor.js +221 -0
  186. package/dist/src/routing-advisor.js.map +1 -0
  187. package/dist/src/self-correct.d.ts +40 -0
  188. package/dist/src/self-correct.js +137 -0
  189. package/dist/src/self-correct.js.map +1 -0
  190. package/dist/src/session-lock.d.ts +35 -0
  191. package/dist/src/session-lock.js +134 -0
  192. package/dist/src/session-lock.js.map +1 -0
  193. package/dist/src/session.d.ts +267 -0
  194. package/dist/src/session.js +1660 -0
  195. package/dist/src/session.js.map +1 -0
  196. package/dist/src/settings-tui.d.ts +5 -0
  197. package/dist/src/settings-tui.js +422 -0
  198. package/dist/src/settings-tui.js.map +1 -0
  199. package/dist/src/setup-flow.d.ts +63 -0
  200. package/dist/src/setup-flow.js +233 -0
  201. package/dist/src/setup-flow.js.map +1 -0
  202. package/dist/src/signal.d.ts +19 -0
  203. package/dist/src/signal.js +122 -0
  204. package/dist/src/signal.js.map +1 -0
  205. package/dist/src/simmer.d.ts +85 -0
  206. package/dist/src/simmer.js +224 -0
  207. package/dist/src/simmer.js.map +1 -0
  208. package/dist/src/state-export.d.ts +129 -0
  209. package/dist/src/state-export.js +233 -0
  210. package/dist/src/state-export.js.map +1 -0
  211. package/dist/src/strategy.d.ts +54 -0
  212. package/dist/src/strategy.js +95 -0
  213. package/dist/src/strategy.js.map +1 -0
  214. package/dist/src/subscription.d.ts +40 -0
  215. package/dist/src/subscription.js +189 -0
  216. package/dist/src/subscription.js.map +1 -0
  217. package/dist/src/templates.d.ts +208 -0
  218. package/dist/src/templates.js +238 -0
  219. package/dist/src/templates.js.map +1 -0
  220. package/dist/src/test.d.ts +9 -0
  221. package/dist/src/test.js +1173 -0
  222. package/dist/src/test.js.map +1 -0
  223. package/dist/src/think-engine.d.ts +67 -0
  224. package/dist/src/think-engine.js +412 -0
  225. package/dist/src/think-engine.js.map +1 -0
  226. package/dist/src/tui.d.ts +71 -0
  227. package/dist/src/tui.js +242 -0
  228. package/dist/src/tui.js.map +1 -0
  229. package/dist/src/types.d.ts +177 -0
  230. package/dist/src/types.js +6 -0
  231. package/dist/src/types.js.map +1 -0
  232. package/dist/src/update-check.d.ts +7 -0
  233. package/dist/src/update-check.js +36 -0
  234. package/dist/src/update-check.js.map +1 -0
  235. package/dist/src/wave-planner.d.ts +30 -0
  236. package/dist/src/wave-planner.js +281 -0
  237. package/dist/src/wave-planner.js.map +1 -0
  238. package/hooks/head-guard.sh +41 -0
  239. package/hooks/precompact.mjs +3 -3
  240. package/hooks/session-end.mjs +3 -3
  241. package/hooks/task-classifier.mjs +328 -0
  242. package/hooks/vibe-router.mjs +387 -0
  243. package/install.mjs +2 -2
  244. package/package.json +29 -153
  245. package/src/agents/registry.mjs +0 -405
  246. package/src/awareness.mjs +0 -425
  247. package/src/brief.mjs +0 -266
  248. package/src/calibration.mjs +0 -148
  249. package/src/checkpoint.mjs +0 -109
  250. package/src/ci-triage.mjs +0 -191
  251. package/src/cognitive-loop.mjs +0 -562
  252. package/src/collaboration.mjs +0 -545
  253. package/src/context-intel.mjs +0 -158
  254. package/src/context.mjs +0 -389
  255. package/src/continuity.mjs +0 -298
  256. package/src/cost-tracker.mjs +0 -184
  257. package/src/debrief.mjs +0 -228
  258. package/src/decide.mjs +0 -1099
  259. package/src/decompose.mjs +0 -331
  260. package/src/detect.mjs +0 -702
  261. package/src/dispatch.mjs +0 -1447
  262. package/src/doctor.mjs +0 -1607
  263. package/src/envelope.mjs +0 -139
  264. package/src/failure-memory.mjs +0 -178
  265. package/src/fx.mjs +0 -276
  266. package/src/governance.mjs +0 -279
  267. package/src/handoff.mjs +0 -87
  268. package/src/head-protocol.mjs +0 -128
  269. package/src/head.mjs +0 -952
  270. package/src/health.mjs +0 -528
  271. package/src/inbox.mjs +0 -195
  272. package/src/index.mjs +0 -44
  273. package/src/install-hooks.mjs +0 -100
  274. package/src/integrity.mjs +0 -245
  275. package/src/intelligence.mjs +0 -447
  276. package/src/ledger.mjs +0 -196
  277. package/src/living-docs.mjs +0 -210
  278. package/src/memory-tiers.mjs +0 -193
  279. package/src/models.mjs +0 -363
  280. package/src/narrative.mjs +0 -169
  281. package/src/nextstep.mjs +0 -100
  282. package/src/observer.mjs +0 -241
  283. package/src/outcome.mjs +0 -400
  284. package/src/pipeline.mjs +0 -1711
  285. package/src/playbook.mjs +0 -257
  286. package/src/pr-agent.mjs +0 -214
  287. package/src/predictive.mjs +0 -250
  288. package/src/profile.mjs +0 -1411
  289. package/src/prompt-audit.mjs +0 -231
  290. package/src/prompt-intel.mjs +0 -325
  291. package/src/provider-context.mjs +0 -257
  292. package/src/receipt.mjs +0 -344
  293. package/src/recommendations.mjs +0 -296
  294. package/src/redact.mjs +0 -192
  295. package/src/replit.mjs +0 -1210
  296. package/src/repo.mjs +0 -445
  297. package/src/revert.mjs +0 -149
  298. package/src/routing-advisor.mjs +0 -204
  299. package/src/self-correct.mjs +0 -147
  300. package/src/session-lock.mjs +0 -160
  301. package/src/session.mjs +0 -1655
  302. package/src/settings-tui.mjs +0 -373
  303. package/src/setup-flow.mjs +0 -223
  304. package/src/signal.mjs +0 -115
  305. package/src/simmer.mjs +0 -241
  306. package/src/strategy.mjs +0 -235
  307. package/src/subscription.mjs +0 -212
  308. package/src/templates.mjs +0 -260
  309. package/src/think-engine.mjs +0 -428
  310. package/src/tui.mjs +0 -276
  311. package/src/update-check.mjs +0 -35
  312. package/src/wave-planner.mjs +0 -294
@@ -1,212 +0,0 @@
1
- // subscription.mjs — Subscription-aware routing defaults
2
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
3
- import { join } from 'node:path';
4
-
5
- export const SUBSCRIPTIONS = {
6
- // Claude subscriptions
7
- 'claude-pro': {
8
- label: 'Claude Pro ($20/mo)',
9
- provider: 'claude',
10
- tokenBudget: 'moderate', // 5-hr rolling window, weekly cap
11
- recommendedProfile: 'balanced',
12
- modelWeights: { haiku: 0.4, sonnet: 0.5, opus: 0.1 },
13
- notes: 'One extended Opus session can use 20% of your allocation. Prefer sonnet for routine work.',
14
- },
15
- 'claude-max-5x': {
16
- label: 'Claude Max 5x ($100/mo)',
17
- provider: 'claude',
18
- tokenBudget: 'generous',
19
- recommendedProfile: 'quality-first',
20
- modelWeights: { haiku: 0.2, sonnet: 0.5, opus: 0.3 },
21
- notes: '5x Pro capacity. Opus is available for complex/creative work without worry.',
22
- },
23
- 'claude-max-20x': {
24
- label: 'Claude Max 20x ($200/mo)',
25
- provider: 'claude',
26
- tokenBudget: 'unlimited',
27
- recommendedProfile: 'quality-first',
28
- modelWeights: { haiku: 0.1, sonnet: 0.4, opus: 0.5 },
29
- notes: 'Effectively unlimited. Use the best model for every task.',
30
- },
31
- 'claude-team': {
32
- label: 'Claude Team ($30/seat/mo)',
33
- provider: 'claude',
34
- tokenBudget: 'moderate',
35
- recommendedProfile: 'balanced',
36
- modelWeights: { haiku: 0.3, sonnet: 0.5, opus: 0.2 },
37
- notes: 'Team tier with admin controls. Collaboration triggers recommended.',
38
- },
39
- // ChatGPT subscriptions
40
- 'chatgpt-plus': {
41
- label: 'ChatGPT Plus ($20/mo)',
42
- provider: 'openai',
43
- tokenBudget: 'limited', // 50 o3/day on Plus
44
- recommendedProfile: 'cost-saver',
45
- modelWeights: { 'o4-mini': 0.6, 'gpt-4.1': 0.3, 'o3': 0.1 },
46
- notes: '50 o3 messages/day limit. Heavy on o4-mini for routine, save o3 for critical decisions.',
47
- },
48
- 'chatgpt-pro': {
49
- label: 'ChatGPT Pro ($200/mo)',
50
- provider: 'openai',
51
- tokenBudget: 'generous',
52
- recommendedProfile: 'quality-first',
53
- modelWeights: { 'o4-mini': 0.3, 'gpt-4.1': 0.4, 'o3': 0.3 },
54
- notes: 'Unlimited access to all models. Use o3 freely for complex reasoning.',
55
- },
56
- // Dual subscription (both providers)
57
- 'dual-pro': {
58
- label: 'Both Pro tiers',
59
- provider: 'both',
60
- tokenBudget: 'moderate',
61
- recommendedProfile: 'balanced',
62
- modelWeights: { haiku: 0.2, sonnet: 0.3, 'gpt-4.1': 0.3, 'o4-mini': 0.2 },
63
- notes: 'Split load across providers. Route by model strength: Claude for code, GPT for reasoning.',
64
- },
65
- 'dual-max': {
66
- label: 'Max + Pro (or both Max)',
67
- provider: 'both',
68
- tokenBudget: 'unlimited',
69
- recommendedProfile: 'quality-first',
70
- modelWeights: { sonnet: 0.3, opus: 0.2, 'gpt-4.1': 0.2, 'o3': 0.3 },
71
- notes: 'Full power from both providers. Route by task fit, not by cost.',
72
- },
73
- };
74
-
75
- const DEFAULT_WEIGHTS = {
76
- modelWeights: { haiku: 0.3, sonnet: 0.5, opus: 0.2 },
77
- profile: 'balanced',
78
- notes: 'No subscription configured. Using balanced defaults.',
79
- };
80
-
81
- function subFile(cwd) {
82
- return join(cwd || process.cwd(), '.dualbrain', 'subscription.json');
83
- }
84
-
85
- /** Returns the subscription config object or null. */
86
- export function getSubscription(subType) {
87
- return SUBSCRIPTIONS[subType] ?? null;
88
- }
89
-
90
- /** Returns { modelWeights, profile, notes } for the subscription. Falls back to balanced defaults. */
91
- export function getRecommendedWeights(subType) {
92
- const sub = SUBSCRIPTIONS[subType];
93
- if (!sub) return DEFAULT_WEIGHTS;
94
- return {
95
- modelWeights: sub.modelWeights,
96
- profile: sub.recommendedProfile,
97
- notes: sub.notes,
98
- };
99
- }
100
-
101
- /** Writes { subscription, configuredAt } to .dualbrain/subscription.json. */
102
- export function saveUserSubscription(subType, cwd) {
103
- const dir = join(cwd || process.cwd(), '.dualbrain');
104
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
105
- writeFileSync(
106
- subFile(cwd),
107
- JSON.stringify({ subscription: subType, configuredAt: new Date().toISOString() }, null, 2),
108
- 'utf8'
109
- );
110
- }
111
-
112
- /** Reads the saved subscription. Returns subType string or null. */
113
- export function loadUserSubscription(cwd) {
114
- try {
115
- const p = subFile(cwd);
116
- if (!existsSync(p)) return null;
117
- const data = JSON.parse(readFileSync(p, 'utf8'));
118
- return data.subscription ?? null;
119
- } catch {
120
- return null;
121
- }
122
- }
123
-
124
- /**
125
- * Generates a text recommendation based on subscription + current routing stats.
126
- * routingStats: return value of getRoutingStats() from routing-advisor.mjs
127
- */
128
- export function generateRecommendation(subType, routingStats) {
129
- const sub = SUBSCRIPTIONS[subType];
130
- if (!sub) return 'No subscription configured. Run `dual-brain subscription set <type>` to enable smart routing defaults.';
131
-
132
- // Tally actual model usage from routing stats cells
133
- const actualUsage = {};
134
- let totalObs = 0;
135
- for (const models of Object.values(routingStats?.cells ?? {})) {
136
- for (const [model, entry] of Object.entries(models)) {
137
- actualUsage[model] = (actualUsage[model] ?? 0) + (entry.observations ?? 0);
138
- totalObs += entry.observations ?? 0;
139
- }
140
- }
141
-
142
- if (totalObs === 0) {
143
- return `You're on ${sub.label}. No routing history yet — recommended profile is ${sub.recommendedProfile}. ${sub.notes}`;
144
- }
145
-
146
- // Compute actual share per model
147
- const actualShare = {};
148
- for (const [model, count] of Object.entries(actualUsage)) {
149
- actualShare[model] = count / totalObs;
150
- }
151
-
152
- const rec = sub.modelWeights;
153
- const budget = sub.tokenBudget;
154
- const lines = [];
155
-
156
- // Check expensive model utilization vs. recommended
157
- const expensiveModels = ['opus', 'o3'];
158
- for (const model of expensiveModels) {
159
- const recW = rec[model] ?? 0;
160
- const actW = actualShare[model] ?? 0;
161
-
162
- if (recW > 0 && budget === 'unlimited' && actW < recW * 0.5) {
163
- lines.push(
164
- `You're on ${sub.label} but only using ${model} ${Math.round(actW * 100)}% of the time` +
165
- ` (recommended: ${Math.round(recW * 100)}%). You're paying for capacity you're not using.` +
166
- ` Consider switching to ${sub.recommendedProfile} mode.`
167
- );
168
- } else if (recW < 0.2 && budget === 'limited' && actW > recW * 2 && actW > 0.1) {
169
- lines.push(
170
- `Your ${sub.label} subscription is token-limited. ${model} usage at ${Math.round(actW * 100)}%` +
171
- ` may exhaust daily limits — recommended cap is ~${Math.round(recW * 100)}%.`
172
- );
173
- }
174
- }
175
-
176
- // Cheap model suggestions on budget-constrained plans
177
- const cheapModels = ['haiku', 'o4-mini'];
178
- if (budget === 'moderate' || budget === 'limited') {
179
- for (const model of cheapModels) {
180
- const recW = rec[model] ?? 0;
181
- const actW = actualShare[model] ?? 0;
182
- if (recW > 0 && actW < recW * 0.5) {
183
- lines.push(
184
- `Your ${sub.label} subscription has a ${budget} budget. Increasing ${model} usage` +
185
- ` (currently ${Math.round(actW * 100)}%, recommended ${Math.round(recW * 100)}%)` +
186
- ` for search and routine tasks would preserve your allocation.`
187
- );
188
- break; // one cheap-model tip is enough
189
- }
190
- }
191
- }
192
-
193
- // Dominant model confirmation — find the most-used model
194
- const topModel = Object.entries(actualShare).sort((a, b) => b[1] - a[1])[0];
195
- if (lines.length === 0 && topModel) {
196
- lines.push(
197
- `Your ${sub.label} subscription is well-matched. ${Math.round(topModel[1] * 100)}% of dispatches` +
198
- ` use ${topModel[0]} — a good fit for your ${budget} budget. ${sub.notes}`
199
- );
200
- }
201
-
202
- return lines.slice(0, 3).join(' ');
203
- }
204
-
205
- /** Returns array of { key, label, provider } for display in UX. */
206
- export function listSubscriptions() {
207
- return Object.entries(SUBSCRIPTIONS).map(([key, sub]) => ({
208
- key,
209
- label: sub.label,
210
- provider: sub.provider,
211
- }));
212
- }
package/src/templates.mjs DELETED
@@ -1,260 +0,0 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
-
4
- /**
5
- * Task contract — every dispatch must have one.
6
- * @typedef {{
7
- * id: string,
8
- * objective: string,
9
- * scope: string[],
10
- * nonGoals?: string[],
11
- * risk: 'low'|'medium'|'high'|'critical',
12
- * acceptanceCriteria: string[],
13
- * allowedOperations?: string[],
14
- * context?: string,
15
- * files?: string[],
16
- * timeoutMs?: number,
17
- * }} TaskContract
18
- */
19
-
20
- /**
21
- * Validate a task contract has all required fields.
22
- * Returns { valid, missing }
23
- */
24
- export function validateContract(contract) {
25
- const required = ['objective', 'scope', 'risk', 'acceptanceCriteria'];
26
- const missing = required.filter(f => !contract?.[f] || (Array.isArray(contract[f]) && contract[f].length === 0));
27
- return {
28
- valid: missing.length === 0,
29
- missing,
30
- contract: missing.length === 0 ? { ...contract, id: contract.id || Date.now().toString(36) } : null,
31
- };
32
- }
33
-
34
- // ── Template definitions ─────────────────────────────────────────────────────
35
-
36
- const TEMPLATES = {
37
- search: {
38
- id: 'search',
39
- version: '1.0',
40
- tier: 'search',
41
- description: 'Read-only lookups, grep, explore. Returns files found, line refs, confidence.',
42
- requiredFields: ['objective', 'scope'],
43
- render(contract, context = {}) {
44
- const lines = [];
45
- lines.push(`Find: ${contract.objective}`);
46
- lines.push('');
47
- if (contract.scope.length) lines.push(`Scope: ${contract.scope.join(', ')}`);
48
- if (contract.files?.length) lines.push(`Start with: ${contract.files.join(', ')}`);
49
- if (contract.context) lines.push(`Context: ${contract.context}`);
50
- lines.push('');
51
- lines.push('Return: file paths, line numbers, relevant code snippets, and confidence level.');
52
- if (contract.nonGoals?.length) lines.push(`Do NOT: ${contract.nonGoals.join('; ')}`);
53
- return lines.join('\n');
54
- },
55
- },
56
-
57
- execute: {
58
- id: 'execute',
59
- version: '1.0',
60
- tier: 'execute',
61
- description: 'Edits, tests, git ops. Returns files changed, tests run, edge cases.',
62
- requiredFields: ['objective', 'scope', 'acceptanceCriteria'],
63
- render(contract, context = {}) {
64
- const lines = [];
65
- lines.push(contract.objective);
66
- lines.push('');
67
- if (contract.scope.length) lines.push(`Files in scope: ${contract.scope.join(', ')}`);
68
- if (contract.files?.length) lines.push(`Read first: ${contract.files.join(', ')}`);
69
- if (contract.context) lines.push(`Context: ${contract.context}`);
70
- lines.push('');
71
- lines.push('Acceptance criteria:');
72
- for (const c of contract.acceptanceCriteria) {
73
- lines.push(`- ${c}`);
74
- }
75
- if (contract.nonGoals?.length) {
76
- lines.push('');
77
- lines.push('Non-goals (do NOT do these):');
78
- for (const ng of contract.nonGoals) lines.push(`- ${ng}`);
79
- }
80
- if (contract.allowedOperations?.length) {
81
- lines.push('');
82
- lines.push(`Allowed operations: ${contract.allowedOperations.join(', ')}`);
83
- }
84
- lines.push('');
85
- lines.push('Return: files changed, tests run, edge cases found.');
86
- return lines.join('\n');
87
- },
88
- },
89
-
90
- think: {
91
- id: 'think',
92
- version: '1.0',
93
- tier: 'think',
94
- description: 'Architecture decisions, design review, planning.',
95
- requiredFields: ['objective'],
96
- render(contract, context = {}) {
97
- const lines = [];
98
- lines.push(contract.objective);
99
- lines.push('');
100
- if (contract.scope?.length) lines.push(`Relevant modules: ${contract.scope.join(', ')}`);
101
- if (contract.context) lines.push(`Background: ${contract.context}`);
102
- if (contract.files?.length) lines.push(`Key files: ${contract.files.join(', ')}`);
103
- lines.push('');
104
- lines.push('Provide: recommendation, rationale, alternatives considered, risks, and confidence level.');
105
- if (contract.acceptanceCriteria?.length) {
106
- lines.push('');
107
- lines.push('Decision criteria:');
108
- for (const c of contract.acceptanceCriteria) lines.push(`- ${c}`);
109
- }
110
- return lines.join('\n');
111
- },
112
- },
113
-
114
- review: {
115
- id: 'review',
116
- version: '1.0',
117
- tier: 'review',
118
- description: 'Code review with severity, line refs, test gaps, security concerns.',
119
- requiredFields: ['objective', 'scope'],
120
- render(contract, context = {}) {
121
- const lines = [];
122
- lines.push(`Review: ${contract.objective}`);
123
- lines.push('');
124
- if (contract.scope.length) lines.push(`Files to review: ${contract.scope.join(', ')}`);
125
- if (contract.context) lines.push(`Context: ${contract.context}`);
126
- lines.push('');
127
- lines.push('Check for:');
128
- lines.push('- Correctness and edge cases');
129
- lines.push('- Security vulnerabilities (OWASP top 10)');
130
- lines.push('- Test coverage gaps');
131
- lines.push('- Architectural drift');
132
- lines.push('- Performance concerns');
133
- if (contract.acceptanceCriteria?.length) {
134
- lines.push('');
135
- lines.push('Specific concerns:');
136
- for (const c of contract.acceptanceCriteria) lines.push(`- ${c}`);
137
- }
138
- lines.push('');
139
- lines.push('Return: findings with severity (critical/high/medium/low), file:line refs, and suggested fixes.');
140
- return lines.join('\n');
141
- },
142
- },
143
- };
144
-
145
- // ── Output schemas ───────────────────────────────────────────────────────────
146
-
147
- const OUTPUT_SCHEMAS = {
148
- think: '{ "decision": "string", "confidence": 0.0-1.0, "reasoning": "string", "workSpec": { "objective": "string", "files": ["path"], "criteria": ["string"] } }',
149
- execute: '{ "filesChanged": ["path"], "testsRun": boolean, "issues": ["string"] }',
150
- review: '{ "pass": boolean, "findings": [{ "severity": "critical|high|medium|low", "file": "path", "line": number, "issue": "string", "fix": "string" }] }',
151
- search: '{ "found": [{ "file": "path", "line": number, "snippet": "string" }], "confidence": 0.0-1.0 }',
152
- };
153
-
154
- // ── Model render hints ────────────────────────────────────────────────────────
155
-
156
- const MODEL_RENDER_HINTS = {
157
- xml: ['claude', 'sonnet', 'haiku', 'opus'],
158
- markdown: ['gpt', 'gpt-4', 'gpt-4.1', 'gpt-4o'],
159
- prose: ['o3', 'o4-mini'],
160
- };
161
-
162
- // ── Template API ─────────────────────────────────────────────────────────────
163
-
164
- /**
165
- * Get the structured output schema for a tier.
166
- */
167
- export function getOutputSchema(tier) {
168
- return OUTPUT_SCHEMAS[tier] || null;
169
- }
170
-
171
- /**
172
- * Get the preferred prompt rendering format for a given model ID.
173
- */
174
- export function getRenderHint(modelId) {
175
- if (!modelId) return 'markdown';
176
- const normalized = String(modelId).toLowerCase();
177
- for (const [format, patterns] of Object.entries(MODEL_RENDER_HINTS)) {
178
- if (patterns.some(p => normalized.includes(p))) return format;
179
- }
180
- return 'markdown';
181
- }
182
-
183
- /**
184
- * Get a template by tier name.
185
- */
186
- export function getTemplate(tier) {
187
- return TEMPLATES[tier] || null;
188
- }
189
-
190
- /**
191
- * List all available templates.
192
- */
193
- export function listTemplates() {
194
- return Object.values(TEMPLATES).map(t => ({
195
- id: t.id,
196
- version: t.version,
197
- tier: t.tier,
198
- description: t.description,
199
- requiredFields: t.requiredFields,
200
- }));
201
- }
202
-
203
- /**
204
- * Render a prompt from a template and task contract.
205
- * Validates contract first. Returns { prompt, template, contract, valid, errors }
206
- */
207
- export function renderPrompt(tier, contract, context = {}) {
208
- const template = TEMPLATES[tier];
209
- if (!template) {
210
- return { prompt: null, valid: false, errors: [`Unknown template tier: ${tier}`] };
211
- }
212
-
213
- // Validate required fields
214
- const missing = template.requiredFields.filter(f => !contract?.[f] || (Array.isArray(contract[f]) && contract[f].length === 0));
215
- if (missing.length > 0) {
216
- return {
217
- prompt: null,
218
- valid: false,
219
- errors: missing.map(f => `Missing required field: ${f}`),
220
- template: { id: template.id, version: template.version },
221
- };
222
- }
223
-
224
- const prompt = template.render(contract, context);
225
-
226
- return {
227
- prompt,
228
- valid: true,
229
- errors: [],
230
- template: { id: template.id, version: template.version },
231
- contract: { ...contract, id: contract.id || Date.now().toString(36) },
232
- outputSchema: OUTPUT_SCHEMAS[tier] || null,
233
- stats: {
234
- words: prompt.split(/\s+/).length,
235
- chars: prompt.length,
236
- estimatedTokens: Math.ceil(prompt.length / 4),
237
- },
238
- };
239
- }
240
-
241
- /**
242
- * Quick render: build a contract from minimal inputs and render.
243
- * For when HEAD knows the tier and objective but hasn't built a full contract.
244
- */
245
- export function quickRender(tier, objective, opts = {}) {
246
- const { scope = [], files = [], risk = 'medium', criteria = [], nonGoals = [], context = '' } = opts;
247
-
248
- const contract = {
249
- objective,
250
- scope,
251
- files,
252
- risk,
253
- acceptanceCriteria: criteria.length ? criteria : [`${objective} is complete and working`],
254
- nonGoals,
255
- context,
256
- allowedOperations: tier === 'search' ? ['read'] : tier === 'execute' ? ['read', 'write', 'test'] : ['read', 'analyze'],
257
- };
258
-
259
- return renderPrompt(tier, contract);
260
- }