elanous 0.2.14 → 0.2.15

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 (293) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/LZcstaO9_NXypPFmujolv/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/{1307-6e7e0377c1db0bdc.js → 1307-33ffcc49ab45123b.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/app/approvals/{page-74383f890f075511.js → page-0e2c57fe6add4b2a.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-e05a77df7e005b04.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-b8c49f6b4fa2df91.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-976813c91d7394ea.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/app/consult/{page-fb260b9283dbf646.js → page-28b9448091776c06.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/app/control/page-1d4901eb7c2d8822.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/app/design-check/page-72d3e7ff35a8c954.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-60c7a8e1a4fe62ce.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-2a52b316cb143d9c.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-c3ce483c8d5db370.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-96f074f6538e8a2c.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-e97be5334d83a9f5.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-955ec2da3be4c560.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-c1343f9499d86f3f.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/app/not-found-8739a2e0ae793130.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-d23b12bb92242558.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c7fea475488201a8.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/app/outputs/{page-04250f0e2c0d03df.js → page-e2ee2896811e98d0.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-5efbf81124c474ae.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-85f1a1fc0a82738c.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/app/sessions/{page-8ac8d71c2adf9c8a.js → page-da6c08aae0f92768.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-06ee266953d9c879.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-153c2b3bcc89e8de.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-94dc383d3d54db1a.js → page-87a11e6a05f15199.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/setup/{page-b3759cc14da80908.js → page-7d4182964407c09c.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-0d6d23b40263a7ea.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-f5597e894c02cd33.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/trace/{page-35bdeae2e2c17147.js → page-73735b64bfe1ae67.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-8fcec4c9ea2e7f70.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f7e631e9b4daf66e.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-9817c7f9007466cc.js} +1 -1
  52. package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-134c1bc762aee62c.js → page-97f9f43172a3a90e.js} +1 -1
  53. package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.css +1 -0
  54. package/apps/pwa/out/approvals/index.html +1 -1
  55. package/apps/pwa/out/approvals/index.txt +14 -14
  56. package/apps/pwa/out/autopilot/index.html +1 -1
  57. package/apps/pwa/out/autopilot/index.txt +14 -14
  58. package/apps/pwa/out/board/index.html +1 -1
  59. package/apps/pwa/out/board/index.txt +14 -14
  60. package/apps/pwa/out/botlab/index.html +1 -1
  61. package/apps/pwa/out/botlab/index.txt +14 -14
  62. package/apps/pwa/out/bots/index.html +1 -1
  63. package/apps/pwa/out/bots/index.txt +14 -14
  64. package/apps/pwa/out/ceo/index.html +1 -0
  65. package/apps/pwa/out/ceo/index.txt +33 -0
  66. package/apps/pwa/out/chat/index.html +1 -1
  67. package/apps/pwa/out/chat/index.txt +14 -14
  68. package/apps/pwa/out/consult/index.html +1 -1
  69. package/apps/pwa/out/consult/index.txt +14 -14
  70. package/apps/pwa/out/control/index.html +1 -1
  71. package/apps/pwa/out/control/index.txt +14 -14
  72. package/apps/pwa/out/design-check/index.html +1 -1
  73. package/apps/pwa/out/design-check/index.txt +14 -14
  74. package/apps/pwa/out/editor/index.html +1 -1
  75. package/apps/pwa/out/editor/index.txt +14 -14
  76. package/apps/pwa/out/exec/index.html +1 -1
  77. package/apps/pwa/out/exec/index.txt +14 -14
  78. package/apps/pwa/out/field/index.html +1 -1
  79. package/apps/pwa/out/field/index.txt +14 -14
  80. package/apps/pwa/out/healthz +1 -1
  81. package/apps/pwa/out/index.html +1 -1
  82. package/apps/pwa/out/index.txt +14 -14
  83. package/apps/pwa/out/inside/index.html +1 -1
  84. package/apps/pwa/out/inside/index.txt +14 -14
  85. package/apps/pwa/out/intake/index.html +1 -1
  86. package/apps/pwa/out/intake/index.txt +14 -14
  87. package/apps/pwa/out/live/index.html +1 -1
  88. package/apps/pwa/out/live/index.txt +14 -14
  89. package/apps/pwa/out/loops/index.html +1 -1
  90. package/apps/pwa/out/loops/index.txt +14 -14
  91. package/apps/pwa/out/market/index.html +1 -1
  92. package/apps/pwa/out/market/index.txt +14 -14
  93. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  94. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  95. package/apps/pwa/out/morning/index.html +1 -1
  96. package/apps/pwa/out/morning/index.txt +14 -14
  97. package/apps/pwa/out/observatory/index.html +1 -1
  98. package/apps/pwa/out/observatory/index.txt +14 -14
  99. package/apps/pwa/out/ops/checklist/index.html +1 -1
  100. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  101. package/apps/pwa/out/ops/release/index.html +1 -1
  102. package/apps/pwa/out/ops/release/index.txt +14 -14
  103. package/apps/pwa/out/ops/seats/index.html +1 -1
  104. package/apps/pwa/out/ops/seats/index.txt +14 -14
  105. package/apps/pwa/out/outputs/index.html +1 -1
  106. package/apps/pwa/out/outputs/index.txt +14 -14
  107. package/apps/pwa/out/reflection/index.html +1 -1
  108. package/apps/pwa/out/reflection/index.txt +15 -15
  109. package/apps/pwa/out/scheduler/index.html +1 -1
  110. package/apps/pwa/out/scheduler/index.txt +14 -14
  111. package/apps/pwa/out/sessions/index.html +1 -1
  112. package/apps/pwa/out/sessions/index.txt +14 -14
  113. package/apps/pwa/out/settings/devices/index.html +1 -1
  114. package/apps/pwa/out/settings/devices/index.txt +14 -14
  115. package/apps/pwa/out/settings/index.html +1 -1
  116. package/apps/pwa/out/settings/index.txt +14 -14
  117. package/apps/pwa/out/setup/done/index.html +1 -1
  118. package/apps/pwa/out/setup/done/index.txt +14 -14
  119. package/apps/pwa/out/setup/index.html +1 -1
  120. package/apps/pwa/out/setup/index.txt +14 -14
  121. package/apps/pwa/out/share/index.html +1 -1
  122. package/apps/pwa/out/share/index.txt +14 -14
  123. package/apps/pwa/out/showroom/index.html +1 -1
  124. package/apps/pwa/out/showroom/index.txt +14 -14
  125. package/apps/pwa/out/tasks/index.html +1 -1
  126. package/apps/pwa/out/tasks/index.txt +14 -14
  127. package/apps/pwa/out/term/index.html +1 -1
  128. package/apps/pwa/out/term/index.txt +14 -14
  129. package/apps/pwa/out/today/index.html +1 -1
  130. package/apps/pwa/out/today/index.txt +14 -14
  131. package/apps/pwa/out/trace/index.html +1 -1
  132. package/apps/pwa/out/trace/index.txt +14 -14
  133. package/apps/pwa/out/vault/index.html +1 -1
  134. package/apps/pwa/out/vault/index.txt +14 -14
  135. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  136. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  137. package/apps/pwa/out/workflows/index.html +1 -1
  138. package/apps/pwa/out/workflows/index.txt +14 -14
  139. package/apps/pwa/out/workspace/index.html +1 -1
  140. package/apps/pwa/out/workspace/index.txt +14 -14
  141. package/apps/pwa/out/worktrees/index.html +1 -1
  142. package/apps/pwa/out/worktrees/index.txt +14 -14
  143. package/graphs/heal/heal-loop.yaml +1 -1
  144. package/graphs/implement-loop.yaml +1 -1
  145. package/graphs/release/recipes.yaml +3 -0
  146. package/graphs/release/release-loop.yaml +4 -2
  147. package/package.json +1 -1
  148. package/scripts/brand/check.ts +7 -5
  149. package/scripts/ci-typecheck-changed.ts +50 -18
  150. package/scripts/docs/generate-graph-anatomy.ts +315 -0
  151. package/scripts/expose-rubric-suggest.ts +18 -12
  152. package/scripts/lecture-note/node.ts +31 -11
  153. package/scripts/lessons/handbook-page.ts +30 -3
  154. package/scripts/marketing/generate-ax-brief.ts +140 -0
  155. package/scripts/marketing/generate-feature-map.ts +118 -0
  156. package/scripts/release-loop/checklist-gate-node.ts +53 -14
  157. package/scripts/release-loop/gate-node.ts +80 -14
  158. package/scripts/release-loop/vault-note-node.ts +240 -8
  159. package/scripts/release-story/draft.ts +57 -9
  160. package/scripts/rhythm/daily.ts +263 -76
  161. package/scripts/rhythm/weekly.ts +30 -9
  162. package/scripts/smart-merge/resync-shadow.ts +147 -0
  163. package/src/agent-env/profile.ts +161 -0
  164. package/src/agent-substrate/pr-reviewer.ts +2 -0
  165. package/src/ask-user-question/pending-questions.ts +5 -0
  166. package/src/auth/github-app-token.ts +112 -13
  167. package/src/autopilot/build/llm-conflict-merge.ts +32 -6
  168. package/src/briefing/brief-items.ts +121 -0
  169. package/src/claims/claims-render.ts +14 -5
  170. package/src/cli/agent-env-cli.ts +21 -0
  171. package/src/cli/decisions-cli.ts +9 -0
  172. package/src/cli/doctor-cli.ts +23 -1
  173. package/src/cli/doctor-node-modules.ts +147 -0
  174. package/src/cli/intake-cli.ts +46 -17
  175. package/src/cli/pod-cli.ts +2 -0
  176. package/src/cli/release-cli.ts +61 -27
  177. package/src/cli/seat-requests-cli.ts +52 -0
  178. package/src/context-bus/context-now-public.ts +16 -6
  179. package/src/context-bus/context-now.ts +3 -1
  180. package/src/context-bus/coord-events.ts +6 -4
  181. package/src/decisions/decision-cards.ts +33 -7
  182. package/src/decisions/proact-meter.ts +286 -0
  183. package/src/decisions/proact1-lite.ts +86 -0
  184. package/src/decisions/web-push-decision-cards.ts +1 -1
  185. package/src/domains/alert-audit.ts +271 -0
  186. package/src/domains/core-tools.ts +4 -3
  187. package/src/domains/ops-facts-tool.ts +31 -1
  188. package/src/harness/harness-cli-command.ts +111 -15
  189. package/src/harness/harness-incidents.ts +2 -0
  190. package/src/harness/harness-pod-dispatch.ts +100 -11
  191. package/src/harness/harness-queue.ts +380 -50
  192. package/src/harness/helper-repair.ts +87 -0
  193. package/src/harness/helper-scan.ts +16 -4
  194. package/src/harness/launch-stamp.ts +236 -0
  195. package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
  196. package/src/harness/ledger-write-guard.ts +35 -0
  197. package/src/harness/pod-memory-policy.ts +15 -0
  198. package/src/harness/repository-name.ts +55 -0
  199. package/src/hq/fence-audit.ts +90 -0
  200. package/src/hq/hq.ts +62 -4
  201. package/src/hq/lease.ts +4 -4
  202. package/src/hq/move-plan.ts +126 -0
  203. package/src/hq/promote.ts +31 -0
  204. package/src/index.ts +77 -7
  205. package/src/intake-plane/check.ts +63 -0
  206. package/src/intake-plane/digest.ts +37 -15
  207. package/src/intake-plane/front-classifier.ts +6 -0
  208. package/src/intake-plane/items.ts +3 -2
  209. package/src/intake-plane/lens.ts +177 -0
  210. package/src/intake-plane/route.ts +145 -1
  211. package/src/launch-head/seat-goal-queue-lock.fixture.ts +15 -0
  212. package/src/launch-head/seat-goal-queue.ts +198 -0
  213. package/src/loops/budget.ts +36 -5
  214. package/src/loops/checker.ts +2 -2
  215. package/src/loops/orchestrator/author-depth.ts +65 -9
  216. package/src/loops/orchestrator/author-input-check.ts +8 -3
  217. package/src/loops/orchestrator/author-ledger.ts +3 -1
  218. package/src/loops/orchestrator/seat-share.ts +56 -0
  219. package/src/loops/orchestrator/tick.ts +11 -4
  220. package/src/loops/orchestrator/traffic.ts +117 -11
  221. package/src/loops/registry.ts +142 -61
  222. package/src/maturity/feature-maturity.ts +2 -1
  223. package/src/maturity/tui-slash-availability.ts +31 -0
  224. package/src/mss/logging/log-store.ts +35 -4
  225. package/src/nexus/api/http-server.ts +28 -14
  226. package/src/nexus/api/loop-edges.ts +173 -0
  227. package/src/nexus/api/schedules-read.ts +13 -0
  228. package/src/nexus/api/seat-requests.ts +79 -56
  229. package/src/prompt-enhance/enhance.ts +6 -1
  230. package/src/release-loop/auto-start.ts +6 -3
  231. package/src/release-loop/checklist.ts +13 -4
  232. package/src/release-loop/feature-store.ts +58 -31
  233. package/src/release-loop/merged-pr-checklist.ts +125 -0
  234. package/src/release-loop/placement.ts +86 -26
  235. package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
  236. package/src/seat-dispatch/seat-request-ledger.ts +133 -0
  237. package/src/seat-loop/seat-loop.ts +126 -10
  238. package/src/self-dev/dev-pipeline.ts +8 -4
  239. package/src/self-dev/draft-sweep.ts +90 -10
  240. package/src/self-dev/draft-triage-rules.ts +25 -8
  241. package/src/self-dev/orchestrate.ts +15 -1
  242. package/src/self-dev/review-provider-fallback.ts +6 -1
  243. package/src/self-dev/run-supervisor.ts +49 -2
  244. package/src/self-implement/frozen-merges.ts +3 -2
  245. package/src/self-implement/goal-author.ts +43 -5
  246. package/src/self-implement/goal-clarification-escalation.ts +5 -0
  247. package/src/self-implement/heal-intake.ts +120 -2
  248. package/src/self-implement/host-regate.ts +9 -2
  249. package/src/self-implement/l8-merge-queue.ts +132 -0
  250. package/src/self-implement/orchestrator.ts +187 -40
  251. package/src/self-implement/pipeline-shape.ts +1 -1
  252. package/src/self-implement/revert-guard.ts +42 -7
  253. package/src/self-implement/run-ledger.ts +2 -0
  254. package/src/self-implement/run-status-mapping.ts +2 -0
  255. package/src/self-implement/seams.ts +1 -1
  256. package/src/self-implement/self-implement-cli.ts +1 -0
  257. package/src/self-implement/speed-claims.ts +338 -0
  258. package/src/steward/triage.ts +74 -3
  259. package/src/steward/working-backwards.ts +4 -1
  260. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
  261. package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
  262. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
  263. package/src/task-orchestrator/surfaces/pod-pool.ts +60 -8
  264. package/src/task-orchestrator/surfaces/self-implement-pod.ts +147 -36
  265. package/src/task-orchestrator/surfaces/self-implement.ts +5 -1
  266. package/src/telegram.ts +25 -4
  267. package/src/user-config.ts +29 -5
  268. package/src/version/packed-revision.json +1 -1
  269. package/src/workflow-runtime/nodes/bash.ts +9 -1
  270. package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +0 -1
  271. package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +0 -1
  272. package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +0 -1
  273. package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +0 -1
  274. package/apps/pwa/out/_next/static/chunks/app/control/page-e6b84ab07d3567d5.js +0 -1
  275. package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +0 -1
  276. package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +0 -1
  277. package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +0 -1
  278. package/apps/pwa/out/_next/static/chunks/app/loops/page-160089123a40dd91.js +0 -1
  279. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2ce5471926761fdd.js +0 -1
  280. package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +0 -1
  281. package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +0 -1
  282. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e7e10cd3386222e5.js +0 -1
  283. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +0 -1
  284. package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +0 -1
  285. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +0 -1
  286. package/apps/pwa/out/_next/static/chunks/app/showroom/page-58043db78ba519ae.js +0 -1
  287. package/apps/pwa/out/_next/static/chunks/app/tasks/page-357750dfb91bf960.js +0 -1
  288. package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +0 -1
  289. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
  290. package/apps/pwa/out/_next/static/chunks/app/workflows/page-f8da5dc6f3752276.js +0 -1
  291. package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +0 -1
  292. package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +0 -1
  293. /package/apps/pwa/out/_next/static/{x6mjI6vqOkVVu0jFuDkz1 → LZcstaO9_NXypPFmujolv}/_ssgManifest.js +0 -0
@@ -0,0 +1,121 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { chmodSync, mkdirSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { elanousStateRoot } from '../autopilot/state-paths.js';
5
+
6
+ export type BriefPriority = 'high' | 'medium' | 'low';
7
+
8
+ export interface BriefItem {
9
+ id: number;
10
+ text: string;
11
+ domain: string;
12
+ priority: BriefPriority;
13
+ deadline: string | null;
14
+ evidence: string | null;
15
+ source: string;
16
+ created_at: string;
17
+ }
18
+
19
+ export interface AddBriefItem {
20
+ text: string;
21
+ domain: string;
22
+ priority: BriefPriority;
23
+ deadline?: string | null;
24
+ evidence?: string | null;
25
+ source: string;
26
+ }
27
+
28
+ export interface BriefItemsOptions {
29
+ stateDir?: string;
30
+ now?: () => Date;
31
+ }
32
+
33
+ export class BriefItemsInputError extends Error {
34
+ override name = 'BriefItemsInputError';
35
+ }
36
+
37
+ function required(value: string, field: string): string {
38
+ if (typeof value !== 'string' || !value.trim()) throw new BriefItemsInputError(`${field} is required`);
39
+ return value;
40
+ }
41
+
42
+ function optional(value: string | null | undefined, field: string): string | null {
43
+ return value == null ? null : required(value, field);
44
+ }
45
+
46
+ /** Append-only item ledger. All three operations open the same instance-scoped SQLite file. */
47
+ export class BriefItemsLedger {
48
+ readonly path: string;
49
+ private readonly now: () => Date;
50
+
51
+ constructor(options: BriefItemsOptions = {}) {
52
+ this.path = join(options.stateDir ?? elanousStateRoot(), 'briefing', 'items.sqlite');
53
+ this.now = options.now ?? (() => new Date());
54
+ }
55
+
56
+ private using<T>(work: (db: Database) => T): T {
57
+ mkdirSync(dirname(this.path), { recursive: true });
58
+ const db = new Database(this.path, { create: true, strict: true });
59
+ try {
60
+ chmodSync(this.path, 0o600);
61
+ db.exec('PRAGMA busy_timeout = 10000');
62
+ db.exec(`CREATE TABLE IF NOT EXISTS items (
63
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
64
+ text TEXT NOT NULL, domain TEXT NOT NULL,
65
+ priority TEXT NOT NULL CHECK(priority IN ('high', 'medium', 'low')),
66
+ deadline TEXT, evidence TEXT, source TEXT NOT NULL, created_at TEXT NOT NULL
67
+ );
68
+ CREATE TRIGGER IF NOT EXISTS items_no_update BEFORE UPDATE ON items
69
+ BEGIN SELECT RAISE(ABORT, 'brief items are immutable'); END;
70
+ CREATE TRIGGER IF NOT EXISTS items_no_delete BEFORE DELETE ON items
71
+ BEGIN SELECT RAISE(ABORT, 'brief items are immutable'); END;`);
72
+ return work(db);
73
+ } finally {
74
+ db.close();
75
+ }
76
+ }
77
+
78
+ add(input: AddBriefItem): BriefItem {
79
+ const text = required(input.text, 'text');
80
+ const domain = required(input.domain, 'domain');
81
+ const source = required(input.source, 'source');
82
+ if (!['high', 'medium', 'low'].includes(input.priority)) throw new BriefItemsInputError('invalid priority');
83
+ const deadline = optional(input.deadline, 'deadline');
84
+ if (deadline !== null) {
85
+ const format = /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2}))?$/;
86
+ if (!format.test(deadline) || !Number.isFinite(Date.parse(deadline))) throw new BriefItemsInputError('invalid deadline');
87
+ }
88
+ const evidence = optional(input.evidence, 'evidence');
89
+ return this.using(db => {
90
+ const createdAt = this.now().toISOString();
91
+ const result = db.query('INSERT INTO items (text, domain, priority, deadline, evidence, source, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)')
92
+ .run(text, domain, input.priority, deadline, evidence, source, createdAt);
93
+ return db.query('SELECT id, text, domain, priority, deadline, evidence, source, created_at FROM items WHERE id = ?')
94
+ .get(result.lastInsertRowid) as BriefItem;
95
+ });
96
+ }
97
+
98
+ /** Higher priorities first, then nearest deadline by instant, then insertion order. */
99
+ list(filters: { domain?: string } = {}): BriefItem[] {
100
+ const items = this.using(db => (filters.domain === undefined
101
+ ? db.query('SELECT id, text, domain, priority, deadline, evidence, source, created_at FROM items').all()
102
+ : db.query('SELECT id, text, domain, priority, deadline, evidence, source, created_at FROM items WHERE domain = ?').all(filters.domain)) as BriefItem[]);
103
+ const rank: Record<BriefPriority, number> = { high: 0, medium: 1, low: 2 };
104
+ return items.sort((a, b) => rank[a.priority] - rank[b.priority]
105
+ || Number(a.deadline === null) - Number(b.deadline === null)
106
+ || (a.deadline !== null && b.deadline !== null ? Date.parse(a.deadline) - Date.parse(b.deadline) : 0)
107
+ || a.id - b.id);
108
+ }
109
+
110
+ /** Pure Markdown output: composing never marks, alters, or sends stored items. */
111
+ compose(slot: string, filters: { domain?: string } = {}): string {
112
+ required(slot, 'slot');
113
+ const items = this.list(filters);
114
+ const lines = [`# Briefing — ${slot}`, ''];
115
+ for (const item of items) {
116
+ lines.push(`- [${item.priority}] ${item.text} (${item.domain})`);
117
+ lines.push(` - Source: ${item.source}${item.deadline ? ` · Deadline: ${item.deadline}` : ''}${item.evidence ? ` · Evidence: ${item.evidence}` : ''}`);
118
+ }
119
+ return `${lines.join('\n').trimEnd()}\n`;
120
+ }
121
+ }
@@ -2,7 +2,7 @@ import { Database } from 'bun:sqlite';
2
2
  import { appendFileSync, existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { dirname, join } from 'node:path';
5
- import { checkBrand } from '../../scripts/brand/check.js';
5
+ import { BRAND_RULES_UNMEASURED, checkBrand } from '../../scripts/brand/check.js';
6
6
  import { debug } from '../debug/log.js';
7
7
  import { effectiveInstanceRoot } from '../instance/resolve.js';
8
8
  import type { ClaimsLedger, ClaimEvidence } from './claims-ledger.js';
@@ -11,7 +11,8 @@ export type ClaimSurface = 'deck' | 'site' | 'notice';
11
11
  export interface RenderedClaims {
12
12
  markdown: string;
13
13
  included: string[];
14
- excluded: Array<{ id: string; reason: 'stale' | 'not-public' | 'retracted' | 'expired' | 'brand' | 'ambiguous-value' }>;
14
+ excluded: Array<{ id: string; reason: 'stale' | 'not-public' | 'retracted' | 'expired' | 'brand' | 'ambiguous-value' | 'rules-missing' }>;
15
+ unmeasured?: string;
15
16
  }
16
17
 
17
18
  const headings: Record<ClaimSurface, string> = {
@@ -48,7 +49,7 @@ export function representativeNumber(value: string): string | undefined {
48
49
  return matches[0];
49
50
  }
50
51
 
51
- export function renderClaims(ledger: ClaimsLedger, options: { surface: ClaimSurface; audience?: string }): RenderedClaims {
52
+ export function renderClaims(ledger: ClaimsLedger, options: { surface: ClaimSurface; audience?: string; rulesPath?: string }): RenderedClaims {
52
53
  const { surface, audience } = options;
53
54
  if (!(surface in headings)) throw new Error(`invalid claims surface: ${surface}`);
54
55
  const excluded: RenderedClaims['excluded'] = [];
@@ -82,13 +83,21 @@ export function renderClaims(ledger: ClaimsLedger, options: { surface: ClaimSurf
82
83
  candidates.push({ id: row.id, line: `- ${claim} — ${number} <!-- claim:${row.id} measured:${evidence.measured_at} cmd:${commentValue(evidence.command)}${evidence.source ? ` source:${commentValue(evidence.source)}` : ''} --> <!-- src:${commentValue(evidence.source ?? evidence.command)} -->` });
83
84
  }
84
85
 
86
+ const rulesMissing = checkBrand(scopes[surface], [], options.rulesPath).missing;
87
+ if (rulesMissing) {
88
+ excluded.push(...candidates.map(({ id }) => ({ id, reason: 'rules-missing' as const })));
89
+ const result: RenderedClaims = { markdown: `${headings[surface]}\n\n> ⚠ ${BRAND_RULES_UNMEASURED}\n`, included: [], excluded, unmeasured: BRAND_RULES_UNMEASURED };
90
+ debug.log('claims.render', 'rendered', { surface, included: result.included, excluded, unmeasured: result.unmeasured });
91
+ return result;
92
+ }
93
+
85
94
  const temp = mkdtempSync(join(tmpdir(), 'claims-render-'));
86
95
  const file = join(temp, 'claims.md');
87
96
  try {
88
97
  const probe = (items: typeof candidates): Set<string> => {
89
98
  writeFileSync(file, [headings[surface], '', ...items.map(item => item.line)].join('\n').trimEnd() + '\n');
90
- const result = checkBrand(scopes[surface], [file]);
91
- if (result.missing) throw new Error('brand rules missing');
99
+ const result = checkBrand(scopes[surface], [file], options.rulesPath);
100
+ if (result.missing) throw new Error(BRAND_RULES_UNMEASURED);
92
101
  const bad = new Set<string>();
93
102
  for (const finding of result.findings) {
94
103
  const owner = items[finding.line - 3];
@@ -0,0 +1,21 @@
1
+ import type { Command } from 'commander';
2
+ import { writeFileSync } from 'node:fs';
3
+ import { captureAgentEnv } from '../agent-env/profile.js';
4
+
5
+ export function registerAgentEnvCommands(program: Command): void {
6
+ const agentEnv = program.command('agent-env').description('에이전트 CLI 환경의 비밀 없는 프로필');
7
+ agentEnv.command('capture').description('로컬 Claude · Codex · Grok 설정을 읽기 전용으로 캡처')
8
+ .option('--agent <agent>', 'claude|codex|grok')
9
+ .option('--out <file>', '프로필을 저장할 파일')
10
+ .option('--json', 'JSON 출력')
11
+ .action((opts: { agent?: string; out?: string; json?: boolean }) => {
12
+ if (opts.agent && !['claude', 'codex', 'grok'].includes(opts.agent)) {
13
+ throw new Error('--agent must be claude, codex or grok');
14
+ }
15
+ const profile = captureAgentEnv({ agent: opts.agent as 'claude' | 'codex' | 'grok' | undefined });
16
+ const text = JSON.stringify(profile, null, 2) + '\n';
17
+ if (opts.out) writeFileSync(opts.out, text, { encoding: 'utf8', mode: 0o600 });
18
+ if (opts.json || !opts.out) process.stdout.write(text);
19
+ else console.log(`agent-env capture · ${opts.out}`);
20
+ });
21
+ }
@@ -10,6 +10,7 @@ import { effectiveInstanceRoot } from '../instance/resolve.js';
10
10
  import { getUserConfig } from '../user-config.js';
11
11
  import { projectDecisionsToLinear } from '../decisions/decision-linear-projection.js';
12
12
  import { DecisionLedger, importDecisionMarkdown, type DecisionCategory, type DecisionLedgerOptions, type DecisionOption, type DecisionTrack, type DecisionEntry, type Seat, type SeatDecisionRecord } from '../decisions/decision-ledger.js';
13
+ import { formatProactMeter, readProactMeter } from '../decisions/proact-meter.js';
13
14
 
14
15
  const repeat = (value: string, values: string[]) => [...values, value];
15
16
  function date(raw?: string): string | undefined {
@@ -137,6 +138,14 @@ export function registerDecisionsCommands(program: Command, config: DecisionLedg
137
138
  const rows = new DecisionLedger(config).seatReport({ since: date(o.since), seat: o.seat });
138
139
  emit(rows, o.json, rows.length ? rows.map(formatSeatDecisionRow).join('\n') : '자리 결정 0건');
139
140
  }));
141
+ root.command('proact').description('선제성 기준선 — 지난 7일, 먼저 낸 수·채택 수·물어서야 드러난 수')
142
+ .option('--days <n>', 'KST 일수 (기본 7)', '7').option('--json')
143
+ .action((o: { days: string; json?: boolean }) => fail(() => {
144
+ const days = Number(o.days);
145
+ if (!Number.isInteger(days) || days < 1) throw new Error('invalid days');
146
+ const meter = readProactMeter({ ...(config.stateDir ? { stateDir: config.stateDir, instanceRoot: config.stateDir } : {}), ...(config.now ? { now: config.now() } : {}), days });
147
+ emit(meter, o.json, formatProactMeter(meter));
148
+ }));
140
149
  root.command('list').description('결정 목록 (기본 열린 것)')
141
150
  .option('--status <open|decided|all>', '기본 open', 'open').option('--since <date>').option('--version <version>')
142
151
  .option('--decided-by <human|auto>').option('--category <category>').option('--json')
@@ -39,6 +39,7 @@ import { formatDoctorLocalLlm, probeDoctorLocalLlm, type DoctorLocalLlm } from '
39
39
  import type { LlmInventory } from '../llm/local-manager/types.js';
40
40
  import { buildDoctorBundle } from './doctor-bundle.js';
41
41
  import { uploadDoctorBundle } from './doctor-bundle-upload.js';
42
+ import { checkNodeModules, formatNodeModulesCheck, nodeModulesTrees } from './doctor-node-modules.js';
42
43
 
43
44
  function defaultCheckPythonEnv(): { status: 'ok' | 'fixable' | 'manual'; evidence: string; remedy?: string } {
44
45
  const c = checkPythonEnv(false);
@@ -1216,9 +1217,30 @@ export function registerDoctorCommand(program: Command, deps: DoctorCliDeps = {}
1216
1217
  const out = deps.out ?? { log: (value: string) => console.log(value) };
1217
1218
  const err = deps.err ?? { error: (value: string) => console.error(value) };
1218
1219
  const setExitCode = deps.setExitCode ?? ((code: number) => { process.exitCode = code; });
1219
- program.command('doctor')
1220
+ const doctor = program.command('doctor')
1220
1221
  .description('Reports whether credentials resolve and where each resolution comes from')
1222
+ .option('--json', 'structured output');
1223
+ doctor.command('node-modules')
1224
+ .description('Check installed top-level packages against bun.lock (read-only)')
1225
+ .option('--dir <tree>', 'tree to check', process.cwd())
1226
+ .option('--all', 'also check nested package.json trees')
1221
1227
  .option('--json', 'structured output')
1228
+ .action((opts: { dir: string; all?: boolean; json?: boolean }, command: Command) => {
1229
+ const json = opts.json || command.parent?.opts().json === true;
1230
+ let dirs: string[];
1231
+ try { dirs = opts.all ? nodeModulesTrees(opts.dir) : [resolve(opts.dir)]; }
1232
+ catch (error) {
1233
+ const reason = error instanceof Error ? error.message : String(error);
1234
+ if (json) out.log(JSON.stringify({ status: 'unreadable', reason }));
1235
+ else out.log(`unreadable: ${reason}`);
1236
+ setExitCode(1);
1237
+ return;
1238
+ }
1239
+ const checks = dirs.map(checkNodeModules);
1240
+ out.log(json ? JSON.stringify(opts.all ? checks : checks[0], null, 2) : checks.map(formatNodeModulesCheck).join('\n'));
1241
+ if (checks.some((check) => check.status !== 'ok')) setExitCode(1);
1242
+ });
1243
+ doctor
1222
1244
  .option('--credentials', 'show per-credential details in human-readable output')
1223
1245
  .option('--bundle', 'write a redacted diagnostics archive for support')
1224
1246
  .option('--upload', 'send the bundle to diagnostics.uploadUrl after consent (requires --bundle)')
@@ -0,0 +1,147 @@
1
+ import { lstatSync, readFileSync, readdirSync, realpathSync } from 'node:fs';
2
+ import { dirname, join, relative, resolve, sep } from 'node:path';
3
+ import { debug } from '../debug/log.js';
4
+
5
+ export interface NodeModulesCheck {
6
+ dir: string;
7
+ status: 'ok' | 'issues' | 'unreadable';
8
+ missing: string[];
9
+ mismatch: { name: string; expected: string; actual: string }[];
10
+ borrowed: { target: string; tree: string } | null;
11
+ reason?: string;
12
+ }
13
+
14
+ function record(value: unknown): value is Record<string, unknown> {
15
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
16
+ }
17
+
18
+ function loadJson(path: string): unknown {
19
+ return JSON.parse(readFileSync(path, 'utf8')) as unknown;
20
+ }
21
+
22
+ function dependencyNames(pkg: Record<string, unknown>): string[] {
23
+ if (!record(pkg.dependencies ?? {}) || !record(pkg.devDependencies ?? {})) throw new Error('invalid package.json dependencies');
24
+ return [...new Set([...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})])].sort();
25
+ }
26
+
27
+ interface LockContext { lockRoot: string; workspace: Record<string, unknown>; packages: Record<string, unknown>; scope: string | null }
28
+
29
+ /** Own bun.lock first; otherwise the nearest ancestor bun.lock whose workspaces list this folder (root-lock Bun workspaces). */
30
+ function findLock(root: string, packageName: string | null): LockContext {
31
+ let current = root;
32
+ for (;;) {
33
+ let text: string | null = null;
34
+ try { text = readFileSync(join(current, 'bun.lock'), 'utf8'); }
35
+ catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error; }
36
+ if (text !== null) {
37
+ const lock = Bun.JSONC.parse(text) as unknown;
38
+ if (!record(lock) || !record(lock.packages) || !record(lock.workspaces)) throw new Error('invalid bun.lock');
39
+ const key = relative(current, root).split(sep).join('/');
40
+ const workspace = lock.workspaces[key];
41
+ if (record(workspace)) return { lockRoot: current, workspace, packages: lock.packages, scope: key === '' ? null : (packageName ?? key) };
42
+ if (key === '') throw new Error('invalid bun.lock');
43
+ throw new Error(`bun.lock at ${current} does not list workspace ${key}`);
44
+ }
45
+ const parent = dirname(current);
46
+ if (parent === current) throw new Error('no bun.lock in this folder or any parent workspace root');
47
+ current = parent;
48
+ }
49
+ }
50
+
51
+ function lockedVersion(packages: Record<string, unknown>, name: string, scope: string | null = null): string {
52
+ // Bun keys a workspace-specific resolution as «<workspace>/<dep>» when it differs from the hoisted one.
53
+ const scoped = scope ? packages[`${scope}/${name}`] : undefined;
54
+ const entry = Array.isArray(scoped) ? scoped : packages[name];
55
+ if (!Array.isArray(entry) || typeof entry[0] !== 'string' || !entry[0].startsWith(`${name}@`) || entry[0].length <= name.length + 1) {
56
+ throw new Error(`bun.lock has no resolved version for ${name}`);
57
+ }
58
+ return entry[0].slice(name.length + 1);
59
+ }
60
+
61
+ export function checkNodeModules(dir: string): NodeModulesCheck {
62
+ const root = resolve(dir);
63
+ const result: NodeModulesCheck = { dir: root, status: 'ok', missing: [], mismatch: [], borrowed: null };
64
+ try {
65
+ const pkg = loadJson(join(root, 'package.json'));
66
+ if (!record(pkg)) throw new Error('invalid package.json');
67
+ const names = dependencyNames(pkg);
68
+ const lock = findLock(root, typeof pkg.name === 'string' ? pkg.name : null);
69
+ const declared = dependencyNames(lock.workspace);
70
+ for (const name of names) {
71
+ if (!declared.includes(name)) throw new Error(`bun.lock does not declare ${name}`);
72
+ }
73
+ const versions = new Map(names.map((name) => [name, lockedVersion(lock.packages, name, lock.scope)]));
74
+ // A workspace without its own node_modules resolves hoisted packages from the lock root.
75
+ const own = join(root, 'node_modules');
76
+ const modules = lock.lockRoot !== root && !lstatSync(own, { throwIfNoEntry: false }) ? join(lock.lockRoot, 'node_modules') : own;
77
+ try {
78
+ const entry = lstatSync(modules);
79
+ if (entry.isSymbolicLink()) {
80
+ const target = realpathSync(modules);
81
+ if (!lstatSync(target).isDirectory()) throw new Error('node_modules symlink target is not a directory');
82
+ result.borrowed = { target, tree: dirname(target) };
83
+ } else if (!entry.isDirectory()) {
84
+ throw new Error('node_modules is not a directory');
85
+ }
86
+ } catch (error) {
87
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
88
+ // A dangling symlink has a different cause from an absent node_modules directory.
89
+ if (lstatSync(modules, { throwIfNoEntry: false })?.isSymbolicLink()) throw new Error('node_modules symlink target is missing');
90
+ result.missing.push(...names);
91
+ if (names.length) result.status = 'issues';
92
+ return result;
93
+ }
94
+ for (const name of names) {
95
+ let installed: unknown;
96
+ const candidates = [...new Set([modules, join(lock.lockRoot, 'node_modules')])];
97
+ for (const base of candidates) {
98
+ try {
99
+ installed = loadJson(join(base, name, 'package.json'));
100
+ break;
101
+ } catch (error) {
102
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
103
+ }
104
+ }
105
+ if (installed === undefined) {
106
+ result.missing.push(name);
107
+ continue;
108
+ }
109
+ if (!record(installed) || typeof installed.version !== 'string') throw new Error(`invalid node_modules/${name}/package.json`);
110
+ const expected = versions.get(name)!;
111
+ if (installed.version !== expected) result.mismatch.push({ name, expected, actual: installed.version });
112
+ }
113
+ if (result.missing.length || result.mismatch.length) result.status = 'issues';
114
+ } catch (error) {
115
+ result.status = 'unreadable';
116
+ result.reason = error instanceof Error ? error.message : String(error);
117
+ } finally {
118
+ debug.log('doctor.node-modules', 'checked', { dir: root, missing: result.missing, mismatch: result.mismatch, borrowed: result.borrowed });
119
+ }
120
+ return result;
121
+ }
122
+
123
+ export function nodeModulesTrees(dir: string): string[] {
124
+ const root = resolve(dir);
125
+ const found = [root];
126
+ const skip = new Set(['node_modules', '.git', '.next', 'dist', 'build', 'out', '.elanous-test']);
127
+ function visit(current: string): void {
128
+ for (const entry of readdirSync(current, { withFileTypes: true })) {
129
+ if (!entry.isDirectory() || skip.has(entry.name) || entry.name.startsWith('.')) continue;
130
+ const child = join(current, entry.name);
131
+ const children = readdirSync(child);
132
+ if (children.includes('package.json')) found.push(child);
133
+ visit(child);
134
+ }
135
+ }
136
+ visit(root);
137
+ return found;
138
+ }
139
+
140
+ export function formatNodeModulesCheck(result: NodeModulesCheck): string {
141
+ const lines = [`${result.dir}: ${result.status === 'ok' ? '정상' : result.status === 'unreadable' ? '못 읽음' : '결손'}`];
142
+ if (result.borrowed) lines.push(` 빌린 트리: ${result.borrowed.tree} (node_modules → ${result.borrowed.target})`);
143
+ if (result.missing.length) lines.push(` 빠짐 ${result.missing.length}: ${result.missing.join(', ')}`);
144
+ if (result.mismatch.length) lines.push(` 버전 다름 ${result.mismatch.length}: ${result.mismatch.map(({ name, expected, actual }) => `${name} (${actual} != ${expected})`).join(', ')}`);
145
+ if (result.reason) lines.push(` 이유: ${result.reason}`);
146
+ return lines.join('\n');
147
+ }
@@ -21,6 +21,13 @@ export interface IntakeToTasksCliDeps {
21
21
  llm?: (input: IntakeTaskInput) => Promise<string>;
22
22
  }
23
23
 
24
+ export interface IntakeDigestCliDeps {
25
+ root?: string;
26
+ annotateLens?: (root: string, day: string) => Promise<unknown>;
27
+ buildDigest?: typeof import('../intake-plane/digest.js').buildIntakeDigest;
28
+ sendTelegram?: (text: string) => Promise<boolean>;
29
+ }
30
+
24
31
  export function intakeToTasksMessages(content: string) {
25
32
  return [
26
33
  { role: 'system' as const, content: 'Interpret the supplied goal line or idea note into up to three actionable tasks. Respond only with JSON: {"tasks":[{"type":"implement|research|document|operate","title":"...","description":"...","priority":"low|medium|high","acceptanceCriteria":["..."]}],"questions":["..."]}. Each task needs a verifiable acceptance criterion; put unresolved requests in questions instead. Do not invent facts. Limit each title to 80 characters and description to 4000 characters.' },
@@ -76,6 +83,44 @@ export async function runIntakeToTasksCli(
76
83
  return runIntakeToTasks(deps.root, opts, { llm, post });
77
84
  }
78
85
 
86
+ /** `--telegram` 다이제스트. 렌즈는 digest 를 만들기 전에 돈다. 렌즈 실패는 보고를 막지 않는다. */
87
+ export async function runIntakeDigestCli(
88
+ opts: { day?: string; json?: boolean; telegram?: boolean; vault?: string; note?: string },
89
+ deps: IntakeDigestCliDeps = {},
90
+ ): Promise<{ sent?: boolean; empty?: boolean }> {
91
+ const { buildIntakeDigest, renderDigestMarkdown, renderDigestTelegram } = await import('../intake-plane/digest.js');
92
+ const { effectiveInstanceRoot } = await import('../instance/resolve.js');
93
+ const root = deps.root ?? effectiveInstanceRoot();
94
+ const day = opts.day ?? new Date(Date.now() + 9 * 3600_000).toISOString().slice(0, 10);
95
+ if (opts.telegram) {
96
+ const annotate = deps.annotateLens ?? (async (lensRoot, lensDay) => {
97
+ const { annotateIntakeLens } = await import('../intake-plane/lens.js');
98
+ return annotateIntakeLens(lensRoot, lensDay);
99
+ });
100
+ try { await annotate(root, day); }
101
+ catch (error) { debug.log('intake.lens', 'annotate-failed', { day, reason: error instanceof Error ? error.message : String(error) }); }
102
+ }
103
+ const d = (deps.buildDigest ?? buildIntakeDigest)(root, day);
104
+ if (opts.telegram) {
105
+ if (!d.absorbed.length) { console.log(`텔레그램: ${day} 흡수 0 — 보내지 않음`); return { empty: true }; }
106
+ const text = renderDigestTelegram(d, { ...(opts.vault ? { vaultRoot: opts.vault } : {}), ...(opts.note ? { notePath: opts.note } : {}) });
107
+ const sent = deps.sendTelegram
108
+ ? await deps.sendTelegram(text)
109
+ : await (async () => {
110
+ const { sendTelegramReport } = await import('../telegram-report.js');
111
+ const { getUserConfig } = await import('../user-config.js');
112
+ return sendTelegramReport(getUserConfig(), text, { markdown: true, kind: 'intake' });
113
+ })();
114
+ debug.log('intake.digest', 'telegram', { day, absorbed: d.absorbed.length, goals: d.goals.length, sent });
115
+ console.log(sent ? `텔레그램 보고 채널로 보냈다 (${day} · 흡수 ${d.absorbed.length} · 골 후보 ${d.goals.length})` : '텔레그램 보고 채널 설정이 없다(telegram.reportChannel) — 보내지 않음');
116
+ if (!sent) process.exitCode = 3;
117
+ return { sent };
118
+ }
119
+ if (opts.json) { await writeStdoutFully(JSON.stringify(d, null, 2)); return {}; }
120
+ await writeStdoutFully(renderDigestMarkdown(d));
121
+ return {};
122
+ }
123
+
79
124
  export function resolveIntakeCheckRoot(
80
125
  cwd: string,
81
126
  git: typeof runGitCommand = runGitCommand,
@@ -283,23 +328,7 @@ export function registerIntakeCommands(program: Command): void {
283
328
  .option('--vault <root>', '옵시디언 볼트 뿌리 — 텔레그램 판에 노트 열기 주소를 싣는다')
284
329
  .option('--note <path>', '열기 주소가 가리킬 노트(그날 트렌드 노트)')
285
330
  .action(async (opts: { day?: string; json?: boolean; telegram?: boolean; vault?: string; note?: string }) => {
286
- const { buildIntakeDigest, renderDigestMarkdown, renderDigestTelegram } = await import('../intake-plane/digest.js');
287
- const { effectiveInstanceRoot } = await import('../instance/resolve.js');
288
- const day = opts.day ?? new Date(Date.now() + 9 * 3600_000).toISOString().slice(0, 10);
289
- const d = buildIntakeDigest(effectiveInstanceRoot(), day);
290
- if (opts.telegram) {
291
- if (!d.absorbed.length) { console.log(`텔레그램: ${day} 흡수 0 — 보내지 않음`); return; }
292
- const { sendTelegramReport } = await import('../telegram-report.js');
293
- const { getUserConfig } = await import('../user-config.js');
294
- const text = renderDigestTelegram(d, { ...(opts.vault ? { vaultRoot: opts.vault } : {}), ...(opts.note ? { notePath: opts.note } : {}) });
295
- const sent = await sendTelegramReport(getUserConfig(), text, { markdown: true, kind: 'intake' });
296
- debug.log('intake.digest', 'telegram', { day, absorbed: d.absorbed.length, goals: d.goals.length, sent });
297
- console.log(sent ? `텔레그램 보고 채널로 보냈다 (${day} · 흡수 ${d.absorbed.length} · 골 후보 ${d.goals.length})` : '텔레그램 보고 채널 설정이 없다(telegram.reportChannel) — 보내지 않음');
298
- if (!sent) process.exitCode = 3;
299
- return;
300
- }
301
- if (opts.json) { await writeStdoutFully(JSON.stringify(d, null, 2)); return; }
302
- await writeStdoutFully(renderDigestMarkdown(d));
331
+ await runIntakeDigestCli(opts);
303
332
  });
304
333
 
305
334
  intakeCmd
@@ -94,6 +94,7 @@ export function registerPodCommands(program: Command, deps: PodCliDeps = {}): vo
94
94
  const mem = (v: number | null) => v === null ? '?' : `${(v / 1024 ** 3).toFixed(1)}Gi`;
95
95
  io.log(`${m.context} | ${m.capacity} | ${m.running ?? '?'} | ${m.pending ?? '?'} | ${mem(m.memoryLimitBytes)}/${mem(m.allocatableMemoryBytes)} | ${m.allocatableCpuMillicores === null ? '?' : m.allocatableCpuMillicores / 1000}${m.reason ? ` (${m.reason})` : ''}`);
96
96
  }
97
+ io.log(`실측 점유: ${measure.members.map((m) => `${m.context} ${m.running === null || m.pending === null ? '못 쟀다' : m.running + m.pending}/${m.capacity}`).join(' · ') || '?'}`);
97
98
  io.log(`권장 지금 ${decision.recommended ?? '?'} 개 더 (limitedBy=${decision.limitedBy ?? 'unknown'})${decision.reason ? ` · ${decision.reason}` : ''}`);
98
99
  io.log(`예약(저작 중) ${host.reserved} · 대기 Job ${waitingJobs ?? '?'} · 실행 ${decision.running ?? '?'}`);
99
100
  io.log(`임대 없는 실행 ${decision.unleasedRunning ?? '?'}`);
@@ -155,6 +156,7 @@ export function registerPodCommands(program: Command, deps: PodCliDeps = {}): vo
155
156
  });
156
157
  if (result.image) io.error(`[pod] image ${result.image} · job ${result.job}`);
157
158
  io.log(result.artifactsDir);
159
+ if (result.artifacts?.error) io.error(`산출 회수 못 함: ${result.artifacts.error}`);
158
160
  io.exit(result.exitCode);
159
161
  } catch (err) {
160
162
  io.error(err instanceof Error ? err.message : String(err));