elanous 0.2.6 → 0.2.8

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 (517) hide show
  1. package/.env.example +10 -7
  2. package/README.md +4 -0
  3. package/apps/pwa/out/404/index.html +1 -1
  4. package/apps/pwa/out/404.html +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/{3995-bee512a7063036ce.js → 2375-cb6debea56cce4fb.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +25 -0
  16. package/apps/pwa/out/_next/static/chunks/{8d0d9faf-97887ff99713b84e.js → 2ea96958-e8c7e112de50b067.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/{5329.8b7e4bca684b6eec.js → 3403.4bc97b4066945a94.js} +2 -2
  18. package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/{7633.0654fe6ea259c1ee.js → 4323.c5dfb6f503304775.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/4836-3084c58c1725a40b.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{2984-977cfa339fcd65a5.js → 5080-1613752891fcadb4.js} +3 -3
  24. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{763-7d0e1913b5074e98.js → 5791-bb82743fa6299562.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/{4240-7153881b0b377306.js → 6062-73e68343a9814f6a.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/7032-f4ef698518fba61c.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/{91ff6dda-3b33d42d20657429.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8894-7317998f6cb97d8c.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/9454-a3e0953307216c58.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/approvals/page-9aa47b0bc6d570ff.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f4d2ecbbae6088d6.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/board/page-2053e49930c82748.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/botlab/page-d4b6df905f185055.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/bots/page-a70ba27c33003dd0.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/chat/page-186fc6952f027acc.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/consult/page-18166f885d831d3e.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/control/page-1c9d6cfe68b121c2.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/design-check/page-cbbe03c0da7b081a.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/editor/page-c108ff1fdb665c97.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/exec/page-147a328753d983e2.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/field/page-799395a9df7d95aa.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/intake/page-6a8158b71d1fe9d7.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/layout-9567560270857531.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/live/page-e7ed3d010153d818.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/market/page-c0c0d19d51342d3e.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2e002ad8faa43291.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/morning/page-b672e02e4bec8deb.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/not-found-1ba4a5d857276334.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/observatory/page-9db2226f38c542fb.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/page-70ee65f5f7a151dc.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/reflection/page-0bb8e43203efb23b.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-092e1bcf27d751cc.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/sessions/page-eb52580c4b75b905.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7464c1987dd76e6d.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/settings/page-e573437b0a1c728f.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-294300625e49e4d3.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/setup/page-085847b088675978.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/share/page-9fe9d38c36999eb4.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/showroom/page-d8ab4d1489223459.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/tasks/page-6141f67f531d8b6a.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/term/page-295a02b1026b8255.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/trace/page-1eb9a0e1e054209d.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/vault/page-82efa177a30f3741.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d0be51cec4f62606.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/workflows/page-2e367075ffdbc062.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/workspace/page-647ed2b66c1d49f1.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ba058c56e38f2792.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/main-app-6870f30ea99f8346.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/{webpack-37b8bf78a3c6eec5.js → webpack-e5bea36f183b25e6.js} +1 -1
  95. package/apps/pwa/out/_next/static/css/5ef72a618a8994f3.css +1 -0
  96. package/apps/pwa/out/_next/static/lywc8m_HDzH9MKSDQhyEj/_buildManifest.js +1 -0
  97. package/apps/pwa/out/approvals/index.html +1 -1
  98. package/apps/pwa/out/approvals/index.txt +22 -22
  99. package/apps/pwa/out/autopilot/index.html +1 -1
  100. package/apps/pwa/out/autopilot/index.txt +22 -22
  101. package/apps/pwa/out/board/index.html +1 -1
  102. package/apps/pwa/out/board/index.txt +22 -22
  103. package/apps/pwa/out/botlab/index.html +1 -1
  104. package/apps/pwa/out/botlab/index.txt +21 -21
  105. package/apps/pwa/out/bots/index.html +1 -1
  106. package/apps/pwa/out/bots/index.txt +21 -21
  107. package/apps/pwa/out/chat/index.html +1 -1
  108. package/apps/pwa/out/chat/index.txt +21 -21
  109. package/apps/pwa/out/consult/index.html +1 -0
  110. package/apps/pwa/out/consult/index.txt +32 -0
  111. package/apps/pwa/out/control/index.html +1 -1
  112. package/apps/pwa/out/control/index.txt +22 -22
  113. package/apps/pwa/out/design-check/index.html +1 -1
  114. package/apps/pwa/out/design-check/index.txt +22 -22
  115. package/apps/pwa/out/editor/index.html +1 -1
  116. package/apps/pwa/out/editor/index.txt +22 -22
  117. package/apps/pwa/out/exec/index.html +1 -0
  118. package/apps/pwa/out/exec/index.txt +32 -0
  119. package/apps/pwa/out/field/index.html +1 -0
  120. package/apps/pwa/out/field/index.txt +32 -0
  121. package/apps/pwa/out/healthz +1 -1
  122. package/apps/pwa/out/index.html +1 -1
  123. package/apps/pwa/out/index.txt +21 -21
  124. package/apps/pwa/out/intake/index.html +1 -1
  125. package/apps/pwa/out/intake/index.txt +22 -22
  126. package/apps/pwa/out/live/index.html +1 -1
  127. package/apps/pwa/out/live/index.txt +22 -22
  128. package/apps/pwa/out/market/index.html +1 -1
  129. package/apps/pwa/out/market/index.txt +21 -21
  130. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  131. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  132. package/apps/pwa/out/morning/index.html +1 -1
  133. package/apps/pwa/out/morning/index.txt +22 -22
  134. package/apps/pwa/out/observatory/index.html +1 -1
  135. package/apps/pwa/out/observatory/index.txt +22 -22
  136. package/apps/pwa/out/reflection/index.html +1 -1
  137. package/apps/pwa/out/reflection/index.txt +22 -22
  138. package/apps/pwa/out/scheduler/index.html +1 -1
  139. package/apps/pwa/out/scheduler/index.txt +22 -22
  140. package/apps/pwa/out/sessions/index.html +1 -1
  141. package/apps/pwa/out/sessions/index.txt +21 -21
  142. package/apps/pwa/out/settings/devices/index.html +1 -1
  143. package/apps/pwa/out/settings/devices/index.txt +22 -22
  144. package/apps/pwa/out/settings/index.html +1 -1
  145. package/apps/pwa/out/settings/index.txt +22 -22
  146. package/apps/pwa/out/setup/done/index.html +1 -1
  147. package/apps/pwa/out/setup/done/index.txt +22 -22
  148. package/apps/pwa/out/setup/index.html +1 -1
  149. package/apps/pwa/out/setup/index.txt +22 -22
  150. package/apps/pwa/out/share/index.html +1 -1
  151. package/apps/pwa/out/share/index.txt +22 -22
  152. package/apps/pwa/out/showroom/index.html +1 -1
  153. package/apps/pwa/out/showroom/index.txt +21 -21
  154. package/apps/pwa/out/tasks/index.html +1 -1
  155. package/apps/pwa/out/tasks/index.txt +22 -22
  156. package/apps/pwa/out/term/index.html +1 -1
  157. package/apps/pwa/out/term/index.txt +22 -22
  158. package/apps/pwa/out/trace/index.html +1 -1
  159. package/apps/pwa/out/trace/index.txt +22 -22
  160. package/apps/pwa/out/vault/index.html +1 -1
  161. package/apps/pwa/out/vault/index.txt +22 -22
  162. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  163. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  164. package/apps/pwa/out/workflows/index.html +1 -1
  165. package/apps/pwa/out/workflows/index.txt +22 -22
  166. package/apps/pwa/out/workspace/index.html +1 -1
  167. package/apps/pwa/out/workspace/index.txt +22 -22
  168. package/apps/pwa/out/worktrees/index.html +1 -1
  169. package/apps/pwa/out/worktrees/index.txt +22 -22
  170. package/bin/elanous.cjs +107 -0
  171. package/graphs/field/field-feed.yaml +24 -0
  172. package/graphs/field/recipes.yaml +12 -0
  173. package/graphs/landing/landing-heal.yaml +3 -1
  174. package/graphs/landing/recipes.yaml +3 -0
  175. package/graphs/release/recipes.yaml +10 -0
  176. package/graphs/release/release-loop.yaml +10 -3
  177. package/graphs/test-diet/recipes.yaml +9 -0
  178. package/graphs/test-diet/test-diet.yaml +22 -0
  179. package/package.json +6 -5
  180. package/persona-presets/_index.yaml +10 -0
  181. package/persona-presets/_schema.json +146 -0
  182. package/persona-presets/dana.yaml +26 -0
  183. package/persona-presets/haru.yaml +27 -0
  184. package/persona-presets/kai.yaml +27 -0
  185. package/persona-presets/leo.yaml +27 -0
  186. package/persona-presets/mira.yaml +30 -0
  187. package/persona-presets/nari.yaml +27 -0
  188. package/persona-presets/sora.yaml +27 -0
  189. package/persona-presets/yuna.yaml +27 -0
  190. package/plugins/card-followup/README.md +13 -4
  191. package/plugins/card-followup/graphs/card-followup.yaml +3 -1
  192. package/plugins/card-followup/graphs/recipes.yaml +5 -1
  193. package/plugins/card-followup/graphs/run-step.ts +122 -18
  194. package/plugins/card-followup/graphs/sales-strategy.ts +173 -0
  195. package/plugins/card-followup/plugin.json +1 -1
  196. package/plugins/doc-draft/README.md +13 -0
  197. package/plugins/doc-draft/examples/brief-sample.json +8 -0
  198. package/plugins/doc-draft/graphs/doc-draft.yaml +14 -0
  199. package/plugins/doc-draft/graphs/recipes.yaml +9 -0
  200. package/plugins/doc-draft/graphs/run-step.ts +113 -0
  201. package/plugins/doc-draft/plugin.json +24 -0
  202. package/plugins/geo-check/README.md +3 -1
  203. package/plugins/geo-check/graphs/run-step.ts +58 -13
  204. package/plugins/geo-check/plugin.json +1 -1
  205. package/plugins/job-coach/.codex-plugin/plugin.json +1 -1
  206. package/plugins/job-coach/README.md +1 -1
  207. package/plugins/job-coach/graphs/run-step.ts +2 -2
  208. package/plugins/job-coach/plugin.json +1 -1
  209. package/plugins/job-coach/plugin.ts +1 -1
  210. package/scripts/affinity/endcard.sh +3 -2
  211. package/scripts/affinity/reframe.sh +3 -1
  212. package/scripts/ci-public-export-leak-gate.ts +48 -0
  213. package/scripts/codex-quota-alert.ts +122 -258
  214. package/scripts/coord-tracks.json +4 -4
  215. package/scripts/field-feed/deliver-node.ts +57 -0
  216. package/scripts/field-feed/feed-node.ts +67 -0
  217. package/scripts/field-feed/lib.ts +164 -0
  218. package/scripts/field-feed/reel-node.ts +24 -0
  219. package/scripts/field-feed/start.ts +62 -0
  220. package/scripts/fixtures/vflow-sample.ndjson +6 -0
  221. package/scripts/install.sh +109 -21
  222. package/scripts/landing-heal/clean-checkout.ts +114 -0
  223. package/scripts/landing-heal/run-mission.ts +38 -3
  224. package/scripts/lib/pwa-scenarios/scenarios.ts +2 -1
  225. package/scripts/persona-presets/lint.ts +79 -0
  226. package/scripts/public-export.ts +8 -4
  227. package/scripts/release-loop/baseline-from-logs.ts +92 -0
  228. package/scripts/release-loop/checklist-gate-node.ts +2 -2
  229. package/scripts/release-loop/env-known-failures.ts +31 -0
  230. package/scripts/release-loop/gate-node.ts +108 -22
  231. package/scripts/release-loop/known-issues-node.ts +159 -0
  232. package/scripts/release-loop/npm-publish-node.ts +124 -0
  233. package/scripts/release-loop/ops-upgrade-node.ts +95 -0
  234. package/scripts/release-loop/publish-node.ts +23 -1
  235. package/scripts/release-loop/release-readiness.ts +58 -0
  236. package/scripts/release-loop/shard-plan.ts +17 -0
  237. package/scripts/release-loop/unattended-release.ts +93 -0
  238. package/scripts/storyboard/storyboard.ts +167 -0
  239. package/scripts/test-diet/lib.ts +88 -0
  240. package/scripts/test-diet/node.ts +102 -0
  241. package/scripts/ux/measure-first-text.ts +102 -0
  242. package/scripts/vflow-collect.ts +20 -7
  243. package/scripts/vflow-search.ts +73 -0
  244. package/scripts/video-free-line-test-walker.ts +36 -0
  245. package/scripts/video-pipeline.ts +2 -2
  246. package/skills/explainer-video/SKILL.md +12 -9
  247. package/skills/explainer-video/engine/build.mjs +1 -1
  248. package/skills/explainer-video/engine/reel.mjs +138 -31
  249. package/skills/explainer-video/engine/reel.sh +2 -1
  250. package/skills/explainer-video/examples/elanous-0.2.5/script.json +1 -1
  251. package/skills/grill-me/SKILL.md +1 -1
  252. package/skills/omni-digest/docs/diagrams/noncode-terminal-promotion-path-2026-08-13.mmd +1 -1
  253. package/src/a2a/server.ts +2 -0
  254. package/src/acp/auth-methods.ts +30 -0
  255. package/src/acp/elanous-aware-peers.ts +30 -0
  256. package/src/acp/server.ts +96 -20
  257. package/src/agent/monad-agent-turn.ts +25 -6
  258. package/src/agent/shared-app-tools.ts +5 -3
  259. package/src/agent-substrate/review-intent.ts +6 -2
  260. package/src/agent-substrate/shard-boundary-intent.ts +70 -0
  261. package/src/auth/github-app-token.ts +4 -1
  262. package/src/autopilot/track-agent-shadow.ts +151 -0
  263. package/src/autopilot/track-agent.ts +23 -3
  264. package/src/boot/acp-server-help.ts +16 -0
  265. package/src/boot/daemon-prompt-turn.ts +31 -10
  266. package/src/boot/daemon-tools/index.ts +7 -1
  267. package/src/budget/codex-credit-pace.ts +84 -0
  268. package/src/budget/codex-credit-plan.ts +154 -0
  269. package/src/budget/codex-reset-credit-state.ts +2 -0
  270. package/src/channel-import/detect.ts +191 -0
  271. package/src/channel-import/run.ts +111 -0
  272. package/src/chat/chat-cli.ts +4 -1
  273. package/src/chat/short-question.ts +40 -0
  274. package/src/cli/agent-cli.ts +24 -15
  275. package/src/cli/ask-bare.ts +42 -0
  276. package/src/cli/cli-user-error.ts +25 -0
  277. package/src/cli/codex-credit-plan-view.ts +25 -0
  278. package/src/cli/config-test-sync.ts +4 -0
  279. package/src/cli/decisions-cli.ts +13 -3
  280. package/src/cli/dependence-gauge.ts +237 -0
  281. package/src/cli/doctor-bundle.ts +128 -0
  282. package/src/cli/doctor-cli.ts +41 -1
  283. package/src/cli/doctor-local-llm.ts +95 -0
  284. package/src/cli/doctor-modes.ts +48 -0
  285. package/src/cli/import-cli.ts +60 -0
  286. package/src/cli/intake-cli.ts +12 -2
  287. package/src/cli/intake-source-cli.ts +58 -0
  288. package/src/cli/machine-cli.ts +23 -0
  289. package/src/cli/mcp-login.ts +13 -4
  290. package/src/cli/msg-cli.ts +137 -0
  291. package/src/cli/persona-cli.ts +116 -0
  292. package/src/cli/plugin-cli.ts +25 -0
  293. package/src/cli/pr-cli.ts +36 -0
  294. package/src/cli/provider-cli.ts +5 -0
  295. package/src/cli/release-cli.ts +50 -8
  296. package/src/cli/release-run-if-ready.ts +48 -0
  297. package/src/cli/setup-cli.ts +27 -6
  298. package/src/cli/setup-recommend.ts +8 -0
  299. package/src/cli/skills-cli.ts +173 -0
  300. package/src/cli/start-cli.ts +13 -4
  301. package/src/cli/usage-cli.ts +11 -1
  302. package/src/codex/setup.ts +37 -16
  303. package/src/dashboard/index.ts +18 -2
  304. package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -2
  305. package/src/dashboard/slash-runtime/help-from-registry.ts +4 -2
  306. package/src/decisions/decision-cards.ts +291 -0
  307. package/src/decisions/decision-ledger.ts +5 -2
  308. package/src/decisions/discord-decision-cards.ts +133 -0
  309. package/src/decisions/telegram-decision-cards.ts +100 -0
  310. package/src/directives/version-at.ts +77 -0
  311. package/src/discord.ts +73 -6
  312. package/src/domains/core-tools.ts +37 -14
  313. package/src/domains/outbound-alert.ts +94 -12
  314. package/src/domains/repeated-failure.ts +1 -1
  315. package/src/error-report/report.ts +200 -0
  316. package/src/exec-requests/planner.ts +146 -0
  317. package/src/exec-requests/runner.ts +378 -0
  318. package/src/exec-requests/store.ts +78 -0
  319. package/src/field/field-feed-auto.ts +48 -0
  320. package/src/field/field-media.ts +8 -3
  321. package/src/field/field-reel.ts +203 -0
  322. package/src/grok/credential.ts +7 -5
  323. package/src/harness/aside-run-status.ts +33 -0
  324. package/src/harness/browser-verify.ts +48 -17
  325. package/src/harness/harness-cli-command.ts +25 -3
  326. package/src/harness/image-load-state.ts +21 -0
  327. package/src/harness/process-garbage.ts +45 -0
  328. package/src/hooks/error-report.ts +210 -0
  329. package/src/hooks/receiver.ts +41 -1
  330. package/src/import/apply.ts +128 -0
  331. package/src/import/plan.ts +197 -0
  332. package/src/index.ts +168 -21
  333. package/src/input-core/text-entry.ts +13 -5
  334. package/src/instance/leader.ts +13 -1
  335. package/src/intake-plane/collect-github.ts +5 -3
  336. package/src/intake-plane/collect-telegram-saved.ts +15 -3
  337. package/src/intake-plane/discord-seat-work.ts +56 -0
  338. package/src/intake-plane/intake-sources.ts +202 -0
  339. package/src/intake-plane/items.ts +28 -12
  340. package/src/intake-plane/seat-answer.ts +96 -0
  341. package/src/intake-plane/seat-doc-route.ts +221 -0
  342. package/src/intake-plane/submit-intake-work.ts +9 -1
  343. package/src/intake-plane/telegram-seat-work.ts +72 -0
  344. package/src/llm/model-defaults.ts +24 -1
  345. package/src/llm.ts +5 -4
  346. package/src/machines/machine-ledger.ts +127 -0
  347. package/src/market/pack-internal-refs.ts +28 -0
  348. package/src/market/publish.ts +31 -4
  349. package/src/maturity/feature-maturity.ts +65 -0
  350. package/src/model-tier/turn-tier.ts +24 -0
  351. package/src/msg/msg-store.ts +149 -0
  352. package/src/native-tool-catalog.ts +2 -1
  353. package/src/nexus/api/chat-fast-path-config.ts +28 -0
  354. package/src/nexus/api/consult-requests.ts +128 -0
  355. package/src/nexus/api/discord-trigger-bot.ts +3 -0
  356. package/src/nexus/api/error-reports.ts +108 -0
  357. package/src/nexus/api/exec-requests.ts +51 -0
  358. package/src/nexus/api/field-uploads.ts +40 -3
  359. package/src/nexus/api/graph-approvals-feed.ts +140 -0
  360. package/src/nexus/api/graph-approvals.ts +71 -4
  361. package/src/nexus/api/http-server.ts +82 -8
  362. package/src/nexus/api/outbound-report.ts +7 -0
  363. package/src/nexus/api/running-runs-background.ts +68 -0
  364. package/src/nexus/api/running-runs-child.ts +6 -0
  365. package/src/nexus/api/seat-requests.ts +254 -0
  366. package/src/nexus/api/share-captures.ts +91 -0
  367. package/src/nexus/api/skill-problems.ts +56 -0
  368. package/src/nexus/api/terminals.ts +24 -8
  369. package/src/nexus/api/trace-run-scan.ts +37 -0
  370. package/src/nexus/api/trace.ts +19 -3
  371. package/src/nexus/index.ts +28 -1
  372. package/src/nexus/setup-status.ts +11 -7
  373. package/src/notify/llm-budget-alert.ts +16 -10
  374. package/src/oauth/browser-availability.ts +14 -0
  375. package/src/oauth/codex-account-rotation.ts +26 -4
  376. package/src/oauth/codex-account-store.ts +20 -2
  377. package/src/oauth/codex-browser-login.ts +173 -0
  378. package/src/oauth/codex-quota-policy.ts +16 -0
  379. package/src/oauth/codex.ts +4 -1
  380. package/src/oauth/fallback-chain.ts +5 -5
  381. package/src/oauth/store.ts +42 -5
  382. package/src/onboarding/continue-here.ts +34 -0
  383. package/src/onboarding/entry-hints.ts +8 -0
  384. package/src/onboarding/full-screen-io.ts +35 -3
  385. package/src/onboarding/help/skills.md +1 -1
  386. package/src/onboarding/non-interactive.ts +1 -0
  387. package/src/onboarding/subscription-hint.ts +27 -0
  388. package/src/onboarding/web-first.ts +90 -0
  389. package/src/onboarding.ts +287 -26
  390. package/src/persona/presets.ts +105 -0
  391. package/src/plugins/maker/node-install.ts +92 -0
  392. package/src/plugins/maker/node-maker.ts +139 -0
  393. package/src/plugins/maker/plugin-maker.ts +19 -7
  394. package/src/policy/llm-policy.ts +268 -0
  395. package/src/prompt-library/session-guidance.ts +7 -1
  396. package/src/prompt-library/universal-preamble.ts +2 -2
  397. package/src/relay/relay-cli.ts +20 -0
  398. package/src/relay/relay-server.ts +220 -0
  399. package/src/release-loop/checklist.ts +37 -13
  400. package/src/self-dev/decompose.ts +1 -0
  401. package/src/self-dev/dev-pipeline.ts +6 -3
  402. package/src/self-dev/orchestrate.ts +18 -11
  403. package/src/self-dev/run-record-mirror.ts +44 -0
  404. package/src/self-dev/run-store.ts +12 -3
  405. package/src/self-implement/harness-policy-gates.ts +4 -1
  406. package/src/self-implement/report-origin.ts +12 -1
  407. package/src/session/chat.ts +17 -5
  408. package/src/shell-env-bootstrap.ts +44 -7
  409. package/src/skills/connect-claude-plugin.ts +142 -0
  410. package/src/skills/connect.ts +70 -0
  411. package/src/skills/index.ts +29 -1
  412. package/src/skills/repair.ts +124 -0
  413. package/src/skills/runner.ts +11 -2
  414. package/src/skills/shared-agent-skill-roots.ts +25 -0
  415. package/src/skills/trigger-extract.ts +1 -1
  416. package/src/steward/failure-streak.ts +78 -0
  417. package/src/steward/steward-cards.ts +24 -0
  418. package/src/steward/triage-budget.ts +127 -0
  419. package/src/steward/triage-plan.ts +75 -0
  420. package/src/steward/triage.ts +244 -42
  421. package/src/task-cards/card-store.ts +1 -1
  422. package/src/task-orchestrator/surfaces/pod-command-job.ts +16 -8
  423. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
  424. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +68 -0
  425. package/src/task-orchestrator/surfaces/self-implement-pod.ts +78 -4
  426. package/src/telegram-commands.ts +13 -0
  427. package/src/telegram.ts +72 -2
  428. package/src/tool-runtime/elanous-skills-list-runtime.ts +3 -3
  429. package/src/tool-runtime/self-cognition-runtimes.ts +160 -2
  430. package/src/user-config.ts +189 -15
  431. package/src/version/packed-revision.json +1 -1
  432. package/src/video-pipeline/recipes/character.ts +42 -5
  433. package/src/video-pipeline/recipes/ffmpeg.ts +1 -1
  434. package/src/video-pipeline/recipes/free-line.ts +16 -3
  435. package/src/video-pipeline/recipes/remote.ts +7 -1
  436. package/src/video-pipeline/vflow-reference.ts +109 -0
  437. package/apps/pwa/out/_next/static/chunks/1169-696263ac7af8c4d4.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/1883.c91a2648af4842fd.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/1947-9d64872eeb9cfdec.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/2808-dc06482994b40f70.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/3165-95515ca5f299a120.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/3190-e9282753e7ebde85.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/4259-99614149577e77cb.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/4283-329eabeaf233e6cd.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/4328-cb5058b1acc2de5e.js +0 -25
  446. package/apps/pwa/out/_next/static/chunks/4801-ecee6ef62b349b18.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/5110-8058791dd25a2e14.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/5330-c41e9670f594c38f.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/5423-e95fd125d1794349.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/5478-b5eae07dc79ea619.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/5787-96ee6642b9a661e5.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/6448-79d72368589f98bc.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/6522-9866c002b5531836.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/6729-b4a558b7aa891e0c.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/6969-cb03f9410b7d2e03.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/7007-3b9861091bb8dd21.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/7930-8dc7e20fc6fc19ca.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/8071-187edbc22a86741f.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/8198-d4101ad70cb9a579.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/8250.4a2c5553a07cdf47.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/8293-123b91e7b0330dc0.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/8296-d6d5d87dfa911d41.js +0 -1
  463. package/apps/pwa/out/_next/static/chunks/8305-603963c33ee8eec3.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/8612-4a0066f20951b8fd.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/9181-cf4e1b61c4783f92.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/9262-26c2ecbd5a67e4a2.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/9328-45abdebce4fbecb7.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/9355-dbc67205adf8745f.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/9763.255b90dd69875689.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/9933-66d31ec25c0231af.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-bd06fb55f4da0381.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4e586142da455a25.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-889ad5cddcf45b0d.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/app/board/page-47ba401d56572c27.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/app/botlab/page-0b54f9c83458e038.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/app/bots/page-4fcc296ef6c8ed90.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/app/chat/page-fe9fda494a87f4e5.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/app/control/page-f9642e25ce129590.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e1063e8ba5f3e2e7.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/app/editor/page-9d0df30027d8aa9d.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/app/healthz/route-bd06fb55f4da0381.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/app/intake/page-d97adf2c9455b8d0.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/app/layout-236b80d202110362.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/app/live/page-21a30f35f1a276ad.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/app/market/page-b1a5be357c4fa25d.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1705a60303f7f573.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/app/morning/page-76b337c11ad0e237.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/app/not-found-7c0f85fbb56f64c6.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/app/observatory/page-65ca16b20617b44d.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/app/page-e7f9b8fc29411c1d.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/app/reflection/page-92ea6213d1aeec04.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a913a005aacb5476.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/app/sessions/page-2d78804a40dfdef6.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-08a99148e780ac00.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/app/settings/page-deb674e7ed36f815.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-49f8de0534d192d7.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/app/setup/layout-bd06fb55f4da0381.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/app/setup/page-ba162a28d2176e18.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/app/share/page-1ff1a37b550ab41a.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/app/showroom/page-ccaa698f56c0fbdb.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/app/tasks/page-53b944082de1d7db.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/app/term/page-9b70adf1552789bf.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/app/trace/page-30d482aa63241fe8.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/app/vault/page-aecf786f54651d8a.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6ab9b62c7b6e08f2.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/app/workflows/page-08949735455e0c9a.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/app/workspace/page-a1479005234a46c2.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3890e90bd232732.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/beb5cf10-9adcc10b83930090.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/framework-d68d41c88e05c801.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/main-app-11c63971b7a9593b.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/main-d09452353bd0d9cc.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/pages/_app-184d727cebeca767.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/pages/_error-21bbbbac99b720b1.js +0 -1
  515. package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +0 -1
  516. package/apps/pwa/out/_next/static/qMI0jnlZaBjjm4aIDYNGM/_buildManifest.js +0 -1
  517. /package/apps/pwa/out/_next/static/{qMI0jnlZaBjjm4aIDYNGM → lywc8m_HDzH9MKSDQhyEj}/_ssgManifest.js +0 -0
@@ -0,0 +1,128 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { closeSync, existsSync, lstatSync, mkdtempSync, mkdirSync, openSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
3
+ import { homedir, platform, arch } from 'node:os';
4
+ import { join, resolve, sep } from 'node:path';
5
+ import { getElanousConfigDir } from '../elanous-config-dir.js';
6
+ import { resolveCurrentInstance } from '../instance/current.js';
7
+ import { LogStore, logsDbPath, type LogStoreRow } from '../mss/logging/log-store.js';
8
+ import { cliVersion, codeRevision, packageVersion } from '../version/code-revision.js';
9
+ import { runDoctor, defaultReadInstallPrefix, type DoctorOptions, type DoctorReport } from './doctor-cli.js';
10
+
11
+ const REDACTED = '<redacted>';
12
+
13
+ /** Recursively redact named secrets and secret-shaped text before serialization. */
14
+ export function redactForBundle(value: unknown, home: string = homedir(), redactNamedFields: boolean = true): unknown {
15
+ if (typeof value === 'string') {
16
+ let text = value;
17
+ if (home && home !== sep) {
18
+ // Match the home as a path component, including JSON-escaped slashes.
19
+ const escaped = home.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
20
+ text = text.replace(new RegExp(`${escaped}(?=[/\\\\]|$)`, 'g'), '~');
21
+ }
22
+ // By kind, not by the last example found: headers that carry credentials, credentials inside URLs,
23
+ // known token shapes, and any `name=value` / `"name": "value"` whose name says it is a credential.
24
+ return text
25
+ .replace(/\b((?:Proxy-)?Authorization\s*:\s*)[^\r\n"']+/gi, `$1${REDACTED}`)
26
+ .replace(/\b((?:Set-)?Cookie\s*:\s*)[^\r\n"']+/gi, `$1${REDACTED}`)
27
+ .replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@"'<>]+@/gi, `$1${REDACTED}@`)
28
+ .replace(/(["']?(?:[\w-]*(?:token|key|secret|password|passwd|pwd|cookie|session|credential|auth))["']?\s*[:=]\s*["']?)[^\s"',}&]+/gi, `$1${REDACTED}`)
29
+ .replace(/\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+/=-]{8,}/g, `$1 ${REDACTED}`)
30
+ .replace(/\b(?:sk-[A-Za-z0-9_-]+|gh[pousr]_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+|glpat-[A-Za-z0-9_-]+|xox[a-z]-[A-Za-z0-9-]+|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35})\b/g, REDACTED)
31
+ .replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, REDACTED);
32
+ }
33
+ if (Array.isArray(value)) return value.map((entry) => redactForBundle(entry, home, redactNamedFields));
34
+ if (value && typeof value === 'object') {
35
+ const result: Record<string, unknown> = Object.create(null);
36
+ for (const [key, entry] of Object.entries(value)) {
37
+ const safeKey = redactForBundle(key, home) as string;
38
+ result[safeKey] = redactNamedFields && /(?:token|key|secret|password|passwd|pwd|authorization|cookie|session|credential|auth)/i.test(key)
39
+ ? REDACTED : redactForBundle(entry, home, redactNamedFields);
40
+ }
41
+ return result;
42
+ }
43
+ return value;
44
+ }
45
+
46
+ export interface DoctorBundleOptions {
47
+ outDir: string;
48
+ now?: Date;
49
+ /** Read-only doctor seams for isolated tests. */
50
+ doctorOptions?: DoctorOptions;
51
+ home?: string;
52
+ configDir?: string;
53
+ logDbPath?: string;
54
+ doctorReport?: DoctorReport;
55
+ }
56
+
57
+ export interface DoctorBundleResult { path: string; files: string[]; bytes: number }
58
+
59
+ /** Bundle only generated, redacted entries. Never put an unprocessed source file into tar. */
60
+ export function buildDoctorBundle({ outDir, now = new Date(), doctorOptions = {}, home = homedir(), configDir = getElanousConfigDir(), logDbPath = logsDbPath(), doctorReport }: DoctorBundleOptions): DoctorBundleResult {
61
+ const instant = now.toISOString();
62
+ const stamp = instant.replace(/[-:]/g, '').slice(0, 15).replace('T', '-');
63
+ mkdirSync(outDir, { recursive: true, mode: 0o700 });
64
+ const destination = resolve(outDir, `elanous-diagnostics-${stamp}.tar.gz`);
65
+ if (existsSync(destination)) throw new Error('diagnostics archive already exists');
66
+ const work = mkdtempSync(join(outDir, '.elanous-diagnostics-'));
67
+ const files: string[] = [];
68
+ const put = (name: string, data: unknown): void => {
69
+ writeFileSync(join(work, name), JSON.stringify(redactForBundle(data, home, name === 'config.redacted.json' || name === 'onboarding.json'), null, 2) + '\n', { mode: 0o600 });
70
+ files.push(name);
71
+ };
72
+ try {
73
+ const report = doctorReport ?? runDoctor(doctorOptions);
74
+ const universe = resolveCurrentInstance({ cwd: () => process.cwd() });
75
+ const packageRoot = resolve(import.meta.dir, '../..');
76
+ const installPrefix = doctorOptions.readInstallPrefix?.() ?? defaultReadInstallPrefix(packageRoot, existsSync);
77
+ let logs: LogStoreRow[] = [];
78
+ let logStatus: 'ok' | 'no-store' = 'no-store';
79
+ if (existsSync(logDbPath)) {
80
+ try {
81
+ const store = LogStore.openReadOnly(logDbPath);
82
+ try {
83
+ logs = store.query({ sinceMs: now.getTime() - 86_400_000, untilMs: now.getTime(), limit: 500 });
84
+ logStatus = 'ok';
85
+ } finally { store.close(); }
86
+ } catch { /* Unreadable store: export no unverified log bytes. */ }
87
+ }
88
+ put('summary.json', {
89
+ version: packageVersion(), sha: codeRevision() ?? 'unknown', cliVersion: cliVersion(),
90
+ os: platform(), arch: arch(), node: process.version, bun: typeof Bun === 'undefined' ? null : Bun.version,
91
+ installPath: installPrefix ?? packageRoot,
92
+ universe: { kind: universe.kind, root: universe.root }, createdAt: instant, logs: logStatus,
93
+ });
94
+ put('doctor.json', report);
95
+ let config: unknown = {};
96
+ const configPath = doctorOptions.configPath ?? join(configDir, 'config.json');
97
+ try {
98
+ if (lstatSync(configPath).isFile()) config = JSON.parse(readFileSync(configPath, 'utf8'));
99
+ } catch { config = { status: 'unavailable' }; }
100
+ put('config.redacted.json', config);
101
+ put('credentials.json', report.credentials.map(({ name, resolved, source }) => ({ name, present: resolved, source })));
102
+ writeFileSync(join(work, 'logs-recent.jsonl'), logs.map((row) => {
103
+ const entry = row as { data: string | null };
104
+ let data = entry.data;
105
+ if (data) {
106
+ try { data = JSON.stringify(redactForBundle(JSON.parse(data), home)); }
107
+ catch { /* Non-JSON log bodies are redacted as plain text below. */ }
108
+ }
109
+ return JSON.stringify(redactForBundle({ ...row, data }, home, false));
110
+ }).join('\n') + (logs.length ? '\n' : ''), { mode: 0o600 });
111
+ files.push('logs-recent.jsonl');
112
+ const onboardingPath = join(configDir, 'onboarding.json');
113
+ try {
114
+ if (lstatSync(onboardingPath).isFile()) put('onboarding.json', JSON.parse(readFileSync(onboardingPath, 'utf8')));
115
+ } catch { /* An absent or unreadable optional record is not exported. */ }
116
+ const archiveFd = openSync(destination, 'wx', 0o600);
117
+ let tarSucceeded = false;
118
+ try {
119
+ const tar = spawnSync('tar', ['-czf', '-', '-C', work, ...files], { stdio: ['ignore', archiveFd, 'pipe'], timeout: 30_000 });
120
+ tarSucceeded = !tar.error && tar.status === 0;
121
+ } finally {
122
+ closeSync(archiveFd);
123
+ if (!tarSucceeded) rmSync(destination, { force: true });
124
+ }
125
+ if (!tarSucceeded) throw new Error('could not create diagnostics archive');
126
+ return { path: destination, files, bytes: statSync(destination).size };
127
+ } finally { rmSync(work, { recursive: true, force: true }); }
128
+ }
@@ -34,6 +34,10 @@ import { ADVISABLE_DOCTOR_FIX_IDS, adviseDoctorFixes, type DoctorAdvice } from '
34
34
  import { gitInstallPlan } from './git-install-plan.js';
35
35
  import { detectDistroFamily, parseOsRelease } from './doctor-distro.js';
36
36
  import { checkPythonEnv } from './python-cli.js';
37
+ import { describeModes, formatModeLine, type DoctorMode } from './doctor-modes.js';
38
+ import { formatDoctorLocalLlm, probeDoctorLocalLlm, type DoctorLocalLlm } from './doctor-local-llm.js';
39
+ import type { LlmInventory } from '../llm/local-manager/types.js';
40
+ import { buildDoctorBundle } from './doctor-bundle.js';
37
41
 
38
42
  function defaultCheckPythonEnv(): { status: 'ok' | 'fixable' | 'manual'; evidence: string; remedy?: string } {
39
43
  const c = checkPythonEnv(false);
@@ -99,6 +103,10 @@ export interface DoctorReport {
99
103
  externalCommands: DoctorExternalCommand[];
100
104
  requiredMissing?: string[];
101
105
  capabilitySummary?: DoctorCapabilitySummary;
106
+ /** Read-only configuration and chat-use status. Present on a successful report. */
107
+ modes?: DoctorMode[];
108
+ /** Local LLM inventory; CLI probes separately so synchronous credential reports stay read-only and unchanged. */
109
+ localLlm?: DoctorLocalLlm;
102
110
  /** F1 readiness. Present on a successful report. Absent when the report itself failed. */
103
111
  readiness?: ReadinessReport;
104
112
  reason?: string;
@@ -183,6 +191,8 @@ export interface DoctorCliDeps extends DoctorOptions, Pick<DoctorFixDeps, 'home'
183
191
  resolveAdviceProvider?: (config: UserConfig) => { provider: string; model: string; auth: string };
184
192
  confirmAdvice?: (message: string) => Promise<boolean>;
185
193
  adviceInteractive?: boolean;
194
+ /** Local-manager inventory seam; default calls getInventory(). */
195
+ localLlmInventory?: () => Promise<LlmInventory>;
186
196
  }
187
197
 
188
198
  const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), '../..');
@@ -1044,6 +1054,7 @@ export function runDoctor(options: DoctorOptions = {}): DoctorReport {
1044
1054
  externalCommands: externalCommands.commands,
1045
1055
  requiredMissing,
1046
1056
  capabilitySummary: summarizeDoctorCapabilities(credentials),
1057
+ modes: describeModes(resolutionOptions.userConfig),
1047
1058
  readiness: evaluateReadiness(readiness),
1048
1059
  retiredConfigKeys: findRetiredConfigKeysInFile(options.configPath, readFile),
1049
1060
  ...(resourceMetadata.available ? {} : { catalogMetadataUnavailable: true }),
@@ -1136,6 +1147,8 @@ export function formatDoctorReport(report: DoctorReport, options: { credentials?
1136
1147
  }),
1137
1148
  ...(report.externalCommandsCatalogUnavailable ? [`External commands catalog unavailable: ${report.externalCommandsCatalogReason ?? 'catalog/external-commands.yaml is unavailable.'}`] : []),
1138
1149
  ...formatCapabilitySummary(capabilitySummary),
1150
+ ...(report.modes ? ['모드:', ...report.modes.map((row) => ` ${formatModeLine(row)}`)] : []),
1151
+ ...(report.localLlm ? formatDoctorLocalLlm(report.localLlm) : []),
1139
1152
  ].join('\n');
1140
1153
  }
1141
1154
 
@@ -1203,12 +1216,37 @@ export function registerDoctorCommand(program: Command, deps: DoctorCliDeps = {}
1203
1216
  .description('Reports whether credentials resolve and where each resolution comes from')
1204
1217
  .option('--json', 'structured output')
1205
1218
  .option('--credentials', 'show per-credential details in human-readable output')
1219
+ .option('--bundle', 'write a redacted diagnostics archive for support')
1220
+ .option('--out <dir>', 'output directory for --bundle')
1206
1221
  .option('--fix', 'show repairs; git installation asks on a TTY (other repairs require --yes)')
1207
1222
  .option('--advise', 'ask the configured LLM to rank catalog repairs (read-only advice)')
1208
1223
  .option('--yes', 'apply planned doctor repairs (requires --fix)')
1209
1224
  .option('--sudo', 'also run the planned sudo install lines — only where `sudo -n true` works (requires --fix --yes)')
1210
1225
  .option('--restart', 'restart the nexus service when it runs a different version than this installed copy, then verify it (requires --fix --yes · interrupts bots, terminals and running turns)')
1211
- .action(async (opts: { json?: boolean; credentials?: boolean; fix?: boolean; advise?: boolean; yes?: boolean; sudo?: boolean; restart?: boolean }) => {
1226
+ .action(async (opts: { json?: boolean; credentials?: boolean; bundle?: boolean; out?: string; fix?: boolean; advise?: boolean; yes?: boolean; sudo?: boolean; restart?: boolean }) => {
1227
+ if (opts.bundle) {
1228
+ if (opts.fix || opts.advise || opts.yes || opts.sudo || opts.restart) {
1229
+ err.error('--bundle cannot be combined with repair or advice options');
1230
+ setExitCode(1);
1231
+ return;
1232
+ }
1233
+ try {
1234
+ // Same report as `doctor --json`, local LLM section included, so support sees what the user saw.
1235
+ const doctorReport = runDoctor(deps);
1236
+ if (doctorReport.ok) doctorReport.localLlm = await probeDoctorLocalLlm(deps.localLlmInventory);
1237
+ const result = buildDoctorBundle({ outDir: opts.out ?? process.cwd(), doctorOptions: deps, doctorReport });
1238
+ out.log(opts.json ? JSON.stringify(result) : [`Diagnostics: ${result.path} (${result.bytes} bytes)`, 'Contents:', ...result.files.map((file) => ` ${file}`)].join('\n'));
1239
+ } catch {
1240
+ err.error('Could not create diagnostics archive.');
1241
+ setExitCode(1);
1242
+ }
1243
+ return;
1244
+ }
1245
+ if (opts.out !== undefined) {
1246
+ err.error('--out requires --bundle');
1247
+ setExitCode(1);
1248
+ return;
1249
+ }
1212
1250
  if (opts.yes && !opts.fix) {
1213
1251
  err.error('--yes requires --fix');
1214
1252
  setExitCode(1);
@@ -1235,6 +1273,7 @@ export function registerDoctorCommand(program: Command, deps: DoctorCliDeps = {}
1235
1273
  setExitCode(1);
1236
1274
  return;
1237
1275
  }
1276
+ report.localLlm = await probeDoctorLocalLlm(deps.localLlmInventory);
1238
1277
  if (!opts.fix && !opts.advise) {
1239
1278
  out.log(opts.json ? JSON.stringify(report, null, 2) : formatDoctorReport(report, { credentials: opts.credentials }));
1240
1279
  if (report.requiredMissing?.length) setExitCode(1);
@@ -1353,6 +1392,7 @@ export function registerDoctorCommand(program: Command, deps: DoctorCliDeps = {}
1353
1392
  : undefined;
1354
1393
  // PATH cannot change in this process; re-probe readiness rather than treating a saved block as PATH=ok.
1355
1394
  const currentReport = results || gitInstall?.ran ? runDoctor(deps) : report;
1395
+ if (currentReport.ok) currentReport.localLlm = report.localLlm;
1356
1396
  if (gitInstall?.ran && gitInstall.ok && currentReport.requiredMissing?.includes('git')) {
1357
1397
  gitInstall.detail = 'git init 성공 · 현재 doctor PATH 에서 git 을 찾지 못했습니다. 새 셸에서 다시 확인하세요.';
1358
1398
  }
@@ -0,0 +1,95 @@
1
+ import { debug } from '../debug/log.js';
2
+ import { probeDocker } from '../llm/local-manager/docker-probe.js';
3
+ import { probeLmstudio } from '../llm/local-manager/lmstudio-probe.js';
4
+ import { probeMlx } from '../llm/local-manager/mlx-probe.js';
5
+ import { listNodes } from '../llm/local-manager/node-registry.js';
6
+ import { probeOllama } from '../llm/local-manager/ollama-probe.js';
7
+ import type { LlmInventory, LlmModel, LlmNode, LlmRuntime } from '../llm/local-manager/types.js';
8
+
9
+ export interface DoctorLocalLlm {
10
+ nodes: { kind: 'ollama' | 'lmstudio' | 'mlx' | 'docker'; baseUrl: string; reachable: boolean; models: string[] }[];
11
+ timedOut: boolean;
12
+ /** Inventory lookup failed; absence of servers was not established. */
13
+ failed?: boolean;
14
+ }
15
+
16
+ const kinds = ['ollama', 'lmstudio', 'mlx', 'docker'] as const;
17
+ type Kind = (typeof kinds)[number];
18
+
19
+ const baseUrlFields: Record<Kind, 'ollamaBaseUrl' | 'lmstudioBaseUrl' | 'mlxBaseUrl' | 'dockerBaseUrl'> = {
20
+ ollama: 'ollamaBaseUrl', lmstudio: 'lmstudioBaseUrl', mlx: 'mlxBaseUrl', docker: 'dockerBaseUrl',
21
+ };
22
+
23
+ type Probe = (node: { id: string; isLocal: boolean }, deps: { timeoutMs?: number }) => Promise<{ reachable: boolean; models: readonly LlmModel[]; warnings: readonly string[] }>;
24
+
25
+ /** Doctor shows only this machine, so it probes only the local node — the full inventory also probes every remote
26
+ * fleet node over ssh and took ~7 s cold, past the 3 s cap («시간 초과» with LM Studio running · 10-01). */
27
+ export async function localInventory(deps: {
28
+ nodes?: () => readonly LlmNode[];
29
+ probes?: Record<'lmstudio' | 'ollama' | 'mlx' | 'docker', Probe>;
30
+ now?: () => number;
31
+ } = {}): Promise<LlmInventory> {
32
+ const probes = deps.probes ?? { lmstudio: probeLmstudio, ollama: probeOllama, mlx: probeMlx, docker: probeDocker } as Record<'lmstudio' | 'ollama' | 'mlx' | 'docker', Probe>;
33
+ const read = deps.nodes ?? listNodes;
34
+ const nodes: LlmNode[] = [];
35
+ const models: LlmModel[] = [];
36
+ const warnings: string[] = [];
37
+ for (const node of read().filter((n) => n.isLocal)) {
38
+ const seed = { id: node.id, isLocal: true };
39
+ const order = ['lmstudio', 'ollama', 'mlx', 'docker'] as const;
40
+ const results = await Promise.all(order.map((kind) => probes[kind](seed, { timeoutMs: 2500 })));
41
+ const runtimes: LlmRuntime[] = order.filter((_, i) => results[i]!.reachable);
42
+ for (const [i, r] of results.entries()) { models.push(...r.models); for (const w of r.warnings) warnings.push(`${node.id}:${order[i]}:${w}`); }
43
+ // Probes record base URLs in the registry on success — read the node back for them.
44
+ const fresh = read().find((n) => n.id === node.id) ?? node;
45
+ nodes.push({ ...fresh, reachable: runtimes.length > 0, runtimes });
46
+ }
47
+ return { nodes, models, at: (deps.now ?? Date.now)(), cached: false, warnings };
48
+ }
49
+
50
+ export async function probeDoctorLocalLlm(inventory: () => Promise<LlmInventory> = () => localInventory(), timeoutMs = 3000): Promise<DoctorLocalLlm> {
51
+ let timer: ReturnType<typeof setTimeout> | undefined;
52
+ let result: DoctorLocalLlm;
53
+ try {
54
+ const snapshot = await Promise.race([
55
+ Promise.resolve().then(inventory),
56
+ new Promise<null>((resolve) => { timer = setTimeout(() => resolve(null), timeoutMs); }),
57
+ ]);
58
+ result = snapshot === null
59
+ ? { nodes: [], timedOut: true }
60
+ : {
61
+ nodes: snapshot.nodes.filter((node) => node.isLocal).flatMap((node) => kinds.flatMap((kind) => {
62
+ const baseUrl = node[baseUrlFields[kind]];
63
+ if (!node.runtimes.includes(kind) && !baseUrl) return [];
64
+ return [{
65
+ kind,
66
+ baseUrl: baseUrl ?? '',
67
+ reachable: node.reachable === true && node.runtimes.includes(kind),
68
+ models: snapshot.models.filter((model) => model.nodeId === node.id && model.runtime === kind).map((model) => model.id),
69
+ }];
70
+ })),
71
+ timedOut: false,
72
+ };
73
+ } catch {
74
+ result = { nodes: [], timedOut: false, failed: true };
75
+ } finally {
76
+ if (timer !== undefined) clearTimeout(timer);
77
+ }
78
+ debug.log('doctor.local-llm', 'probed', {
79
+ nodes: result.nodes.length,
80
+ reachable: result.nodes.filter((node) => node.reachable).length,
81
+ models: result.nodes.reduce((total, node) => total + node.models.length, 0),
82
+ timedOut: result.timedOut,
83
+ });
84
+ return result;
85
+ }
86
+
87
+ export function formatDoctorLocalLlm(result: DoctorLocalLlm): string[] {
88
+ if (result.timedOut) return ['Local LLM:', ' 시간 초과'];
89
+ if (result.failed) return ['Local LLM:', ' 로컬 LLM 인벤토리를 조회하지 못했다'];
90
+ if (result.nodes.length === 0) return ['Local LLM:', ' 로컬 LLM 서버를 찾지 못했다 — Ollama(:11434) · LM Studio(:1234) 를 띄우면 여기 보인다'];
91
+ return [
92
+ 'Local LLM:',
93
+ ...result.nodes.map((node) => ` ${node.kind} · ${node.baseUrl} · ${node.reachable ? '닿음' : '안 닿음'} · 모델 ${node.models.length}개${node.models.length ? ` · ${node.models.slice(0, 5).join(', ')}` : ''}`),
94
+ ];
95
+ }
@@ -0,0 +1,48 @@
1
+ import { DEFAULT_MODEL_TIER, isModelTier, type ModelTier } from '../model-tier/types.js';
2
+
3
+ export interface DoctorMode {
4
+ id: 'model-tier' | 'auto-route' | 'fast-mode';
5
+ value: ModelTier | boolean | 'none';
6
+ source: 'config' | 'default' | 'none';
7
+ usedByChat: boolean;
8
+ note: string;
9
+ }
10
+
11
+ /** Report configured switches without changing them or implying they affect chat turns. */
12
+ export function describeModes(cfg: { modelTier?: { llm?: unknown }; llm?: { autoRoute?: { enabled?: unknown } } }): DoctorMode[] {
13
+ const tier = cfg.modelTier?.llm;
14
+ const autoRoute = cfg.llm?.autoRoute?.enabled;
15
+ return [
16
+ {
17
+ id: 'model-tier',
18
+ value: isModelTier(tier) ? tier : DEFAULT_MODEL_TIER,
19
+ source: isModelTier(tier) ? 'config' : 'default',
20
+ usedByChat: false,
21
+ note: '대화 턴 모델 선택에 쓰이지 않는다(표시·전환 계획만)',
22
+ },
23
+ {
24
+ id: 'auto-route',
25
+ value: typeof autoRoute === 'boolean' ? autoRoute : false,
26
+ source: typeof autoRoute === 'boolean' ? 'config' : 'default',
27
+ usedByChat: false,
28
+ note: '오토파일럿 턴만 읽는다 · 켜기 = elanous config set llm.autoRoute.enabled true',
29
+ },
30
+ {
31
+ id: 'fast-mode',
32
+ value: 'none',
33
+ source: 'none',
34
+ usedByChat: false,
35
+ note: '스위치 없음(분류기 호출부 0)',
36
+ },
37
+ ];
38
+ }
39
+
40
+ export function formatModeLine(row: DoctorMode): string {
41
+ const label = {
42
+ 'model-tier': 'smart·등급',
43
+ 'auto-route': 'smart·자동 라우팅',
44
+ 'fast-mode': '빠른 모드',
45
+ }[row.id];
46
+ const source = { config: '설정', default: '기본', none: '없음' }[row.source];
47
+ return `${label}: ${String(row.value)}(${source}) · 대화에 ${row.usedByChat ? '쓰임' : '안 쓰임'} — ${row.note}`;
48
+ }
@@ -0,0 +1,60 @@
1
+ // EN4 — `elanous import`: one read-only plan over the agents you already use. Applying stays with the sub-commands each item names.
2
+ import type { Command } from 'commander';
3
+ import { homedir } from 'node:os';
4
+ import { debug } from '../debug/log.js';
5
+ import { formatImportPlan, planImport, type ImportPlan } from '../import/plan.js';
6
+ import { applyImport, formatApplied, IMPORT_MODES, undoImport, type ImportMode } from '../import/apply.js';
7
+ import { dirname, join } from 'node:path';
8
+
9
+ export async function buildImportPlan(home = homedir()): Promise<ImportPlan> {
10
+ const { defaultSkillDirs, getUserConfig } = await import('../user-config.js');
11
+ const cfg = getUserConfig();
12
+ const plan = planImport({
13
+ home,
14
+ ...(process.env.CODEX_HOME ? { codexHome: process.env.CODEX_HOME } : {}),
15
+ activeSkillDirs: defaultSkillDirs(cfg),
16
+ mcpServerIds: (cfg.mcp?.servers ?? []).map((server) => server.id),
17
+ });
18
+ for (const item of plan.items) debug.log('import.plan', 'item', { source: item.source, kind: item.kind, status: item.status });
19
+ debug.log('import.plan', 'planned', { sources: plan.sources.filter((s) => s.detected).map((s) => s.id), items: plan.items.length });
20
+ return plan;
21
+ }
22
+
23
+ export function registerImportCommand(program: Command): void {
24
+ program.command('import')
25
+ .description('Show what elanous can bring over from Claude Code, Codex, ~/.agents, OpenClaw and Hermes — a plan first; `--apply` registers skills in place and adds MCP switched off, `--undo` puts everything back')
26
+ .option('--json', 'Print the plan (or the apply result) as JSON')
27
+ .option('--apply', 'Apply the plan: skills in place (ref) or as link/copy · MCP switched off · secrets and hooks only listed')
28
+ .option('--mode <mode>', `How skills come over: ${IMPORT_MODES.join('|')} (default ref — nothing copied)`)
29
+ .option('--pick <ids>', 'Only these item ids (comma-separated, see --json)')
30
+ .option('--undo', 'Undo the last --apply: restore the config bytes and remove links/copies')
31
+ .action(async (opts: { json?: boolean; apply?: boolean; mode?: string; pick?: string; undo?: boolean }) => {
32
+ const { userConfigPath } = await import('../user-config.js');
33
+ const importRoot = join(dirname(userConfigPath()), 'import');
34
+ if (opts.undo) {
35
+ const undone = undoImport(importRoot);
36
+ const { resetSkillIndex } = await import('../skills/index.js');
37
+ resetSkillIndex();
38
+ debug.log('import.apply', 'undone', { found: undone !== null, created: undone?.created.length ?? 0 });
39
+ if (opts.json) console.log(JSON.stringify({ undone: undone !== null }));
40
+ else console.log(undone ? `되돌렸습니다 — 설정을 ${undone.at} 이전으로 돌리고, 만든 것 ${undone.created.length}개를 지웠습니다.` : '되돌릴 가져오기가 없습니다.');
41
+ return;
42
+ }
43
+ const plan = await buildImportPlan();
44
+ if (!opts.apply) { console.log(opts.json ? JSON.stringify(plan, null, 2) : formatImportPlan(plan)); return; }
45
+ if (opts.mode !== undefined && !IMPORT_MODES.includes(opts.mode as ImportMode)) throw new Error(`--mode 는 ${IMPORT_MODES.join('|')} 중 하나다(받은 값: ${opts.mode})`);
46
+ const { getUserConfig, saveUserConfig } = await import('../user-config.js');
47
+ const cfg = getUserConfig();
48
+ const rawMcp = (cfg.raw.mcp && typeof cfg.raw.mcp === 'object' && !Array.isArray(cfg.raw.mcp) ? cfg.raw.mcp : {}) as Record<string, unknown>;
49
+ const manifest = applyImport(plan, { ...(opts.mode ? { mode: opts.mode as ImportMode } : {}), ...(opts.pick ? { pick: opts.pick.split(',').map((id) => id.trim()).filter(Boolean) } : {}) }, {
50
+ home: homedir(), configPath: userConfigPath(), importRoot,
51
+ sources: cfg.skills.sources ?? [],
52
+ rawMcpServers: (Array.isArray(rawMcp.servers) ? rawMcp.servers : []) as Array<Record<string, unknown>>,
53
+ save: (sources, servers) => saveUserConfig({ ...cfg, raw: { ...cfg.raw, mcp: { ...rawMcp, servers } }, skills: { ...cfg.skills, sources } }),
54
+ });
55
+ const { resetSkillIndex } = await import('../skills/index.js');
56
+ resetSkillIndex();
57
+ debug.log('import.apply', 'applied', { mode: manifest.mode, applied: manifest.applied.length, kept: manifest.kept.length, created: manifest.created.length });
58
+ console.log(opts.json ? JSON.stringify({ mode: manifest.mode, applied: manifest.applied, kept: manifest.kept, created: manifest.created.length }, null, 2) : formatApplied(manifest));
59
+ });
60
+ }
@@ -11,6 +11,7 @@ import { writeStdoutFully } from './stdout-flush.js';
11
11
  import { runGitCommand } from '../git-fs/runner.js';
12
12
  import { debug } from '../debug/log.js';
13
13
  import { getUserConfig } from '../user-config.js';
14
+ import { registerIntakeSourceCommands } from './intake-source-cli.js';
14
15
 
15
16
  export interface IntakeToTasksCliDeps {
16
17
  root: string;
@@ -91,6 +92,7 @@ export function resolveIntakeCheckRoot(
91
92
  export function registerIntakeCommands(program: Command): void {
92
93
  // ── intake check — 바깥 사실을 elanous 현재와 대조 (태스크 등록 없음) ──
93
94
  const intakeCmd = program.command('intake').description('바깥 사실·문서를 elanous 현재와 대조하거나 태스크로 받는다');
95
+ registerIntakeSourceCommands(intakeCmd);
94
96
  intakeCmd.hook('preAction', async () => {
95
97
  try {
96
98
  const { registerStandaloneLogSink } = await import('../domains/standalone-log-sink.js');
@@ -217,7 +219,9 @@ export function registerIntakeCommands(program: Command): void {
217
219
  const { readPipedStdin } = await import('./piped-stdin.js');
218
220
  const text = opts.file ? readFileSync(opts.file, 'utf8') : (await readPipedStdin()) ?? '';
219
221
  const { raws, bad } = parseRawIntakeJsonl(text);
220
- const result = ingestIntakeItems(effectiveInstanceRoot(), opts.source as (typeof INTAKE_SOURCES)[number], raws);
222
+ let result: ReturnType<typeof ingestIntakeItems>;
223
+ try { result = ingestIntakeItems(effectiveInstanceRoot(), opts.source as (typeof INTAKE_SOURCES)[number], raws); }
224
+ catch (error) { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 2; return; }
221
225
  const out = { source: opts.source, inputLines: raws.length + bad, badInputLines: bad, ...result };
222
226
  if (opts.json) await writeStdoutFully(JSON.stringify(out, null, 2));
223
227
  else console.log(`흡수 원장 · ${opts.source}: 새 ${result.added} · 합침 ${result.merged} · 이미 끝난 것 ${result.seen} · 버림 ${result.skipped}${bad ? ` · 깨진 입력 ${bad}` : ''}${result.badLines ? ` · 깨진 원장 줄 ${result.badLines}` : ''}`);
@@ -228,14 +232,20 @@ export function registerIntakeCommands(program: Command): void {
228
232
  .description('흡수 원장 항목 보기 (최근 본 순)')
229
233
  .option('--status <status>', 'new | queued | absorbed | checked | routed | discarded | deferred')
230
234
  .option('--source <source>', '입력원으로 거르기')
235
+ .option('--seat <id>', '자리 id (별칭 허용; 자리 없는 옛 항목은 제외)')
231
236
  .option('--limit <n>', '최대 줄 수 (기본 30)')
232
237
  .option('--json', '구조화 출력')
233
- .action(async (opts: { status?: string; source?: string; limit?: string; json?: boolean }) => {
238
+ .action(async (opts: { status?: string; source?: string; seat?: string; limit?: string; json?: boolean }) => {
234
239
  const { listIntakeItems } = await import('../intake-plane/items.js');
235
240
  const { effectiveInstanceRoot } = await import('../instance/resolve.js');
241
+ const { canonicalSeat } = await import('../intake-plane/intake-sources.js');
242
+ let seat: string | undefined;
243
+ try { seat = opts.seat === undefined ? undefined : canonicalSeat(opts.seat); }
244
+ catch (error) { console.error((error as Error).message); process.exitCode = 2; return; }
236
245
  const all = listIntakeItems(effectiveInstanceRoot(), {
237
246
  ...(opts.status ? { status: opts.status as never } : {}),
238
247
  ...(opts.source ? { source: opts.source as never } : {}),
248
+ ...(seat ? { seat } : {}),
239
249
  });
240
250
  const limit = Math.max(1, Number(opts.limit ?? 30) || 30);
241
251
  const shown = all.slice(0, limit);
@@ -0,0 +1,58 @@
1
+ import type { Command } from 'commander';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { effectiveInstanceRoot } from '../instance/resolve.js';
4
+ import { addSource, canonicalSeat, dueSources, expiredSource, listSources, removeSource, runDue, type SourceKind } from '../intake-plane/intake-sources.js';
5
+
6
+ export function registerIntakeSourceCommands(intake: Command, root: () => string = effectiveInstanceRoot): void {
7
+ const source = intake.command('source').description('자리별 흡수 원천과 주기 등록·조회·실행');
8
+ const fail = (error: unknown) => { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 2; };
9
+ source.command('add')
10
+ .requiredOption('--seat <id>', '자리 id 또는 별칭')
11
+ .requiredOption('--kind <kind>', 'github-query | rss | command')
12
+ .requiredOption('--spec <spec>', 'GitHub 질의 · 피드 URL · 명령')
13
+ .requiredOption('--every <cadence>', 'Nh | Nd | Nw')
14
+ .option('--until <date>', '만료 시작 날짜 YYYY-MM-DD (UTC)')
15
+ .option('--why <reason>', '등록 이유')
16
+ .option('--id <id>', '원천 id')
17
+ .action((opts: { seat: string; kind: string; spec: string; every: string; until?: string; why?: string; id?: string }) => {
18
+ try {
19
+ const added = addSource({ id: opts.id ?? `src-${randomUUID().slice(0, 8)}`, seat: opts.seat, kind: opts.kind as SourceKind,
20
+ spec: opts.spec, every: opts.every, ...(opts.until ? { until: opts.until } : {}), ...(opts.why ? { why: opts.why } : {}) }, root());
21
+ console.log(`${added.id} ${added.seat} ${added.kind} ${added.every}`);
22
+ } catch (error) { fail(error); }
23
+ });
24
+ source.command('list')
25
+ .option('--seat <id>', '자리 id 또는 별칭')
26
+ .option('--json', 'JSON 출력')
27
+ .action((opts: { seat?: string; json?: boolean }) => {
28
+ try {
29
+ const now = new Date();
30
+ const sources = listSources({ seat: opts.seat }, root()).map((entry) => ({ ...entry, expired: expiredSource(entry, now) }));
31
+ if (opts.json) console.log(JSON.stringify(sources));
32
+ else for (const entry of sources) console.log(`${entry.id} ${entry.seat} ${entry.kind} ${entry.every} ${entry.expired ? '만료' : '활성'} ${entry.spec}`);
33
+ } catch (error) { fail(error); }
34
+ });
35
+ source.command('remove <id>').action((id: string) => {
36
+ try { if (!removeSource(id, root())) { console.error(`원천 id 없음: ${id}`); process.exitCode = 2; return; } console.log(`removed ${id}`); }
37
+ catch (error) { fail(error); }
38
+ });
39
+ source.command('run-due')
40
+ .option('--seat <id>', '자리 id 또는 별칭')
41
+ .option('--dry-run', '기한 원천 목록만 · 수집·원장·마지막 실행 시각 무변')
42
+ .option('--json', 'JSON 출력')
43
+ .action(async (opts: { seat?: string; dryRun?: boolean; json?: boolean }) => {
44
+ try {
45
+ const seat = opts.seat === undefined ? undefined : canonicalSeat(opts.seat);
46
+ if (opts.dryRun) {
47
+ const sources = dueSources(new Date(), root()).filter((entry) => seat === undefined || entry.seat === seat);
48
+ if (opts.json) console.log(JSON.stringify(sources));
49
+ else for (const entry of sources) console.log(`${entry.id} ${entry.seat} ${entry.kind} ${entry.every}`);
50
+ return;
51
+ }
52
+ const result = await runDue({ seat, deps: { stateDir: root() } });
53
+ if (opts.json) console.log(JSON.stringify(result));
54
+ else for (const row of result.ran) console.log(`${row.id} ${row.items}${row.error ? ` 실패: ${row.error}` : ''}`);
55
+ if (result.ran.some((row) => row.error)) process.exitCode = 1;
56
+ } catch (error) { fail(error); }
57
+ });
58
+ }
@@ -1,4 +1,6 @@
1
1
  import type { Command } from 'commander';
2
+ import { readFileSync, writeFileSync } from 'node:fs';
3
+ import { loadMachineLedger, machineMarkdownPath, renderLedgerMarkdown, setMachineField } from '../machines/machine-ledger.js';
2
4
  import { MACHINE_NAME, resolveMachineName } from '../roles/machine-name.js';
3
5
  import { readMachineProfile, seatRank, validSeatRank, writeMachineProfile } from '../roles/machine-profile.js';
4
6
 
@@ -28,6 +30,27 @@ export function registerMachineCommands(program: Command, root?: string): void {
28
30
  writeMachineProfile({ id, duties, seats }, root);
29
31
  console.log(`machine · ${id} · ${duties.join(',') || '-'} · ${Object.entries(seats).map(([seat, value]) => `${seat}:${value.rank}`).join(',') || '-'}`);
30
32
  });
33
+ machine.command('render').description('YAML 원장에서 Markdown 생성·검증')
34
+ .option('--check', 'Markdown 과 YAML 동기화 검증')
35
+ .action((opts: { check?: boolean }) => {
36
+ const markdown = renderLedgerMarkdown(loadMachineLedger(process.cwd()));
37
+ const path = machineMarkdownPath(process.cwd());
38
+ if (opts.check) {
39
+ let current: string;
40
+ try { current = readFileSync(path, 'utf8'); } catch { throw new Error(`machine ledger Markdown missing: ${path}`); }
41
+ if (current !== markdown) throw new Error(`machine ledger Markdown stale: ${path}`);
42
+ console.log('machine render --check OK');
43
+ } else {
44
+ writeFileSync(path, markdown, 'utf8');
45
+ console.log(`machine render · ${path}`);
46
+ }
47
+ });
48
+ machine.command('ledger-set').description('YAML 보유 자원 필드 수정')
49
+ .argument('<id>').argument('<field>').argument('<value>')
50
+ .action((id: string, field: string, value: string) => {
51
+ setMachineField(process.cwd(), id, field, value);
52
+ console.log(`machine ledger-set · ${id} · ${field}`);
53
+ });
31
54
  machine.command('show').option('--json', 'JSON 출력')
32
55
  .action((opts: { json?: boolean }) => {
33
56
  const resolved = resolveMachineName({ root });
@@ -12,6 +12,7 @@ import {
12
12
  } from '../mcp/mcp-oauth.js';
13
13
  import { parseWwwAuthenticate } from '../mcp/client.js';
14
14
  import { debug } from '../debug/log.js';
15
+ import { browserUnavailableReason } from '../oauth/browser-availability.js';
15
16
 
16
17
  const CALLBACK_PATH = '/oauth/callback';
17
18
  const DEFAULT_TIMEOUT_MS = 120_000;
@@ -31,6 +32,8 @@ export interface McpLoginOpts {
31
32
  readConfigFn?: () => { mcp?: { servers: McpServerSpec[] } };
32
33
  fetch?: McpOAuthFetch;
33
34
  openBrowser?: (url: string) => Promise<void>;
35
+ browserEnv?: NodeJS.ProcessEnv;
36
+ browserPlatform?: NodeJS.Platform;
34
37
  createListener?: (handler: (req: IncomingMessage, res: ServerResponse) => void) => Server;
35
38
  /** 발견한 issuer/tokenEndpoint 를 config 에 되쓰는 자리. 테스트 심. */
36
39
  persistDiscoveryFn?: (input: PersistDiscoveryInput) => PersistDiscoveryResult;
@@ -91,10 +94,16 @@ export async function runMcpLogin(opts: McpLoginOpts): Promise<McpLoginResult> {
91
94
  void callbackPromise.catch(() => undefined);
92
95
  out.log(`Open this URL to authorize '${opts.serverId}':`);
93
96
  out.log(authorization.request.url);
94
- try {
95
- await (opts.openBrowser ?? openDefaultBrowser)(authorization.request.url);
96
- } catch {
97
- out.error('Could not open the default browser; open the URL above manually.');
97
+ const why = browserUnavailableReason(opts.browserEnv ?? process.env, opts.browserPlatform ?? process.platform);
98
+ if (why) {
99
+ out.log(why);
100
+ debug.log('browser.open', 'skipped', { reason: why.includes('ssh') ? 'ssh' : 'no-display' });
101
+ } else {
102
+ try {
103
+ await (opts.openBrowser ?? openDefaultBrowser)(authorization.request.url);
104
+ } catch {
105
+ out.error('Could not open the default browser; open the URL above manually.');
106
+ }
98
107
  }
99
108
  const callback = await callbackPromise;
100
109
  await exchangeAuthorizationCode(authorization.metadata, authorization.request, callback, {