@isaacriehm/cairn 0.1.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 (562) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/backprop/id.d.ts +14 -0
  5. package/dist/backprop/id.js +40 -0
  6. package/dist/backprop/id.js.map +1 -0
  7. package/dist/backprop/index.d.ts +23 -0
  8. package/dist/backprop/index.js +21 -0
  9. package/dist/backprop/index.js.map +1 -0
  10. package/dist/backprop/prompt.d.ts +16 -0
  11. package/dist/backprop/prompt.js +101 -0
  12. package/dist/backprop/prompt.js.map +1 -0
  13. package/dist/backprop/runner.d.ts +18 -0
  14. package/dist/backprop/runner.js +95 -0
  15. package/dist/backprop/runner.js.map +1 -0
  16. package/dist/backprop/schema.d.ts +61 -0
  17. package/dist/backprop/schema.js +55 -0
  18. package/dist/backprop/schema.js.map +1 -0
  19. package/dist/backprop/types.d.ts +101 -0
  20. package/dist/backprop/types.js +24 -0
  21. package/dist/backprop/types.js.map +1 -0
  22. package/dist/backprop/writer.d.ts +27 -0
  23. package/dist/backprop/writer.js +301 -0
  24. package/dist/backprop/writer.js.map +1 -0
  25. package/dist/claude/error.d.ts +33 -0
  26. package/dist/claude/error.js +58 -0
  27. package/dist/claude/error.js.map +1 -0
  28. package/dist/claude/index.d.ts +3 -0
  29. package/dist/claude/index.js +3 -0
  30. package/dist/claude/index.js.map +1 -0
  31. package/dist/claude/runner.d.ts +11 -0
  32. package/dist/claude/runner.js +132 -0
  33. package/dist/claude/runner.js.map +1 -0
  34. package/dist/claude/types.d.ts +52 -0
  35. package/dist/claude/types.js +14 -0
  36. package/dist/claude/types.js.map +1 -0
  37. package/dist/cli/attention.d.ts +11 -0
  38. package/dist/cli/attention.js +234 -0
  39. package/dist/cli/attention.js.map +1 -0
  40. package/dist/cli/daemon.d.ts +54 -0
  41. package/dist/cli/daemon.js +351 -0
  42. package/dist/cli/daemon.js.map +1 -0
  43. package/dist/cli/doctor.d.ts +8 -0
  44. package/dist/cli/doctor.js +116 -0
  45. package/dist/cli/doctor.js.map +1 -0
  46. package/dist/cli/gc.d.ts +15 -0
  47. package/dist/cli/gc.js +139 -0
  48. package/dist/cli/gc.js.map +1 -0
  49. package/dist/cli/hook.d.ts +18 -0
  50. package/dist/cli/hook.js +57 -0
  51. package/dist/cli/hook.js.map +1 -0
  52. package/dist/cli/index.d.ts +2 -0
  53. package/dist/cli/index.js +127 -0
  54. package/dist/cli/index.js.map +1 -0
  55. package/dist/cli/init.d.ts +1 -0
  56. package/dist/cli/init.js +77 -0
  57. package/dist/cli/init.js.map +1 -0
  58. package/dist/cli/install.d.ts +52 -0
  59. package/dist/cli/install.js +308 -0
  60. package/dist/cli/install.js.map +1 -0
  61. package/dist/cli/join.d.ts +1 -0
  62. package/dist/cli/join.js +84 -0
  63. package/dist/cli/join.js.map +1 -0
  64. package/dist/cli/mcp.d.ts +1 -0
  65. package/dist/cli/mcp.js +62 -0
  66. package/dist/cli/mcp.js.map +1 -0
  67. package/dist/cli/mirror.d.ts +1 -0
  68. package/dist/cli/mirror.js +97 -0
  69. package/dist/cli/mirror.js.map +1 -0
  70. package/dist/cli/run.d.ts +1 -0
  71. package/dist/cli/run.js +174 -0
  72. package/dist/cli/run.js.map +1 -0
  73. package/dist/cli/scope.d.ts +8 -0
  74. package/dist/cli/scope.js +65 -0
  75. package/dist/cli/scope.js.map +1 -0
  76. package/dist/cli/task.d.ts +18 -0
  77. package/dist/cli/task.js +137 -0
  78. package/dist/cli/task.js.map +1 -0
  79. package/dist/cli/watch.d.ts +1 -0
  80. package/dist/cli/watch.js +73 -0
  81. package/dist/cli/watch.js.map +1 -0
  82. package/dist/decision-capture/capture.d.ts +57 -0
  83. package/dist/decision-capture/capture.js +186 -0
  84. package/dist/decision-capture/capture.js.map +1 -0
  85. package/dist/decision-capture/extractor.d.ts +20 -0
  86. package/dist/decision-capture/extractor.js +103 -0
  87. package/dist/decision-capture/extractor.js.map +1 -0
  88. package/dist/decision-capture/id.d.ts +14 -0
  89. package/dist/decision-capture/id.js +44 -0
  90. package/dist/decision-capture/id.js.map +1 -0
  91. package/dist/decision-capture/index.d.ts +25 -0
  92. package/dist/decision-capture/index.js +21 -0
  93. package/dist/decision-capture/index.js.map +1 -0
  94. package/dist/decision-capture/prompt.d.ts +15 -0
  95. package/dist/decision-capture/prompt.js +68 -0
  96. package/dist/decision-capture/prompt.js.map +1 -0
  97. package/dist/decision-capture/refinement-prompt.d.ts +25 -0
  98. package/dist/decision-capture/refinement-prompt.js +146 -0
  99. package/dist/decision-capture/refinement-prompt.js.map +1 -0
  100. package/dist/decision-capture/refinement-schema.d.ts +52 -0
  101. package/dist/decision-capture/refinement-schema.js +61 -0
  102. package/dist/decision-capture/refinement-schema.js.map +1 -0
  103. package/dist/decision-capture/refinement.d.ts +60 -0
  104. package/dist/decision-capture/refinement.js +439 -0
  105. package/dist/decision-capture/refinement.js.map +1 -0
  106. package/dist/decision-capture/schema.d.ts +70 -0
  107. package/dist/decision-capture/schema.js +71 -0
  108. package/dist/decision-capture/schema.js.map +1 -0
  109. package/dist/decision-capture/types.d.ts +201 -0
  110. package/dist/decision-capture/types.js +20 -0
  111. package/dist/decision-capture/types.js.map +1 -0
  112. package/dist/decision-capture/writer.d.ts +90 -0
  113. package/dist/decision-capture/writer.js +267 -0
  114. package/dist/decision-capture/writer.js.map +1 -0
  115. package/dist/frontend/discord/acl.d.ts +6 -0
  116. package/dist/frontend/discord/acl.js +19 -0
  117. package/dist/frontend/discord/acl.js.map +1 -0
  118. package/dist/frontend/discord/channels.d.ts +29 -0
  119. package/dist/frontend/discord/channels.js +58 -0
  120. package/dist/frontend/discord/channels.js.map +1 -0
  121. package/dist/frontend/discord/classifier.d.ts +16 -0
  122. package/dist/frontend/discord/classifier.js +29 -0
  123. package/dist/frontend/discord/classifier.js.map +1 -0
  124. package/dist/frontend/discord/index.d.ts +118 -0
  125. package/dist/frontend/discord/index.js +1104 -0
  126. package/dist/frontend/discord/index.js.map +1 -0
  127. package/dist/frontend/discord/slash.d.ts +18 -0
  128. package/dist/frontend/discord/slash.js +90 -0
  129. package/dist/frontend/discord/slash.js.map +1 -0
  130. package/dist/frontend/inbox.d.ts +17 -0
  131. package/dist/frontend/inbox.js +30 -0
  132. package/dist/frontend/inbox.js.map +1 -0
  133. package/dist/frontend/index.d.ts +8 -0
  134. package/dist/frontend/index.js +6 -0
  135. package/dist/frontend/index.js.map +1 -0
  136. package/dist/frontend/stub/index.d.ts +58 -0
  137. package/dist/frontend/stub/index.js +144 -0
  138. package/dist/frontend/stub/index.js.map +1 -0
  139. package/dist/frontend/types.d.ts +247 -0
  140. package/dist/frontend/types.js +15 -0
  141. package/dist/frontend/types.js.map +1 -0
  142. package/dist/gc/apply.d.ts +26 -0
  143. package/dist/gc/apply.js +48 -0
  144. package/dist/gc/apply.js.map +1 -0
  145. package/dist/gc/canary.d.ts +42 -0
  146. package/dist/gc/canary.js +134 -0
  147. package/dist/gc/canary.js.map +1 -0
  148. package/dist/gc/classify.d.ts +25 -0
  149. package/dist/gc/classify.js +89 -0
  150. package/dist/gc/classify.js.map +1 -0
  151. package/dist/gc/doc-gardening.d.ts +29 -0
  152. package/dist/gc/doc-gardening.js +146 -0
  153. package/dist/gc/doc-gardening.js.map +1 -0
  154. package/dist/gc/frontmatter.d.ts +35 -0
  155. package/dist/gc/frontmatter.js +111 -0
  156. package/dist/gc/frontmatter.js.map +1 -0
  157. package/dist/gc/generator-drift.d.ts +28 -0
  158. package/dist/gc/generator-drift.js +53 -0
  159. package/dist/gc/generator-drift.js.map +1 -0
  160. package/dist/gc/index.d.ts +35 -0
  161. package/dist/gc/index.js +26 -0
  162. package/dist/gc/index.js.map +1 -0
  163. package/dist/gc/quality-update.d.ts +23 -0
  164. package/dist/gc/quality-update.js +69 -0
  165. package/dist/gc/quality-update.js.map +1 -0
  166. package/dist/gc/stub-hits.d.ts +31 -0
  167. package/dist/gc/stub-hits.js +125 -0
  168. package/dist/gc/stub-hits.js.map +1 -0
  169. package/dist/gc/sweep.d.ts +56 -0
  170. package/dist/gc/sweep.js +178 -0
  171. package/dist/gc/sweep.js.map +1 -0
  172. package/dist/gc/types.d.ts +129 -0
  173. package/dist/gc/types.js +26 -0
  174. package/dist/gc/types.js.map +1 -0
  175. package/dist/ground/drift.d.ts +8 -0
  176. package/dist/ground/drift.js +23 -0
  177. package/dist/ground/drift.js.map +1 -0
  178. package/dist/ground/frontmatter.d.ts +20 -0
  179. package/dist/ground/frontmatter.js +49 -0
  180. package/dist/ground/frontmatter.js.map +1 -0
  181. package/dist/ground/glob.d.ts +10 -0
  182. package/dist/ground/glob.js +46 -0
  183. package/dist/ground/glob.js.map +1 -0
  184. package/dist/ground/index.d.ts +14 -0
  185. package/dist/ground/index.js +10 -0
  186. package/dist/ground/index.js.map +1 -0
  187. package/dist/ground/ledgers.d.ts +18 -0
  188. package/dist/ground/ledgers.js +103 -0
  189. package/dist/ground/ledgers.js.map +1 -0
  190. package/dist/ground/manifest.d.ts +10 -0
  191. package/dist/ground/manifest.js +88 -0
  192. package/dist/ground/manifest.js.map +1 -0
  193. package/dist/ground/paths.d.ts +20 -0
  194. package/dist/ground/paths.js +61 -0
  195. package/dist/ground/paths.js.map +1 -0
  196. package/dist/ground/quality-grades.d.ts +11 -0
  197. package/dist/ground/quality-grades.js +98 -0
  198. package/dist/ground/quality-grades.js.map +1 -0
  199. package/dist/ground/schemas.d.ts +306 -0
  200. package/dist/ground/schemas.js +188 -0
  201. package/dist/ground/schemas.js.map +1 -0
  202. package/dist/ground/walk.d.ts +7 -0
  203. package/dist/ground/walk.js +53 -0
  204. package/dist/ground/walk.js.map +1 -0
  205. package/dist/index.d.ts +8 -0
  206. package/dist/index.js +9 -0
  207. package/dist/index.js.map +1 -0
  208. package/dist/init/detect.d.ts +25 -0
  209. package/dist/init/detect.js +336 -0
  210. package/dist/init/detect.js.map +1 -0
  211. package/dist/init/index.d.ts +14 -0
  212. package/dist/init/index.js +9 -0
  213. package/dist/init/index.js.map +1 -0
  214. package/dist/init/init.d.ts +68 -0
  215. package/dist/init/init.js +673 -0
  216. package/dist/init/init.js.map +1 -0
  217. package/dist/init/mapper.d.ts +160 -0
  218. package/dist/init/mapper.js +248 -0
  219. package/dist/init/mapper.js.map +1 -0
  220. package/dist/init/prompts.d.ts +70 -0
  221. package/dist/init/prompts.js +80 -0
  222. package/dist/init/prompts.js.map +1 -0
  223. package/dist/init/secrets.d.ts +18 -0
  224. package/dist/init/secrets.js +76 -0
  225. package/dist/init/secrets.js.map +1 -0
  226. package/dist/init/seed.d.ts +21 -0
  227. package/dist/init/seed.js +75 -0
  228. package/dist/init/seed.js.map +1 -0
  229. package/dist/init/setup-runners.d.ts +17 -0
  230. package/dist/init/setup-runners.js +70 -0
  231. package/dist/init/setup-runners.js.map +1 -0
  232. package/dist/init/types.d.ts +59 -0
  233. package/dist/init/types.js +10 -0
  234. package/dist/init/types.js.map +1 -0
  235. package/dist/init/walker.d.ts +53 -0
  236. package/dist/init/walker.js +460 -0
  237. package/dist/init/walker.js.map +1 -0
  238. package/dist/init/workflow-block.d.ts +34 -0
  239. package/dist/init/workflow-block.js +110 -0
  240. package/dist/init/workflow-block.js.map +1 -0
  241. package/dist/logger.d.ts +3 -0
  242. package/dist/logger.js +23 -0
  243. package/dist/logger.js.map +1 -0
  244. package/dist/mcp/context.d.ts +16 -0
  245. package/dist/mcp/context.js +8 -0
  246. package/dist/mcp/context.js.map +1 -0
  247. package/dist/mcp/errors.d.ts +17 -0
  248. package/dist/mcp/errors.js +23 -0
  249. package/dist/mcp/errors.js.map +1 -0
  250. package/dist/mcp/index.d.ts +10 -0
  251. package/dist/mcp/index.js +7 -0
  252. package/dist/mcp/index.js.map +1 -0
  253. package/dist/mcp/path-allowlist.d.ts +25 -0
  254. package/dist/mcp/path-allowlist.js +66 -0
  255. package/dist/mcp/path-allowlist.js.map +1 -0
  256. package/dist/mcp/result.d.ts +8 -0
  257. package/dist/mcp/result.js +18 -0
  258. package/dist/mcp/result.js.map +1 -0
  259. package/dist/mcp/schemas.d.ts +153 -0
  260. package/dist/mcp/schemas.js +135 -0
  261. package/dist/mcp/schemas.js.map +1 -0
  262. package/dist/mcp/server.d.ts +11 -0
  263. package/dist/mcp/server.js +58 -0
  264. package/dist/mcp/server.js.map +1 -0
  265. package/dist/mcp/telemetry.d.ts +15 -0
  266. package/dist/mcp/telemetry.js +13 -0
  267. package/dist/mcp/telemetry.js.map +1 -0
  268. package/dist/mcp/tools/append.d.ts +8 -0
  269. package/dist/mcp/tools/append.js +33 -0
  270. package/dist/mcp/tools/append.js.map +1 -0
  271. package/dist/mcp/tools/archive.d.ts +8 -0
  272. package/dist/mcp/tools/archive.js +49 -0
  273. package/dist/mcp/tools/archive.js.map +1 -0
  274. package/dist/mcp/tools/ask-operator.d.ts +34 -0
  275. package/dist/mcp/tools/ask-operator.js +93 -0
  276. package/dist/mcp/tools/ask-operator.js.map +1 -0
  277. package/dist/mcp/tools/canonical-for-topic.d.ts +6 -0
  278. package/dist/mcp/tools/canonical-for-topic.js +40 -0
  279. package/dist/mcp/tools/canonical-for-topic.js.map +1 -0
  280. package/dist/mcp/tools/decision-get.d.ts +6 -0
  281. package/dist/mcp/tools/decision-get.js +49 -0
  282. package/dist/mcp/tools/decision-get.js.map +1 -0
  283. package/dist/mcp/tools/decisions-for-symbol.d.ts +7 -0
  284. package/dist/mcp/tools/decisions-for-symbol.js +42 -0
  285. package/dist/mcp/tools/decisions-for-symbol.js.map +1 -0
  286. package/dist/mcp/tools/decisions-in-scope.d.ts +7 -0
  287. package/dist/mcp/tools/decisions-in-scope.js +47 -0
  288. package/dist/mcp/tools/decisions-in-scope.js.map +1 -0
  289. package/dist/mcp/tools/drop-task.d.ts +12 -0
  290. package/dist/mcp/tools/drop-task.js +47 -0
  291. package/dist/mcp/tools/drop-task.js.map +1 -0
  292. package/dist/mcp/tools/get-full.d.ts +7 -0
  293. package/dist/mcp/tools/get-full.js +46 -0
  294. package/dist/mcp/tools/get-full.js.map +1 -0
  295. package/dist/mcp/tools/ground-get.d.ts +7 -0
  296. package/dist/mcp/tools/ground-get.js +80 -0
  297. package/dist/mcp/tools/ground-get.js.map +1 -0
  298. package/dist/mcp/tools/index.d.ts +3 -0
  299. package/dist/mcp/tools/index.js +44 -0
  300. package/dist/mcp/tools/index.js.map +1 -0
  301. package/dist/mcp/tools/invariant-get.d.ts +6 -0
  302. package/dist/mcp/tools/invariant-get.js +49 -0
  303. package/dist/mcp/tools/invariant-get.js.map +1 -0
  304. package/dist/mcp/tools/invariants-in-scope.d.ts +7 -0
  305. package/dist/mcp/tools/invariants-in-scope.js +65 -0
  306. package/dist/mcp/tools/invariants-in-scope.js.map +1 -0
  307. package/dist/mcp/tools/query-history.d.ts +9 -0
  308. package/dist/mcp/tools/query-history.js +33 -0
  309. package/dist/mcp/tools/query-history.js.map +1 -0
  310. package/dist/mcp/tools/record-decision.d.ts +14 -0
  311. package/dist/mcp/tools/record-decision.js +101 -0
  312. package/dist/mcp/tools/record-decision.js.map +1 -0
  313. package/dist/mcp/tools/record-run-event.d.ts +10 -0
  314. package/dist/mcp/tools/record-run-event.js +28 -0
  315. package/dist/mcp/tools/record-run-event.js.map +1 -0
  316. package/dist/mcp/tools/search.d.ts +9 -0
  317. package/dist/mcp/tools/search.js +165 -0
  318. package/dist/mcp/tools/search.js.map +1 -0
  319. package/dist/mcp/tools/supersedes-chain.d.ts +6 -0
  320. package/dist/mcp/tools/supersedes-chain.js +66 -0
  321. package/dist/mcp/tools/supersedes-chain.js.map +1 -0
  322. package/dist/mcp/tools/timeline.d.ts +9 -0
  323. package/dist/mcp/tools/timeline.js +65 -0
  324. package/dist/mcp/tools/timeline.js.map +1 -0
  325. package/dist/mcp/tools/types.d.ts +9 -0
  326. package/dist/mcp/tools/types.js +2 -0
  327. package/dist/mcp/tools/types.js.map +1 -0
  328. package/dist/mirror/clone.d.ts +6 -0
  329. package/dist/mirror/clone.js +48 -0
  330. package/dist/mirror/clone.js.map +1 -0
  331. package/dist/mirror/dirty-overlap.d.ts +13 -0
  332. package/dist/mirror/dirty-overlap.js +77 -0
  333. package/dist/mirror/dirty-overlap.js.map +1 -0
  334. package/dist/mirror/index.d.ts +7 -0
  335. package/dist/mirror/index.js +7 -0
  336. package/dist/mirror/index.js.map +1 -0
  337. package/dist/mirror/paths.d.ts +18 -0
  338. package/dist/mirror/paths.js +45 -0
  339. package/dist/mirror/paths.js.map +1 -0
  340. package/dist/mirror/push.d.ts +9 -0
  341. package/dist/mirror/push.js +27 -0
  342. package/dist/mirror/push.js.map +1 -0
  343. package/dist/mirror/state.d.ts +4 -0
  344. package/dist/mirror/state.js +36 -0
  345. package/dist/mirror/state.js.map +1 -0
  346. package/dist/mirror/sync.d.ts +9 -0
  347. package/dist/mirror/sync.js +33 -0
  348. package/dist/mirror/sync.js.map +1 -0
  349. package/dist/mirror/types.d.ts +77 -0
  350. package/dist/mirror/types.js +2 -0
  351. package/dist/mirror/types.js.map +1 -0
  352. package/dist/orchestrator/activity-summarizer.d.ts +33 -0
  353. package/dist/orchestrator/activity-summarizer.js +120 -0
  354. package/dist/orchestrator/activity-summarizer.js.map +1 -0
  355. package/dist/orchestrator/inbox.d.ts +78 -0
  356. package/dist/orchestrator/inbox.js +115 -0
  357. package/dist/orchestrator/inbox.js.map +1 -0
  358. package/dist/orchestrator/index.d.ts +9 -0
  359. package/dist/orchestrator/index.js +7 -0
  360. package/dist/orchestrator/index.js.map +1 -0
  361. package/dist/orchestrator/orchestrator.d.ts +154 -0
  362. package/dist/orchestrator/orchestrator.js +2437 -0
  363. package/dist/orchestrator/orchestrator.js.map +1 -0
  364. package/dist/orchestrator/prompt.d.ts +19 -0
  365. package/dist/orchestrator/prompt.js +50 -0
  366. package/dist/orchestrator/prompt.js.map +1 -0
  367. package/dist/orchestrator/queue.d.ts +21 -0
  368. package/dist/orchestrator/queue.js +80 -0
  369. package/dist/orchestrator/queue.js.map +1 -0
  370. package/dist/orchestrator/run-log.d.ts +53 -0
  371. package/dist/orchestrator/run-log.js +92 -0
  372. package/dist/orchestrator/run-log.js.map +1 -0
  373. package/dist/orchestrator/runner.d.ts +56 -0
  374. package/dist/orchestrator/runner.js +172 -0
  375. package/dist/orchestrator/runner.js.map +1 -0
  376. package/dist/orchestrator/tool-digest.d.ts +35 -0
  377. package/dist/orchestrator/tool-digest.js +116 -0
  378. package/dist/orchestrator/tool-digest.js.map +1 -0
  379. package/dist/orchestrator/types.d.ts +263 -0
  380. package/dist/orchestrator/types.js +2 -0
  381. package/dist/orchestrator/types.js.map +1 -0
  382. package/dist/orchestrator/workspace.d.ts +21 -0
  383. package/dist/orchestrator/workspace.js +31 -0
  384. package/dist/orchestrator/workspace.js.map +1 -0
  385. package/dist/profiles/index.d.ts +3 -0
  386. package/dist/profiles/index.js +3 -0
  387. package/dist/profiles/index.js.map +1 -0
  388. package/dist/profiles/registry.d.ts +5 -0
  389. package/dist/profiles/registry.js +31 -0
  390. package/dist/profiles/registry.js.map +1 -0
  391. package/dist/profiles/types.d.ts +48 -0
  392. package/dist/profiles/types.js +11 -0
  393. package/dist/profiles/types.js.map +1 -0
  394. package/dist/profiles/unknown.d.ts +9 -0
  395. package/dist/profiles/unknown.js +17 -0
  396. package/dist/profiles/unknown.js.map +1 -0
  397. package/dist/reviewer/index.d.ts +6 -0
  398. package/dist/reviewer/index.js +5 -0
  399. package/dist/reviewer/index.js.map +1 -0
  400. package/dist/reviewer/prompt.d.ts +11 -0
  401. package/dist/reviewer/prompt.js +132 -0
  402. package/dist/reviewer/prompt.js.map +1 -0
  403. package/dist/reviewer/remediation.d.ts +15 -0
  404. package/dist/reviewer/remediation.js +61 -0
  405. package/dist/reviewer/remediation.js.map +1 -0
  406. package/dist/reviewer/reviewer.d.ts +9 -0
  407. package/dist/reviewer/reviewer.js +89 -0
  408. package/dist/reviewer/reviewer.js.map +1 -0
  409. package/dist/reviewer/schema.d.ts +45 -0
  410. package/dist/reviewer/schema.js +43 -0
  411. package/dist/reviewer/schema.js.map +1 -0
  412. package/dist/reviewer/types.d.ts +74 -0
  413. package/dist/reviewer/types.js +14 -0
  414. package/dist/reviewer/types.js.map +1 -0
  415. package/dist/sensors/attestation.d.ts +44 -0
  416. package/dist/sensors/attestation.js +262 -0
  417. package/dist/sensors/attestation.js.map +1 -0
  418. package/dist/sensors/catalog.d.ts +41 -0
  419. package/dist/sensors/catalog.js +123 -0
  420. package/dist/sensors/catalog.js.map +1 -0
  421. package/dist/sensors/decisions.d.ts +30 -0
  422. package/dist/sensors/decisions.js +393 -0
  423. package/dist/sensors/decisions.js.map +1 -0
  424. package/dist/sensors/diff.d.ts +27 -0
  425. package/dist/sensors/diff.js +148 -0
  426. package/dist/sensors/diff.js.map +1 -0
  427. package/dist/sensors/index.d.ts +13 -0
  428. package/dist/sensors/index.js +9 -0
  429. package/dist/sensors/index.js.map +1 -0
  430. package/dist/sensors/remediation.d.ts +20 -0
  431. package/dist/sensors/remediation.js +65 -0
  432. package/dist/sensors/remediation.js.map +1 -0
  433. package/dist/sensors/runner.d.ts +44 -0
  434. package/dist/sensors/runner.js +95 -0
  435. package/dist/sensors/runner.js.map +1 -0
  436. package/dist/sensors/structural.d.ts +30 -0
  437. package/dist/sensors/structural.js +204 -0
  438. package/dist/sensors/structural.js.map +1 -0
  439. package/dist/sensors/stub-catalog.d.ts +39 -0
  440. package/dist/sensors/stub-catalog.js +115 -0
  441. package/dist/sensors/stub-catalog.js.map +1 -0
  442. package/dist/sensors/types.d.ts +135 -0
  443. package/dist/sensors/types.js +14 -0
  444. package/dist/sensors/types.js.map +1 -0
  445. package/dist/tier0/classify.d.ts +5 -0
  446. package/dist/tier0/classify.js +91 -0
  447. package/dist/tier0/classify.js.map +1 -0
  448. package/dist/tier0/index.d.ts +3 -0
  449. package/dist/tier0/index.js +3 -0
  450. package/dist/tier0/index.js.map +1 -0
  451. package/dist/tier0/ollama.d.ts +22 -0
  452. package/dist/tier0/ollama.js +63 -0
  453. package/dist/tier0/ollama.js.map +1 -0
  454. package/dist/tier0/types.d.ts +24 -0
  455. package/dist/tier0/types.js +7 -0
  456. package/dist/tier0/types.js.map +1 -0
  457. package/dist/tightener/index.d.ts +4 -0
  458. package/dist/tightener/index.js +4 -0
  459. package/dist/tightener/index.js.map +1 -0
  460. package/dist/tightener/prompt.d.ts +3 -0
  461. package/dist/tightener/prompt.js +67 -0
  462. package/dist/tightener/prompt.js.map +1 -0
  463. package/dist/tightener/schema.d.ts +68 -0
  464. package/dist/tightener/schema.js +44 -0
  465. package/dist/tightener/schema.js.map +1 -0
  466. package/dist/tightener/tighten.d.ts +2 -0
  467. package/dist/tightener/tighten.js +66 -0
  468. package/dist/tightener/tighten.js.map +1 -0
  469. package/dist/tightener/types.d.ts +74 -0
  470. package/dist/tightener/types.js +6 -0
  471. package/dist/tightener/types.js.map +1 -0
  472. package/dist/uat/bundle.d.ts +68 -0
  473. package/dist/uat/bundle.js +168 -0
  474. package/dist/uat/bundle.js.map +1 -0
  475. package/dist/uat/index.d.ts +15 -0
  476. package/dist/uat/index.js +10 -0
  477. package/dist/uat/index.js.map +1 -0
  478. package/dist/uat/persistent.d.ts +64 -0
  479. package/dist/uat/persistent.js +206 -0
  480. package/dist/uat/persistent.js.map +1 -0
  481. package/dist/uat/probes/cli.d.ts +11 -0
  482. package/dist/uat/probes/cli.js +107 -0
  483. package/dist/uat/probes/cli.js.map +1 -0
  484. package/dist/uat/probes/http.d.ts +12 -0
  485. package/dist/uat/probes/http.js +139 -0
  486. package/dist/uat/probes/http.js.map +1 -0
  487. package/dist/uat/probes/index.d.ts +21 -0
  488. package/dist/uat/probes/index.js +30 -0
  489. package/dist/uat/probes/index.js.map +1 -0
  490. package/dist/uat/probes/integration.d.ts +18 -0
  491. package/dist/uat/probes/integration.js +188 -0
  492. package/dist/uat/probes/integration.js.map +1 -0
  493. package/dist/uat/probes/sql/config.d.ts +14 -0
  494. package/dist/uat/probes/sql/config.js +57 -0
  495. package/dist/uat/probes/sql/config.js.map +1 -0
  496. package/dist/uat/probes/sql/index.d.ts +29 -0
  497. package/dist/uat/probes/sql/index.js +43 -0
  498. package/dist/uat/probes/sql/index.js.map +1 -0
  499. package/dist/uat/probes/sql/mysql.d.ts +12 -0
  500. package/dist/uat/probes/sql/mysql.js +96 -0
  501. package/dist/uat/probes/sql/mysql.js.map +1 -0
  502. package/dist/uat/probes/sql/pg.d.ts +20 -0
  503. package/dist/uat/probes/sql/pg.js +102 -0
  504. package/dist/uat/probes/sql/pg.js.map +1 -0
  505. package/dist/uat/probes/sql/sqlite.d.ts +9 -0
  506. package/dist/uat/probes/sql/sqlite.js +58 -0
  507. package/dist/uat/probes/sql/sqlite.js.map +1 -0
  508. package/dist/uat/probes/sql/types.d.ts +46 -0
  509. package/dist/uat/probes/sql/types.js +10 -0
  510. package/dist/uat/probes/sql/types.js.map +1 -0
  511. package/dist/uat/probes/sql.d.ts +9 -0
  512. package/dist/uat/probes/sql.js +119 -0
  513. package/dist/uat/probes/sql.js.map +1 -0
  514. package/dist/uat/probes/ui.d.ts +19 -0
  515. package/dist/uat/probes/ui.js +244 -0
  516. package/dist/uat/probes/ui.js.map +1 -0
  517. package/dist/uat/prompt.d.ts +10 -0
  518. package/dist/uat/prompt.js +85 -0
  519. package/dist/uat/prompt.js.map +1 -0
  520. package/dist/uat/question.d.ts +50 -0
  521. package/dist/uat/question.js +139 -0
  522. package/dist/uat/question.js.map +1 -0
  523. package/dist/uat/rejection.d.ts +58 -0
  524. package/dist/uat/rejection.js +163 -0
  525. package/dist/uat/rejection.js.map +1 -0
  526. package/dist/uat/runner.d.ts +6 -0
  527. package/dist/uat/runner.js +96 -0
  528. package/dist/uat/runner.js.map +1 -0
  529. package/dist/uat/schema.d.ts +322 -0
  530. package/dist/uat/schema.js +189 -0
  531. package/dist/uat/schema.js.map +1 -0
  532. package/dist/uat/types.d.ts +268 -0
  533. package/dist/uat/types.js +18 -0
  534. package/dist/uat/types.js.map +1 -0
  535. package/dist/uat/uat.d.ts +89 -0
  536. package/dist/uat/uat.js +256 -0
  537. package/dist/uat/uat.js.map +1 -0
  538. package/dist/voice/index.d.ts +4 -0
  539. package/dist/voice/index.js +4 -0
  540. package/dist/voice/index.js.map +1 -0
  541. package/dist/voice/model.d.ts +23 -0
  542. package/dist/voice/model.js +46 -0
  543. package/dist/voice/model.js.map +1 -0
  544. package/dist/voice/pipe.d.ts +9 -0
  545. package/dist/voice/pipe.js +47 -0
  546. package/dist/voice/pipe.js.map +1 -0
  547. package/dist/voice/transcribe.d.ts +3 -0
  548. package/dist/voice/transcribe.js +43 -0
  549. package/dist/voice/transcribe.js.map +1 -0
  550. package/dist/voice/types.d.ts +26 -0
  551. package/dist/voice/types.js +9 -0
  552. package/dist/voice/types.js.map +1 -0
  553. package/dist/watch/daemon.d.ts +21 -0
  554. package/dist/watch/daemon.js +143 -0
  555. package/dist/watch/daemon.js.map +1 -0
  556. package/dist/watch/index.d.ts +4 -0
  557. package/dist/watch/index.js +3 -0
  558. package/dist/watch/index.js.map +1 -0
  559. package/dist/watch/regenerate.d.ts +25 -0
  560. package/dist/watch/regenerate.js +51 -0
  561. package/dist/watch/regenerate.js.map +1 -0
  562. package/package.json +75 -0
@@ -0,0 +1,234 @@
1
+ /**
2
+ * `cairn attention` — show pending operator review items.
3
+ *
4
+ * Reads two sources from the adopted project:
5
+ * 1. `.cairn/ground/decisions/_inbox/*.draft.md` — DEC drafts awaiting confirm
6
+ * 2. `.cairn/baseline/sensor-audit-*.yaml` (latest) — pre-Cairn sensor findings
7
+ *
8
+ * Prints a structured summary; exits 0 when there are no pending items, 2 when
9
+ * any are present (so scripts can branch on attention).
10
+ */
11
+ import { existsSync, readFileSync, readdirSync, } from "node:fs";
12
+ import { join, resolve } from "node:path";
13
+ import { parse as parseYaml } from "yaml";
14
+ const FINDINGS_PER_SENSOR = 3;
15
+ function parseRepoFlag(argv) {
16
+ const idx = argv.indexOf("--repo");
17
+ if (idx === -1)
18
+ return process.cwd();
19
+ const candidate = argv[idx + 1];
20
+ if (candidate === undefined || candidate.startsWith("--")) {
21
+ console.error("--repo requires a path argument");
22
+ process.exit(2);
23
+ }
24
+ return resolve(candidate);
25
+ }
26
+ function ensureAdopted(repoRoot) {
27
+ if (!existsSync(repoRoot)) {
28
+ console.error(`cairn attention: repo root does not exist: ${repoRoot}`);
29
+ process.exit(2);
30
+ }
31
+ if (!existsSync(`${repoRoot}/.cairn`)) {
32
+ console.error(`cairn attention: ${repoRoot} is not cairn-adopted (no .cairn/). Run \`cairn init\` first.`);
33
+ process.exit(2);
34
+ }
35
+ }
36
+ function readFrontmatter(text) {
37
+ const m = text.match(/^---\n([\s\S]*?)\n---/);
38
+ if (!m || m[1] === undefined)
39
+ return {};
40
+ try {
41
+ const parsed = parseYaml(m[1]);
42
+ return typeof parsed === "object" && parsed !== null
43
+ ? parsed
44
+ : {};
45
+ }
46
+ catch {
47
+ return {};
48
+ }
49
+ }
50
+ function listDrafts(repoRoot) {
51
+ const dir = join(repoRoot, ".cairn", "ground", "decisions", "_inbox");
52
+ if (!existsSync(dir))
53
+ return [];
54
+ let entries;
55
+ try {
56
+ entries = readdirSync(dir, { withFileTypes: true, encoding: "utf8" });
57
+ }
58
+ catch {
59
+ return [];
60
+ }
61
+ const out = [];
62
+ for (const e of entries) {
63
+ if (!e.isFile() || !e.name.endsWith(".draft.md"))
64
+ continue;
65
+ const abs = join(dir, e.name);
66
+ let text;
67
+ try {
68
+ text = readFileSync(abs, "utf8");
69
+ }
70
+ catch {
71
+ continue;
72
+ }
73
+ const fm = readFrontmatter(text);
74
+ const id = typeof fm["id"] === "string"
75
+ ? fm["id"]
76
+ : e.name.replace(/\.draft\.md$/, "");
77
+ const title = typeof fm["title"] === "string"
78
+ ? fm["title"]
79
+ : "(untitled draft)";
80
+ const sourceFile = typeof fm["sourceFile"] === "string" ? fm["sourceFile"] : null;
81
+ const captureSource = typeof fm["capture_source"] === "string"
82
+ ? fm["capture_source"]
83
+ : null;
84
+ const rationale = typeof fm["proposedRationale"] === "string"
85
+ ? fm["proposedRationale"]
86
+ : null;
87
+ out.push({ id, title, sourceFile, captureSource, rationale });
88
+ }
89
+ out.sort((a, b) => a.id.localeCompare(b.id));
90
+ return out;
91
+ }
92
+ function readLatestBaseline(repoRoot) {
93
+ const dir = join(repoRoot, ".cairn", "baseline");
94
+ if (!existsSync(dir))
95
+ return null;
96
+ let entries;
97
+ try {
98
+ entries = readdirSync(dir, { encoding: "utf8" });
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ const matching = entries
104
+ .filter((name) => /^sensor-audit-.*\.yaml$/.test(name))
105
+ .sort();
106
+ const latest = matching.at(-1);
107
+ if (latest === undefined)
108
+ return null;
109
+ const abs = join(dir, latest);
110
+ let parsed;
111
+ try {
112
+ parsed = parseYaml(readFileSync(abs, "utf8"));
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ if (typeof parsed !== "object" || parsed === null)
118
+ return null;
119
+ const obj = parsed;
120
+ const runAt = typeof obj["run_at"] === "string" ? obj["run_at"] : null;
121
+ const totalFindings = typeof obj["total_findings"] === "number" ? obj["total_findings"] : 0;
122
+ const filesScanned = typeof obj["files_scanned"] === "number" ? obj["files_scanned"] : 0;
123
+ const bySensor = new Map();
124
+ if (Array.isArray(obj["sensors"])) {
125
+ for (const raw of obj["sensors"]) {
126
+ if (typeof raw !== "object" || raw === null)
127
+ continue;
128
+ const r = raw;
129
+ const sensorId = typeof r["sensor_id"] === "string" ? r["sensor_id"] : "";
130
+ if (sensorId.length === 0)
131
+ continue;
132
+ const findingsRaw = Array.isArray(r["findings"]) ? r["findings"] : [];
133
+ const findings = [];
134
+ for (const f of findingsRaw) {
135
+ if (typeof f !== "object" || f === null)
136
+ continue;
137
+ const fr = f;
138
+ findings.push({
139
+ sensor_id: sensorId,
140
+ path: typeof fr["path"] === "string" ? fr["path"] : "",
141
+ line: typeof fr["line"] === "number" ? fr["line"] : 0,
142
+ message: typeof fr["message"] === "string" ? fr["message"] : "",
143
+ severity: fr["severity"] === "hard" || fr["severity"] === "soft"
144
+ ? fr["severity"]
145
+ : "soft",
146
+ });
147
+ }
148
+ if (findings.length > 0)
149
+ bySensor.set(sensorId, findings);
150
+ }
151
+ }
152
+ return {
153
+ path: abs.startsWith(repoRoot) ? abs.slice(repoRoot.length + 1) : abs,
154
+ runAt,
155
+ totalFindings,
156
+ filesScanned,
157
+ bySensor,
158
+ };
159
+ }
160
+ function shortenAge(iso) {
161
+ if (iso === null)
162
+ return "";
163
+ const t = Date.parse(iso);
164
+ if (!Number.isFinite(t))
165
+ return "";
166
+ const minutes = Math.floor((Date.now() - t) / 60_000);
167
+ if (minutes < 60)
168
+ return ` (${minutes}m ago)`;
169
+ const hours = Math.floor(minutes / 60);
170
+ if (hours < 24)
171
+ return ` (${hours}h ago)`;
172
+ const days = Math.floor(hours / 24);
173
+ return ` (${days}d ago)`;
174
+ }
175
+ function renderDraftsSection(drafts) {
176
+ process.stdout.write(` Decision drafts pending confirm — ${drafts.length}\n`);
177
+ for (const d of drafts) {
178
+ const tag = d.captureSource !== null ? ` [${d.captureSource}]` : "";
179
+ process.stdout.write(` • ${d.id}${tag} ${d.title}\n`);
180
+ if (d.sourceFile !== null) {
181
+ process.stdout.write(` from ${d.sourceFile}\n`);
182
+ }
183
+ if (d.rationale !== null && d.rationale.length > 0) {
184
+ const cap = d.rationale.length > 140 ? `${d.rationale.slice(0, 137)}…` : d.rationale;
185
+ process.stdout.write(` ${cap}\n`);
186
+ }
187
+ }
188
+ process.stdout.write("\n Edit, accept, or discard each draft, then run `cairn attention` again.\n");
189
+ }
190
+ function renderBaselineSection(summary) {
191
+ const age = shortenAge(summary.runAt);
192
+ process.stdout.write(` Baseline sensor findings — ${summary.totalFindings} (across ${summary.filesScanned} files)${age}\n`);
193
+ process.stdout.write(` audit: ${summary.path}\n`);
194
+ for (const [sensorId, findings] of summary.bySensor) {
195
+ process.stdout.write(` ${sensorId} — ${findings.length}\n`);
196
+ const head = findings.slice(0, FINDINGS_PER_SENSOR);
197
+ for (const f of head) {
198
+ const loc = f.line > 0 ? `:${f.line}` : "";
199
+ const msg = f.message.length > 80 ? `${f.message.slice(0, 77)}…` : f.message;
200
+ process.stdout.write(` ${f.path}${loc} ${msg}\n`);
201
+ }
202
+ if (findings.length > head.length) {
203
+ process.stdout.write(` …${findings.length - head.length} more\n`);
204
+ }
205
+ }
206
+ process.stdout.write("\n These are pre-Cairn violations. Address them before starting new work, or accept as debt.\n");
207
+ }
208
+ export async function attentionCli(argv) {
209
+ if (argv[0] === "--help" || argv[0] === "-h") {
210
+ process.stdout.write("Usage: cairn attention [--repo <path>]\n" +
211
+ " Show DEC drafts pending confirm + latest baseline sensor findings.\n" +
212
+ " Exit 0 when nothing pending; 2 when any items are pending.\n");
213
+ process.exit(0);
214
+ }
215
+ const repoRoot = parseRepoFlag(argv);
216
+ ensureAdopted(repoRoot);
217
+ const drafts = listDrafts(repoRoot);
218
+ const baseline = readLatestBaseline(repoRoot);
219
+ process.stdout.write(` ⬡ cairn attention — ${repoRoot}\n\n`);
220
+ if (drafts.length === 0 && (baseline === null || baseline.totalFindings === 0)) {
221
+ process.stdout.write(" Nothing pending. Project brain is up to date.\n");
222
+ process.exit(0);
223
+ }
224
+ if (drafts.length > 0) {
225
+ renderDraftsSection(drafts);
226
+ }
227
+ if (baseline !== null && baseline.totalFindings > 0) {
228
+ if (drafts.length > 0)
229
+ process.stdout.write("\n");
230
+ renderBaselineSection(baseline);
231
+ }
232
+ process.exit(2);
233
+ }
234
+ //# sourceMappingURL=attention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attention.js","sourceRoot":"","sources":["../../src/cli/attention.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * `harness daemon` — single supervisor process.
3
+ *
4
+ * Spawns and supervises three things in one cohesive long-lived process:
5
+ * 1. `harness watch --project <slug>` — grounding daemon (chokidar)
6
+ * 2. `harness run --project <slug> --frontend <adapters>` — orchestrator
7
+ * + frontend adapters (Discord by default)
8
+ * 3. periodic `harness gc run --apply-classes safe` — nightly cleanup
9
+ * (runs once on start + every 24h)
10
+ *
11
+ * Both child processes restart on crash with exponential backoff
12
+ * (1s → 2s → 4s, capped at 60s; reset on a clean run > 30s). All output
13
+ * tees into ~/.local/harness/logs/<slug>.{watch,run,gc}.log so launchd /
14
+ * journalctl users have a single tail target.
15
+ *
16
+ * SIGINT / SIGTERM cleanly stop both children + the gc cron, then exit.
17
+ *
18
+ * Used by `harness install` (launchd plist) so a friend can `harness
19
+ * install --project myapp` once and have everything come up on every
20
+ * reboot under one supervised process — no tmux / two-terminal dance.
21
+ */
22
+ export type SupervisorEvent = {
23
+ kind: "started";
24
+ child: "watch" | "run";
25
+ } | {
26
+ kind: "exited";
27
+ child: "watch" | "run";
28
+ code: number | null;
29
+ signal: NodeJS.Signals | null;
30
+ } | {
31
+ kind: "restart-scheduled";
32
+ child: "watch" | "run";
33
+ delayMs: number;
34
+ } | {
35
+ kind: "gc-tick";
36
+ ok: boolean;
37
+ durationMs: number;
38
+ } | {
39
+ kind: "stopped";
40
+ };
41
+ export declare function daemonCli(argv: string[]): Promise<void>;
42
+ /** Test seam — start a supervisor with custom event hook + return stop fn. */
43
+ export declare function startSupervisorForTest(args: {
44
+ project: string;
45
+ frontends: string;
46
+ logDir: string;
47
+ harnessBin: string;
48
+ nodeBin: string;
49
+ onEvent: (event: SupervisorEvent) => void;
50
+ gcIntervalSec?: number | null;
51
+ once?: boolean;
52
+ }): Promise<{
53
+ stop: () => Promise<void>;
54
+ }>;
@@ -0,0 +1,351 @@
1
+ /**
2
+ * `harness daemon` — single supervisor process.
3
+ *
4
+ * Spawns and supervises three things in one cohesive long-lived process:
5
+ * 1. `harness watch --project <slug>` — grounding daemon (chokidar)
6
+ * 2. `harness run --project <slug> --frontend <adapters>` — orchestrator
7
+ * + frontend adapters (Discord by default)
8
+ * 3. periodic `harness gc run --apply-classes safe` — nightly cleanup
9
+ * (runs once on start + every 24h)
10
+ *
11
+ * Both child processes restart on crash with exponential backoff
12
+ * (1s → 2s → 4s, capped at 60s; reset on a clean run > 30s). All output
13
+ * tees into ~/.local/harness/logs/<slug>.{watch,run,gc}.log so launchd /
14
+ * journalctl users have a single tail target.
15
+ *
16
+ * SIGINT / SIGTERM cleanly stop both children + the gc cron, then exit.
17
+ *
18
+ * Used by `harness install` (launchd plist) so a friend can `harness
19
+ * install --project myapp` once and have everything come up on every
20
+ * reboot under one supervised process — no tmux / two-terminal dance.
21
+ */
22
+ import { spawn } from "node:child_process";
23
+ import { mkdirSync, openSync, writeSync, closeSync } from "node:fs";
24
+ import { homedir } from "node:os";
25
+ import { resolve } from "node:path";
26
+ import { defaultStatusJson, logger, normalizeProjectName, writeStatusJsonForSlug, } from "@devplusllc/harness-core";
27
+ const log = logger("cli.daemon");
28
+ function parseArgs(argv) {
29
+ const positional = [];
30
+ const flags = {};
31
+ for (let i = 0; i < argv.length; i++) {
32
+ const arg = argv[i];
33
+ if (arg === undefined)
34
+ continue;
35
+ if (arg.startsWith("--")) {
36
+ const key = arg.slice(2);
37
+ const next = argv[i + 1];
38
+ if (next !== undefined && !next.startsWith("--")) {
39
+ flags[key] = next;
40
+ i += 1;
41
+ }
42
+ else {
43
+ flags[key] = true;
44
+ }
45
+ }
46
+ else {
47
+ positional.push(arg);
48
+ }
49
+ }
50
+ return { positional, flags };
51
+ }
52
+ function usage() {
53
+ console.error("Usage: harness daemon --project <slug> [options]\n" +
54
+ " --project project slug (required)\n" +
55
+ " --frontend adapter list for `harness run` (default: discord)\n" +
56
+ " --gc-interval seconds between gc passes (default: 86400 = 24h)\n" +
57
+ " --no-gc disable the periodic gc tick\n" +
58
+ " --log-dir override log dir (default: ~/.local/harness/logs)\n" +
59
+ " --once run children once + exit when both die (no restart)\n" +
60
+ "\n" +
61
+ "Single supervised process running watch + run + nightly gc. Logs to\n" +
62
+ "<log-dir>/<slug>.{watch,run,gc}.log. SIGINT/SIGTERM stops everything.");
63
+ process.exit(1);
64
+ }
65
+ const RESET_BACKOFF_AFTER_MS = 30_000;
66
+ const MAX_BACKOFF_MS = 60_000;
67
+ function openLog(dir, slug, label) {
68
+ mkdirSync(dir, { recursive: true });
69
+ return openSync(resolve(dir, `${slug}.${label}.log`), "a");
70
+ }
71
+ function writeLine(fd, line) {
72
+ try {
73
+ writeSync(fd, `${line}\n`);
74
+ }
75
+ catch {
76
+ // best-effort
77
+ }
78
+ }
79
+ function spawnChild(child, shared) {
80
+ const proc = spawn(shared.nodeBin, [shared.harnessBin, ...child.args], {
81
+ stdio: ["ignore", "pipe", "pipe"],
82
+ env: process.env,
83
+ });
84
+ child.proc = proc;
85
+ child.startedAt = Date.now();
86
+ proc.stdout.on("data", (chunk) => {
87
+ writeSync(child.logFd, chunk);
88
+ });
89
+ proc.stderr.on("data", (chunk) => {
90
+ writeSync(child.logFd, chunk);
91
+ });
92
+ writeLine(child.logFd, `\n── [supervisor] ${new Date().toISOString()} spawned ${child.name} (pid ${proc.pid})`);
93
+ }
94
+ async function supervise(args) {
95
+ const watchLogFd = openLog(args.logDir, args.project, "watch");
96
+ const runLogFd = openLog(args.logDir, args.project, "run");
97
+ const gcLogFd = openLog(args.logDir, args.project, "gc");
98
+ const supervisorLogFd = openLog(args.logDir, args.project, "supervisor");
99
+ writeLine(supervisorLogFd, `── ${new Date().toISOString()} supervisor start · project=${args.project} · frontends=${args.frontends}`);
100
+ // Initialise status.json with a complete default — Claude Code's
101
+ // `harness status-line` reader does strict shape validation, so a partial
102
+ // patch on first start would render as the placeholder "daemon:down".
103
+ // Best-effort write; status line is cosmetic.
104
+ try {
105
+ writeStatusJsonForSlug(args.project, defaultStatusJson(true));
106
+ }
107
+ catch (err) {
108
+ log.warn({ err: String(err) }, "initial status.json write failed");
109
+ }
110
+ const children = [
111
+ {
112
+ name: "watch",
113
+ args: ["watch", "--project", args.project],
114
+ proc: undefined,
115
+ backoffMs: 1000,
116
+ startedAt: 0,
117
+ stopped: false,
118
+ logFd: watchLogFd,
119
+ },
120
+ {
121
+ name: "run",
122
+ args: [
123
+ "run",
124
+ "--project",
125
+ args.project,
126
+ "--frontend",
127
+ args.frontends,
128
+ ],
129
+ proc: undefined,
130
+ backoffMs: 1000,
131
+ startedAt: 0,
132
+ stopped: false,
133
+ logFd: runLogFd,
134
+ },
135
+ ];
136
+ let stopRequested = false;
137
+ let gcTimer;
138
+ let heartbeat;
139
+ const stopChild = async (child) => {
140
+ child.stopped = true;
141
+ if (child.proc !== undefined && child.proc.exitCode === null) {
142
+ try {
143
+ child.proc.kill("SIGTERM");
144
+ }
145
+ catch {
146
+ // best-effort
147
+ }
148
+ // Give it 10s grace then SIGKILL.
149
+ await new Promise((res) => {
150
+ const grace = setTimeout(() => {
151
+ if (child.proc !== undefined && child.proc.exitCode === null) {
152
+ try {
153
+ child.proc.kill("SIGKILL");
154
+ }
155
+ catch {
156
+ // best-effort
157
+ }
158
+ }
159
+ res();
160
+ }, 10_000);
161
+ child.proc?.once("exit", () => {
162
+ clearTimeout(grace);
163
+ res();
164
+ });
165
+ });
166
+ }
167
+ };
168
+ const stopAll = async () => {
169
+ if (stopRequested)
170
+ return;
171
+ stopRequested = true;
172
+ if (gcTimer !== undefined)
173
+ clearInterval(gcTimer);
174
+ if (heartbeat !== undefined)
175
+ clearInterval(heartbeat);
176
+ writeLine(supervisorLogFd, `── ${new Date().toISOString()} stop signal received`);
177
+ await Promise.all(children.map((c) => stopChild(c)));
178
+ // Mark the status file so Claude Code's status line shows "down" before
179
+ // the LaunchAgent (or operator) restarts us.
180
+ try {
181
+ writeStatusJsonForSlug(args.project, {
182
+ daemon_alive: false,
183
+ updated_at: new Date().toISOString(),
184
+ });
185
+ }
186
+ catch {
187
+ // best-effort
188
+ }
189
+ writeLine(supervisorLogFd, `── ${new Date().toISOString()} supervisor exit`);
190
+ args.onEvent?.({ kind: "stopped" });
191
+ closeSync(watchLogFd);
192
+ closeSync(runLogFd);
193
+ closeSync(gcLogFd);
194
+ closeSync(supervisorLogFd);
195
+ };
196
+ process.on("SIGINT", () => {
197
+ void stopAll().then(() => process.exit(0));
198
+ });
199
+ process.on("SIGTERM", () => {
200
+ void stopAll().then(() => process.exit(0));
201
+ });
202
+ const launch = (child) => {
203
+ if (child.stopped || stopRequested)
204
+ return;
205
+ spawnChild(child, { nodeBin: args.nodeBin, harnessBin: args.harnessBin });
206
+ args.onEvent?.({ kind: "started", child: child.name });
207
+ log.info({ child: child.name, pid: child.proc?.pid, args: child.args }, "child spawned");
208
+ child.proc?.once("exit", (code, signal) => {
209
+ writeLine(child.logFd, `── [supervisor] ${new Date().toISOString()} ${child.name} exited code=${code ?? "null"} signal=${signal ?? "null"}`);
210
+ args.onEvent?.({
211
+ kind: "exited",
212
+ child: child.name,
213
+ code,
214
+ signal,
215
+ });
216
+ log.warn({ child: child.name, code, signal }, "child exited");
217
+ if (child.stopped || stopRequested)
218
+ return;
219
+ if (args.once)
220
+ return;
221
+ const ranLong = Date.now() - child.startedAt > RESET_BACKOFF_AFTER_MS;
222
+ if (ranLong)
223
+ child.backoffMs = 1000;
224
+ const delay = child.backoffMs;
225
+ child.backoffMs = Math.min(child.backoffMs * 2, MAX_BACKOFF_MS);
226
+ args.onEvent?.({
227
+ kind: "restart-scheduled",
228
+ child: child.name,
229
+ delayMs: delay,
230
+ });
231
+ writeLine(child.logFd, `── [supervisor] ${new Date().toISOString()} ${child.name} restart in ${delay}ms`);
232
+ setTimeout(() => launch(child), delay);
233
+ });
234
+ };
235
+ for (const c of children)
236
+ launch(c);
237
+ // Heartbeat — refresh `updated_at` every 30s so a crashed-without-shutdown
238
+ // daemon eventually appears stale to readers that check timestamp drift.
239
+ heartbeat = setInterval(() => {
240
+ try {
241
+ writeStatusJsonForSlug(args.project, {
242
+ daemon_alive: true,
243
+ updated_at: new Date().toISOString(),
244
+ });
245
+ }
246
+ catch {
247
+ // best-effort
248
+ }
249
+ }, 30_000);
250
+ // Allow process to exit if everything else is done — heartbeat shouldn't
251
+ // hold the event loop open on its own.
252
+ heartbeat.unref();
253
+ // Periodic GC tick.
254
+ if (args.gcIntervalSec !== null) {
255
+ const runGc = async () => {
256
+ const startedAt = Date.now();
257
+ writeLine(gcLogFd, `── ${new Date().toISOString()} gc tick start`);
258
+ try {
259
+ await new Promise((res, rej) => {
260
+ const proc = spawn(args.nodeBin, [
261
+ args.harnessBin,
262
+ "gc",
263
+ "run",
264
+ "--apply-classes",
265
+ "safe",
266
+ "--repo-root",
267
+ process.cwd(),
268
+ ], { stdio: ["ignore", "pipe", "pipe"], env: process.env });
269
+ proc.stdout.on("data", (chunk) => writeSync(gcLogFd, chunk));
270
+ proc.stderr.on("data", (chunk) => writeSync(gcLogFd, chunk));
271
+ proc.on("exit", (code) => {
272
+ if (code === 0)
273
+ res();
274
+ else
275
+ rej(new Error(`gc exited ${code}`));
276
+ });
277
+ proc.on("error", rej);
278
+ });
279
+ const dur = Date.now() - startedAt;
280
+ writeLine(gcLogFd, `── gc tick OK (${dur}ms)`);
281
+ args.onEvent?.({ kind: "gc-tick", ok: true, durationMs: dur });
282
+ }
283
+ catch (err) {
284
+ const dur = Date.now() - startedAt;
285
+ writeLine(gcLogFd, `── gc tick FAIL (${dur}ms): ${String(err)}`);
286
+ args.onEvent?.({ kind: "gc-tick", ok: false, durationMs: dur });
287
+ }
288
+ };
289
+ // Fire once on start (after a 60s warmup so children are settled).
290
+ setTimeout(() => void runGc(), 60_000);
291
+ gcTimer = setInterval(() => void runGc(), args.gcIntervalSec * 1000);
292
+ }
293
+ }
294
+ export async function daemonCli(argv) {
295
+ const { flags } = parseArgs(argv);
296
+ if (flags["help"] === true || flags["h"] === true)
297
+ usage();
298
+ const slugRaw = typeof flags["project"] === "string" ? flags["project"] : "";
299
+ if (slugRaw.length === 0) {
300
+ console.error("harness daemon: --project required\n");
301
+ usage();
302
+ }
303
+ const project = normalizeProjectName(slugRaw);
304
+ const frontends = typeof flags["frontend"] === "string" ? flags["frontend"] : "discord";
305
+ const logDir = typeof flags["log-dir"] === "string"
306
+ ? resolve(flags["log-dir"])
307
+ : resolve(homedir(), ".local", "harness", "logs");
308
+ const once = flags["once"] === true;
309
+ const gcIntervalSec = flags["no-gc"] === true
310
+ ? null
311
+ : typeof flags["gc-interval"] === "string"
312
+ ? Math.max(60, Number.parseInt(flags["gc-interval"], 10) || 86400)
313
+ : 86400;
314
+ // Resolve the harness CLI binary path. In normal operation argv[1] is the
315
+ // dist/cli/index.js file or a node_modules/.bin shim. We re-invoke it with
316
+ // the same node binary for cross-platform consistency.
317
+ const harnessBin = resolve(process.argv[1] ?? "");
318
+ const nodeBin = process.execPath;
319
+ await supervise({
320
+ project,
321
+ frontends,
322
+ logDir,
323
+ once,
324
+ gcIntervalSec,
325
+ harnessBin,
326
+ nodeBin,
327
+ });
328
+ }
329
+ /** Test seam — start a supervisor with custom event hook + return stop fn. */
330
+ export async function startSupervisorForTest(args) {
331
+ let stopFn = async () => { };
332
+ await supervise({
333
+ ...args,
334
+ once: args.once ?? false,
335
+ gcIntervalSec: args.gcIntervalSec ?? null,
336
+ onEvent: (e) => {
337
+ args.onEvent(e);
338
+ if (e.kind === "stopped") {
339
+ // noop — actual stop fn drives this
340
+ }
341
+ },
342
+ });
343
+ // We can't easily expose the real stop without restructuring supervise.
344
+ // For test-time, send SIGTERM to ourselves is not appropriate. Instead,
345
+ // surface a kill helper via process events.
346
+ stopFn = async () => {
347
+ process.emit("SIGTERM");
348
+ };
349
+ return { stop: stopFn };
350
+ }
351
+ //# sourceMappingURL=daemon.js.map