elanous 0.2.4 → 0.2.6

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 (455) hide show
  1. package/README.md +7 -6
  2. package/apps/pwa/out/404/index.html +1 -1
  3. package/apps/pwa/out/404.html +1 -1
  4. package/apps/pwa/out/_next/static/chunks/1169-696263ac7af8c4d4.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1883.c91a2648af4842fd.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1947-9d64872eeb9cfdec.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/2808-dc06482994b40f70.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/{5080-1613752891fcadb4.js → 2984-977cfa339fcd65a5.js} +3 -3
  9. package/apps/pwa/out/_next/static/chunks/3165-95515ca5f299a120.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/{8142-011e1f474efb8b33.js → 3190-e9282753e7ebde85.js} +1 -1
  11. package/apps/pwa/out/_next/static/chunks/3995-bee512a7063036ce.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 4240-7153881b0b377306.js} +1 -1
  13. package/apps/pwa/out/_next/static/chunks/4259-99614149577e77cb.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/4283-329eabeaf233e6cd.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/4328-cb5058b1acc2de5e.js +25 -0
  16. package/apps/pwa/out/_next/static/chunks/4801-ecee6ef62b349b18.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/5110-8058791dd25a2e14.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/{3403.4bc97b4066945a94.js → 5329.8b7e4bca684b6eec.js} +2 -2
  19. package/apps/pwa/out/_next/static/chunks/5330-c41e9670f594c38f.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/5423-e95fd125d1794349.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/5478-b5eae07dc79ea619.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/5787-96ee6642b9a661e5.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/6448-79d72368589f98bc.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/6522-9866c002b5531836.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/6729-b4a558b7aa891e0c.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/6969-cb03f9410b7d2e03.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/7007-3b9861091bb8dd21.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/{5791-bb82743fa6299562.js → 763-7d0e1913b5074e98.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7633.0654fe6ea259c1ee.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/7930-8dc7e20fc6fc19ca.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/8071-187edbc22a86741f.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/8198-d4101ad70cb9a579.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/8250.4a2c5553a07cdf47.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/8293-123b91e7b0330dc0.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/8296-d6d5d87dfa911d41.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/8305-603963c33ee8eec3.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/8612-4a0066f20951b8fd.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → 8d0d9faf-97887ff99713b84e.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/9181-cf4e1b61c4783f92.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 91ff6dda-3b33d42d20657429.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/9262-26c2ecbd5a67e4a2.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/9328-45abdebce4fbecb7.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/9355-dbc67205adf8745f.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/9763.255b90dd69875689.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/9933-66d31ec25c0231af.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-bd06fb55f4da0381.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4e586142da455a25.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-889ad5cddcf45b0d.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/board/page-47ba401d56572c27.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/botlab/page-0b54f9c83458e038.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/bots/page-4fcc296ef6c8ed90.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/chat/page-fe9fda494a87f4e5.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/control/page-f9642e25ce129590.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e1063e8ba5f3e2e7.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/editor/page-9d0df30027d8aa9d.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/healthz/route-bd06fb55f4da0381.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/intake/page-d97adf2c9455b8d0.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/layout-236b80d202110362.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/live/page-21a30f35f1a276ad.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/market/page-b1a5be357c4fa25d.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1705a60303f7f573.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/morning/page-76b337c11ad0e237.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/not-found-7c0f85fbb56f64c6.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/observatory/page-65ca16b20617b44d.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/page-e7f9b8fc29411c1d.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/reflection/page-92ea6213d1aeec04.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a913a005aacb5476.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/sessions/page-2d78804a40dfdef6.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-08a99148e780ac00.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/settings/page-deb674e7ed36f815.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-49f8de0534d192d7.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/setup/layout-bd06fb55f4da0381.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/setup/page-ba162a28d2176e18.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/share/page-1ff1a37b550ab41a.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/showroom/page-ccaa698f56c0fbdb.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/tasks/page-53b944082de1d7db.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/term/page-9b70adf1552789bf.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/trace/page-30d482aa63241fe8.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/vault/page-aecf786f54651d8a.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6ab9b62c7b6e08f2.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/workflows/page-08949735455e0c9a.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/workspace/page-a1479005234a46c2.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3890e90bd232732.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/beb5cf10-9adcc10b83930090.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/framework-d68d41c88e05c801.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/main-app-11c63971b7a9593b.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/main-d09452353bd0d9cc.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/pages/_app-184d727cebeca767.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/pages/_error-21bbbbac99b720b1.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/{webpack-e5bea36f183b25e6.js → webpack-37b8bf78a3c6eec5.js} +1 -1
  91. package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +1 -0
  92. package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → qMI0jnlZaBjjm4aIDYNGM}/_buildManifest.js +1 -1
  93. package/apps/pwa/out/approvals/index.html +1 -1
  94. package/apps/pwa/out/approvals/index.txt +29 -27
  95. package/apps/pwa/out/autopilot/index.html +1 -1
  96. package/apps/pwa/out/autopilot/index.txt +29 -27
  97. package/apps/pwa/out/board/index.html +1 -1
  98. package/apps/pwa/out/board/index.txt +29 -27
  99. package/apps/pwa/out/botlab/index.html +1 -1
  100. package/apps/pwa/out/botlab/index.txt +26 -24
  101. package/apps/pwa/out/bots/index.html +1 -1
  102. package/apps/pwa/out/bots/index.txt +26 -24
  103. package/apps/pwa/out/chat/index.html +1 -1
  104. package/apps/pwa/out/chat/index.txt +26 -24
  105. package/apps/pwa/out/control/index.html +1 -1
  106. package/apps/pwa/out/control/index.txt +29 -27
  107. package/apps/pwa/out/design-check/index.html +1 -1
  108. package/apps/pwa/out/design-check/index.txt +29 -27
  109. package/apps/pwa/out/editor/index.html +1 -1
  110. package/apps/pwa/out/editor/index.txt +29 -27
  111. package/apps/pwa/out/healthz +1 -1
  112. package/apps/pwa/out/icons/elanous-16.png +0 -0
  113. package/apps/pwa/out/icons/elanous-180.png +0 -0
  114. package/apps/pwa/out/icons/elanous-192.png +0 -0
  115. package/apps/pwa/out/icons/elanous-32.png +0 -0
  116. package/apps/pwa/out/icons/elanous-512.png +0 -0
  117. package/apps/pwa/out/index.html +1 -1
  118. package/apps/pwa/out/index.txt +26 -24
  119. package/apps/pwa/out/intake/index.html +1 -1
  120. package/apps/pwa/out/intake/index.txt +29 -27
  121. package/apps/pwa/out/live/index.html +1 -1
  122. package/apps/pwa/out/live/index.txt +29 -27
  123. package/apps/pwa/out/manifest.webmanifest +5 -2
  124. package/apps/pwa/out/market/index.html +1 -1
  125. package/apps/pwa/out/market/index.txt +26 -24
  126. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  127. package/apps/pwa/out/missions/placeholder/index.txt +26 -24
  128. package/apps/pwa/out/morning/index.html +1 -1
  129. package/apps/pwa/out/morning/index.txt +29 -27
  130. package/apps/pwa/out/observatory/index.html +1 -1
  131. package/apps/pwa/out/observatory/index.txt +29 -27
  132. package/apps/pwa/out/reflection/index.html +1 -1
  133. package/apps/pwa/out/reflection/index.txt +27 -25
  134. package/apps/pwa/out/scheduler/index.html +1 -1
  135. package/apps/pwa/out/scheduler/index.txt +29 -27
  136. package/apps/pwa/out/sessions/index.html +1 -1
  137. package/apps/pwa/out/sessions/index.txt +26 -24
  138. package/apps/pwa/out/settings/devices/index.html +1 -1
  139. package/apps/pwa/out/settings/devices/index.txt +29 -27
  140. package/apps/pwa/out/settings/index.html +1 -1
  141. package/apps/pwa/out/settings/index.txt +29 -27
  142. package/apps/pwa/out/setup/done/index.html +1 -1
  143. package/apps/pwa/out/setup/done/index.txt +29 -27
  144. package/apps/pwa/out/setup/index.html +1 -1
  145. package/apps/pwa/out/setup/index.txt +29 -27
  146. package/apps/pwa/out/share/index.html +1 -1
  147. package/apps/pwa/out/share/index.txt +29 -27
  148. package/apps/pwa/out/showroom/index.html +1 -1
  149. package/apps/pwa/out/showroom/index.txt +26 -24
  150. package/apps/pwa/out/sw-strategies.js +44 -0
  151. package/apps/pwa/out/sw.js +6 -44
  152. package/apps/pwa/out/tasks/index.html +1 -1
  153. package/apps/pwa/out/tasks/index.txt +29 -27
  154. package/apps/pwa/out/term/index.html +1 -1
  155. package/apps/pwa/out/term/index.txt +29 -27
  156. package/apps/pwa/out/trace/index.html +1 -1
  157. package/apps/pwa/out/trace/index.txt +29 -27
  158. package/apps/pwa/out/vault/index.html +1 -1
  159. package/apps/pwa/out/vault/index.txt +29 -27
  160. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  161. package/apps/pwa/out/workflows/chat-ui/index.txt +29 -27
  162. package/apps/pwa/out/workflows/index.html +1 -1
  163. package/apps/pwa/out/workflows/index.txt +29 -27
  164. package/apps/pwa/out/workspace/index.html +1 -1
  165. package/apps/pwa/out/workspace/index.txt +29 -27
  166. package/apps/pwa/out/worktrees/index.html +1 -1
  167. package/apps/pwa/out/worktrees/index.txt +29 -27
  168. package/catalog/resources.yaml +6 -6
  169. package/docs/goal-context/steward-directives-2026-09-28.md +35 -37
  170. package/graphs/landing/landing-heal.yaml +1 -1
  171. package/graphs/release/recipes.yaml +11 -1
  172. package/graphs/release/release-loop.yaml +9 -2
  173. package/graphs/video/README.md +1 -0
  174. package/graphs/video/explainer-line.yaml +58 -0
  175. package/package.json +3 -1
  176. package/plugins/card-followup/README.md +33 -0
  177. package/plugins/card-followup/examples/card-sample.json +7 -0
  178. package/plugins/card-followup/graphs/card-followup.yaml +16 -0
  179. package/plugins/card-followup/graphs/recipes.yaml +12 -0
  180. package/plugins/card-followup/graphs/run-step.ts +172 -0
  181. package/plugins/card-followup/plugin.json +25 -0
  182. package/plugins/geo-check/README.md +13 -0
  183. package/plugins/geo-check/examples/brand-sample.json +8 -0
  184. package/plugins/geo-check/graphs/geo-check.yaml +20 -0
  185. package/plugins/geo-check/graphs/recipes.yaml +18 -0
  186. package/plugins/geo-check/graphs/run-step.ts +177 -0
  187. package/plugins/geo-check/plugin.json +24 -0
  188. package/plugins/job-coach/.codex-plugin/plugin.json +18 -1
  189. package/plugins/job-coach/README.md +42 -0
  190. package/plugins/job-coach/connectors/ncs/server.ts +4 -3
  191. package/plugins/job-coach/graphs/gap-judge.ts +44 -0
  192. package/plugins/job-coach/graphs/recipes.yaml +27 -0
  193. package/plugins/job-coach/graphs/report-enterprise.yaml +28 -0
  194. package/plugins/job-coach/graphs/run-step.ts +211 -17
  195. package/plugins/job-coach/plugin.json +16 -3
  196. package/plugins/job-coach/skills/enterprise-ai-fit/SKILL.md +8 -0
  197. package/plugins/job-coach/skills/enterprise-competency/SKILL.md +8 -0
  198. package/plugins/job-coach/skills/enterprise-needs/SKILL.md +8 -0
  199. package/plugins/job-coach/skills/enterprise-roadmap/SKILL.md +8 -0
  200. package/plugins/job-coach/skills/enterprise-task-analysis/SKILL.md +8 -0
  201. package/plugins/job-coach/skills/interview-to-profile/SKILL.md +18 -1
  202. package/scripts/backup/elanous-backup.sh +4 -1
  203. package/scripts/build-docs-site.ts +4 -3
  204. package/scripts/coord-channel-watch.sh +23 -4
  205. package/scripts/coord-post.sh +49 -12
  206. package/scripts/coord-tracks.json +5 -5
  207. package/scripts/daemon-port-baseline.txt +1 -2
  208. package/scripts/eval-bot-vs-grok.ts +79 -0
  209. package/scripts/eval-grok-live-baseline.ts +36 -0
  210. package/scripts/fixtures/front-route-corpus.sample.json +17 -0
  211. package/scripts/fleet/node-setup.sh +46 -7
  212. package/scripts/landing-heal/must-fix-match.ts +26 -0
  213. package/scripts/landing-heal/run-mission.ts +114 -20
  214. package/scripts/lib/android-test-device.ts +32 -0
  215. package/scripts/lib/front-route-measurement.ts +131 -0
  216. package/scripts/lib/tui-sim-keys.ts +42 -0
  217. package/scripts/measure-front-route-classifier.ts +56 -0
  218. package/scripts/media/ocr-leak-scan.sh +21 -0
  219. package/scripts/public-export.ts +2 -3
  220. package/scripts/public-import-pr.ts +80 -6
  221. package/scripts/publish-internal-dist.ts +2 -3
  222. package/scripts/red-sweep-diff.ts +1 -1
  223. package/scripts/release-loop/auto-approve-node.ts +72 -0
  224. package/scripts/release-loop/checklist-gate-node.ts +35 -0
  225. package/scripts/release-loop/gate-diff.ts +29 -0
  226. package/scripts/release-loop/gate-node.ts +366 -45
  227. package/scripts/release-loop/publish-node.ts +7 -3
  228. package/scripts/release-loop/release-docs.ts +20 -13
  229. package/scripts/release-loop/shard-plan.ts +47 -0
  230. package/scripts/release-loop/test-durations.ts +63 -0
  231. package/scripts/release-loop/upgrade-node.ts +3 -2
  232. package/scripts/test-audit-coverage-per-file.sh +16 -0
  233. package/scripts/test-audit-meaningless.ts +88 -0
  234. package/scripts/test-audit-overlap-and-weak.ts +73 -0
  235. package/scripts/test-deterministic.ts +27 -5
  236. package/scripts/tui-sim.ts +59 -14
  237. package/scripts/ux/bump-installed.sh +20 -0
  238. package/scripts/ux/lib/cdp.ts +72 -0
  239. package/scripts/ux/record-page.ts +53 -0
  240. package/skills/explainer-video/SKILL.md +117 -0
  241. package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -0
  242. package/skills/explainer-video/engine/build.mjs +431 -0
  243. package/skills/explainer-video/engine/reel.mjs +128 -0
  244. package/skills/explainer-video/engine/reel.sh +8 -0
  245. package/skills/explainer-video/engine/vo.ts +79 -0
  246. package/skills/explainer-video/examples/elanous-0.2.5/script.json +155 -0
  247. package/src/agent/nested-agent-env.ts +1 -0
  248. package/src/agent-mission/driver.ts +95 -16
  249. package/src/agent-mission/mission-cli.ts +6 -0
  250. package/src/agent-mission/resource-ladder.ts +73 -25
  251. package/src/auth/acp-token.ts +16 -0
  252. package/src/auth/github-app-token.ts +112 -0
  253. package/src/autopilot/gh-rest-fallback.ts +177 -0
  254. package/src/autopilot/mission-origin.ts +20 -4
  255. package/src/autopilot/mission-phase-track.ts +11 -1
  256. package/src/autopilot/pr-manager.ts +51 -4
  257. package/src/autopilot/track-agent.ts +3 -3
  258. package/src/autopilot/track-courier.ts +2 -2
  259. package/src/boot/acp-server.ts +2 -16
  260. package/src/boot/daemon-tools/web-search.ts +2 -1
  261. package/src/boot/ws-bridge.ts +11 -0
  262. package/src/chat/index.ts +37 -8
  263. package/src/chat/log-scroll-key.ts +12 -0
  264. package/src/chat/submit-trace.ts +30 -0
  265. package/src/cli/agent-cli.ts +7 -2
  266. package/src/cli/decisions-cli.ts +94 -0
  267. package/src/cli/doctor-cli.ts +40 -54
  268. package/src/cli/doctor-readiness.ts +16 -24
  269. package/src/cli/intake-cli.ts +1 -1
  270. package/src/cli/mcp-call.ts +9 -0
  271. package/src/cli/mcp-gateway-cli.ts +7 -3
  272. package/src/cli/nexus-ios-bind.ts +25 -8
  273. package/src/cli/phone-link.ts +117 -0
  274. package/src/cli/plugin-cli.ts +136 -11
  275. package/src/cli/pr-lineage.ts +2 -1
  276. package/src/cli/private-ledger-commands.ts +31 -0
  277. package/src/cli/pwa-start.ts +3 -0
  278. package/src/cli/release-cli.ts +97 -7
  279. package/src/cli/self-update.ts +7 -4
  280. package/src/cli/start-cli.ts +33 -14
  281. package/src/dashboard/acp-boot.ts +3 -0
  282. package/src/dashboard/index.ts +62 -13
  283. package/src/dashboard/input/no-provider-reply.ts +13 -0
  284. package/src/dashboard/slash-runtime/unknown-slash-reply.ts +78 -0
  285. package/src/dashboard/stale-install-check.ts +77 -0
  286. package/src/decisions/decision-ledger.ts +404 -0
  287. package/src/domains/outbound-alert.ts +13 -7
  288. package/src/domains/telegram-kind-route.ts +21 -0
  289. package/src/execution-loop/dispatch-task.ts +139 -0
  290. package/src/execution-loop/gate-decision-summary.ts +29 -0
  291. package/src/execution-loop/gates.ts +197 -0
  292. package/src/field/field-media.ts +245 -0
  293. package/src/graph-kinds/installed-plugin-nodes.ts +95 -0
  294. package/src/graph-kinds/plugin-nodes.ts +10 -2
  295. package/src/graph-runner/runner.ts +35 -5
  296. package/src/grounding/token.ts +12 -6
  297. package/src/harness/harness-cli-command.ts +68 -9
  298. package/src/harness/harness-pod-dispatch.ts +8 -2
  299. package/src/harness/harness-stop.ts +147 -18
  300. package/src/harness/self-send-decide.ts +26 -17
  301. package/src/harness/self-send-target.ts +22 -1
  302. package/src/harness/worktree-branch-prefix.ts +4 -3
  303. package/src/index.ts +50 -13
  304. package/src/instance/resolve.ts +6 -0
  305. package/src/intake-plane/collect-telegram-saved.ts +5 -0
  306. package/src/intake-plane/digest.ts +11 -2
  307. package/src/live/detail-switch.ts +8 -3
  308. package/src/llm/usable-llm.ts +32 -0
  309. package/src/loops/registry.ts +27 -5
  310. package/src/market/publish.ts +59 -3
  311. package/src/native-tool-catalog.ts +2 -2
  312. package/src/nexus/api/field-uploads.ts +143 -0
  313. package/src/nexus/api/graph-approvals.ts +174 -0
  314. package/src/nexus/api/harness-api.ts +219 -5
  315. package/src/nexus/api/http-server.ts +69 -6
  316. package/src/nexus/api/log-fabric.ts +10 -1
  317. package/src/nexus/api/mcp-oauth-hint.ts +10 -9
  318. package/src/nexus/api/merge-approvals.ts +2 -1
  319. package/src/nexus/api/meta-api.ts +14 -0
  320. package/src/nexus/api/plugins-credentials-api.ts +47 -0
  321. package/src/nexus/api/plugins-market.ts +87 -3
  322. package/src/nexus/api/pod-credential-api.ts +68 -14
  323. package/src/nexus/api/public-routes.ts +7 -0
  324. package/src/nexus/api/trace.ts +57 -6
  325. package/src/nexus/boot/register-mcp-clients.ts +99 -14
  326. package/src/nexus/index.ts +33 -32
  327. package/src/nexus/outbound/router.ts +1 -1
  328. package/src/notify/llm-budget-alert.ts +119 -0
  329. package/src/onboarding.ts +25 -2
  330. package/src/plugins/core/host.ts +14 -0
  331. package/src/plugins/install/market-fetch.ts +4 -3
  332. package/src/plugins/install/plugin-credentials.ts +106 -0
  333. package/src/plugins/install/plugin-install.ts +163 -32
  334. package/src/plugins/maker/plugin-maker.ts +208 -0
  335. package/src/prompt-library/universal-preamble.ts +8 -2
  336. package/src/relay/channel.ts +184 -0
  337. package/src/relay/mux.ts +182 -0
  338. package/src/relay/test-vectors.json +29 -0
  339. package/src/release-loop/checklist.ts +217 -0
  340. package/src/release-loop/release-note.ts +6 -3
  341. package/src/seat-address/seat-address.ts +34 -0
  342. package/src/self-dev/dev-cli.ts +40 -20
  343. package/src/self-dev/dev-pipeline.ts +77 -1
  344. package/src/self-implement/gate-baseline.ts +3 -1
  345. package/src/self-implement/goal-author.ts +18 -1
  346. package/src/self-implement/host-regate.ts +6 -2
  347. package/src/self-implement/orchestrator.ts +7 -2
  348. package/src/self-implement/run-ledger-compact.ts +1 -1
  349. package/src/self-implement/seams.ts +4 -2
  350. package/src/session/index.ts +23 -10
  351. package/src/skills/tools/dev-harness.ts +42 -0
  352. package/src/steward/steward-cards.ts +45 -0
  353. package/src/steward/triage.ts +50 -14
  354. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +61 -3
  355. package/src/task-orchestrator/surfaces/pod-bun-cache.ts +18 -0
  356. package/src/task-orchestrator/surfaces/pod-command-job.ts +36 -6
  357. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +39 -9
  358. package/src/task-orchestrator/surfaces/pod-source-bundle.ts +16 -1
  359. package/src/task-orchestrator/surfaces/pod-source-receive.ts +51 -13
  360. package/src/task-orchestrator/surfaces/self-implement-pod.ts +172 -28
  361. package/src/telegram-report.ts +12 -6
  362. package/src/telegram.ts +108 -1
  363. package/src/tui-client/acp-transport-ws-client.ts +72 -5
  364. package/src/tui.ts +3 -2
  365. package/src/user-config.ts +32 -2
  366. package/src/util/spawn-sync-output.ts +15 -0
  367. package/src/version/code-revision.ts +1 -1
  368. package/src/version/packed-revision.json +1 -1
  369. package/src/video-pipeline/recipes/explainer.ts +213 -0
  370. package/src/video-pipeline/walk-line.ts +3 -2
  371. package/src/web-search/free.ts +77 -0
  372. package/src/web-search/index.ts +2 -0
  373. package/src/workflow-runtime/deps-bridge.ts +4 -3
  374. package/src/workflow-runtime/plugin-kind-node.ts +32 -4
  375. package/src/workflow-runtime/types.ts +2 -1
  376. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  377. package/apps/pwa/out/_next/static/chunks/1191-7b13a24117a689fa.js +0 -1
  378. package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
  379. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  380. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  381. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  382. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  383. package/apps/pwa/out/_next/static/chunks/2375-3fcae5186c64fe6b.js +0 -1
  384. package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +0 -1
  385. package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
  386. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  387. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  388. package/apps/pwa/out/_next/static/chunks/319-d37df1b6e0cbeff4.js +0 -1
  389. package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
  390. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  391. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  392. package/apps/pwa/out/_next/static/chunks/4836-3c93795ab834381e.js +0 -1
  393. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  394. package/apps/pwa/out/_next/static/chunks/6090-1bd6077591603947.js +0 -1
  395. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  396. package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
  397. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  398. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  399. package/apps/pwa/out/_next/static/chunks/7457-f5cb1cc8db9f7453.js +0 -1
  400. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  401. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  402. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  403. package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +0 -1
  404. package/apps/pwa/out/_next/static/chunks/8519-0059d82bdb4812bf.js +0 -1
  405. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  406. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  407. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  408. package/apps/pwa/out/_next/static/chunks/9740-52467f244755917f.js +0 -1
  409. package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +0 -1
  410. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  412. package/apps/pwa/out/_next/static/chunks/app/approvals/page-21c3261af6ab377c.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1991445b8ea86274.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/app/board/page-4ebfaee2369fe248.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/app/botlab/page-9fa879ebf5959096.js +0 -1
  416. package/apps/pwa/out/_next/static/chunks/app/bots/page-ca7c3cc305c3285a.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/app/chat/page-9951930add3d2051.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/app/control/page-4472ddf1e2458e69.js +0 -1
  419. package/apps/pwa/out/_next/static/chunks/app/design-check/page-4de08d4b766f134b.js +0 -1
  420. package/apps/pwa/out/_next/static/chunks/app/editor/page-7a975e2b9db1dfb7.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  422. package/apps/pwa/out/_next/static/chunks/app/intake/page-49270714ef35bb96.js +0 -1
  423. package/apps/pwa/out/_next/static/chunks/app/layout-33fbdf353e2a5982.js +0 -1
  424. package/apps/pwa/out/_next/static/chunks/app/live/page-259f122e2dd64317.js +0 -1
  425. package/apps/pwa/out/_next/static/chunks/app/market/page-65b82726753658e1.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4b8df3748dc06285.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/app/morning/page-112f7e477bda77fd.js +0 -1
  428. package/apps/pwa/out/_next/static/chunks/app/not-found-542a5c0006da637c.js +0 -1
  429. package/apps/pwa/out/_next/static/chunks/app/observatory/page-63ffb347831262ea.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/app/page-98e4e359d97263b4.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/app/reflection/page-97e24ac675ce576e.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ac6391f26391d89d.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f45c9d45ffc43bd.js +0 -1
  434. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-36995b9cb5d98218.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/app/settings/page-0c795b577988d63f.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b4311e2f71c6735a.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/app/setup/page-eb2381074df20dba.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/app/share/page-b01336e64eba01ae.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/app/showroom/page-7af7f21c1f2858ef.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/app/tasks/page-a98ecd364586c89b.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/app/term/page-57f0502dfbfa089b.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/app/trace/page-3467d2e1abd8a14a.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/app/vault/page-7842ca23d53ba682.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-ae9ac882912b0198.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/app/workflows/page-3a0d72e3f783bcef.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/app/workspace/page-f3b39fe8358c2a97.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-5a22af3181ac2afb.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/main-app-1457627bb302a22c.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  454. package/apps/pwa/out/_next/static/css/e66b7f5efc8df3f3.css +0 -1
  455. /package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → qMI0jnlZaBjjm4aIDYNGM}/_ssgManifest.js +0 -0
@@ -0,0 +1,42 @@
1
+ // Named tui-sim keys and raw PTY input bytes.
2
+ const KEYS: Record<string, string> = {
3
+ enter: '\r', return: '\r', tab: '\t', 'shift-tab': '\x1b[Z', esc: '\x1b', escape: '\x1b',
4
+ space: ' ', backspace: '\x7f', delete: '\x1b[3~',
5
+ up: '\x1b[A', down: '\x1b[B', right: '\x1b[C', left: '\x1b[D',
6
+ pageup: '\x1b[5~', pagedown: '\x1b[6~', home: '\x1b[H', end: '\x1b[F',
7
+ 'shift-enter': '\x1b[13;2u',
8
+ };
9
+
10
+ export function resolveKeyBytes(name: string): string | null {
11
+ const key = name.toLowerCase();
12
+ if (Object.hasOwn(KEYS, key)) return KEYS[key]!;
13
+ if (/^ctrl-[a-z]$/.test(key)) return String.fromCharCode(key.charCodeAt(5) - 96);
14
+ if (key === 'ctrl-\\') return '\x1c';
15
+ if (key === 'ctrl-]') return '\x1d';
16
+ if (key.startsWith('hex:')) {
17
+ const hex = key.slice(4);
18
+ return hex.length > 0 && hex.length % 2 === 0 && /^[0-9a-f]+$/.test(hex)
19
+ ? Buffer.from(hex, 'hex').toString('latin1')
20
+ : null;
21
+ }
22
+ if (key.startsWith('alt-')) {
23
+ const suffix = name.slice(4);
24
+ const bytes = Object.hasOwn(KEYS, suffix.toLowerCase()) ? KEYS[suffix.toLowerCase()]!
25
+ : suffix.toLowerCase().startsWith('ctrl-') ? resolveKeyBytes(suffix)
26
+ : [...suffix].length === 1 ? suffix : null;
27
+ return bytes === null ? null : `\x1b${bytes}`;
28
+ }
29
+ return null;
30
+ }
31
+
32
+ /** Keep raw hex as a Buffer: passing a latin1 string to the PTY would UTF-8 encode bytes >= 0x80. */
33
+ export function resolveKeyInput(name: string): string | Buffer | null {
34
+ const bytes = resolveKeyBytes(name);
35
+ if (bytes === null) return null;
36
+ return name.toLowerCase().startsWith('hex:') ? Buffer.from(name.slice(4), 'hex') : bytes;
37
+ }
38
+
39
+ /** Bun's native PTY passes Buffer bytes through unchanged; its typed registry API only declares strings. */
40
+ export function writeKeyInput(handle: { write(chars: string): void }, input: string | Buffer): void {
41
+ handle.write(input as string);
42
+ }
@@ -0,0 +1,56 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import type { ResolveRoleProviderFn } from '../src/intake-plane/runtime-callables.js';
4
+ import type { LLMProvider } from '../src/llm.js';
5
+ import { formatFrontRouteReport, loadFrontRouteCorpus, measureFrontRoute } from './lib/front-route-measurement.js';
6
+
7
+ interface RunnerOptions { providers: string[]; runs: number; json: boolean }
8
+
9
+ export function parseFrontRouteArgs(args: readonly string[]): RunnerOptions {
10
+ const options: RunnerOptions = { providers: [], runs: 1, json: false };
11
+ for (let i = 0; i < args.length; i++) {
12
+ const arg = args[i];
13
+ if (arg === '--json') options.json = true;
14
+ else if (arg === '--provider') {
15
+ const value = args[++i];
16
+ if (!value || value.startsWith('--') || !/^[\w-]+(?:\/\S+)?$/.test(value)) throw new Error('--provider requires <name>[/<model>]');
17
+ options.providers.push(value);
18
+ } else if (arg === '--runs') {
19
+ const value = args[++i];
20
+ if (!value || !/^[1-9]\d*$/.test(value) || !Number.isSafeInteger(Number(value))) throw new Error('--runs requires a positive integer');
21
+ options.runs = Number(value);
22
+ } else throw new Error(`Unknown option: ${arg}`);
23
+ }
24
+ return options;
25
+ }
26
+
27
+ export function frontRouteProviders(specs: readonly string[], registry: Record<string, LLMProvider>): { label: string; resolve: ResolveRoleProviderFn }[] {
28
+ return specs.map((label) => {
29
+ const slash = label.indexOf('/');
30
+ const name = slash < 0 ? label : label.slice(0, slash);
31
+ const model = slash < 0 ? undefined : label.slice(slash + 1);
32
+ const provider = registry[name];
33
+ if (!provider) throw new Error(`Unknown provider: ${name}`);
34
+ const selectedModel = model ?? provider.defaultModel;
35
+ const resolveRole: ResolveRoleProviderFn = (_role) => ({ provider: provider as unknown as ReturnType<ResolveRoleProviderFn>['provider'], ...(typeof selectedModel === 'string' ? { model: selectedModel } : {}) });
36
+ return { label, resolve: resolveRole };
37
+ });
38
+ }
39
+
40
+ export async function main(args: readonly string[] = process.argv.slice(2), env: NodeJS.ProcessEnv = process.env): Promise<void> {
41
+ const options = parseFrontRouteArgs(args);
42
+ if (!env.CORPUS_PATH?.trim()) throw new Error('CORPUS_PATH is required (path to front-route corpus JSON)');
43
+ const corpus = loadFrontRouteCorpus(readFileSync(resolve(process.cwd(), env.CORPUS_PATH), 'utf8'));
44
+ const { PROVIDERS } = await import('../src/llm.js');
45
+ let selected = options.providers;
46
+ if (!selected.length) {
47
+ const { resolveRoleLlm } = await import('../src/user-config.js');
48
+ const { provider, model } = resolveRoleLlm('classify');
49
+ selected = [model ? `${provider}/${model}` : provider];
50
+ }
51
+ const providers = frontRouteProviders(selected, PROVIDERS);
52
+ const result = await measureFrontRoute(corpus.items, { providers, runs: options.runs });
53
+ console.log(options.json ? JSON.stringify(result, null, 2) : formatFrontRouteReport(result));
54
+ }
55
+
56
+ if (import.meta.main) await main();
@@ -0,0 +1,21 @@
1
+ #!/bin/zsh
2
+ # 공개 영상 누출 관문 — 1초 한 장(또는 --fps N) 전수 OCR → 금지 목록 → 초 단위 표(hits.tsv).
3
+ # 사용: scripts/media/ocr-leak-scan.sh <영상> <산출 폴더> [--fps N]
4
+ # 한계: tesseract eng — 한글 칸·이모지는 사람 눈으로 따로 본다(내부 문서 `TC` §2). 걸림은 오독이 많다 → 그 초 프레임을 눈으로 대조한다.
5
+ V=$1; O=$2; FPS=1
6
+ [ "${3:-}" = "--fps" ] && FPS=${4:-1}
7
+ [ -f "$V" ] && [ -n "$O" ] || { echo "사용: $0 <영상> <산출 폴더> [--fps N]" >&2; exit 2; }
8
+ command -v tesseract >/dev/null && command -v ffmpeg >/dev/null || { echo "tesseract·ffmpeg 가 필요하다" >&2; exit 2; }
9
+ mkdir -p "$O/f" && ffmpeg -loglevel error -i "$V" -vf "fps=$FPS" "$O/f/%05d.png" || exit 2
10
+ # 금지: 홈 경로 · 실명 계정 표지 · 사설 호스트 · tailnet · 금액 · 크레딧 수 · 메일 · 키·토큰 모양(elanous 단기 토큰 elt_ · bearer · GitHub 토큰 · 40자+ 연속)
11
+ PAT='/Users/|msb[0-9]|\bmbp\b|tail[0-9a-f]{4,}|ts\.net|\$[0-9]|USD|credit[s]? *[0-9]|@gmail|api[_-]?key|sk-[A-Za-z0-9]{8}|elt_[A-Za-z0-9_-]{6}|bearer|ghs_|gh[po]_|[A-Za-z0-9_-]{40,}'
12
+ [ -n "${OCR_EXTRA_PATTERN:-}" ] && PAT="$PAT|$OCR_EXTRA_PATTERN" # 계정 실명 등은 저장소에 적지 않고 환경변수로 준다
13
+ : > "$O/hits.tsv"
14
+ for f in "$O"/f/*.png; do
15
+ n=${${f:t:r}#0*}; s=$(( (n - 1) / FPS ))
16
+ t=$(tesseract "$f" - -l eng --psm 11 2>/dev/null)
17
+ print -r -- "$t" > "$O/f/${f:t:r}.txt"
18
+ print -r -- "$t" | grep -inE "$PAT" | grep -vE '^[0-9]+:[^ ]*run-[0-9a-f]{8}-[0-9a-f-]{27}$' \
19
+ | while read -r line; do print -r -- "$s\t$line" >> "$O/hits.tsv"; done
20
+ done
21
+ print "frames=$(ls "$O"/f/*.png | wc -l | tr -d ' ') hits=$(wc -l < "$O/hits.tsv" | tr -d ' ')"
@@ -23,6 +23,7 @@ import { userInfo } from 'node:os';
23
23
  import { dirname, join, posix, resolve } from 'node:path';
24
24
  import ts from 'typescript';
25
25
  import { parse } from 'yaml';
26
+ import { spawnSyncText } from '../src/util/spawn-sync-output.js';
26
27
 
27
28
  export interface ExportTransform {
28
29
  readonly id: string;
@@ -369,9 +370,7 @@ export function summarizeLeaks(hits: readonly LeakHit[]): Record<string, { hits:
369
370
  }
370
371
 
371
372
  function trackedFiles(root: string): string[] {
372
- const listed = spawnSync('git', ['-C', root, 'ls-files', '-z'], { encoding: 'utf8' });
373
- if (listed.status !== 0) throw new Error(`git ls-files failed: ${listed.stderr.trim()}`);
374
- return listed.stdout.split('\0').filter(Boolean);
373
+ return spawnSyncText('git', ['-C', root, 'ls-files', '-z']).split('\0').filter(Boolean);
375
374
  }
376
375
 
377
376
  export function run(argv: readonly string[], root = resolve(import.meta.dir, '..')): number {
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env bun
2
- /** Import a public PR patch without fetching, committing, or publishing it. */
2
+ /** Import a public PR patch without committing or publishing it. */
3
3
  import { spawnSync } from 'node:child_process';
4
4
  import { existsSync, lstatSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync, unlinkSync } from 'node:fs';
5
5
  import { tmpdir } from 'node:os';
6
6
  import { dirname, isAbsolute, join } from 'node:path';
7
7
  import { loadExportConfig, selectExportFiles, transformExportFiles } from './public-export.js';
8
+ import { debug } from '../src/debug/log.js';
9
+ import { spawnSyncText } from '../src/util/spawn-sync-output.js';
8
10
 
9
11
  export type ImportKind = 'clean' | 'renamed' | 'transformed' | 'outside-export';
10
12
  export interface ImportFile { publicPath: string; sourcePath?: string; kind: ImportKind; reason?: string }
@@ -83,9 +85,7 @@ function gitApply(root: string, patch: string, check = false): string | undefine
83
85
  }
84
86
 
85
87
  function trackedFiles(root: string): string[] {
86
- const result = spawnSync('git', ['ls-files', '-z'], { cwd: root, encoding: 'utf8' });
87
- if (result.status !== 0) throw new Error(`git ls-files failed: ${result.stderr}`);
88
- return result.stdout.split('\0').filter(Boolean);
88
+ return spawnSyncText('git', ['ls-files', '-z'], { cwd: root, encoding: 'utf8' }).split('\0').filter(Boolean);
89
89
  }
90
90
 
91
91
  /** Classify against the real export, stage eligible edits, then write only on --apply. */
@@ -177,8 +177,82 @@ export function importPublicPatch(patch: string, root: string, apply = false, au
177
177
  } finally { rmSync(staging, { recursive: true, force: true }); }
178
178
  }
179
179
 
180
- /** CLI entrypoint: bun scripts/public-import-pr.ts [patch-file] --author 'Name <email>' [--apply]. */
181
- export function run(argv: readonly string[], root = process.cwd(), stdin?: string): number {
180
+ interface GhResult { status: number | null; stdout: string; stderr?: string }
181
+ interface RunDeps { gh?: (argv: readonly string[], root: string) => GhResult }
182
+
183
+ function prAuthors(raw: string, databaseIdForLogin: (login: string) => number): string[] {
184
+ const parsed: unknown = JSON.parse(raw);
185
+ if (!parsed || typeof parsed !== 'object' || !('commits' in parsed) || !Array.isArray(parsed.commits) || !parsed.commits.length) {
186
+ throw new Error('PR has no commits');
187
+ }
188
+ const unique = new Map<string, string>();
189
+ const seenEmails = new Set<string>();
190
+ for (const commit of parsed.commits) {
191
+ const identities = commit?.authors ?? (commit?.author ? [commit.author] : []);
192
+ if (!Array.isArray(identities) || !identities.length) throw new Error('PR commit has no author');
193
+ for (const identity of identities) {
194
+ const name = identity?.name?.trim();
195
+ const email = identity?.email?.trim();
196
+ const login = identity?.login?.trim() ?? identity?.user?.login?.trim();
197
+ const key = login ? `login:${login.toLowerCase()}` : `email:${email?.toLowerCase()}`;
198
+ if (unique.has(key) || (email && seenEmails.has(email.toLowerCase()))) continue;
199
+ const id = identity?.user?.databaseId ?? identity?.id;
200
+ const numericId = /^[1-9]\d*$/.test(String(id)) ? Number(id) : undefined;
201
+ const address = email || (login && /^[A-Za-z0-9-]+$/.test(login)
202
+ ? `${numericId && Number.isSafeInteger(numericId) ? numericId : databaseIdForLogin(login)}+${login}@users.noreply.github.com`
203
+ : undefined);
204
+ if (typeof name !== 'string' || !/^[^<>\r\n]+$/.test(name) || !address || !/^[^<>\s@]+@[^<>\s@]+$/.test(address)) {
205
+ throw new Error('PR commit author lacks a valid name or email/identity');
206
+ }
207
+ unique.set(key, `${name} <${address}>`);
208
+ seenEmails.add(address.toLowerCase());
209
+ }
210
+ }
211
+ return [...unique.values()];
212
+ }
213
+
214
+ /** CLI entrypoint: patch + --author, or --pr <number> --repo <owner/name>. */
215
+ export function run(argv: readonly string[], root = process.cwd(), stdin?: string, deps: RunDeps = {}): number {
216
+ const prMode = argv.includes('--pr') || argv.includes('--repo');
217
+ if (prMode) {
218
+ const prIndex = argv.indexOf('--pr');
219
+ const repoIndex = argv.indexOf('--repo');
220
+ const pr = argv[prIndex + 1];
221
+ const repo = argv[repoIndex + 1];
222
+ const flags = argv.filter((arg) => arg === '--apply' || arg === '--dry-run');
223
+ if (prIndex < 0 || repoIndex < 0 || !pr || !/^[1-9]\d*$/.test(pr) || !repo ||
224
+ !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repo) ||
225
+ argv.length !== 4 + flags.length || flags.length > 1 ||
226
+ argv.some((arg, i) => (arg === '--pr' && i !== prIndex) || (arg === '--repo' && i !== repoIndex))) {
227
+ console.error('usage: bun scripts/public-import-pr.ts --pr <number> --repo <owner/name> [--dry-run|--apply]');
228
+ return 2;
229
+ }
230
+ try {
231
+ const gh = deps.gh ?? ((args: readonly string[], cwd: string) => spawnSync('gh', [...args], { cwd, encoding: 'utf8' }));
232
+ const fetch = (args: string[]): string => {
233
+ const result = gh(args, root);
234
+ if (result.status !== 0) throw new Error(result.stderr || `gh ${args[1]} failed`);
235
+ return result.stdout;
236
+ };
237
+ const patch = fetch(['pr', 'diff', pr, '--repo', repo]);
238
+ const authors = prAuthors(fetch(['pr', 'view', pr, '--repo', repo, '--json', 'commits']), (login) => {
239
+ const user: unknown = JSON.parse(fetch(['api', `users/${login}`]));
240
+ const id = user && typeof user === 'object' && 'id' in user ? user.id : undefined;
241
+ const resolvedLogin = user && typeof user === 'object' && 'login' in user ? user.login : undefined;
242
+ if (typeof id !== 'number' || !Number.isSafeInteger(id) || id <= 0 ||
243
+ typeof resolvedLogin !== 'string' || resolvedLogin.toLowerCase() !== login.toLowerCase()) {
244
+ throw new Error('GitHub user lacks a matching login or numeric database ID');
245
+ }
246
+ return id;
247
+ });
248
+ const result = importPublicPatch(patch, root, flags.includes('--apply'));
249
+ debug.log('public-import.pr', 'fetched', { repo, pr: Number(pr), files: result.files.length, authors: authors.length });
250
+ for (const file of result.files) console.log(`${file.kind}\t${file.publicPath}\t${file.sourcePath ?? '-'}${file.reason ? `\t${file.reason}` : ''}`);
251
+ console.log(`\nCommit message draft (not committed):\nImport public PR ${repo}#${pr}\n\nhttps://github.com/${repo}/pull/${pr}\n\n${authors.map((author) => `Co-Authored-By: ${author}`).join('\n')}`);
252
+ if (result.rc) console.error('import incomplete: one or more files were not applied');
253
+ return result.rc;
254
+ } catch (error) { console.error(`import failed: ${String(error)}`); return 1; }
255
+ }
182
256
  const apply = argv.includes('--apply');
183
257
  const authorIndex = argv.indexOf('--author');
184
258
  const author = authorIndex >= 0 ? argv[authorIndex + 1] : undefined;
@@ -3,6 +3,7 @@ import { createHash } from 'node:crypto';
3
3
  import { spawnSync } from 'node:child_process';
4
4
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, statSync, symlinkSync, writeFileSync } from 'node:fs';
5
5
  import { join, resolve } from 'node:path';
6
+ import { spawnSyncText } from '../src/util/spawn-sync-output.js';
6
7
 
7
8
  export function publishInternalDist(checkout: string, out: string, keep = 3): { ok: true; version: string; commit: string; sha256: string; out: string; pwaBuild: 'present' | 'missing' } {
8
9
  checkout = resolve(checkout);
@@ -42,9 +43,7 @@ export function publishInternalDist(checkout: string, out: string, keep = 3): {
42
43
  const isBuildOutput = (path: string): boolean => path.startsWith('apps/pwa/out/') || path === 'src/version/packed-revision.json';
43
44
  const publishedPaths = [...new Set([...packedPaths, 'scripts/install.sh'])].filter((path) => !isBuildOutput(path));
44
45
  const pwaBuild: 'present' | 'missing' = packedPaths.includes('apps/pwa/out/index.html') ? 'present' : 'missing';
45
- const tracked = spawnSync('git', ['ls-files', '--cached', '-z'], { cwd: checkout, encoding: 'utf8' });
46
- if (tracked.status !== 0) throw new Error(`cannot list committed files: ${tracked.stderr}`);
47
- const committed = new Set(tracked.stdout.split('\0').filter(Boolean));
46
+ const committed = new Set(spawnSyncText('git', ['ls-files', '--cached', '-z'], { cwd: checkout, encoding: 'utf8' }).split('\0').filter(Boolean));
48
47
  const uncommitted = publishedPaths.filter((path) => !committed.has(path));
49
48
  if (uncommitted.length) throw new Error(`packed files not committed at HEAD: ${uncommitted.join(', ')}`);
50
49
  const changed = spawnSync('git', ['diff', '--quiet', 'HEAD', '--', ...publishedPaths], { cwd: checkout, encoding: 'utf8' });
@@ -71,7 +71,7 @@ export async function main(argv: readonly string[]): Promise<number> {
71
71
  const grown = diffs.filter((d) => d.newRed.length > 0);
72
72
  if (argv.includes('--notify') && grown.length > 0) {
73
73
  const { sendOutbound } = await import('../src/domains/outbound-alert.js');
74
- sendOutbound(`⚠️ **파일별 빨강 스윕 — 새 빨강**\n\n${grown.map(renderDiff).join('\n\n')}`, 'alert');
74
+ sendOutbound(`⚠️ **파일별 빨강 스윕 — 새 빨강**\n\n${grown.map(renderDiff).join('\n\n')}`, 'ops-alert');
75
75
  }
76
76
  return diffs.length === 0 ? 2 : 0;
77
77
  }
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env bun
2
+ import { mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
3
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
4
+ import { debug } from '../../src/debug/log.js';
5
+ import { effectiveInstanceRoot } from '../../src/instance/resolve.js';
6
+ import { emitNodeResult, readGraphContext, type GraphContext } from './node-verdict.js';
7
+
8
+ export type MetricVerdict = 'pass' | 'fail' | 'unmeasured';
9
+ export interface ApprovalMetric { name: string; value: string; verdict: MetricVerdict }
10
+
11
+ const outcomeMetric = (name: string, node: Record<string, unknown> | undefined): ApprovalMetric => ({
12
+ name, value: node?.outcome === undefined ? 'missing' : String(node.outcome),
13
+ verdict: node?.outcome === undefined ? 'unmeasured' : node.outcome === 'ok' ? 'pass' : 'fail',
14
+ });
15
+
16
+ export function runAutoApprove(context: GraphContext = readGraphContext(), deps: { instanceRoot?: string; repo?: string; bunVersion?: string } = {}) {
17
+ const { version } = context.input;
18
+ const root = deps.instanceRoot ?? effectiveInstanceRoot();
19
+ const repo = deps.repo ?? resolve(import.meta.dir, '../..');
20
+ const gate = context.outputs.gate;
21
+ const prepare = context.outputs.prepare;
22
+ let pinned: string | undefined;
23
+ try { pinned = readFileSync(join(repo, '.bun-version'), 'utf8').trim(); } catch { pinned = undefined; }
24
+ const bunVersion = deps.bunVersion ?? Bun.version;
25
+ const out = prepare?.out;
26
+ let distCount: number | undefined;
27
+ if (prepare?.outcome === 'ok' && typeof out === 'string' && isAbsolute(out)
28
+ && resolve(out) === join(root, 'release', version, 'prepared')) {
29
+ try { distCount = readdirSync(join(out, 'dist'), { withFileTypes: true }).filter((entry) => entry.isFile()).length; }
30
+ catch { distCount = undefined; }
31
+ }
32
+ const gateErrorAbsent = gate && !Object.hasOwn(gate, 'error');
33
+ const metrics: ApprovalMetric[] = [
34
+ { name: 'gate regressions and measurement', value: JSON.stringify({ outcome: gate?.outcome ?? null, introduced: gate?.introduced ?? null, error: gate?.error ?? null }),
35
+ verdict: !gate || gate.outcome === undefined || !Array.isArray(gate.introduced) ? 'unmeasured'
36
+ : gate.outcome === 'ok' && gate.introduced.length === 0 && gateErrorAbsent ? 'pass' : 'fail' },
37
+ { name: 'bun version', value: `running=${bunVersion}; pinned=${pinned ?? 'missing'}`,
38
+ verdict: !pinned ? 'unmeasured' : bunVersion === pinned ? 'pass' : 'fail' },
39
+ outcomeMetric('upgrade', context.outputs.upgrade),
40
+ outcomeMetric('tui', context.outputs.tui),
41
+ outcomeMetric('notes-check', context.outputs['notes-check']),
42
+ { name: 'public leak and prepare completeness', value: `prepare=${prepare?.outcome ?? 'missing'}`,
43
+ verdict: prepare?.outcome === undefined ? 'unmeasured' : prepare.outcome === 'ok' ? 'pass' : 'fail' },
44
+ { name: 'docs notes page', value: `docs=${context.outputs.docs?.outcome ?? 'missing'}; notes=${String(context.outputs.docs?.notes ?? 'missing')}`,
45
+ verdict: context.outputs.docs?.outcome === undefined || context.outputs.docs.notes === undefined ? 'unmeasured'
46
+ : context.outputs.docs.outcome === 'ok' && typeof context.outputs.docs.notes === 'string' && context.outputs.docs.notes.trim() ? 'pass' : 'fail' },
47
+ { name: 'installation files', value: `dist files=${distCount ?? 'missing'}; prepare.out=${String(out ?? 'missing')}`,
48
+ verdict: prepare?.outcome !== 'ok' ? prepare?.outcome === undefined ? 'unmeasured' : 'fail'
49
+ : distCount === undefined ? 'unmeasured' : distCount === 5 ? 'pass' : 'fail' },
50
+ ];
51
+ const outcome = metrics.every((metric) => metric.verdict === 'pass') ? 'ok' as const : 'fail' as const;
52
+ const report = join(root, 'release', version, 'auto-approval.md');
53
+ const lines = [`# v${version} auto-approval`, '', `Outcome: ${outcome}`, '', '| # | Metric | Value | Verdict |', '|---|---|---|---|',
54
+ ...metrics.map((metric, i) => `| ${i + 1} | ${metric.name} | ${metric.value.replaceAll('|', '\\|').replace(/[\r\n]/g, ' ')} | ${metric.verdict} |`),
55
+ '', '## 되돌리는 법', `gh release edit v${version} --draft`, `git tag -d v${version} && git push origin :refs/tags/v${version}`, '해당 docs PR revert', ''];
56
+ mkdirSync(dirname(report), { recursive: true });
57
+ writeFileSync(report, lines.join('\n'));
58
+ debug.log('release-loop.approve', 'auto-decision', { version, outcome, metrics });
59
+ return { outcome, verdict: outcome === 'ok' ? 'pass' as const : 'fail' as const, summary: `auto approval ${outcome}: ${metrics.filter((metric) => metric.verdict === 'pass').length}/8`,
60
+ ...(outcome === 'ok' ? { decidedBy: 'release-loop metrics' as const } : {}), metrics, report };
61
+ }
62
+
63
+ if (import.meta.main) {
64
+ try {
65
+ const result = runAutoApprove();
66
+ emitNodeResult(result);
67
+ process.exitCode = result.outcome === 'ok' ? 0 : 1;
68
+ } catch (error) {
69
+ emitNodeResult({ outcome: 'fail', verdict: 'fail', summary: `auto approval unmeasured: ${error instanceof Error ? error.message : String(error)}` });
70
+ process.exitCode = 1;
71
+ }
72
+ }
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bun
2
+ import { addItem, checklistGate, listChecklist, summarizeChecklist } from '../../src/release-loop/checklist.js';
3
+ import { emitNodeResult, errorResult, readGraphContext, type GraphContext } from './node-verdict.js';
4
+
5
+ export function runChecklistGate(context: GraphContext = readGraphContext()) {
6
+ const version = context.input.version;
7
+ const data = listChecklist(version);
8
+ const gate = checklistGate(version);
9
+ const counts = summarizeChecklist(data);
10
+ const summary = `확인표 🟢${counts.green} 🟡${counts.yellow}(이동 ${gate.moved.length} · 알려진 문제 ${gate.knownIssues.length} · 막음 ${gate.blocked.length} · 판정 없음 ${gate.undecided.length}) 🔴${counts.red}${gate.red.length ? ` (${gate.red.join(', ')})` : ''}`;
11
+ if (gate.ok && gate.moved.length) {
12
+ const [major, minor, patch] = version.split('.').map(Number);
13
+ const next = `${major}.${minor}.${patch! + 1}`;
14
+ for (const id of gate.moved) {
15
+ const item = data.items.find((candidate) => candidate.id === id)!;
16
+ if (listChecklist(next).items.some((candidate) => candidate.id === id)) continue;
17
+ try { addItem(next, { id, title: item.title, ...(item.owner !== undefined ? { owner: item.owner } : {}) }); }
18
+ catch (error) {
19
+ if (!listChecklist(next).items.some((candidate) => candidate.id === id)) throw error;
20
+ }
21
+ }
22
+ }
23
+ return { outcome: gate.ok ? 'ok' as const : 'fail' as const, verdict: gate.ok ? 'pass' as const : 'fail' as const, summary, ...gate };
24
+ }
25
+
26
+ if (import.meta.main) {
27
+ try {
28
+ const result = runChecklistGate();
29
+ emitNodeResult(result);
30
+ process.exitCode = result.outcome === 'ok' ? 0 : 1;
31
+ } catch (error) {
32
+ emitNodeResult(errorResult(error));
33
+ process.exitCode = 2;
34
+ }
35
+ }
@@ -18,6 +18,35 @@ export function parseFailures(output: string): string[] {
18
18
  return [...failures].sort();
19
19
  }
20
20
 
21
+ const xmlText = (value: string): string => value
22
+ .replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&lt;/g, '<').replace(/&gt;/g, '>')
23
+ .replace(/&#(\d+);/g, (_, code: string) => String.fromCodePoint(Number(code))).replace(/&amp;/g, '&');
24
+
25
+ /** Failed test names from a bun junit report, in the same `<file> > <describe> > <name>` shape as {@link parseFailures}.
26
+ * Used when the console summary counts a failure but prints no `(fail)` line for it (09-30 0.2.6 gate: `--dots`). */
27
+ export function junitFailures(xml: string): string[] {
28
+ const failures = new Set<string>();
29
+ const suites: string[] = [];
30
+ let file: string | undefined;
31
+ let open: string | undefined;
32
+ for (const match of xml.matchAll(/<(\/?)(testsuite|testcase|failure)\b([^>]*?)(\/?)>/g)) {
33
+ const [, closing, tag, attrs, selfClosing] = match;
34
+ const attr = (name: string) => { const m = new RegExp(`\\b${name}="([^"]*)"`).exec(attrs!); return m ? xmlText(m[1]!) : undefined; };
35
+ if (tag === 'testsuite') {
36
+ if (closing) { suites.pop(); continue; }
37
+ suites.push(attr('name') ?? '');
38
+ if (suites.length === 1) file = attr('file') ?? attr('name');
39
+ } else if (tag === 'testcase') {
40
+ if (closing) { open = undefined; continue; }
41
+ const name = [...suites.slice(1), attr('name') ?? ''].join(' > ');
42
+ open = selfClosing ? undefined : `${attr('file') ?? file} > ${name}`;
43
+ } else if (tag === 'failure' && !closing && open) {
44
+ failures.add(open);
45
+ }
46
+ }
47
+ return [...failures].sort();
48
+ }
49
+
21
50
  export function diffFailures(cut: Iterable<string>, baseline: Iterable<string>): FailureDiff {
22
51
  const c = new Set(cut);
23
52
  const b = new Set(baseline);