dual-brain 0.2.30 → 0.3.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 (309) 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/task-classifier.mjs +328 -0
  240. package/hooks/vibe-router.mjs +387 -0
  241. package/package.json +29 -153
  242. package/src/agents/registry.mjs +0 -405
  243. package/src/awareness.mjs +0 -425
  244. package/src/brief.mjs +0 -266
  245. package/src/calibration.mjs +0 -148
  246. package/src/checkpoint.mjs +0 -109
  247. package/src/ci-triage.mjs +0 -191
  248. package/src/cognitive-loop.mjs +0 -562
  249. package/src/collaboration.mjs +0 -545
  250. package/src/context-intel.mjs +0 -158
  251. package/src/context.mjs +0 -389
  252. package/src/continuity.mjs +0 -298
  253. package/src/cost-tracker.mjs +0 -184
  254. package/src/debrief.mjs +0 -228
  255. package/src/decide.mjs +0 -1099
  256. package/src/decompose.mjs +0 -331
  257. package/src/detect.mjs +0 -702
  258. package/src/dispatch.mjs +0 -1447
  259. package/src/doctor.mjs +0 -1607
  260. package/src/envelope.mjs +0 -139
  261. package/src/failure-memory.mjs +0 -178
  262. package/src/fx.mjs +0 -276
  263. package/src/governance.mjs +0 -279
  264. package/src/handoff.mjs +0 -87
  265. package/src/head-protocol.mjs +0 -128
  266. package/src/head.mjs +0 -952
  267. package/src/health.mjs +0 -528
  268. package/src/inbox.mjs +0 -195
  269. package/src/index.mjs +0 -44
  270. package/src/install-hooks.mjs +0 -100
  271. package/src/integrity.mjs +0 -245
  272. package/src/intelligence.mjs +0 -447
  273. package/src/ledger.mjs +0 -196
  274. package/src/living-docs.mjs +0 -210
  275. package/src/memory-tiers.mjs +0 -193
  276. package/src/models.mjs +0 -363
  277. package/src/narrative.mjs +0 -169
  278. package/src/nextstep.mjs +0 -100
  279. package/src/observer.mjs +0 -241
  280. package/src/outcome.mjs +0 -400
  281. package/src/pipeline.mjs +0 -1711
  282. package/src/playbook.mjs +0 -257
  283. package/src/pr-agent.mjs +0 -214
  284. package/src/predictive.mjs +0 -250
  285. package/src/profile.mjs +0 -1411
  286. package/src/prompt-audit.mjs +0 -231
  287. package/src/prompt-intel.mjs +0 -325
  288. package/src/provider-context.mjs +0 -257
  289. package/src/receipt.mjs +0 -344
  290. package/src/recommendations.mjs +0 -296
  291. package/src/redact.mjs +0 -192
  292. package/src/replit.mjs +0 -1210
  293. package/src/repo.mjs +0 -445
  294. package/src/revert.mjs +0 -149
  295. package/src/routing-advisor.mjs +0 -204
  296. package/src/self-correct.mjs +0 -147
  297. package/src/session-lock.mjs +0 -160
  298. package/src/session.mjs +0 -1655
  299. package/src/settings-tui.mjs +0 -373
  300. package/src/setup-flow.mjs +0 -223
  301. package/src/signal.mjs +0 -115
  302. package/src/simmer.mjs +0 -241
  303. package/src/strategy.mjs +0 -235
  304. package/src/subscription.mjs +0 -212
  305. package/src/templates.mjs +0 -260
  306. package/src/think-engine.mjs +0 -428
  307. package/src/tui.mjs +0 -276
  308. package/src/update-check.mjs +0 -35
  309. package/src/wave-planner.mjs +0 -294
@@ -1,148 +0,0 @@
1
- // User calibration module — tracks specificity, corrections, and autonomy signals
2
- // to adapt dual-brain behavior to any user from vague vibe-coder to precise expert.
3
-
4
- const FILE_REF_RE = /(?:src\/|\.mjs|\.tsx?|\.jsx?|\.json|\.ya?ml|\.sh|line\s+\d+|\bL\d+\b)/i;
5
- const TECH_TERM_RE = /\b(?:regex|middleware|api|endpoint|refactor|migration|schema|auth(?:entication)?|jwt|token|hook|dispatch|pipeline|module|function|class|interface|type|import|export|async|await|promise|callback|handler|router|controller|service|repository|factory|singleton|decorator|mixin|proxy|guard|interceptor|serializer|validator|transformer|adapter|facade|strategy|observer|subscriber|emitter|stream|buffer|cache|queue|worker|thread|mutex|semaphore|socket|websocket|http|grpc|graphql|rest|orm|sql|nosql|index|query|transaction|migration|seed|fixture|mock|stub|spy|assertion|coverage|lint|typecheck|bundle|compile|transpile|minify|tree.shake|dead.code|chunk|lazy.load|ssr|csr|hydrat)\b/i;
6
- const VAGUE_RE = /\b(?:idk|just|make\s+it|fix\s+it|whatever|vibes?|better|nicer|faster|cleaner|improve|help|do\s+it|yeah|sure|ok|okay|hmm|uh|er|um)\b/i;
7
- const AUTONOMY_HIGH_RE = /\b(?:just\s+do\s+it|go(?:\s+ahead)?|build\s+it|ship\s+it|run\s+it|execute|do\s+it|proceed|continue|carry\s+on|handle\s+it|take\s+care|make\s+it\s+happen|yolo)\b/i;
8
- const QUESTION_RE = /\?|^(?:how|what|why|when|where|which|should|can|could|would|is|are|do|does|did|will|won't|don't)\b/i;
9
- const CORRECTION_RE = /^(?:no[,.]?|not\s|wrong|stop|don't|that'?s?\s+not|i\s+said|i\s+meant)\b|(?:\binstead\b|\brather\b|\bactually\b)/i;
10
-
11
- export function analyzeInput(input) {
12
- const text = (input || '').trim();
13
- const lower = text.toLowerCase();
14
- const words = text.split(/\s+/).filter(Boolean);
15
- const wordCount = words.length;
16
-
17
- const hasFileRefs = FILE_REF_RE.test(text);
18
- const hasTechTerms = TECH_TERM_RE.test(text);
19
- const hasExactInstructions = /\b(?:step\s+\d|first[,\s]|then[,\s]|finally[,\s]|specifically|exactly|must|should\s+(?:use|call|return|handle)|(?:use|call|return|throw|emit|dispatch)\s+\w)/i.test(text);
20
- const isVague = VAGUE_RE.test(lower) || wordCount <= 3;
21
-
22
- let specificity;
23
- if (hasFileRefs || /\bline\s*\d+\b|\bL\d+\b/i.test(text) || (hasTechTerms && hasExactInstructions)) {
24
- specificity = 5;
25
- } else if (hasTechTerms && wordCount >= 6) {
26
- specificity = 4;
27
- } else if (!isVague && wordCount >= 8) {
28
- specificity = 3;
29
- } else if (wordCount >= 4 && !isVague) {
30
- specificity = 2;
31
- } else {
32
- specificity = 1;
33
- }
34
-
35
- return {
36
- specificity,
37
- signals: {
38
- hasFileRefs,
39
- hasTechTerms,
40
- hasExactInstructions,
41
- isVague,
42
- wordCount
43
- }
44
- };
45
- }
46
-
47
- export function detectCorrection(input) {
48
- return CORRECTION_RE.test((input || '').trim());
49
- }
50
-
51
- function clamp(value, min = 1, max = 5) {
52
- return Math.min(max, Math.max(min, value));
53
- }
54
-
55
- function round1(value) {
56
- return Math.round(value * 10) / 10;
57
- }
58
-
59
- function detectAutonomySignal(input) {
60
- const text = (input || '').trim();
61
- if (AUTONOMY_HIGH_RE.test(text)) return 5;
62
- if (QUESTION_RE.test(text)) return 2;
63
- return null;
64
- }
65
-
66
- export function updateCalibration(calibration, input, correction = false) {
67
- const { specificity: newSpec } = analyzeInput(input);
68
- const autonomySignal = detectAutonomySignal(input);
69
-
70
- const prev = {
71
- specificity: calibration.specificity ?? 3,
72
- corrections: calibration.corrections ?? 3,
73
- autonomy: calibration.autonomy ?? 3,
74
- interactions: calibration.interactions ?? 0
75
- };
76
-
77
- const specificity = round1(clamp(prev.specificity * 0.7 + newSpec * 0.3));
78
-
79
- const corrections = round1(clamp(
80
- correction ? prev.corrections - 0.3 : prev.corrections + 0.1
81
- ));
82
-
83
- let autonomy = prev.autonomy;
84
- if (autonomySignal !== null) {
85
- autonomy = round1(clamp(prev.autonomy * 0.7 + autonomySignal * 0.3));
86
- }
87
-
88
- return {
89
- specificity,
90
- corrections,
91
- autonomy,
92
- interactions: prev.interactions + 1,
93
- lastUpdated: new Date().toISOString()
94
- };
95
- }
96
-
97
- export function getAdaptation(calibration) {
98
- const s = calibration.specificity ?? 3;
99
- const c = calibration.corrections ?? 3;
100
- const a = calibration.autonomy ?? 3;
101
-
102
- const clarifyBeforeActing = s < 2.5 || c > 3;
103
- const explainReasoning = a < 3;
104
- const suggestNextSteps = a < 4;
105
- const askForApproval = c < 2.5;
106
- const autoExecute = a > 4 && c > 3.5;
107
-
108
- let responseStyle;
109
- const combined = (s + c + a) / 3;
110
- if (combined >= 4) {
111
- responseStyle = 'terse';
112
- } else if (combined >= 2.5) {
113
- responseStyle = 'normal';
114
- } else {
115
- responseStyle = 'detailed';
116
- }
117
-
118
- let userLevel;
119
- if (s >= 4) {
120
- userLevel = 'advanced';
121
- } else if (s >= 2.5) {
122
- userLevel = 'intermediate';
123
- } else {
124
- userLevel = 'beginner';
125
- }
126
-
127
- return {
128
- clarifyBeforeActing,
129
- explainReasoning,
130
- suggestNextSteps,
131
- askForApproval,
132
- autoExecute,
133
- responseStyle,
134
- userLevel
135
- };
136
- }
137
-
138
- export function formatCalibration(calibration) {
139
- const { userLevel, responseStyle } = getAdaptation(calibration);
140
- const s = calibration.specificity ?? 3;
141
- const c = calibration.corrections ?? 3;
142
- const a = calibration.autonomy ?? 3;
143
-
144
- const autonomyLabel = a >= 4 ? 'high autonomy' : a >= 2.5 ? 'normal autonomy' : 'low autonomy';
145
- const trustLabel = c >= 4 ? 'high trust' : c >= 2.5 ? 'good trust' : 'low trust';
146
-
147
- return `User: ${userLevel} · ${autonomyLabel} · ${trustLabel}\n (specificity: ${s}, corrections: ${c}, autonomy: ${a})`;
148
- }
@@ -1,109 +0,0 @@
1
- // checkpoint.mjs — Checkpoint wrapper for dual-brain execution safety.
2
- // Wraps Replit's native checkpoint system with a git-based fallback.
3
- // Exports: hasCheckpoints, createCheckpoint, listCheckpoints, getLastCheckpoint
4
-
5
- import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
6
- import { join } from 'node:path';
7
- import { execSync } from 'node:child_process';
8
-
9
- /**
10
- * Check if checkpoint capability is available.
11
- * @returns {boolean}
12
- */
13
- export function hasCheckpoints() {
14
- try {
15
- // Check for Replit checkpoint binary
16
- if (existsSync('/usr/local/bin/replit-checkpoint')) return true;
17
- execSync('which replit-checkpoint', { stdio: 'pipe', timeout: 2000 });
18
- return true;
19
- } catch {
20
- return false;
21
- }
22
- }
23
-
24
- /**
25
- * Create a checkpoint before a risky operation.
26
- * @param {string} label — human-readable label like "before auth refactor"
27
- * @param {object} [opts]
28
- * @param {string} [opts.cwd]
29
- * @returns {{ success: boolean, id: string|null, label: string, timestamp: string }}
30
- */
31
- export function createCheckpoint(label, opts = {}) {
32
- const cwd = opts.cwd || process.cwd();
33
- const timestamp = new Date().toISOString();
34
- const id = `cp-${Date.now()}`;
35
-
36
- // Try Replit checkpoint first
37
- if (hasCheckpoints()) {
38
- try {
39
- execSync('replit-checkpoint create', { cwd, stdio: 'pipe', timeout: 10000 });
40
- _logCheckpoint({ id, label, timestamp, type: 'replit', status: 'created' }, cwd);
41
- return { success: true, id, label, timestamp };
42
- } catch {
43
- // Fall through to git-based checkpoint
44
- }
45
- }
46
-
47
- // Fallback: git stash + tag
48
- try {
49
- // Stash any uncommitted changes
50
- const status = execSync('git status --porcelain', { cwd, encoding: 'utf8', timeout: 5000 }).trim();
51
- if (status) {
52
- execSync(`git stash push -m "dual-brain-checkpoint: ${label}"`, { cwd, stdio: 'pipe', timeout: 10000 });
53
- execSync('git stash pop', { cwd, stdio: 'pipe', timeout: 10000 });
54
- }
55
- // Create a lightweight tag
56
- const safeLabel = label.replace(/[^a-zA-Z0-9-_]/g, '-').slice(0, 50);
57
- const tagName = `db-checkpoint/${safeLabel}-${Date.now()}`;
58
- execSync(`git tag "${tagName}"`, { cwd, stdio: 'pipe', timeout: 5000 });
59
- _logCheckpoint({ id, label, timestamp, type: 'git-tag', tag: tagName, status: 'created' }, cwd);
60
- return { success: true, id, label, timestamp };
61
- } catch {
62
- _logCheckpoint({ id, label, timestamp, type: 'failed', status: 'failed' }, cwd);
63
- return { success: false, id: null, label, timestamp };
64
- }
65
- }
66
-
67
- /**
68
- * List recent checkpoints (most recent first, up to 20).
69
- * @param {string} [cwd]
70
- * @returns {object[]}
71
- */
72
- export function listCheckpoints(cwd) {
73
- const logPath = join(cwd || process.cwd(), '.dual-brain', 'checkpoints.jsonl');
74
- if (!existsSync(logPath)) return [];
75
- try {
76
- return readFileSync(logPath, 'utf8')
77
- .trim()
78
- .split('\n')
79
- .filter(Boolean)
80
- .map(line => JSON.parse(line))
81
- .reverse()
82
- .slice(0, 20);
83
- } catch {
84
- return [];
85
- }
86
- }
87
-
88
- /**
89
- * Get the most recent checkpoint.
90
- * @param {string} [cwd]
91
- * @returns {object|null}
92
- */
93
- export function getLastCheckpoint(cwd) {
94
- const checkpoints = listCheckpoints(cwd);
95
- return checkpoints[0] || null;
96
- }
97
-
98
- // ─── Internal helpers ─────────────────────────────────────────────────────────
99
-
100
- function _logCheckpoint(entry, cwd) {
101
- const dir = join(cwd || process.cwd(), '.dual-brain');
102
- mkdirSync(dir, { recursive: true });
103
- const logPath = join(dir, 'checkpoints.jsonl');
104
- const line = JSON.stringify(entry) + '\n';
105
- try {
106
- const existing = existsSync(logPath) ? readFileSync(logPath, 'utf8') : '';
107
- writeFileSync(logPath, existing + line);
108
- } catch {}
109
- }
package/src/ci-triage.mjs DELETED
@@ -1,191 +0,0 @@
1
- import { execSync } from 'node:child_process';
2
- import { existsSync, readFileSync } from 'node:fs';
3
- import { join } from 'node:path';
4
-
5
- /**
6
- * Detect CI system in use.
7
- * @param {string} [cwd]
8
- * @returns {{ systems: string[], primary: string|null }}
9
- */
10
- export function detectCI(cwd) {
11
- const root = cwd || process.cwd();
12
- const systems = [];
13
-
14
- if (existsSync(join(root, '.github/workflows'))) systems.push('github-actions');
15
- if (existsSync(join(root, '.circleci'))) systems.push('circleci');
16
- if (existsSync(join(root, '.gitlab-ci.yml'))) systems.push('gitlab-ci');
17
- if (existsSync(join(root, 'Jenkinsfile'))) systems.push('jenkins');
18
- if (existsSync(join(root, '.travis.yml'))) systems.push('travis');
19
- if (existsSync(join(root, 'vercel.json')) || existsSync(join(root, '.vercel'))) systems.push('vercel');
20
- if (existsSync(join(root, 'netlify.toml'))) systems.push('netlify');
21
-
22
- return { systems, primary: systems[0] || null };
23
- }
24
-
25
- /**
26
- * Get recent CI run status using gh CLI.
27
- * @param {string} [cwd]
28
- * @returns {{ available: boolean, runs: object[], hasFailures: boolean, lastRun: object|null }}
29
- */
30
- export function getCIStatus(cwd) {
31
- try {
32
- const json = execSync(
33
- 'gh run list --limit 5 --json databaseId,name,status,conclusion,headBranch,createdAt',
34
- { cwd, encoding: 'utf8', timeout: 10000 }
35
- );
36
- const runs = JSON.parse(json);
37
- return {
38
- available: true,
39
- runs: runs.map(r => ({
40
- id: r.databaseId,
41
- name: r.name,
42
- status: r.status,
43
- conclusion: r.conclusion,
44
- branch: r.headBranch,
45
- createdAt: r.createdAt,
46
- })),
47
- hasFailures: runs.some(r => r.conclusion === 'failure'),
48
- lastRun: runs[0] || null,
49
- };
50
- } catch {
51
- return { available: false, runs: [], hasFailures: false, lastRun: null };
52
- }
53
- }
54
-
55
- /**
56
- * Get failed CI run logs and classify the failure.
57
- * @param {string|number} runId
58
- * @param {string} [cwd]
59
- * @returns {object}
60
- */
61
- export function triageFailure(runId, cwd) {
62
- try {
63
- const logs = execSync(`gh run view ${runId} --log-failed 2>/dev/null | tail -100`, {
64
- cwd, encoding: 'utf8', timeout: 15000,
65
- });
66
-
67
- const classification = classifyFailure(logs);
68
- const fileHints = extractFileHints(logs, cwd);
69
-
70
- return {
71
- success: true,
72
- runId,
73
- logs: logs.slice(-3000), // last 3000 chars
74
- classification,
75
- fileHints,
76
- suggestedAction: getSuggestedAction(classification),
77
- };
78
- } catch (err) {
79
- return { success: false, runId, error: err.message };
80
- }
81
- }
82
-
83
- /**
84
- * Classify a CI failure from log output.
85
- * @param {string} logs
86
- * @returns {{ type: string, confidence: string }}
87
- */
88
- function classifyFailure(logs) {
89
- const lower = logs.toLowerCase();
90
-
91
- if (lower.includes('syntaxerror') || lower.includes('parse error')) return { type: 'syntax', confidence: 'high' };
92
- if (lower.includes('typeerror') || lower.includes('type error')) return { type: 'type-error', confidence: 'high' };
93
- if (lower.includes('referenceerror')) return { type: 'reference-error', confidence: 'high' };
94
- if (lower.includes('test fail') || lower.includes('tests failed') || lower.includes('assertion')) return { type: 'test-failure', confidence: 'high' };
95
- if (lower.includes('enoent') || lower.includes('no such file')) return { type: 'missing-file', confidence: 'high' };
96
- if (lower.includes('permission denied') || lower.includes('eacces')) return { type: 'permissions', confidence: 'high' };
97
- if (lower.includes('timeout') || lower.includes('timed out')) return { type: 'timeout', confidence: 'medium' };
98
- if (lower.includes('out of memory') || lower.includes('heap')) return { type: 'oom', confidence: 'medium' };
99
- if (lower.includes('npm err') || lower.includes('yarn error') || lower.includes('dependency')) return { type: 'dependency', confidence: 'medium' };
100
- if (lower.includes('lint') || lower.includes('eslint')) return { type: 'lint', confidence: 'high' };
101
- if (lower.includes('build fail')) return { type: 'build', confidence: 'medium' };
102
- if (lower.includes('docker') || lower.includes('container')) return { type: 'container', confidence: 'medium' };
103
-
104
- return { type: 'unknown', confidence: 'low' };
105
- }
106
-
107
- /**
108
- * Extract local file paths referenced in CI logs.
109
- * @param {string} logs
110
- * @param {string} [cwd]
111
- * @returns {string[]}
112
- */
113
- function extractFileHints(logs, cwd) {
114
- const files = new Set();
115
- const root = cwd || process.cwd();
116
-
117
- const patterns = [
118
- /(?:at\s+)?([a-zA-Z0-9_./\\-]+\.[a-zA-Z]+):(\d+)/g,
119
- /(?:in\s+)?([a-zA-Z0-9_./\\-]+\.[a-zA-Z]+)\((\d+)\)/g,
120
- /Error in ([a-zA-Z0-9_./\\-]+\.[a-zA-Z]+)/g,
121
- ];
122
-
123
- for (const pattern of patterns) {
124
- for (const match of logs.matchAll(pattern)) {
125
- const file = match[1];
126
- if (file && !file.includes('node_modules') && existsSync(join(root, file))) {
127
- files.add(file);
128
- }
129
- }
130
- }
131
-
132
- return [...files];
133
- }
134
-
135
- /**
136
- * Get a human-readable suggested action for a failure classification.
137
- * @param {{ type: string }} classification
138
- * @returns {string}
139
- */
140
- function getSuggestedAction(classification) {
141
- const actions = {
142
- 'syntax': 'Fix syntax error in the identified file',
143
- 'type-error': 'Check type annotations and function signatures',
144
- 'reference-error': 'Check for undefined variables or missing imports',
145
- 'test-failure': 'Run tests locally and fix failing assertions',
146
- 'missing-file': 'Check if a required file was deleted or not committed',
147
- 'permissions': 'Check file permissions and access rights',
148
- 'timeout': 'Investigate slow operations or increase timeout',
149
- 'oom': 'Check for memory leaks or reduce batch size',
150
- 'dependency': 'Run npm install and check for version conflicts',
151
- 'lint': 'Run linter locally and fix violations',
152
- 'build': 'Check build configuration and dependencies',
153
- 'container': 'Check Dockerfile and container configuration',
154
- 'unknown': 'Review full CI logs for error details',
155
- };
156
- return actions[classification.type] || actions.unknown;
157
- }
158
-
159
- /**
160
- * Full CI triage: detect CI, fetch status, classify failures, map to files.
161
- * @param {string} [cwd]
162
- * @returns {object}
163
- */
164
- export function fullTriage(cwd) {
165
- const ci = detectCI(cwd);
166
- if (!ci.primary) return { available: false, reason: 'no-ci-detected' };
167
-
168
- const status = getCIStatus(cwd);
169
- if (!status.available) return { available: false, reason: 'gh-cli-unavailable' };
170
- if (!status.hasFailures) return { available: true, healthy: true, message: 'All CI runs passing' };
171
-
172
- const failedRuns = status.runs.filter(r => r.conclusion === 'failure');
173
- const triages = failedRuns.slice(0, 3).map(r => triageFailure(r.id, cwd));
174
-
175
- return {
176
- available: true,
177
- healthy: false,
178
- failedRuns: failedRuns.length,
179
- triages,
180
- topIssue: triages[0]?.classification || null,
181
- };
182
- }
183
-
184
- // ─── CLI (direct invocation) ──────────────────────────────────────────────────
185
-
186
- const isMain = process.argv[1]?.endsWith('ci-triage.mjs');
187
- if (isMain) {
188
- const cwd = process.argv[2] || process.cwd();
189
- const result = fullTriage(cwd);
190
- process.stdout.write(JSON.stringify(result, null, 2) + '\n');
191
- }