elanous 0.2.16 → 0.2.18

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 (354) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/P6YTVgzuZz5oTZhY4Din_/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/{1307-b46d45b705e8c9ae.js → 1307-30dff22c9e1f3073.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{6158.9c0449a9a8d0064b.js → 6158.af2e93b7adff709b.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d92f7ee3994e84c7.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-3cb52e5ea8fc2fa5.js → page-4733c967b56a4277.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/app/board/page-d22fd849cc22f104.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-c1e20d7d338713f1.js → page-bb742f995c6769e7.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/bots/{page-482c5dbde699947c.js → page-02b073a2ac793b83.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/chat/page-8a9b3594bc23289b.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/app/consult/page-a0eb02638b1e6f31.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/app/control/{page-a0e2a6d1bfb52fc5.js → page-308111abadf35670.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-c53598cfce6156f2.js → page-5d97b2901e0d1ea6.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/app/editor/{page-13a63953f53c6a23.js → page-c39554c321576982.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/exec/{page-5d3c8429d04f3831.js → page-8c3d8e63ae2e1c98.js} +1 -1
  32. package/apps/pwa/out/_next/static/chunks/app/field/{page-3193bdc0bbab047a.js → page-9b72a07d424701c6.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/app/intake/{page-6107ca8ddd5e3bdd.js → page-a572f58cd3ca5864.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/app/live/page-be5ea965a07fbc41.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/app/loops/page-c130b63862437814.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/app/market/page-fa244bd146a00ae8.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-44d56d6391965a6a.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/app/morning/{page-4167348049166852.js → page-83a700d4c754d802.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/{not-found-de52e8fdf9095f7a.js → not-found-7b3170f374f77b9d.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-ca3952cd78a83ca2.js → page-a3c58f5c72a5f3f9.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/outputs/page-e8e1ecaea7cd71d2.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-1ebfe049c640e171.js → page-1467047104141297.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-2da6f9b8a38c6978.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/sessions/page-102310930791197a.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/settings/page-6ed0b73bae8351d8.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-abe8e3b74bea1ffc.js → page-44d5f767491692bf.js} +1 -1
  54. package/apps/pwa/out/_next/static/chunks/app/setup/page-43336055451f6e37.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/share/{page-d28573d6792b4275.js → page-3cc47dca0922ab9a.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a77ee355a2304ff0.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/tasks/{page-d751de5f8062ff5e.js → page-b17cd46cf0e9b8cf.js} +1 -1
  58. package/apps/pwa/out/_next/static/chunks/app/term/{page-fe1759f94003b32b.js → page-61553851aa606146.js} +1 -1
  59. package/apps/pwa/out/_next/static/chunks/app/today/{page-d64c984e855c8c68.js → page-a1f585e154e61c59.js} +1 -1
  60. package/apps/pwa/out/_next/static/chunks/app/trace/page-7bf54c9bb9274af8.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/vault/{page-9afdee66b820c76e.js → page-5150ef17b5383a57.js} +1 -1
  62. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-3544f154c87b43ee.js → page-134da468538baf48.js} +1 -1
  65. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/{webpack-539cde99554be20f.js → webpack-c2782b330447ffb6.js} +1 -1
  67. package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.css +1 -0
  68. package/apps/pwa/out/approvals/index.html +1 -1
  69. package/apps/pwa/out/approvals/index.txt +14 -14
  70. package/apps/pwa/out/autopilot/index.html +1 -1
  71. package/apps/pwa/out/autopilot/index.txt +14 -14
  72. package/apps/pwa/out/board/index.html +1 -1
  73. package/apps/pwa/out/board/index.txt +14 -14
  74. package/apps/pwa/out/botlab/index.html +1 -1
  75. package/apps/pwa/out/botlab/index.txt +14 -14
  76. package/apps/pwa/out/bots/index.html +1 -1
  77. package/apps/pwa/out/bots/index.txt +14 -14
  78. package/apps/pwa/out/ceo/index.html +1 -1
  79. package/apps/pwa/out/ceo/index.txt +14 -14
  80. package/apps/pwa/out/chat/index.html +1 -1
  81. package/apps/pwa/out/chat/index.txt +14 -14
  82. package/apps/pwa/out/consult/index.html +1 -1
  83. package/apps/pwa/out/consult/index.txt +14 -14
  84. package/apps/pwa/out/control/index.html +1 -1
  85. package/apps/pwa/out/control/index.txt +14 -14
  86. package/apps/pwa/out/decisions/index.html +1 -0
  87. package/apps/pwa/out/decisions/index.txt +30 -0
  88. package/apps/pwa/out/design-check/index.html +1 -1
  89. package/apps/pwa/out/design-check/index.txt +14 -14
  90. package/apps/pwa/out/editor/index.html +1 -1
  91. package/apps/pwa/out/editor/index.txt +14 -14
  92. package/apps/pwa/out/exec/index.html +1 -1
  93. package/apps/pwa/out/exec/index.txt +14 -14
  94. package/apps/pwa/out/field/index.html +1 -1
  95. package/apps/pwa/out/field/index.txt +14 -14
  96. package/apps/pwa/out/healthz +1 -1
  97. package/apps/pwa/out/index.html +1 -1
  98. package/apps/pwa/out/index.txt +14 -14
  99. package/apps/pwa/out/inside/index.html +1 -1
  100. package/apps/pwa/out/inside/index.txt +14 -14
  101. package/apps/pwa/out/intake/index.html +1 -1
  102. package/apps/pwa/out/intake/index.txt +14 -14
  103. package/apps/pwa/out/live/index.html +1 -1
  104. package/apps/pwa/out/live/index.txt +14 -14
  105. package/apps/pwa/out/loops/index.html +1 -1
  106. package/apps/pwa/out/loops/index.txt +14 -14
  107. package/apps/pwa/out/market/index.html +1 -1
  108. package/apps/pwa/out/market/index.txt +14 -14
  109. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  110. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  111. package/apps/pwa/out/morning/index.html +1 -1
  112. package/apps/pwa/out/morning/index.txt +14 -14
  113. package/apps/pwa/out/observatory/index.html +1 -1
  114. package/apps/pwa/out/observatory/index.txt +14 -14
  115. package/apps/pwa/out/ops/checklist/index.html +1 -1
  116. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  117. package/apps/pwa/out/ops/release/index.html +1 -1
  118. package/apps/pwa/out/ops/release/index.txt +14 -14
  119. package/apps/pwa/out/ops/seats/index.html +1 -1
  120. package/apps/pwa/out/ops/seats/index.txt +14 -14
  121. package/apps/pwa/out/outputs/index.html +1 -1
  122. package/apps/pwa/out/outputs/index.txt +14 -14
  123. package/apps/pwa/out/reflection/index.html +1 -1
  124. package/apps/pwa/out/reflection/index.txt +15 -15
  125. package/apps/pwa/out/scheduler/index.html +1 -1
  126. package/apps/pwa/out/scheduler/index.txt +14 -14
  127. package/apps/pwa/out/sessions/index.html +1 -1
  128. package/apps/pwa/out/sessions/index.txt +14 -14
  129. package/apps/pwa/out/settings/devices/index.html +1 -1
  130. package/apps/pwa/out/settings/devices/index.txt +14 -14
  131. package/apps/pwa/out/settings/index.html +1 -1
  132. package/apps/pwa/out/settings/index.txt +14 -14
  133. package/apps/pwa/out/setup/done/index.html +1 -1
  134. package/apps/pwa/out/setup/done/index.txt +14 -14
  135. package/apps/pwa/out/setup/index.html +1 -1
  136. package/apps/pwa/out/setup/index.txt +14 -14
  137. package/apps/pwa/out/share/index.html +1 -1
  138. package/apps/pwa/out/share/index.txt +14 -14
  139. package/apps/pwa/out/showroom/index.html +1 -1
  140. package/apps/pwa/out/showroom/index.txt +14 -14
  141. package/apps/pwa/out/tasks/index.html +1 -1
  142. package/apps/pwa/out/tasks/index.txt +14 -14
  143. package/apps/pwa/out/term/index.html +1 -1
  144. package/apps/pwa/out/term/index.txt +14 -14
  145. package/apps/pwa/out/today/index.html +1 -1
  146. package/apps/pwa/out/today/index.txt +14 -14
  147. package/apps/pwa/out/trace/index.html +1 -1
  148. package/apps/pwa/out/trace/index.txt +14 -14
  149. package/apps/pwa/out/vault/index.html +1 -1
  150. package/apps/pwa/out/vault/index.txt +14 -14
  151. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  152. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  153. package/apps/pwa/out/workflows/index.html +1 -1
  154. package/apps/pwa/out/workflows/index.txt +14 -14
  155. package/apps/pwa/out/workspace/index.html +1 -1
  156. package/apps/pwa/out/workspace/index.txt +14 -14
  157. package/apps/pwa/out/worktrees/index.html +1 -1
  158. package/apps/pwa/out/worktrees/index.txt +14 -14
  159. package/graphs/intake/intake-daily.yaml +9 -2
  160. package/graphs/intake/news-sources.yaml +4 -0
  161. package/graphs/intake/recipes.yaml +6 -1
  162. package/package.json +2 -1
  163. package/samples/workflows/templates/approval-gated-deploy.yaml +30 -0
  164. package/samples/workflows/templates/chat-driven-research.yaml +35 -0
  165. package/samples/workflows/templates/discord-listen-and-respond.yaml +29 -0
  166. package/samples/workflows/templates/http-poll-then-alert.yaml +30 -0
  167. package/samples/workflows/templates/manual-quickstart.yaml +21 -0
  168. package/samples/workflows/templates/scheduled-daily-digest.yaml +30 -0
  169. package/samples/workflows/templates/webhook-deploy-hook.yaml +27 -0
  170. package/scripts/__fixtures__/intake-news.xml +7 -0
  171. package/scripts/ci-public-export-import-gate.ts +7 -2
  172. package/scripts/ci-typecheck-changed.ts +21 -6
  173. package/scripts/discord-seat-probe.ts +3 -2
  174. package/scripts/docs/generate-cli-anatomy.ts +162 -0
  175. package/scripts/goals/authoring-metrics.ts +73 -0
  176. package/scripts/intake-news.ts +169 -0
  177. package/scripts/landing-heal/must-fix-match.ts +7 -7
  178. package/scripts/landing-heal/run-mission.ts +3 -2
  179. package/scripts/public-export-test-run.ts +37 -2
  180. package/scripts/release-loop/checklist-gate-node.ts +13 -11
  181. package/scripts/release-loop/cut-branch.ts +2 -1
  182. package/scripts/release-loop/cutoff.ts +4 -2
  183. package/scripts/release-loop/docs-land-node.ts +77 -11
  184. package/scripts/release-loop/docs-node.ts +1 -1
  185. package/scripts/release-loop/gate-node.ts +99 -14
  186. package/scripts/release-loop/node-verdict.ts +2 -1
  187. package/scripts/release-loop/npm-publish-node.ts +11 -7
  188. package/scripts/release-loop/ops-upgrade-node.ts +8 -1
  189. package/scripts/release-loop/preflight.ts +246 -0
  190. package/scripts/release-loop/publish-node.ts +10 -3
  191. package/scripts/release-loop/release-docs.ts +134 -0
  192. package/scripts/release-loop/release-readiness.ts +11 -6
  193. package/scripts/release-loop/release-version.ts +48 -0
  194. package/scripts/release-loop/resume-release.ts +115 -0
  195. package/scripts/release-loop/unattended-release.ts +43 -7
  196. package/scripts/release-loop/version-node.ts +149 -41
  197. package/scripts/release-story/draft.ts +42 -14
  198. package/scripts/rhythm/daily.ts +25 -4
  199. package/scripts/skills/public-check.ts +62 -12
  200. package/scripts/test-deterministic.ts +51 -33
  201. package/scripts/test-diet/effectiveness.ts +62 -1
  202. package/scripts/test-diet/lib.ts +1 -1
  203. package/scripts/test-diet/node.ts +8 -1
  204. package/src/agent-mission/driver.ts +53 -10
  205. package/src/agent-mission/review-loop.ts +4 -3
  206. package/src/agent-substrate/pr-comment-meta.ts +20 -0
  207. package/src/autopilot/build/llm-conflict-merge.ts +28 -8
  208. package/src/autopilot/track-agent.ts +5 -3
  209. package/src/briefing/brief-items.ts +86 -17
  210. package/src/cli/autopilot-cli.ts +86 -1
  211. package/src/cli/brief-cli.ts +36 -2
  212. package/src/cli/decisions-cli.ts +4 -1
  213. package/src/cli/freeze-cli.ts +3 -2
  214. package/src/cli/intake-cli.ts +74 -2
  215. package/src/cli/log-event-names.ts +1 -0
  216. package/src/cli/notify-cli.ts +75 -0
  217. package/src/cli/pr-cli.ts +12 -1
  218. package/src/cli/provider-cli.ts +24 -0
  219. package/src/cli/release-cli.ts +101 -17
  220. package/src/cli/self-update-canary.ts +46 -0
  221. package/src/cli/self-update.ts +200 -1
  222. package/src/cli/tasks-cli.ts +309 -3
  223. package/src/context-bus/condense.ts +19 -3
  224. package/src/context-bus/context-day.ts +77 -24
  225. package/src/context-bus/context-first.ts +33 -0
  226. package/src/context-card/index.ts +45 -0
  227. package/src/context-card/task.ts +76 -0
  228. package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -0
  229. package/src/decisions/decision-ledger.ts +10 -1
  230. package/src/discord-self-message.ts +20 -2
  231. package/src/discord-slash-wire.ts +1 -0
  232. package/src/domains/outbound-alert.ts +200 -27
  233. package/src/domains/outbound-log-probe.ts +16 -1
  234. package/src/domains/release-tool.ts +2 -2
  235. package/src/execution-loop/gates.ts +11 -2
  236. package/src/flow-loop/split.ts +8 -4
  237. package/src/git-fs/worktree.ts +23 -3
  238. package/src/harness/harness-cli-command.ts +151 -11
  239. package/src/harness/harness-pod-dispatch.ts +11 -0
  240. package/src/harness/harness-queue.ts +466 -32
  241. package/src/harness/helper-scan.ts +10 -3
  242. package/src/harness/nested-elanous-policy.ts +23 -4
  243. package/src/hq/fence-wrapper.ts +123 -0
  244. package/src/hq/hq.ts +8 -0
  245. package/src/hq/seats.ts +369 -0
  246. package/src/index.ts +190 -23
  247. package/src/intake-plane/adopt-gate.ts +49 -0
  248. package/src/intake-plane/check.ts +3 -0
  249. package/src/intake-plane/collect-github.ts +57 -25
  250. package/src/intake-plane/digest.ts +39 -4
  251. package/src/intake-plane/discord-seat-work.ts +8 -2
  252. package/src/intake-plane/requirement-funnel.ts +316 -0
  253. package/src/knowledge/know-find.ts +10 -1
  254. package/src/loops/budget.ts +36 -0
  255. package/src/loops/orchestrator/finish-rate.ts +73 -1
  256. package/src/loops/orchestrator/grid-place.ts +11 -0
  257. package/src/loops/orchestrator/tick.ts +295 -20
  258. package/src/maturity/feature-maturity.ts +3 -2
  259. package/src/nexus/api/decisions-route.ts +11 -1
  260. package/src/nexus/api/grid.ts +61 -0
  261. package/src/nexus/api/http-server.ts +19 -1
  262. package/src/nexus/api/loop-edges.ts +69 -13
  263. package/src/nexus/api/ops-seats-sources.ts +2 -2
  264. package/src/nexus/api/task-cards-api.ts +19 -0
  265. package/src/nexus/api/workflow-templates.ts +3 -0
  266. package/src/oauth/codex.ts +120 -2
  267. package/src/onboarding/first-chat-gate.ts +29 -0
  268. package/src/release-loop/checklist.ts +104 -6
  269. package/src/release-loop/feature-store.ts +10 -9
  270. package/src/release-loop/landing-freeze.ts +24 -14
  271. package/src/release-loop/placement.ts +13 -3
  272. package/src/release-loop/rubric.ts +80 -0
  273. package/src/seat-dispatch/seat-ask.ts +79 -16
  274. package/src/seat-loop/seat-loop.ts +107 -37
  275. package/src/self-dev/ask-launch-flow.ts +50 -10
  276. package/src/self-dev/ask-path-candidates.ts +68 -0
  277. package/src/self-dev/dev-pipeline.ts +4 -0
  278. package/src/self-dev/draft-triage-rules.ts +1 -1
  279. package/src/self-dev/orchestrate-cli.ts +22 -0
  280. package/src/self-dev/round-pr-supersede.ts +244 -0
  281. package/src/self-dev/run-store.ts +5 -1
  282. package/src/self-dev/run-supervisor.ts +21 -1
  283. package/src/self-dev/supervisor-verdict-edges.ts +46 -0
  284. package/src/self-implement/follow-up-goals.ts +2 -2
  285. package/src/self-implement/frozen-merges.ts +25 -20
  286. package/src/self-implement/gate-remote.ts +427 -0
  287. package/src/self-implement/goal-author-clarification.ts +81 -0
  288. package/src/self-implement/goal-author-cli.ts +33 -5
  289. package/src/self-implement/goal-author.ts +93 -12
  290. package/src/self-implement/goal-clarification-escalation.ts +37 -1
  291. package/src/self-implement/goal-document-write.ts +28 -0
  292. package/src/self-implement/heal-intake.ts +4 -0
  293. package/src/self-implement/host-regate.ts +3 -1
  294. package/src/self-implement/l8-merge-queue.ts +29 -5
  295. package/src/self-implement/orchestrator.ts +78 -13
  296. package/src/self-implement/prior-draft-findings.ts +4 -3
  297. package/src/skills/skill-feedback.ts +76 -0
  298. package/src/task-agent/actions.ts +151 -0
  299. package/src/task-agent/judge.ts +122 -0
  300. package/src/task-agent/mission.ts +423 -0
  301. package/src/task-agent/shadow.ts +90 -0
  302. package/src/task-agent/task-hand.ts +235 -0
  303. package/src/task-cards/card-store.ts +4 -1
  304. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +43 -4
  305. package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
  306. package/src/task-orchestrator/surfaces/pod-pool.ts +81 -13
  307. package/src/task-orchestrator/surfaces/self-implement-pod.ts +225 -35
  308. package/src/telegram-commands.ts +1 -1
  309. package/src/telegram-loops-command.ts +22 -15
  310. package/src/telegram.ts +5 -20
  311. package/src/user-config.ts +86 -4
  312. package/src/version/packed-revision.json +1 -1
  313. package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
  314. package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
  315. package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
  316. package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
  317. package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
  318. package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
  319. package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +0 -1
  320. package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +0 -1
  321. package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +0 -1
  322. package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +0 -1
  323. package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
  324. package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +0 -1
  325. package/apps/pwa/out/_next/static/chunks/9247-29d889b809288e65.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/app/chat/page-8c2d9526164f5d09.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/layout-dce686e582c23937.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/loops/page-1a61c7ca1ceb3ae7.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/market/page-51f811fa119308b6.js +0 -1
  336. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +0 -1
  337. package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +0 -1
  338. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-5b042736aa113756.js +0 -1
  339. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +0 -1
  340. package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +0 -1
  341. package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +0 -1
  342. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-57924393e3e4e539.js +0 -1
  343. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +0 -1
  344. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +0 -1
  345. package/apps/pwa/out/_next/static/chunks/app/settings/page-0f5cd57ca928f42b.js +0 -1
  346. package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +0 -1
  347. package/apps/pwa/out/_next/static/chunks/app/showroom/page-59f03fda76c03d22.js +0 -1
  348. package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +0 -1
  349. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +0 -1
  350. package/apps/pwa/out/_next/static/chunks/app/workflows/page-1ea64acadd704ace.js +0 -1
  351. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +0 -1
  352. package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
  353. package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +0 -1
  354. /package/apps/pwa/out/_next/static/{rv9cierV0jSOvDBm1g0XB → P6YTVgzuZz5oTZhY4Din_}/_ssgManifest.js +0 -0
package/src/index.ts CHANGED
@@ -74,6 +74,7 @@ import { registerModelWatchCommand } from './cli/model-watch-cli.js';
74
74
  import { registerIntakeCommands } from './cli/intake-cli.js';
75
75
  import { registerTasksCommands } from './cli/tasks-cli.js';
76
76
  import { registerBriefCommands } from './cli/brief-cli.js';
77
+ import { registerNotifyCommand } from './cli/notify-cli.js';
77
78
  import { registerLabelsCommands } from './cli/labels-cli.js';
78
79
  import { registerMsgCommands } from './cli/msg-cli.js';
79
80
  import { registerDoctorCommand } from './cli/doctor-cli.js';
@@ -135,6 +136,7 @@ import type { ReviewResult } from './agent-substrate/pr-reviewer.js';
135
136
  import type { ReviewVerdict } from './agent-mission/review-loop.js';
136
137
  import type { SyncMode } from './types.js';
137
138
  import { runOnboarding, runOnboardingStep, runOnboardingNonInteractive, needsOnboarding, needsFirstRun, handleOnboardingRefusal, type OnboardingStepId } from './onboarding.js';
139
+ import { firstChatGate } from './onboarding/first-chat-gate.js';
138
140
  import {
139
141
  getUserConfig, reloadUserConfig, userConfigPath, saveUserConfig,
140
142
  backupUserConfig, backupConfigPath, addRotationEntry, type RotationEntry,
@@ -259,7 +261,7 @@ interface RunDevAskFromGoalFileDeps {
259
261
  runSayLaunchFlow: (
260
262
  sayText: string,
261
263
  entrance?: import('./self-dev/entrance-registry.js').EntranceDeclaration,
262
- selection?: { readonly forcePreflight?: boolean; readonly goalType?: import('./self-implement/goal-author.js').GoalType; readonly groundingCwd?: string },
264
+ selection?: { readonly forcePreflight?: boolean; readonly goalType?: import('./self-implement/goal-author.js').GoalType; readonly groundingCwd?: string; readonly authorGrade?: import('./self-implement/goal-author.js').GoalAuthorGradeSelection },
263
265
  ) => Promise<RunDevAskFlowResult>;
264
266
  runAskFileLaunchFlow: (
265
267
  askPath: string,
@@ -294,7 +296,7 @@ export function assembleAskLaunchPolicy(selection: {
294
296
  async function runDefaultSayLaunchFlow(
295
297
  sayText: string,
296
298
  entrance?: import('./self-dev/entrance-registry.js').EntranceDeclaration,
297
- selection: { readonly forcePreflight?: boolean; readonly goalType?: import('./self-implement/goal-author.js').GoalType; readonly groundingCwd?: string } = {},
299
+ selection: { readonly forcePreflight?: boolean; readonly goalType?: import('./self-implement/goal-author.js').GoalType; readonly groundingCwd?: string; readonly authorGrade?: import('./self-implement/goal-author.js').GoalAuthorGradeSelection } = {},
298
300
  ): Promise<RunDevAskFlowResult> {
299
301
  const { readFileSync, writeFileSync } = await import('node:fs');
300
302
  const [flowMod, { runGoalAuthorCli }, { relative: relativeToCwdPath }, { CLI_HARNESS_SAY_ENTRANCE }] = await Promise.all([
@@ -319,6 +321,7 @@ async function runDefaultSayLaunchFlow(
319
321
  ...assembleAskLaunchPolicy(selection),
320
322
  ...(selection.goalType === undefined ? {} : { goalType: selection.goalType }),
321
323
  ...(selection.groundingCwd === undefined ? {} : { groundingCwd: selection.groundingCwd }),
324
+ ...(selection.authorGrade === undefined ? {} : { authorGrade: selection.authorGrade }),
322
325
  }, {
323
326
  print: (line) => console.error(line),
324
327
  log: (event, data, level) => debug.log('dev-pipeline', event, data, { level }),
@@ -328,6 +331,9 @@ async function runDefaultSayLaunchFlow(
328
331
  cwd: () => process.cwd(),
329
332
  now: () => Date.now(),
330
333
  isInteractive: () => process.stdin.isTTY === true && process.stdout.isTTY === true,
334
+ // POD-SELF-ANSWER: non-TTY (Pod) authoring self-answers clarifications with the author resolver.
335
+ selfResolveClarification: (await import('./self-implement/goal-author-clarification.js')).defaultGoalAuthorSelfResolve,
336
+ writeGoalDocument: (await import('./self-implement/goal-document-write.js')).writeGoalDocumentAtomic,
331
337
  buildPreflightDeps: askIo.buildAskPreflightDeps,
332
338
  priorBlockSamples: () => askIo.priorBlockSamplesFrom(askLogRows),
333
339
  recentAuthoringSamples: () => askIo.recentAuthoringSamplesFrom(askLogRows),
@@ -376,6 +382,9 @@ async function runDefaultAskFileLaunchFlow(
376
382
  cwd: () => process.cwd(),
377
383
  now: () => Date.now(),
378
384
  isInteractive: () => process.stdin.isTTY === true && process.stdout.isTTY === true,
385
+ // POD-SELF-ANSWER: non-TTY (Pod) authoring self-answers clarifications with the author resolver.
386
+ selfResolveClarification: (await import('./self-implement/goal-author-clarification.js')).defaultGoalAuthorSelfResolve,
387
+ writeGoalDocument: (await import('./self-implement/goal-document-write.js')).writeGoalDocumentAtomic,
379
388
  buildPreflightDeps: askIo.buildAskPreflightDeps,
380
389
  priorBlockSamples: () => askIo.priorBlockSamplesFrom(askLogRows),
381
390
  recentAuthoringSamples: () => askIo.recentAuthoringSamplesFrom(askLogRows),
@@ -818,6 +827,7 @@ async function runDevSayFromWords(
818
827
  authoringEntrance,
819
828
  {
820
829
  ...(opts.forcePreflight === undefined ? {} : { forcePreflight: opts.forcePreflight }),
830
+ ...(opts.authorGrade === undefined ? {} : { authorGrade: { grade: opts.authorGrade, source: opts.authorGradeSource ?? 'flag' } }),
821
831
  ...(grounding.groundingCwd === undefined ? {} : { groundingCwd: grounding.groundingCwd }),
822
832
  },
823
833
  );
@@ -880,6 +890,7 @@ registerLiveCommands(program);
880
890
  registerResearchCommand(program);
881
891
  registerReleaseCommands(program);
882
892
  registerKnowCommand(program);
893
+ registerNotifyCommand(program);
883
894
  registerFreezeCommands(program);
884
895
  registerFlowCommands(program);
885
896
  // HQ-HB · HQ-FENCE (10-04): one writer across mbp · node-b · cloud-vm — lease on the arbiter, 2-of-3 quorum, generation fencing.
@@ -977,12 +988,78 @@ hqCmd.command('seen').description('이 호스트가 본 가장 높은 임대 세
977
988
  process.exitCode = result.seen ? 0 : 3;
978
989
  });
979
990
  registerHqMovePlanCommand(hqCmd);
991
+ hqCmd.command('init').description('한 저장소의 bare 미러와 집 원장을 만들고 5분 fetch 크론 줄만 출력')
992
+ .option('--root <dir>', 'HQ 폴더 (기본 ~/elanous-hq)')
993
+ .action(async (opts: { root?: string }) => {
994
+ try {
995
+ const { hqInit } = await import('./hq/seats.js');
996
+ const { getElanousConfigDir } = await import('./elanous-config-dir.js');
997
+ const result = hqInit({ ...opts, home: getElanousConfigDir() });
998
+ console.log(`hq init: ${result.created ? 'created' : 'already exists'} ${result.root}\n# OP installs this line; nothing was installed:\n${result.cron}`);
999
+ } catch (error) { console.error(`hq init: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
1000
+ });
1001
+ hqCmd.command('seat <role>').description('OP|MK|TC|UX 자리 워크트리 (깨끗할 때만 갱신)')
1002
+ .option('--refresh', 'origin/main으로 깨끗한 자리만 최신화')
1003
+ .option('--root <dir>', 'HQ 폴더')
1004
+ .action(async (role: string, opts: { refresh?: boolean; root?: string }) => {
1005
+ try {
1006
+ const { hqSeat } = await import('./hq/seats.js');
1007
+ const result = hqSeat(role, opts);
1008
+ if (result.outcome === 'dirty') console.warn(`hq seat ${role}: dirty — refresh skipped; move edits to hq work new`);
1009
+ else console.log(`hq seat ${role}: ${result.outcome} ${result.path}`);
1010
+ } catch (error) { console.error(`hq seat: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
1011
+ });
1012
+ const hqWorkCmd = hqCmd.command('work').description('고칠 때만 작업 워크트리를 만들고 병합 후 회수');
1013
+ for (const action of ['new', 'done'] as const) hqWorkCmd.command(`${action} <role> <name>`)
1014
+ .option('--root <dir>', 'HQ 폴더')
1015
+ .action(async (role: string, name: string, opts: { root?: string }) => {
1016
+ try {
1017
+ const { hqWork } = await import('./hq/seats.js');
1018
+ const result = hqWork(action, role, name, opts);
1019
+ console.log(`hq work ${action}: ${result.outcome} ${result.path}`);
1020
+ } catch (error) { console.error(`hq work ${action}: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
1021
+ });
1022
+ const fenceWrapperCmd = hqCmd.command('fence-wrapper').description('설치본 HQ 울타리 크론 래퍼');
1023
+ fenceWrapperCmd.command('install').description('래퍼 제안 또는 백업 후 원자적 설치')
1024
+ .option('--dry-run', '변경 내용만 출력 (기본)')
1025
+ .option('--yes', '기존 파일 백업 후 설치')
1026
+ .action(async (opts: { dryRun?: boolean; yes?: boolean }) => {
1027
+ try {
1028
+ if (opts.dryRun && opts.yes) throw new Error('--dry-run and --yes cannot be used together');
1029
+ const { getElanousConfigDir } = await import('./elanous-config-dir.js');
1030
+ const { installedHqFenceEntry, installHqFenceWrapper, renderHqFenceWrapper, resolveHqFenceBun } = await import('./hq/fence-wrapper.js');
1031
+ const configDir = getElanousConfigDir();
1032
+ const bun = resolveHqFenceBun();
1033
+ const body = renderHqFenceWrapper({ entry: installedHqFenceEntry(), configDir, bun });
1034
+ const result = installHqFenceWrapper(configDir, body, opts.yes === true);
1035
+ if (opts.yes) console.log(`hq-fence installed: ${result.path} · bun: ${bun}${result.backup ? ` · backup: ${result.backup}` : ''}`);
1036
+ else console.log(`hq-fence bun (absolute · cron has no PATH): ${bun}\n${result.preview}`);
1037
+ } catch (error) { console.error(`hq fence-wrapper install: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
1038
+ });
1039
+ fenceWrapperCmd.command('alert <reason>').description('HQ 울타리 실패를 기존 루프 소유자 요청 원장에 적재')
1040
+ .action(async (reason: string) => {
1041
+ try {
1042
+ const { notifyOwners } = await import('./loops/checker.js');
1043
+ const { getElanousConfigDir } = await import('./elanous-config-dir.js');
1044
+ notifyOwners([{ id: `hq-fence:${Date.now()}:${process.pid}`, owner: 'TC', enabled: true, registered: true,
1045
+ state: 'failing', reason }], { root: getElanousConfigDir() });
1046
+ } catch (error) { console.error(`hq fence-wrapper alert: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
1047
+ });
980
1048
  hqCmd.command('fence-audit').description('운영 crontab 의 HQ 울타리 밖 쓰기 잡 목록과 감싼 줄 제안(읽기 전용)')
981
1049
  .option('--json', 'JSON 출력')
982
1050
  .action(async (opts: { json?: boolean }) => {
983
1051
  try {
984
1052
  await hqSink();
985
1053
  const rows = (await import('./hq/fence-audit.js')).hqFenceAudit();
1054
+ const { getElanousConfigDir } = await import('./elanous-config-dir.js');
1055
+ const { repositoryWorktreeCdWarning } = await import('./hq/fence-wrapper.js');
1056
+ const wrapper = _joinPath(getElanousConfigDir(), 'bin', 'hq-fence');
1057
+ if (existsSync(wrapper)) {
1058
+ try {
1059
+ const warning = repositoryWorktreeCdWarning(readFileSync(wrapper, 'utf8'));
1060
+ if (warning) console.error(warning);
1061
+ } catch (error) { console.error(`hq fence-audit: wrapper inspection unavailable: ${error instanceof Error ? error.message : String(error)}`); }
1062
+ }
986
1063
  if (opts.json) await writeStdoutJson(`${JSON.stringify(rows)}\n`);
987
1064
  else if (!rows.length) console.log('hq fence-audit: unfenced 0');
988
1065
  else for (const row of rows) console.log(`${row.raw}\n role: ${row.recommendedRole}\n suggested: ${row.suggestedLine}`);
@@ -999,6 +1076,40 @@ hqCmd.command('fence').description('이 호스트가 임대를 쥐었을 때만
999
1076
  });
1000
1077
  const seatCmd = program.command('seat').description('분배된 자리의 하루 루프와 보고');
1001
1078
  registerSeatRequestsCommands(seatCmd);
1079
+ seatCmd.command('asks').description('최근 CTO 되묻기 왕복과 10분 판정')
1080
+ .option('--since <duration>', '조회 창 (예: 24h)', '24h')
1081
+ .option('--json', 'JSON 출력')
1082
+ .action(async (opts: { since: string; json?: boolean }) => {
1083
+ try {
1084
+ const duration = /^(\d+)([mhd])$/.exec(opts.since);
1085
+ if (!duration || !Number.isSafeInteger(Number(duration[1]))) throw new Error('--since: 24h, 30m, 7d 형식 필요');
1086
+ const ms = Number(duration[1]) * ({ m: 60_000, h: 3_600_000, d: 86_400_000 }[duration[2]!] ?? 0);
1087
+ if (!Number.isSafeInteger(ms) || ms <= 0) throw new Error('--since: 양의 시간 필요');
1088
+ const { defaultMsgStorePath } = await import('./msg/msg-store.js');
1089
+ const path = defaultMsgStorePath();
1090
+ if (!(await import('node:fs')).existsSync(path)) {
1091
+ if (opts.json) await writeStdoutJson('[]\n');
1092
+ else console.log(`원장 없음 — ${path}`);
1093
+ return;
1094
+ }
1095
+ const { listSeatAskRoundTrips } = await import('./seat-dispatch/seat-ask.js');
1096
+ const now = Date.now();
1097
+ const rows = listSeatAskRoundTrips({ since: now - ms, now });
1098
+ if (!rows.length) {
1099
+ if (opts.json) await writeStdoutJson('[]\n');
1100
+ else console.log('이 창에 되묻기 0건');
1101
+ return;
1102
+ }
1103
+ if (opts.json) { await writeStdoutJson(`${JSON.stringify(rows)}\n`); return; }
1104
+ const clock = (value: number | null) => value === null ? '-' : new Intl.DateTimeFormat('ko-KR', {
1105
+ timeZone: 'Asia/Seoul', hour: '2-digit', minute: '2-digit', hourCycle: 'h23',
1106
+ }).format(value);
1107
+ console.log('id · 표면 · 물음 HH:MM KST · 답 · 도착 · 왕복 분 · 판정');
1108
+ for (const row of rows) console.log(`${row.id} · ${row.surface} · ${clock(row.askedAt)} KST · ${clock(row.answeredAt)} · ${clock(row.deliveredAt)} · ${row.roundTripMs === null ? '-' : (row.roundTripMs / 60_000).toFixed(1)}분 · ${row.verdict}`);
1109
+ const count = (verdict: string) => rows.filter(row => row.verdict === verdict).length;
1110
+ console.log(`10분 안 ${count('10분 안')} / 넘음 ${count('10분 넘음')} / 미답 ${count('미답')} / 대기 ${count('답 대기') + count('전달 대기')} / 미상 ${count('시각 미상')}`);
1111
+ } catch (error) { console.error(`seat asks: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
1112
+ });
1002
1113
  seatCmd.command('answer <askId> <answer>').description('자리 질문에 답을 남기고 원래 표면으로 회신 예약')
1003
1114
  .action(async (askId: string, answer: string) => {
1004
1115
  try {
@@ -1255,7 +1366,7 @@ pythonCmd.command('setup').description('elanous venv(~/.local/share/elanous/pyth
1255
1366
 
1256
1367
  program.command('self-update')
1257
1368
  .alias('update')
1258
- .description('설치본은 릴리스로, 체크아웃은 깨끗한 체크아웃으로 갱신하고 승인 시 넥서스 재시작 · `elanous update` 와 같다 · `--auto on` 이면 매일 자동')
1369
+ .description('설치본은 릴리스로, 체크아웃은 깨끗한 체크아웃으로 갱신 · --auto 는 캐너리 자동 판단, --auto on|off|status 는 기존 자동 갱신 서비스 관리')
1259
1370
  .option('--from <checkout>', '설치할 체크아웃 (기본: 설치본은 릴리스, 체크아웃은 현재 체크아웃)')
1260
1371
  .option('--version <version>', '설치본에서 지정한 릴리스 버전 설치 (기본: latest)')
1261
1372
  .option('--restart', '넥서스 재시작 승인')
@@ -1265,9 +1376,13 @@ program.command('self-update')
1265
1376
  .option('--skip-pwa-build', '체크아웃 설치 전 PWA 빌드를 명시적으로 건너뜀')
1266
1377
  .option('--dev', '판 사이 dev 빌드 설치 — versions/<판>-dev.<sha12> · install.json channel=dev · 이전 versionDir 보존 (사람 승인 · 자동 아님)')
1267
1378
  .option('--rollback', 'install.json 의 이전 versionDir 로 current 를 되돌린다 (이전이 없으면 거부)')
1268
- .option('--auto <on|off|status>', '자동 갱신 — macOS launchd · Linux systemd 타이머가 매일 04:17 에 `self-update --restart --alert` (크론이 이미 부르면 켜지 않는다)')
1269
- .action(async (opts: { from?: string; version?: string; restart?: boolean; json?: boolean; keep?: string; alert?: boolean; skipPwaBuild?: boolean; dev?: boolean; rollback?: boolean; auto?: string }) => {
1270
- if (opts.auto !== undefined) {
1379
+ .option('--auto [on|off|status]', '인자 없이: 에지 레일 계획/실행 · on|off|status: 기존 자동 갱신 서비스 관리')
1380
+ .option('--dry-run', '에지 레일 계획만 출력; 설치·되돌림·상태 쓰기 없음')
1381
+ .option('--seat <seat>', '에지 레일 자리 (OP|TC|MK|UX)')
1382
+ .option('--quiet-window', '상주 프로세스 재시작이 가능한 조용한 창이라고 명시')
1383
+ .option('--canary-run-id <id>', '캐너리 성공 런의 고유 ID (독립 검증된 성공 런에서만 입력; 같은 ID 는 중복 적중하지 않음)')
1384
+ .action(async (opts: { from?: string; version?: string; restart?: boolean; json?: boolean; keep?: string; alert?: boolean; skipPwaBuild?: boolean; dev?: boolean; rollback?: boolean; auto?: string | boolean; dryRun?: boolean; seat?: string; quietWindow?: boolean; canaryRunId?: string }) => {
1385
+ if (typeof opts.auto === 'string') {
1271
1386
  if (!['on', 'off', 'status'].includes(opts.auto)) { console.error(`--auto 는 on · off · status 중 하나: ${opts.auto}`); process.exitCode = 2; return; }
1272
1387
  const { runAutoUpdate } = await import('./cli/update-auto.js');
1273
1388
  process.exitCode = (await runAutoUpdate(opts.auto as 'on' | 'off' | 'status')).exitCode;
@@ -1275,9 +1390,9 @@ program.command('self-update')
1275
1390
  }
1276
1391
  const { runUpdateForInstallation } = await import('./cli/self-update.js');
1277
1392
  // ⭐ 한 번 도는 CLI 는 logs.db 싱크를 스스로 붙여야 `debug.log('self-update', …)` 가 저장된다(없으면 조용히 사라진다 · 09-24 실측).
1278
- try { const { registerStandaloneLogSink } = await import('./domains/standalone-log-sink.js'); await registerStandaloneLogSink('self-update'); } catch { /* 관측 실패가 갱신을 막지 않는다 */ }
1393
+ if (!opts.dryRun) try { const { registerStandaloneLogSink } = await import('./domains/standalone-log-sink.js'); await registerStandaloneLogSink('self-update'); } catch { /* 관측 실패가 갱신을 막지 않는다 */ }
1279
1394
  const keep = Number.parseInt(opts.keep ?? '3', 10);
1280
- const result = await runUpdateForInstallation({ ...opts, skipPwaBuild: opts.skipPwaBuild, keep: Number.isFinite(keep) ? keep : 3 }, { cliRoot: REPOSITORY_ROOT });
1395
+ const result = await runUpdateForInstallation({ ...opts, auto: opts.auto === true, quietWindow: opts.quietWindow === true, skipPwaBuild: opts.skipPwaBuild, keep: Number.isFinite(keep) ? keep : 3 }, { cliRoot: REPOSITORY_ROOT });
1281
1396
  process.exitCode = result.exitCode;
1282
1397
  });
1283
1398
 
@@ -3252,11 +3367,20 @@ contextCmd.command('day')
3252
3367
  .description('자리 넷과 외부 세션의 최근 맥락 이벤트 수·마지막 시각 (읽기 전용)')
3253
3368
  .option('--since <hours>', '최근 시간 창 (예: 24h)', '24h')
3254
3369
  .option('--json', '구조화된 JSON 출력')
3255
- .action(async (opts: { since: string; json?: boolean }) => {
3370
+ .option('--timeline', '시간순 사건 한 줄씩 (KST)')
3371
+ .option('--seat <source>', '한 출처만 (OP|TC|MK|UX|claude-code|codex-agent-mission|harness-child)')
3372
+ .action(async (opts: { since: string; json?: boolean; timeline?: boolean; seat?: string }) => {
3256
3373
  try {
3257
- const { contextDay, renderContextDay } = await import('./context-bus/context-day.js');
3258
- const report = contextDay(opts.since);
3259
- await writeStdoutJson(`${opts.json ? JSON.stringify(report) : renderContextDay(report)}\n`);
3374
+ const { contextDay, contextDayTimeline, renderContextDay, renderContextDayTimeline, CONTEXT_DAY_SOURCES } = await import('./context-bus/context-day.js');
3375
+ if (opts.seat && !CONTEXT_DAY_SOURCES.some(source => source === opts.seat)) throw new Error(`unknown --seat: ${opts.seat}`);
3376
+ if (opts.seat && !opts.timeline) throw new Error('--seat requires --timeline');
3377
+ if (opts.timeline) {
3378
+ const report = contextDayTimeline(opts.since, { seat: opts.seat as typeof CONTEXT_DAY_SOURCES[number] | undefined });
3379
+ await writeStdoutJson(`${opts.json ? JSON.stringify(report) : renderContextDayTimeline(report)}\n`);
3380
+ } else {
3381
+ const report = contextDay(opts.since);
3382
+ await writeStdoutJson(`${opts.json ? JSON.stringify(report) : renderContextDay(report)}\n`);
3383
+ }
3260
3384
  } catch (error) {
3261
3385
  console.error(`context day: ${error instanceof Error ? error.message : String(error)}`);
3262
3386
  process.exitCode = 2;
@@ -4290,16 +4414,52 @@ selfCmd
4290
4414
  .option('--base <ref>', '커밋됐지만 PR 없는 변경의 비교 기준(ref...HEAD)')
4291
4415
  .option('--pr <number>', '열린 PR 번호에서 gh pr view files로 변경 파일을 읽는다')
4292
4416
  .option('--shards [number]', '메모리 예산에 맞춰 시험을 별도 프로세스로 분할한다 (기본 2)')
4293
- .action((opts: { changed?: boolean; base?: string; pr?: string; shards?: string | boolean }) => {
4417
+ .option('--remote [host]', 'GATE-REMOTE — 같은 커밋을 원격 호스트(기본 config gateRemote.host=node-b)에서 돌리고 결과만 가져온다(깨끗한 트리 · --pr 은 로컬)')
4418
+ .option('--local', '원격 자동 분배(로컬 1분 부하 > gateRemote.loadThreshold=20)를 끄고 로컬에서 돌린다')
4419
+ .action(async (opts: { changed?: boolean; base?: string; pr?: string; shards?: string | boolean; remote?: string | boolean; local?: boolean }) => {
4294
4420
  if (!opts.changed && opts.shards === undefined) { ui.error('self gate requires --changed or --shards'); process.exitCode = 2; return; }
4421
+ // The root strips `--local` from argv anywhere (remote-bookmark attach · src/cli/remote-resolve.ts) before Commander
4422
+ // sees it, so this subcommand reads it from the untouched process.argv.
4423
+ opts.local = !!opts.local || process.argv.slice(2).includes('--local');
4424
+ if (opts.remote !== undefined && opts.local) { ui.error('self gate: --remote and --local cannot be combined'); process.exitCode = 2; return; }
4425
+ if (typeof opts.remote === 'string') {
4426
+ const { isValidGateHost } = await import('./self-implement/gate-remote.js');
4427
+ if (!isValidGateHost(opts.remote)) { ui.error(`self gate: invalid --remote host: ${opts.remote}`); process.exitCode = 2; return; }
4428
+ }
4429
+ const shards = opts.shards === undefined ? undefined : opts.shards === true ? 2 : Number(opts.shards);
4430
+ const runLocal = () => {
4431
+ try {
4432
+ const { runSelfGateCli } = require('./self-implement/gate-cli.js') as typeof import('./self-implement/gate-cli.js');
4433
+ const result = runSelfGateCli(process.cwd(), { base: opts.base, pr: opts.pr, shards });
4434
+ console.log(result.lines.join('\n'));
4435
+ return result.exitCode;
4436
+ } catch (error) {
4437
+ ui.error(`self gate failed: ${error instanceof Error ? error.message : String(error)}`);
4438
+ return 1;
4439
+ }
4440
+ };
4295
4441
  try {
4296
- const { runSelfGateCli } = require('./self-implement/gate-cli.js') as typeof import('./self-implement/gate-cli.js');
4297
- const result = runSelfGateCli(process.cwd(), { base: opts.base, pr: opts.pr, shards: opts.shards === undefined ? undefined : opts.shards === true ? 2 : Number(opts.shards) });
4298
- console.log(result.lines.join('\n'));
4299
- process.exitCode = result.exitCode;
4442
+ const { dispatchHeavyCheck } = await import('./self-implement/gate-remote.js');
4443
+ const { execFileSync } = await import('node:child_process');
4444
+ const repo = execFileSync('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).trim();
4445
+ // Only a committed --base range is reproducible on another host: the default mode measures uncommitted files, --pr
4446
+ // reads files through gh on this host, and the remote checkout runs from the repository root — those stay local.
4447
+ const { realpathSync } = await import('node:fs');
4448
+ const localOnlyReason = opts.pr ? 'self gate --pr reads PR files through gh on this host'
4449
+ : !opts.base ? 'self gate without --base measures uncommitted files on this host'
4450
+ : realpathSync(repo) !== realpathSync(process.cwd()) ? 'self gate run from a sub-directory' : undefined;
4451
+ // origin/main is pinned to this machine's origin/main on the host; any other base is passed as its resolved sha.
4452
+ const remoteArgv = (sha: (ref: string) => string) => ['bun', 'bin/elanous.mjs', 'self', 'gate', '--local', ...(opts.changed ? ['--changed'] : []),
4453
+ ...(opts.base ? ['--base', opts.base === 'origin/main' ? opts.base : sha(opts.base)] : []), ...(shards !== undefined ? ['--shards', String(shards)] : [])];
4454
+ process.exitCode = await dispatchHeavyCheck({
4455
+ tool: 'self-gate', repo, installPwa: true, remoteArgv, refs: opts.base ? [opts.base] : [],
4456
+ localOnlyReason,
4457
+ flags: { remote: typeof opts.remote === 'string' ? opts.remote : opts.remote ? true : undefined, local: !!opts.local },
4458
+ runLocal,
4459
+ });
4300
4460
  } catch (error) {
4301
- ui.error(`self gate failed: ${error instanceof Error ? error.message : String(error)}`);
4302
- process.exitCode = 1;
4461
+ ui.error(`self gate dispatch failed: ${error instanceof Error ? error.message : String(error)}`);
4462
+ process.exitCode = runLocal();
4303
4463
  }
4304
4464
  });
4305
4465
 
@@ -7001,6 +7161,8 @@ const selfDevCmd = program
7001
7161
  now: () => Date.now(),
7002
7162
  // ⛔ 「대화형인가」는 «두 쪽»을 본다 — 파이프로 몰면 물어도 답이 안 온다.
7003
7163
  isInteractive: () => process.stdin.isTTY === true && process.stdout.isTTY === true,
7164
+ selfResolveClarification: (await import('./self-implement/goal-author-clarification.js')).defaultGoalAuthorSelfResolve,
7165
+ writeGoalDocument: (await import('./self-implement/goal-document-write.js')).writeGoalDocumentAtomic,
7004
7166
  buildPreflightDeps: buildAskPreflightDeps,
7005
7167
  priorBlockSamples: () => askIo.priorBlockSamplesFrom(askLogRows),
7006
7168
  recentAuthoringSamples: () => askIo.recentAuthoringSamplesFrom(askLogRows),
@@ -8550,10 +8712,15 @@ program
8550
8712
  }
8551
8713
  if (opts.tools) announceChatToolsCompatibility();
8552
8714
  const cfg = getUserConfig();
8553
- if (needsOnboarding(cfg)) {
8554
- if (cfg.onboarding.webFirst === false) ui.info('No config yet — launching setup wizard first.');
8555
- if (!await runFirstSetupForCli(cfg, true)) return;
8556
- }
8715
+ if (await firstChatGate(cfg, {
8716
+ isTTY: process.stdin.isTTY === true,
8717
+ runFirstRun: async (deps) => (await import('./onboarding/first-run.js')).runFirstRun(deps),
8718
+ runInteractive: () => {
8719
+ if (cfg.onboarding.webFirst === false) ui.info('No config yet — launching setup wizard first.');
8720
+ return runFirstSetupForCli(cfg, true);
8721
+ },
8722
+ print: (line) => console.log(line),
8723
+ }) === 'stop') return;
8557
8724
  const refreshed = reloadUserConfig();
8558
8725
  // ★ 관측갭 수리(2026-07-21·제1원칙·트랙A) — self-implement 자식 goal-loop(`chat --goal-loop`)은 데몬과
8559
8726
  // 별개 독립 프로세스라 nexus StoreSink 를 상속 안 한다. 부모 `self implement`(위 self 커맨드)는 sink 를
@@ -0,0 +1,49 @@
1
+ import { debug } from '../debug/log.js';
2
+
3
+ export interface AdoptionCandidate {
4
+ readonly id: string;
5
+ readonly grade: 'P1' | 'P2' | 'P3' | 'P4';
6
+ readonly risk: number;
7
+ readonly money: number;
8
+ readonly security: number;
9
+ readonly license: string | null;
10
+ readonly touchesPatentCandidate: boolean;
11
+ }
12
+
13
+ export type AdoptShadowReason = 'grade' | 'risk' | 'money' | 'security' | 'license' | 'patent' | 'daily-cap';
14
+
15
+ export interface AdoptShadowVerdict {
16
+ readonly id: string;
17
+ readonly adopt: boolean;
18
+ readonly reasons: readonly AdoptShadowReason[];
19
+ }
20
+
21
+ const ALLOWED_LICENSES: ReadonlySet<string> = new Set([
22
+ 'MIT', 'Apache-2.0', 'BSD-2-Clause', 'BSD-3-Clause', 'ISC', 'MPL-2.0',
23
+ ]);
24
+
25
+ export function judgeAdoption(candidate: AdoptionCandidate, adoptedToday: number): AdoptShadowVerdict {
26
+ const reasons: AdoptShadowReason[] = [];
27
+ if (candidate.grade !== 'P1' && candidate.grade !== 'P2') reasons.push('grade');
28
+ if (!(candidate.risk <= 1)) reasons.push('risk');
29
+ if (candidate.money !== 0) reasons.push('money');
30
+ if (candidate.security !== 0) reasons.push('security');
31
+ if (candidate.license === null || !ALLOWED_LICENSES.has(candidate.license)) reasons.push('license');
32
+ if (candidate.touchesPatentCandidate !== false) reasons.push('patent');
33
+ if (!(adoptedToday < 5)) reasons.push('daily-cap');
34
+ return { id: candidate.id, adopt: reasons.length === 0, reasons };
35
+ }
36
+
37
+ export function shadowAdopt(candidates: readonly AdoptionCandidate[]): AdoptShadowVerdict[] {
38
+ const verdicts: AdoptShadowVerdict[] = [];
39
+ let adoptedToday = 0;
40
+ for (const candidate of candidates) {
41
+ const verdict = judgeAdoption(candidate, adoptedToday);
42
+ if (verdict.adopt) adoptedToday++;
43
+ verdicts.push(verdict);
44
+ debug.log('intake.adopt', 'shadow-verdict', {
45
+ id: verdict.id, adopt: verdict.adopt, reasons: verdict.reasons,
46
+ });
47
+ }
48
+ return verdicts;
49
+ }
@@ -21,6 +21,7 @@ import { parse as parseYaml } from 'yaml';
21
21
  import { debug } from '../debug/log.js';
22
22
  import { openSurfaceEventsDb, recallEvents } from '../domains/surface-events.js';
23
23
  import { extractJsonBlock, skeletonFallback } from './decompose.js';
24
+ import type { AdoptShadowVerdict } from './adopt-gate.js';
24
25
 
25
26
  export const INTAKE_CHECK_MODE = 'check' as const;
26
27
 
@@ -110,6 +111,7 @@ export interface IntakeCheckReport {
110
111
  /** 비교·시너지 제안 수(근거 없음 포함). */
111
112
  readonly proposalCount?: number;
112
113
  readonly proposals?: readonly IntakeCompareProposal[];
114
+ readonly adoptShadow?: readonly AdoptShadowVerdict[];
113
115
  }
114
116
 
115
117
  /** 선가공 렌즈 여섯. 🅞 원장 · PR #20009. */
@@ -1826,6 +1828,7 @@ export function intakeCheckReportJson(report: IntakeCheckReport): Record<string,
1826
1828
  proposalCount: report.proposalCount,
1827
1829
  proposals: report.proposals,
1828
1830
  } : {}),
1831
+ ...(report.adoptShadow !== undefined ? { adoptShadow: report.adoptShadow } : {}),
1829
1832
  };
1830
1833
  }
1831
1834
 
@@ -6,14 +6,18 @@ import { execFileSync } from 'node:child_process';
6
6
  import { debug } from '../debug/log.js';
7
7
  import { ingestIntakeItems, intakeLedgerDir, type IngestResult, type RawIntakeItem } from './items.js';
8
8
 
9
- /** 한 판에 열 번 안쪽 — 검색 API 비율 제한(인증 시 분당 30회)을 시험 밖에서 상수로 묶는다. */
9
+ /** 한 판에 열 번 이하 — 검색 API 비율 제한(인증 시 분당 30회)을 시험 밖에서 상수로 묶는다. */
10
10
  export const GITHUB_STAR_QUERIES = [
11
11
  'topic:ai-agents',
12
- 'topic:llm-agent',
13
12
  'topic:coding-agent',
14
13
  'topic:mcp',
15
14
  'topic:video-generation',
16
- '"ai agent" in:name,description',
15
+ 'topic:agent-framework',
16
+ 'topic:computer-use',
17
+ 'topic:local-llm',
18
+ 'topic:claude-code',
19
+ 'topic:codex',
20
+ 'topic:agent-memory',
17
21
  ] as const;
18
22
 
19
23
  /** `--days` 기본. 최근 이 일수 안에 만들어졌거나 푸시된 저장소만 받는다. */
@@ -32,6 +36,7 @@ export interface GithubStarRepo {
32
36
  stars: number;
33
37
  createdAt: string;
34
38
  pushedAt: string;
39
+ license?: string;
35
40
  }
36
41
 
37
42
  /** 질의 하나 → 저장소 목록. 실제 구현은 `gh api` · 시험은 가짜. */
@@ -43,6 +48,7 @@ export interface GithubStarSignals {
43
48
  stars: number;
44
49
  starsDelta?: number;
45
50
  starsPerDay?: number;
51
+ baselineDays?: number;
46
52
  }
47
53
 
48
54
  export interface CollectGithubQueryCount { query: string; received: number }
@@ -55,6 +61,8 @@ export interface CollectGithubRepoRow {
55
61
  stars: number;
56
62
  starsDelta?: number;
57
63
  starsPerDay?: number;
64
+ baselineDays?: number;
65
+ license?: string;
58
66
  }
59
67
 
60
68
  export interface CollectGithubResult {
@@ -99,6 +107,17 @@ export function precedingSnapshot(snaps: readonly StarSnapshot[], repo: string,
99
107
  return best;
100
108
  }
101
109
 
110
+ /** 7일 전 또는 그 이전 중 가장 가까운 스냅숏; 없으면 직전 스냅숏. */
111
+ export function surgeBaseline(snaps: readonly StarSnapshot[], repo: string, day: string): StarSnapshot | undefined {
112
+ const cutoff = new Date(Date.parse(`${day}T00:00:00.000Z`) - 7 * 86_400_000).toISOString().slice(0, 10);
113
+ let best: StarSnapshot | undefined;
114
+ for (const s of snaps) {
115
+ if (s.repo !== repo || s.day > cutoff) continue;
116
+ if (!best || s.day > best.day) best = s;
117
+ }
118
+ return best ?? precedingSnapshot(snaps, repo, day);
119
+ }
120
+
102
121
  function utcDay(iso: string): string {
103
122
  const t = Date.parse(iso);
104
123
  if (!Number.isFinite(t)) return '';
@@ -128,7 +147,7 @@ export function signalsFromSnapshot(stars: number, prev: StarSnapshot | undefine
128
147
  if (!prev) return { stars };
129
148
  const span = Math.max(1, daysBetween(prev.day, day));
130
149
  const starsDelta = stars - prev.stars;
131
- return { stars, starsDelta, starsPerDay: starsDelta / span };
150
+ return { stars, starsDelta, starsPerDay: starsDelta / span, baselineDays: span };
132
151
  }
133
152
 
134
153
  export function appendGithubStarSnapshots(root: string, rows: StarSnapshot[]): void {
@@ -154,13 +173,13 @@ export interface CollectGithubOpts {
154
173
  /** 스냅숏·비교의 «오늘». 생략하면 UTC 날짜. */
155
174
  day?: string;
156
175
  now?: () => Date;
157
- /** 등록 원천이 질의 하나만 수집할 때; 생략 시 기존 관심 질의 전부. */
176
+ /** 등록 원천이 질의 하나만 수집할 때; 생략 시 관심 질의 전부 (최대 10개). */
158
177
  queries?: readonly string[];
159
178
  }
160
179
 
161
180
  /**
162
181
  * 관심 주제 질의마다 별 순 검색을 부르고, 최근 창 안의 저장소만 남긴 뒤 질의 사이에서 중복을 없앤다.
163
- * 저장소마다 오늘 별 수 스냅숏을 덧붙이고, 직전 스냅숏과 비교한 신호를 원장에 넣는다.
182
+ * 저장소마다 오늘 별 수 스냅숏을 덧붙이고, 7일 기준(없으면 직전) 스냅숏과 비교한 신호를 원장에 넣는다.
164
183
  * dryRun 이면 스냅숏·원장을 바꾸지 않는다.
165
184
  */
166
185
  export async function collectGithubStars(
@@ -175,7 +194,7 @@ export async function collectGithubStars(
175
194
  const found: GithubStarRepo[] = [];
176
195
  // «급상승»은 최근에 생긴 저장소 중 별 순이다 — 전체 기간 별 순이면 활발한 거대 저장소가 매일 같은 목록으로 나온다(2026-09-26 검토).
177
196
  const windowStart = new Date(Date.parse(`${day}T00:00:00.000Z`) - days * 86_400_000).toISOString().slice(0, 10);
178
- for (const query of opts.queries ?? GITHUB_STAR_QUERIES) {
197
+ for (const query of (opts.queries ?? GITHUB_STAR_QUERIES).slice(0, 10)) {
179
198
  const rows = await search(`${query} created:>=${windowStart}`, perQuery);
180
199
  const recent = rows.filter((r) => isRecentRepo(r, day, days));
181
200
  queries.push({ query, received: recent.length });
@@ -186,20 +205,24 @@ export async function collectGithubStars(
186
205
  const repos: CollectGithubRepoRow[] = unique.map((repo) => {
187
206
  const prev = precedingSnapshot(prior, repo.fullName, day);
188
207
  const signals = signalsFromSnapshot(repo.stars, prev, day);
208
+ const surge = signalsFromSnapshot(repo.stars, surgeBaseline(prior, repo.fullName, day), day);
189
209
  return {
190
210
  repo: repo.fullName,
191
211
  url: repo.url,
192
212
  title: repo.fullName,
193
213
  text: repo.description,
194
214
  stars: signals.stars,
215
+ ...(repo.license !== undefined ? { license: repo.license } : {}),
195
216
  ...(signals.starsDelta !== undefined ? { starsDelta: signals.starsDelta } : {}),
196
- ...(signals.starsPerDay !== undefined ? { starsPerDay: signals.starsPerDay } : {}),
217
+ ...(surge.starsPerDay !== undefined ? { starsPerDay: surge.starsPerDay } : {}),
218
+ ...(surge.baselineDays !== undefined ? { baselineDays: surge.baselineDays } : {}),
197
219
  };
198
- });
220
+ }).sort((a, b) => (b.starsPerDay ?? -Infinity) - (a.starsPerDay ?? -Infinity) || b.stars - a.stars);
199
221
  const raws: RawIntakeItem[] = repos.map((row) => ({
200
222
  url: row.url,
201
223
  title: row.title,
202
- text: row.text,
224
+ // 모든 행 첫 줄에 라이선스: SPDX · 확인된 부재는 «없음» · 응답이 알려 주지 않으면 «미상»(없음으로 읽지 않는다).
225
+ text: `라이선스: ${row.license ?? '미상'}\n${row.text}`,
203
226
  signals: {
204
227
  stars: row.stars,
205
228
  ...(row.starsDelta !== undefined ? { starsDelta: row.starsDelta } : {}),
@@ -226,23 +249,10 @@ interface GhSearchItem {
226
249
  stargazers_count?: number;
227
250
  created_at?: string;
228
251
  pushed_at?: string;
252
+ license?: { spdx_id?: string | null } | null;
229
253
  }
230
254
 
231
- /** `gh api search/repositories` 를 `sort=stars` 로 한 번 부른다. */
232
- export const ghApiSearchRepos: SearchGithubRepos = async (query, perQuery) => {
233
- const perPage = Math.min(100, Math.max(1, perQuery));
234
- const q = `${query} sort:stars`;
235
- const out = execFileSync('gh', [
236
- 'api',
237
- '-X', 'GET',
238
- 'search/repositories',
239
- '-f', `q=${q}`,
240
- '-f', 'sort=stars',
241
- '-f', 'order=desc',
242
- '-f', `per_page=${perPage}`,
243
- '--jq', '.items',
244
- ], { encoding: 'utf8', timeout: 30_000 });
245
- const items = JSON.parse(out) as GhSearchItem[];
255
+ export function reposFromGhSearchItems(items: GhSearchItem[], perQuery: number): GithubStarRepo[] {
246
256
  if (!Array.isArray(items)) return [];
247
257
  const repos: GithubStarRepo[] = [];
248
258
  for (const item of items) {
@@ -254,7 +264,29 @@ export const ghApiSearchRepos: SearchGithubRepos = async (query, perQuery) => {
254
264
  stars: Number(item.stargazers_count ?? 0),
255
265
  createdAt: item.created_at ?? '',
256
266
  pushedAt: item.pushed_at ?? '',
267
+ ...(item.license === null
268
+ ? { license: '없음' }
269
+ : typeof item.license?.spdx_id === 'string' && item.license.spdx_id
270
+ ? { license: item.license.spdx_id }
271
+ : {}),
257
272
  });
258
273
  }
259
274
  return repos.slice(0, perQuery);
275
+ }
276
+
277
+ /** `gh api search/repositories` 를 `sort=stars` 로 한 번 부른다. */
278
+ export const ghApiSearchRepos: SearchGithubRepos = async (query, perQuery) => {
279
+ const perPage = Math.min(100, Math.max(1, perQuery));
280
+ const q = `${query} sort:stars`;
281
+ const out = execFileSync('gh', [
282
+ 'api',
283
+ '-X', 'GET',
284
+ 'search/repositories',
285
+ '-f', `q=${q}`,
286
+ '-f', 'sort=stars',
287
+ '-f', 'order=desc',
288
+ '-f', `per_page=${perPage}`,
289
+ '--jq', '.items',
290
+ ], { encoding: 'utf8', timeout: 30_000 });
291
+ return reposFromGhSearchItems(JSON.parse(out) as GhSearchItem[], perQuery);
260
292
  };