elanous 0.2.10 → 0.2.11

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 (584) 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/Y4zKzL62sakG5momz3SoQ/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1394-556de6d71bbdb0fb.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
  7. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
  19. package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
  20. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
  25. package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/{8067-87ce253ec2f23697.js → 5845-e6c003ce8d5fd36a.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/8605-ea5bee9dfae512c7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-6a061bd97f0b9398.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-54b32bd27726588d.js → page-b12b6d2f8d2d1aea.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/chat/page-7c97dcdfe2e37a60.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/control/page-5721537ca8a3598c.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/design-check/page-b654240a572e4c48.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/editor/page-b9750abc897508c3.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/inside/page-50cb2a0eb15e6c83.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/intake/page-c8b1ccda6ba7ae98.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/layout-d6b1d156fb95e74b.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/market/page-9688f5b4f1ff950f.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/morning/page-35414e0a007b4082.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/observatory/page-e001823c35c9dd67.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-371423d1a573d64b.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4b764b125339ac17.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cdf8e4b01067bcf1.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a24072b35470157.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d37cb0d52f9b6bd5.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/sessions/page-686da0df9bf23e5c.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4dc614f3ce043e63.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d02c782fc792e2af.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/setup/page-239c17b911b84e72.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/share/page-dc6a79fb003f746c.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/showroom/page-0fffb84cafdc9da6.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/tasks/page-1218753c82d585f6.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/term/page-4b5935e67531efb5.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/today/page-3285bc87caf1eaa5.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/trace/page-ad553b0e06505ecd.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/vault/page-fca096e3e860306f.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ebfa78f0d9885c3.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/workflows/page-3dc5f21f7e39622d.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → f48170cc-d05d28bf87a43bf4.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +1 -0
  105. package/apps/pwa/out/_next/static/css/543f56bcb1f79fca.css +1 -0
  106. package/apps/pwa/out/approvals/index.html +1 -1
  107. package/apps/pwa/out/approvals/index.txt +23 -23
  108. package/apps/pwa/out/autopilot/index.html +1 -1
  109. package/apps/pwa/out/autopilot/index.txt +23 -23
  110. package/apps/pwa/out/board/index.html +1 -1
  111. package/apps/pwa/out/board/index.txt +23 -23
  112. package/apps/pwa/out/botlab/index.html +1 -1
  113. package/apps/pwa/out/botlab/index.txt +22 -22
  114. package/apps/pwa/out/bots/index.html +1 -1
  115. package/apps/pwa/out/bots/index.txt +22 -22
  116. package/apps/pwa/out/chat/index.html +1 -1
  117. package/apps/pwa/out/chat/index.txt +22 -22
  118. package/apps/pwa/out/consult/index.html +1 -1
  119. package/apps/pwa/out/consult/index.txt +23 -23
  120. package/apps/pwa/out/control/index.html +1 -1
  121. package/apps/pwa/out/control/index.txt +23 -23
  122. package/apps/pwa/out/design-check/index.html +1 -1
  123. package/apps/pwa/out/design-check/index.txt +23 -23
  124. package/apps/pwa/out/editor/index.html +1 -1
  125. package/apps/pwa/out/editor/index.txt +23 -23
  126. package/apps/pwa/out/exec/index.html +1 -1
  127. package/apps/pwa/out/exec/index.txt +23 -23
  128. package/apps/pwa/out/field/index.html +1 -1
  129. package/apps/pwa/out/field/index.txt +23 -23
  130. package/apps/pwa/out/healthz +1 -1
  131. package/apps/pwa/out/index.html +1 -1
  132. package/apps/pwa/out/index.txt +22 -22
  133. package/apps/pwa/out/inside/index.html +1 -0
  134. package/apps/pwa/out/inside/index.txt +31 -0
  135. package/apps/pwa/out/intake/index.html +1 -1
  136. package/apps/pwa/out/intake/index.txt +23 -23
  137. package/apps/pwa/out/live/index.html +1 -1
  138. package/apps/pwa/out/live/index.txt +23 -23
  139. package/apps/pwa/out/market/index.html +1 -1
  140. package/apps/pwa/out/market/index.txt +22 -22
  141. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  142. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  143. package/apps/pwa/out/morning/index.html +1 -1
  144. package/apps/pwa/out/morning/index.txt +23 -23
  145. package/apps/pwa/out/observatory/index.html +1 -1
  146. package/apps/pwa/out/observatory/index.txt +23 -23
  147. package/apps/pwa/out/ops/checklist/index.html +1 -1
  148. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  149. package/apps/pwa/out/ops/release/index.html +1 -1
  150. package/apps/pwa/out/ops/release/index.txt +22 -22
  151. package/apps/pwa/out/ops/seats/index.html +1 -1
  152. package/apps/pwa/out/ops/seats/index.txt +22 -22
  153. package/apps/pwa/out/outputs/index.html +1 -0
  154. package/apps/pwa/out/outputs/index.txt +30 -0
  155. package/apps/pwa/out/reflection/index.html +1 -1
  156. package/apps/pwa/out/reflection/index.txt +23 -23
  157. package/apps/pwa/out/scheduler/index.html +1 -1
  158. package/apps/pwa/out/scheduler/index.txt +23 -23
  159. package/apps/pwa/out/sessions/index.html +1 -1
  160. package/apps/pwa/out/sessions/index.txt +22 -22
  161. package/apps/pwa/out/settings/devices/index.html +1 -1
  162. package/apps/pwa/out/settings/devices/index.txt +23 -23
  163. package/apps/pwa/out/settings/index.html +1 -1
  164. package/apps/pwa/out/settings/index.txt +23 -23
  165. package/apps/pwa/out/setup/done/index.html +1 -1
  166. package/apps/pwa/out/setup/done/index.txt +23 -23
  167. package/apps/pwa/out/setup/index.html +1 -1
  168. package/apps/pwa/out/setup/index.txt +23 -23
  169. package/apps/pwa/out/share/index.html +1 -1
  170. package/apps/pwa/out/share/index.txt +23 -23
  171. package/apps/pwa/out/showroom/index.html +1 -1
  172. package/apps/pwa/out/showroom/index.txt +22 -22
  173. package/apps/pwa/out/tasks/index.html +1 -1
  174. package/apps/pwa/out/tasks/index.txt +23 -23
  175. package/apps/pwa/out/term/index.html +1 -1
  176. package/apps/pwa/out/term/index.txt +23 -24
  177. package/apps/pwa/out/today/index.html +1 -1
  178. package/apps/pwa/out/today/index.txt +23 -23
  179. package/apps/pwa/out/trace/index.html +1 -1
  180. package/apps/pwa/out/trace/index.txt +23 -23
  181. package/apps/pwa/out/vault/index.html +1 -1
  182. package/apps/pwa/out/vault/index.txt +23 -23
  183. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  184. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  185. package/apps/pwa/out/workflows/index.html +1 -1
  186. package/apps/pwa/out/workflows/index.txt +23 -23
  187. package/apps/pwa/out/workspace/index.html +1 -1
  188. package/apps/pwa/out/workspace/index.txt +23 -24
  189. package/apps/pwa/out/worktrees/index.html +1 -1
  190. package/apps/pwa/out/worktrees/index.txt +23 -23
  191. package/graphs/drafts/draft-cleanup.yaml +19 -0
  192. package/graphs/drafts/recipes.yaml +23 -0
  193. package/graphs/intake/intake-daily.yaml +2 -1
  194. package/graphs/ops/recipes.yaml +3 -0
  195. package/graphs/ops/secret-exposure.yaml +24 -0
  196. package/graphs/release/recipes.yaml +3 -0
  197. package/graphs/release/release-loop.yaml +4 -1
  198. package/graphs/video/explainer-line.yaml +2 -2
  199. package/package.json +2 -1
  200. package/plugins/elanous-hwp/README.md +25 -0
  201. package/plugins/elanous-hwp/cli.ts +22 -0
  202. package/plugins/elanous-hwp/converter.ts +237 -0
  203. package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
  204. package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
  205. package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
  206. package/plugins/elanous-hwp/plugin.json +16 -0
  207. package/plugins/elanous-hwp/plugin.ts +40 -0
  208. package/scripts/ci-public-export-leak-gate.ts +75 -14
  209. package/scripts/ci-public-leak-gate.ts +44 -9
  210. package/scripts/context-hooks/claude-stop.sh +4 -0
  211. package/scripts/context-hooks/emit.ts +24 -0
  212. package/scripts/context-hooks/post-tool-use.sh +4 -0
  213. package/scripts/coord-channel-watch.sh +12 -0
  214. package/scripts/coord-requests-bridge.ts +143 -0
  215. package/scripts/field-feed/lib.ts +4 -0
  216. package/scripts/fleet/node-setup.sh +20 -3
  217. package/scripts/homebrew-formula-bump.ts +69 -0
  218. package/scripts/marketing/measure-site-numbers.ts +169 -0
  219. package/scripts/measure-gate-shards.ts +309 -0
  220. package/scripts/mission-request-judge.ts +31 -1
  221. package/scripts/morning-report.ts +2 -2
  222. package/scripts/opportunity-scan.ts +1 -1
  223. package/scripts/release-loop/auto-approve-node.ts +21 -4
  224. package/scripts/release-loop/cut-branch.ts +84 -0
  225. package/scripts/release-loop/gate-node.ts +14 -13
  226. package/scripts/release-loop/mac-smoke-node.ts +229 -0
  227. package/scripts/release-loop/node-verdict.ts +3 -3
  228. package/scripts/release-loop/tui-node-checks.ts +42 -0
  229. package/scripts/release-loop/tui-sim-node.ts +32 -8
  230. package/scripts/release-loop/unattended-release.ts +5 -3
  231. package/scripts/release-loop/version-node.ts +61 -10
  232. package/scripts/signal-digest-cycle.ts +2 -7
  233. package/scripts/signal-digest-photo.ts +11 -0
  234. package/scripts/tech1-public-measure.ts +94 -0
  235. package/scripts/test-diet/node.ts +1 -1
  236. package/scripts/tui-regress.ts +2 -2
  237. package/scripts/ux/demo-rehearsal.ts +243 -0
  238. package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
  239. package/skills/diagram-master/SKILL.md +366 -0
  240. package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
  241. package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
  242. package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
  243. package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
  244. package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
  245. package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
  246. package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
  247. package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
  248. package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
  249. package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
  250. package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
  251. package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
  252. package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
  253. package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
  254. package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
  255. package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
  256. package/skills/diagram-master/references/examples/README.md +69 -0
  257. package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
  258. package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
  259. package/skills/diagram-master/references/pyproject.toml +16 -0
  260. package/skills/diagram-master/references/quality-checklist.md +33 -0
  261. package/skills/diagram-master/references/routing-guide.md +164 -0
  262. package/skills/diagram-master/references/uv.lock +1356 -0
  263. package/skills/google-workspace/SKILL.md +33 -0
  264. package/skills/google-workspace/scripts/gws-safe.sh +177 -0
  265. package/skills/grill-me/SKILL.md +1 -1
  266. package/skills/lecture-note-digitizer/SKILL.md +926 -0
  267. package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
  268. package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
  269. package/skills/lecture-note-digitizer/references/html-template.md +167 -0
  270. package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
  271. package/skills/omni-crawl/SKILL.md +21 -11
  272. package/skills/omni-crawl/scripts/main.ts +6 -6
  273. package/skills/omni-crawl/src/router.ts +3 -3
  274. package/skills/omni-digest/SKILL.md +17 -9
  275. package/skills/omni-digest/digest.md +2 -2
  276. package/skills/omni-digest/src/fetch.ts +30 -3
  277. package/skills/pitch-deck/SKILL.md +2 -2
  278. package/skills/pitch-deck/template.html +2 -2
  279. package/skills/youtube-master/SKILL.md +163 -0
  280. package/skills/youtube-master/package-lock.json +558 -0
  281. package/skills/youtube-master/package.json +13 -0
  282. package/skills/youtube-master/references/study-note-template.md +124 -0
  283. package/skills/youtube-master/scripts/main.ts +435 -0
  284. package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
  285. package/skills/youtube-master/src/audio.ts +63 -0
  286. package/skills/youtube-master/src/domain-staging.ts +50 -0
  287. package/skills/youtube-master/src/env.ts +36 -0
  288. package/skills/youtube-master/src/obsidian.ts +155 -0
  289. package/skills/youtube-master/src/router.ts +110 -0
  290. package/skills/youtube-master/src/stt.ts +199 -0
  291. package/skills/youtube-master/src/study-note.ts +175 -0
  292. package/skills/youtube-master/src/summarize.ts +358 -0
  293. package/skills/youtube-master/src/transcript.ts +76 -0
  294. package/skills/youtube-master/src/types.ts +91 -0
  295. package/skills/youtube-master/src/util.ts +93 -0
  296. package/skills/youtube-master/src/youtube.ts +70 -0
  297. package/skills/youtube-master/tsconfig.json +13 -0
  298. package/src/acp/server.ts +5 -2
  299. package/src/agent-mission/driver.ts +80 -10
  300. package/src/agent-mission/pty-decision.ts +71 -0
  301. package/src/agent-mission/review-loop.ts +30 -0
  302. package/src/autopilot/build/llm-conflict-merge.ts +16 -3
  303. package/src/autopilot/tool-registry.ts +2 -0
  304. package/src/boot/daemon-prompt-turn.ts +5 -0
  305. package/src/boot/daemon-runtime.ts +9 -3
  306. package/src/boot/daemon-tools/web-search.ts +3 -0
  307. package/src/card-followup/core.ts +109 -0
  308. package/src/chat/index.ts +1 -0
  309. package/src/cli/drive-input-jail.ts +101 -0
  310. package/src/cli/hooks-cli.ts +17 -3
  311. package/src/cli/make-cli.ts +43 -0
  312. package/src/cli/persona-cli.ts +90 -4
  313. package/src/cli/plugin-cli.ts +31 -22
  314. package/src/cli/pod-cli.ts +19 -9
  315. package/src/cli/pr-cli.ts +51 -15
  316. package/src/cli/pty-drive-cli.ts +41 -7
  317. package/src/cli/pty-drive-verdict.ts +40 -0
  318. package/src/cli/release-cli.ts +17 -4
  319. package/src/cli/setup-cli.ts +43 -1
  320. package/src/cli/skills-cli.ts +17 -1
  321. package/src/context-bus/claude-hooks-install.ts +10 -0
  322. package/src/context-bus/context-now-surfaces.ts +48 -0
  323. package/src/context-bus/context-now.ts +104 -0
  324. package/src/context-bus/coord-events.ts +2 -2
  325. package/src/context-bus/external-events.ts +52 -0
  326. package/src/context-bus/long-term-memory.ts +108 -0
  327. package/src/dashboard/index.ts +9 -4
  328. package/src/dashboard/slash-runtime/dashboard-handlers.ts +7 -1
  329. package/src/decisions/decision-cards.ts +10 -3
  330. package/src/decisions/decision-ledger.ts +13 -5
  331. package/src/discord/sprint21-runtime.ts +5 -1
  332. package/src/discord-self-message.ts +32 -0
  333. package/src/discord-slash-wire.ts +5 -1
  334. package/src/discord.ts +17 -1
  335. package/src/domains/core-tools.ts +3 -2
  336. package/src/domains/outbound-alert.ts +6 -1
  337. package/src/domains/telegram-kind-route.ts +51 -13
  338. package/src/exec-requests/default-outputs.ts +25 -0
  339. package/src/exec-requests/planner.ts +10 -2
  340. package/src/exec-requests/runner.ts +20 -6
  341. package/src/exec-requests/store.ts +3 -0
  342. package/src/execution-loop/launch-gate.ts +100 -0
  343. package/src/field/field-reel.ts +5 -1
  344. package/src/field-note/index.ts +201 -0
  345. package/src/field-note/transcribe.ts +248 -0
  346. package/src/graph-kinds/registry.ts +3 -3
  347. package/src/graph-runner/graph-cli.ts +61 -1
  348. package/src/graph-runner/graph-grow-llm.ts +37 -0
  349. package/src/graph-runner/graph-grow.ts +47 -0
  350. package/src/graph-runner/runner.ts +317 -25
  351. package/src/grok/agent-search.ts +8 -2
  352. package/src/guardian/secret-exposure-scan.ts +101 -0
  353. package/src/harness/harness-cli-command.ts +247 -7
  354. package/src/harness/harness-pod-dispatch.ts +43 -23
  355. package/src/harness/harness-seams.ts +38 -4
  356. package/src/harness/harness-stop.ts +23 -4
  357. package/src/harness/launch-registry.ts +91 -0
  358. package/src/harness/process-garbage.ts +5 -3
  359. package/src/hooks/dispatch.ts +82 -0
  360. package/src/hooks/expose.ts +254 -0
  361. package/src/hooks/github-shadow.ts +108 -0
  362. package/src/hooks/providers.ts +71 -5
  363. package/src/hooks/queue.ts +1 -1
  364. package/src/hooks/receiver.ts +21 -20
  365. package/src/index.ts +52 -14
  366. package/src/intake-plane/categorize.ts +18 -46
  367. package/src/intake-plane/discord-seat-work.ts +10 -0
  368. package/src/intake-plane/front-route-classifier.ts +33 -39
  369. package/src/intake-plane/persona-answer.ts +30 -0
  370. package/src/intake-plane/runtime-callables.ts +8 -1
  371. package/src/intake-plane/telegram-seat-work.ts +36 -2
  372. package/src/intake-plane/wish-folder.ts +67 -0
  373. package/src/intent-gate/gate.ts +30 -10
  374. package/src/live/detail-switch.ts +5 -0
  375. package/src/llm/difficulty.ts +57 -0
  376. package/src/llm/judge-layer.ts +101 -0
  377. package/src/llm/mission-router.ts +33 -19
  378. package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
  379. package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
  380. package/src/loops/contract/examples/steward.loop.yaml +50 -0
  381. package/src/loops/contract/loop-agent.schema.json +138 -0
  382. package/src/loops/contract/validate.ts +64 -0
  383. package/src/loops/registry.ts +20 -6
  384. package/src/market/signed-index.ts +4 -0
  385. package/src/maturity/bot-command-maturity.ts +45 -0
  386. package/src/maturity/feature-maturity.ts +26 -4
  387. package/src/model-tier/index.ts +2 -2
  388. package/src/model-tier/tier-resolver.ts +20 -20
  389. package/src/model-tier/types.ts +6 -6
  390. package/src/mss/logging/log-store.ts +15 -0
  391. package/src/nexus/api/config-model-tier.ts +5 -4
  392. package/src/nexus/api/fabric-plans.ts +53 -0
  393. package/src/nexus/api/harness-api.ts +5 -1
  394. package/src/nexus/api/health.ts +2 -0
  395. package/src/nexus/api/http-server.ts +36 -0
  396. package/src/nexus/api/inside-events.ts +226 -0
  397. package/src/nexus/api/meta-api.ts +21 -8
  398. package/src/nexus/api/missions.ts +6 -1
  399. package/src/nexus/api/ops-seats.ts +33 -0
  400. package/src/nexus/api/outputs-route.ts +44 -0
  401. package/src/nexus/api/seat-requests.ts +44 -2
  402. package/src/nexus/api/sessions-store.ts +17 -38
  403. package/src/nexus/api/sub-seat-charters.ts +34 -0
  404. package/src/nexus/api/tabs-mutations.ts +3 -1
  405. package/src/nexus/api/trace.ts +18 -6
  406. package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
  407. package/src/nexus/api/workflows.ts +80 -6
  408. package/src/nexus/index.ts +2 -0
  409. package/src/nexus/kinds/webterm.ts +2 -1
  410. package/src/nexus/webterm/pty.ts +6 -2
  411. package/src/onboarding/cli-auth-status.ts +80 -0
  412. package/src/onboarding/voice-ai.ts +3 -3
  413. package/src/org/seat-budget.ts +48 -0
  414. package/src/org/stall-escalation.ts +63 -0
  415. package/src/outputs/ledger.ts +120 -0
  416. package/src/persona/persona-todo.ts +79 -0
  417. package/src/persona/presets.ts +116 -2
  418. package/src/persona/team-set.ts +155 -0
  419. package/src/plugins/install/plugin-install.ts +14 -0
  420. package/src/plugins/maker/market-bundle.ts +109 -0
  421. package/src/plugins/maker/plugin-maker.ts +254 -11
  422. package/src/plugins/maker/wizard-generate.ts +103 -0
  423. package/src/pod-lease/admission.ts +150 -0
  424. package/src/pod-lease/dependency-state.ts +85 -0
  425. package/src/pod-lease/host-lease.ts +127 -0
  426. package/src/project/project-suggestion.ts +16 -0
  427. package/src/project/session-context.ts +24 -0
  428. package/src/release-loop/checklist.ts +2 -2
  429. package/src/release-loop/feature-store.ts +5 -3
  430. package/src/release-loop/next-md-merge.ts +58 -0
  431. package/src/release-loop/release-note.ts +59 -4
  432. package/src/seat-dispatch/seat-ask.ts +130 -0
  433. package/src/seat-dispatch/seat-questions.ts +100 -0
  434. package/src/seat-loop/seat-loop.ts +548 -29
  435. package/src/seat-loop/seat-report.ts +37 -2
  436. package/src/self-dev/dev-cli.ts +4 -1
  437. package/src/self-dev/dev-pipeline.ts +37 -1
  438. package/src/self-dev/draft-sweep.ts +38 -33
  439. package/src/self-dev/draft-triage-rules.ts +8 -5
  440. package/src/self-dev/fabric-plan-core.ts +297 -0
  441. package/src/self-dev/orchestrate.ts +36 -4
  442. package/src/self-dev/release-path-guard.ts +62 -0
  443. package/src/self-dev/shard-aggregate.ts +230 -0
  444. package/src/self-dev/shard-placement.ts +60 -0
  445. package/src/self-dev/shard-plan.ts +48 -0
  446. package/src/self-dev/shard-pod-run.ts +77 -0
  447. package/src/self-dev/shard-run.ts +105 -0
  448. package/src/self-implement/gate-baseline.ts +1 -1
  449. package/src/self-implement/gate-cli.ts +40 -1
  450. package/src/self-implement/gate-scope.ts +37 -7
  451. package/src/self-implement/graph-variant.ts +171 -0
  452. package/src/self-implement/harness-policy-gates.ts +1 -1
  453. package/src/self-implement/headless-elanous-driver.ts +22 -3
  454. package/src/self-implement/host-regate.ts +22 -1
  455. package/src/self-implement/importer-test-index.ts +2 -1
  456. package/src/self-implement/l8-merge-queue.ts +130 -0
  457. package/src/self-implement/orchestrator.ts +242 -9
  458. package/src/self-implement/route-consumer-test-index.ts +122 -0
  459. package/src/self-implement/seams.ts +72 -11
  460. package/src/self-implement/test-seams.ts +1 -0
  461. package/src/self-implement/uncommitted-work.ts +59 -0
  462. package/src/session/index.ts +36 -13
  463. package/src/skills/gift-install.ts +215 -0
  464. package/src/skills/tools/web-search.ts +4 -1
  465. package/src/steward/launch.ts +42 -26
  466. package/src/steward/steward-cards.ts +17 -9
  467. package/src/steward/triage.ts +46 -14
  468. package/src/task-cards/card-cli.ts +25 -1
  469. package/src/task-orchestrator/mission.ts +5 -0
  470. package/src/task-orchestrator/store.ts +12 -3
  471. package/src/task-orchestrator/surfaces/pod-command-job.ts +29 -7
  472. package/src/task-orchestrator/surfaces/pod-lease.ts +156 -19
  473. package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
  474. package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -40
  475. package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
  476. package/src/task-orchestrator/types.ts +2 -0
  477. package/src/telegram-card-followup.ts +16 -96
  478. package/src/telegram-commands.ts +10 -1
  479. package/src/telegram-project-command.ts +71 -0
  480. package/src/telegram-report.ts +21 -17
  481. package/src/telegram.ts +29 -3
  482. package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
  483. package/src/user-config.ts +102 -20
  484. package/src/version/packed-revision.json +1 -1
  485. package/src/web-search/grok.ts +7 -1
  486. package/src/web-search/index.ts +28 -1
  487. package/src/web-search/provider.ts +4 -0
  488. package/src/web-search/x-intent.ts +5 -0
  489. package/src/workflow-runtime/executor.ts +277 -47
  490. package/src/workflow-runtime/index.ts +1 -0
  491. package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
  492. package/src/workflow-runtime/plugin-kind-node.ts +1 -1
  493. package/src/workflow-runtime/schema.ts +79 -4
  494. package/src/workflow-runtime/types.ts +35 -4
  495. package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  505. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  506. package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/8195-6fde3d7a39859206.js +0 -10
  525. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/app/chat/page-f8116559c7424da7.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/app/design-check/page-54e1f966ab60a4dc.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/app/market/page-b31610deda76337b.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6cb0e1bbefe5fb3d.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e168fa1030af0548.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/showroom/page-4fddc90b23feb215.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
  583. package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +0 -1
  584. /package/apps/pwa/out/_next/static/{2DsiDfDCks44g1cPUMhiW → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
@@ -39,6 +39,7 @@ import { writeScopedTypecheckConfig } from './scoped-typecheck-config.js';
39
39
  // ⭐ 게이트 스코프 근본 수리(2026-07-26) — 풀 `bun test` 폴백 제거 + 연관 테스트 유도(순수).
40
40
  import { resolveGateScope } from './gate-scope.js';
41
41
  import { buildImporterTestIndex, isTestPath } from './importer-test-index.js';
42
+ import { buildRouteConsumerTestIndex } from './route-consumer-test-index.js';
42
43
  import { countTestDeclarations } from './test-declarations.js';
43
44
  import {
44
45
  allowsBaselineOnlyFailure,
@@ -75,6 +76,7 @@ import { tscEnv, assessTypecheckExecution, classifyTypecheckErrors, diffTypechec
75
76
  import { nestCapReached, childNestEnv, nestInfo } from '../agent/nest-depth.js';
76
77
  import { harnessBoundaryEnv, harnessBoundaryRequestsEnv, harnessBoundaryResponsesEnv, harnessSpaceEnv, getHarnessSpace, normalizeSpaceId, getHarnessRunId, resolveRunIdentity } from '../harness/harness-space.js';
77
78
  import { debug } from '../debug/log.js';
79
+ import { RELEASE_PATH_HOLD_MARKER, RELEASE_PATH_LABEL, releasePathHoldShouldPost, releasePathHoldCommentsArgs, releasePrFilePaths } from '../self-dev/release-path-guard.js';
78
80
  import { emitDecision } from '../live/detail-switch.js';
79
81
  import { gateDecision, reviewDecision, mergeDecision } from './decision-events.js';
80
82
  import { LogStore } from '../mss/logging/log-store.js';
@@ -85,7 +87,9 @@ import { countReflectFactConflicts, type MustFixRefutation, type ReflectEvidence
85
87
  import { isSupervisorDecisionSection, splitGoalSections, supervisorGoalDigest } from './goal-digest.js';
86
88
  import type { SupervisionReworkSource } from './supervision-vocabulary.js';
87
89
  import type { ReworkBudgetVerdict } from './run-outcome.js';
88
- import { runHeadlessGoalLoopPty } from './headless-elanous-driver.js';
90
+ import { pickImplementRoleForGoal, runHeadlessGoalLoopPty } from './headless-elanous-driver.js';
91
+ import { childLlmSelectionEnv } from '../agent/run-context.js';
92
+ import { observeExternalEvent } from '../context-bus/external-events.js';
89
93
  import { buildLlmHitlRelay, SIDE_EFFECT_RE } from '../harness/llm-hitl-relay.js';
90
94
  import { dispatchAskUserQuestion } from '../ask-user-question/tool.js';
91
95
  import { parseQuestionRequest } from '../ask-user-question/types.js';
@@ -1641,7 +1645,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1641
1645
  if ((o.ptyAvailable ?? ptyAvailable)()) {
1642
1646
  try {
1643
1647
  const res = await (o.runHeadlessGoalLoopPty ?? runHeadlessGoalLoopPty)({
1644
- binRoot, cwd, featurePrompt: prompt, maxWaitSec, brain,
1648
+ binRoot, cwd, featurePrompt: prompt, goalDocument: feature, maxWaitSec, brain,
1645
1649
  ...(activityGraceSec !== undefined ? { activityGraceSec, activityGraceSource: 'caller' as const } : {}),
1646
1650
  autoStop: getUserConfig().tools.selfImplement.autoStop,
1647
1651
  autoAssist: getUserConfig().tools.selfImplement.autoAssist,
@@ -1698,9 +1702,14 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1698
1702
  // 폴백 — PTY 불가/cap 초과/에러. 기존 헤드리스 spawnSync 경로 보존(블로킹이지만 견고).
1699
1703
  const args = [`${binRoot}/bin/elanous.mjs`, 'dev', '--implement'];
1700
1704
  const executionId = randomUUID();
1705
+ const fallbackImplementRole = !childLlm && !process.env.ELANOUS_LLM_MODEL?.trim()
1706
+ ? pickImplementRoleForGoal(feature) : undefined;
1701
1707
  if (o.configDir) args.push('--config-dir', o.configDir);
1702
1708
  args.push(prompt);
1703
- const r = runSpawnSync('bun', args, {
1709
+ const fallbackSource = `elanous://harness/${encodeURIComponent(runId ?? 'unknown')}/${encodeURIComponent(executionId)}`;
1710
+ observeExternalEvent({ origin: 'harness-child', kind: 'started', summary: 'Harness child started (fallback)', source: fallbackSource });
1711
+ let r: ReturnType<typeof runSpawnSync>;
1712
+ try { r = runSpawnSync('bun', args, {
1704
1713
  cwd, encoding: 'utf8', timeout: maxWaitSec * 1000, maxBuffer: 32 * 1024 * 1024,
1705
1714
  stdio: ['ignore', 'pipe', 'pipe'],
1706
1715
  ...(executionSignal ? { signal: executionSignal } : {}), // /cancel 또는 구현 timeout → 자식 SIGTERM(고아 방지)
@@ -1716,6 +1725,9 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1716
1725
  // ⇒ 실물: 쿼터 신호가 갱신 안 되는 우주를 봐서 전 계정 unknown ⇒ 회전이 100% 계정 선택 ⇒ 429(4회).
1717
1726
  ...(o.stateDir && o.stateDirSource ? { ELANOUS_STATE_DIR_SOURCE: o.stateDirSource } : {}),
1718
1727
  ...childNestEnv(),
1728
+ ...childLlmSelectionEnv(childLlm ?? (fallbackImplementRole
1729
+ ? { provider: fallbackImplementRole.provider, model: fallbackImplementRole.model, source: 'config' }
1730
+ : undefined)),
1719
1731
  ...((): Record<string, string> => {
1720
1732
  const sp = getHarnessSpace() ?? { inHarness: true as const, kind: 'self-implement' as const, id: normalizeSpaceId(basename(cwd)), runId: getHarnessRunId() };
1721
1733
  // ★ K run-identity 공백 방어 — driver 와 동일 계약(호출자 > 상속 > canonical mint). 빈 runId 는
@@ -1727,7 +1739,12 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1727
1739
  ...harnessBoundaryResponsesEnv(executionId),
1728
1740
  ...harnessPolicyEnv(),
1729
1741
  },
1730
- });
1742
+ }); } catch (error) {
1743
+ observeExternalEvent({ origin: 'harness-child', kind: 'finished', summary: 'Harness child failed (fallback)', source: fallbackSource });
1744
+ throw error;
1745
+ }
1746
+ observeExternalEvent({ origin: 'harness-child', kind: 'finished',
1747
+ summary: `Harness child finished (fallback): ${r.status ?? 'unknown'}`, source: fallbackSource });
1731
1748
  const transcript = `${r.stdout ?? ''}${r.stderr ?? ''}`;
1732
1749
  const errCode = (r.error as { code?: string } | undefined)?.code;
1733
1750
  const timedOut = errCode === 'ETIMEDOUT' || r.signal === 'SIGTERM';
@@ -1807,9 +1824,11 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1807
1824
  trackedFiles.filter(isTestPath),
1808
1825
  [...new Set([...trackedFiles, ...changedAll, ...observationChangedFiles])].filter((file) => /\.(?:[cm]?[jt]sx?)$/.test(file)),
1809
1826
  );
1827
+ const routeConsumerTestIndex = buildRouteConsumerTestIndex(cwd, trackedFiles.filter(isTestPath), changedAll, comparisonBase ?? 'HEAD');
1810
1828
  // 워크트리에 없는 변경 경로 = 지운 파일 — 미검증으로 세지 않는다(gate-scope `isDeleted` · 2026-09-26 run-7dd4cce6).
1811
1829
  const isDeleted = (path: string): boolean => !isFile(path);
1812
- const scope = resolveGateScope(changedAll, isFile, importerTestIndex ?? undefined, postsync ? { mode: 'postsync', isDeleted } : { isDeleted });
1830
+ const scopeOpts = { isDeleted, routeConsumerTestIndex: routeConsumerTestIndex ?? undefined };
1831
+ const scope = resolveGateScope(changedAll, isFile, importerTestIndex ?? undefined, postsync ? { ...scopeOpts, mode: 'postsync' } : scopeOpts);
1813
1832
  const comparisonFailureReason = observationChanges.comparisonBaseStatus === 'unavailable'
1814
1833
  ? 'comparison-base-unavailable'
1815
1834
  : observationChanges.comparisonBaseStatus === 'comparison-failed'
@@ -1817,10 +1836,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1817
1836
  : undefined;
1818
1837
  const unmeasuredPostsync = postsync && !comparisonFailureReason && scope.reason === 'unmeasured';
1819
1838
  const scopeReason = comparisonFailureReason ?? scope.reason;
1820
- const observationScope = resolveGateScope(observationChangedFiles, isFile, importerTestIndex ?? undefined);
1821
- const observedChangedTestFiles = observationScope.reason === 'changed-tests'
1822
- ? (observationScope.testArgs ?? []).filter((file) => !observationScope.pulledInRelatedTests.includes(file))
1823
- : [];
1839
+ const observedChangedTestFiles = [...new Set(observationChangedFiles.filter((file) => isTestPath(file) && isFile(file)))];
1824
1840
  // This reads the merge-base without executing base tests. Its static declaration count is
1825
1841
  // observational: dynamic generation and renamed helpers can make it incomplete.
1826
1842
  const declarationDecline = testDeclarationDecline(cwd, comparisonBase, observedChangedTestFiles);
@@ -1863,6 +1879,9 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1863
1879
  documentPaths: scope.documentPaths.slice(0, 8), documentPathCount: scope.documentPaths.length,
1864
1880
  documentsWithoutDerivedTests: scope.documentsWithoutDerivedTests.slice(0, 8),
1865
1881
  documentsWithoutDerivedTestCount: scope.documentsWithoutDerivedTests.length,
1882
+ callerTests: scope.callerTests,
1883
+ callerTestsOverflow: scope.callerTestsOverflow,
1884
+ routeBaseLookupFailures: routeConsumerTestIndex.lookupFailures,
1866
1885
  pulledInRelatedTests: scope.pulledInRelatedTests.slice(0, 8),
1867
1886
  pulledInRelatedTestCount: scope.pulledInRelatedTests.length,
1868
1887
  importerTestIndexAvailable: importerTestIndex !== null,
@@ -1871,7 +1890,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
1871
1890
  importerTestsNotRunTruncated: scope.importerTestsNotRun?.truncated ?? null,
1872
1891
  unresolvedRelativeImportSpecifiers: scope.importerTestsNotRun?.unresolvedRelativeSpecifiers ?? null,
1873
1892
  // ⚠️ 커버 안 된 변경이 있으면 **테스트가 돌았어도** warn — debug 는 운영 로그에서 필터링된다(리뷰 should-fix).
1874
- }, { level: scope.reason === 'no-related-tests' || scope.missingTestFiles > 0 || scope.unverified.length > 0 ? 'warn' : 'debug' });
1893
+ }, { level: scope.reason === 'no-related-tests' || scope.missingTestFiles > 0 || scope.unverified.length > 0 || scope.callerTestsOverflow.length > 0 || routeConsumerTestIndex.lookupFailures.length > 0 ? 'warn' : 'debug' });
1875
1894
  try { emitDecision(gateDecision({ runId: ctx?.runId, reason: scopeReason, testStepSkipped })); } catch { /* observation must not change the gate result */ }
1876
1895
  // `defaultSeams.gate` owns the changed-file list and runs before the test-step skip,
1877
1896
  // so platform-only changes remain measured even when gate-scope has no Bun filters.
@@ -2174,6 +2193,12 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
2174
2193
  + `${scope.pulledInRelatedTests.slice(0, 8).join(', ')}`
2175
2194
  + `\n(편집된 테스트만 돌렸다면 이것들이 빠졌다)`
2176
2195
  : '';
2196
+ const callerOverflowNote = scope.callerTestsOverflow.length > 0
2197
+ ? `\n\n[gate-scope] ⚠️ caller test cap exceeded — not run: ${scope.callerTestsOverflow.map(({ file, reasons }) => `${file} (${reasons.join('+')})`).join(', ')}`
2198
+ : '';
2199
+ const routeLookupNote = routeConsumerTestIndex.lookupFailures.length > 0
2200
+ ? `\n\n[gate-scope] ⚠️ caller route base lookup failed — unmeasured: ${routeConsumerTestIndex.lookupFailures.map(({ source, reason }) => `${source} (${reason})`).join(', ')}`
2201
+ : '';
2177
2202
  const scopeNote = testStepSkipped
2178
2203
  ? `\n\n[gate-scope] ${scope.reason} — test 스텝을 건너뛰었다(변경파일 tsc 만 검증). `
2179
2204
  + (scope.reason === 'no-related-tests'
@@ -2204,7 +2229,7 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
2204
2229
  : '';
2205
2230
  return {
2206
2231
  passed: gatePassed,
2207
- log: `${baseLog}${policyFailureLog}${dependencyFailureLog}${dependencyUnmeasuredNote}${comparisonFailureLog}${baselineNote}${verifyByBreakingNote}${scopeNote}${uncoveredNote}${pulledInRelatedTestsNote}`,
2232
+ log: `${baseLog}${policyFailureLog}${dependencyFailureLog}${dependencyUnmeasuredNote}${comparisonFailureLog}${baselineNote}${verifyByBreakingNote}${scopeNote}${uncoveredNote}${pulledInRelatedTestsNote}${callerOverflowNote}${routeLookupNote}`,
2208
2233
  testStepExecuted: !testStepSkipped && testStep !== undefined && !testStep.skipped,
2209
2234
  scopeReason,
2210
2235
  comparisonBaseStatus: observationChanges.comparisonBaseStatus,
@@ -2349,11 +2374,47 @@ export function defaultSeams(o: DefaultSeamsOptions = {}): SelfImplementSeams {
2349
2374
  postPrComment: async ({ number, body, cwd }) => {
2350
2375
  // Round comments go out as the GitHub App like the PR itself — a personal `gh` login made the owner a
2351
2376
  // participant and mailed them on merge (#22089).
2377
+ if (body.startsWith(RELEASE_PATH_HOLD_MARKER)) {
2378
+ // Several merge surfaces may hold the same PR; post the release-path hold comment once.
2379
+ let history: unknown;
2380
+ try {
2381
+ history = JSON.parse((await execFileAsync('gh', releasePathHoldCommentsArgs(number), {
2382
+ cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, maxBuffer: 64 * 1024 * 1024, env: ghAutomationEnv(process.env),
2383
+ })).stdout);
2384
+ } catch { history = undefined; }
2385
+ if (!releasePathHoldShouldPost(() => history)) return;
2386
+ }
2352
2387
  await execFileAsync('gh', ['pr', 'comment', String(number), '--body', body], {
2353
2388
  cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, maxBuffer: 64 * 1024 * 1024, env: ghAutomationEnv(process.env),
2354
2389
  });
2355
2390
  },
2356
2391
 
2392
+ addPrLabel: async ({ number, label, cwd }) => {
2393
+ if (label === RELEASE_PATH_LABEL) {
2394
+ const existing = await execFileAsync('gh', ['label', 'list', '--search', label, '--json', 'name'], {
2395
+ cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, env: ghAutomationEnv(process.env),
2396
+ });
2397
+ const names: unknown = JSON.parse(existing.stdout);
2398
+ if (!Array.isArray(names)) throw new Error('gh label list returned an invalid list');
2399
+ if (!names.some((item: unknown) => !!item && typeof item === 'object' && 'name' in item && item.name === label)) {
2400
+ await execFileAsync('gh', ['label', 'create', label, '--color', 'D93F0B', '--description', 'Release path requires OP approval'], {
2401
+ cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, env: ghAutomationEnv(process.env),
2402
+ });
2403
+ }
2404
+ }
2405
+ await execFileAsync('gh', ['pr', 'edit', String(number), '--add-label', label], {
2406
+ cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, env: ghAutomationEnv(process.env),
2407
+ });
2408
+ },
2409
+ readPrFiles: async ({ number, cwd }) => {
2410
+ // Paginate the PR file API: a single `gh pr view --json files` can omit files after the first page.
2411
+ const { stdout } = await execFileAsync('gh', [
2412
+ 'api', '--paginate', '--slurp', '--method', 'GET', '-f', 'per_page=100',
2413
+ `repos/{owner}/{repo}/pulls/${number}/files`,
2414
+ ], { cwd, encoding: 'utf8', timeout: GIT_TIMEOUT, maxBuffer: 64 * 1024 * 1024, env: ghAutomationEnv(process.env) });
2415
+ return releasePrFilePaths(JSON.parse(stdout));
2416
+ },
2417
+
2357
2418
  ...(o.approvePr ? { approvePr: o.approvePr } : {}),
2358
2419
 
2359
2420
  // ★ 내부 리뷰(2026-07-21·review-gated merge) — worktree diff → reviewPullRequest(agent-substrate·미션/
@@ -32,6 +32,7 @@ export function seams(over: Partial<SelfImplementSeams> & { gateResults?: boolea
32
32
  defaultBranchRef: () => 'origin/main',
33
33
  mergeMain: async () => ({ status: 'up-to-date' }),
34
34
  openPr: async ({ head }) => ({ url: `https://pr/${head}`, number: 7 }),
35
+ readPrFiles: async () => ['src/ordinary.ts'],
35
36
  readPrDiff: async () => '',
36
37
  readPrCommitShas: async () => ({ baseCommit: 'base-sha', headCommit: 'checked-head-sha' }),
37
38
  approvePr: async () => true,
@@ -0,0 +1,59 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { isElanousRuntimeArtifactPath } from './gate-scope.js';
3
+
4
+ export type UncommittedWork =
5
+ | { status: 'clean'; trackedChanges: string[]; untrackedFiles: string[] }
6
+ | { status: 'changes'; trackedChanges: string[]; untrackedFiles: string[] }
7
+ | { status: 'unavailable'; reason: string };
8
+
9
+ /** Observe the index and working tree without staging, refreshing, or changing any files. */
10
+ export function detectUncommittedWork(worktree: string): UncommittedWork {
11
+ const gitEnv = { ...process.env };
12
+ for (const key of Object.keys(gitEnv)) {
13
+ if (key.startsWith('GIT_')) delete gitEnv[key];
14
+ }
15
+ let result: ReturnType<typeof spawnSync>;
16
+ try {
17
+ result = spawnSync('git', ['--no-optional-locks', 'status', '--porcelain=v1', '-z', '--untracked-files=all'], {
18
+ cwd: worktree,
19
+ env: gitEnv,
20
+ encoding: 'utf8',
21
+ timeout: 10_000,
22
+ });
23
+ } catch (error) {
24
+ return { status: 'unavailable', reason: error instanceof Error ? error.message : String(error) };
25
+ }
26
+ if (result.error || result.status !== 0 || typeof result.stdout !== 'string') {
27
+ const stderr = typeof result.stderr === 'string' ? result.stderr.trim() : '';
28
+ return { status: 'unavailable', reason: result.error?.message || stderr || 'git status failed' };
29
+ }
30
+
31
+ const trackedChanges = new Set<string>();
32
+ const untrackedFiles = new Set<string>();
33
+ const entries = result.stdout.split('\0');
34
+ if (entries.at(-1) !== '') return { status: 'unavailable', reason: 'incomplete git status output' };
35
+ entries.pop();
36
+ for (let i = 0; i < entries.length; i += 1) {
37
+ const entry = entries[i]!;
38
+ if (entry.length < 4 || entry[2] !== ' ') return { status: 'unavailable', reason: 'invalid git status output' };
39
+ const kind = entry.slice(0, 2);
40
+ const path = entry.slice(3);
41
+ if (!path) return { status: 'unavailable', reason: 'invalid git status path' };
42
+ if (kind === '??') {
43
+ if (!isElanousRuntimeArtifactPath(path) && path !== '.elanous-test' && !path.startsWith('.elanous-test/')
44
+ && path !== '.elanous-skill-artifacts' && !path.startsWith('.elanous-skill-artifacts/')) {
45
+ untrackedFiles.add(path);
46
+ }
47
+ } else if (kind !== '!!') {
48
+ trackedChanges.add(path);
49
+ if (kind.includes('R') || kind.includes('C')) {
50
+ const original = entries[++i];
51
+ if (!original) return { status: 'unavailable', reason: 'incomplete git rename/copy output' };
52
+ if (kind.includes('R')) trackedChanges.add(original);
53
+ }
54
+ }
55
+ }
56
+ return trackedChanges.size || untrackedFiles.size
57
+ ? { status: 'changes', trackedChanges: [...trackedChanges], untrackedFiles: [...untrackedFiles] }
58
+ : { status: 'clean', trackedChanges: [], untrackedFiles: [] };
59
+ }
@@ -453,6 +453,33 @@ function writeIndex(entries: SessionMeta[], root: string = sessionRoot(), remove
453
453
  });
454
454
  }
455
455
 
456
+ /** 세션 메타 하나를 잠금 «안에서» 디스크의 최신 목록 위에 고쳐 쓴다(IA1b).
457
+ * `appendMessage` 가 잠금 밖에서 읽은 목록을 통째로 쓰면, 그 사이 다른 프로세스가 바꾼
458
+ * 같은 세션의 `projectId`·`messageCount` 를 옛 값으로 덮었다. 없는 id 면 `null`. */
459
+ export function updateSessionMeta(
460
+ id: string,
461
+ mutate: (meta: SessionMeta) => void,
462
+ root: string = sessionRoot(),
463
+ opts: { bubble?: boolean } = {},
464
+ ): SessionMeta | null {
465
+ ensureRoots(root);
466
+ const p = indexPath(root);
467
+ return withDirLock(join(root, '.index.lock'), INDEX_LOCK_TIMEOUT_MS, () => {
468
+ const idx = readIndex(root);
469
+ const pos = idx.findIndex((m) => m.id === id);
470
+ if (pos < 0) return null;
471
+ const meta = { ...idx[pos] };
472
+ mutate(meta);
473
+ idx.splice(pos, 1);
474
+ if (opts.bubble) idx.unshift(meta);
475
+ else idx.splice(pos, 0, meta);
476
+ const tmp = `${p}.${process.pid}.${randomUUID().slice(0, 8)}.tmp`;
477
+ writeFileSync(tmp, JSON.stringify(idx, null, 2) + '\n', 'utf-8');
478
+ renameSync(tmp, p);
479
+ return meta;
480
+ });
481
+ }
482
+
456
483
  // ── Tier 1 Phase 3 — listener primitive ──────────────────────────────
457
484
  //
458
485
  // Channel-agnostic subscribe API the daemon-mirror module (PR 2) hooks
@@ -633,20 +660,16 @@ export function appendMessage(id: string, msg: SerializedMessage, root: string =
633
660
  const line = JSON.stringify(enriched) + '\n';
634
661
  appendFileSync(file, line, 'utf-8');
635
662
 
636
- const idx = readIndex(root);
637
- const pos = idx.findIndex(m => m.id === id);
638
- if (pos < 0) throw new Error(`session missing from index: ${id}`);
639
- const meta = idx[pos];
640
- meta.updatedAt = msg.ts || new Date().toISOString();
641
- meta.messageCount = meta.messageCount + 1;
642
- // Auto-title on first user message.
643
- if (meta.title === '(new session)' && msg.role === 'user' && msg.content) {
644
- meta.title = msg.content.replace(/\s+/g, ' ').slice(0, 60).trim() || '(empty)';
645
- }
646
663
  // Bubble to front of index so listSessions returns newest-first.
647
- idx.splice(pos, 1);
648
- idx.unshift(meta);
649
- writeIndex(idx, root);
664
+ const meta = updateSessionMeta(id, (m) => {
665
+ m.updatedAt = msg.ts || new Date().toISOString();
666
+ m.messageCount = m.messageCount + 1;
667
+ // Auto-title on first user message.
668
+ if (m.title === '(new session)' && msg.role === 'user' && msg.content) {
669
+ m.title = msg.content.replace(/\s+/g, ' ').slice(0, 60).trim() || '(empty)';
670
+ }
671
+ }, root, { bubble: true });
672
+ if (!meta) throw new Error(`session missing from index: ${id}`);
650
673
  // Tier 1 Phase 3 — fire AFTER on-disk write + index update.
651
674
  // Listeners (daemon-mirror) read the just-appended message + meta
652
675
  // from a consistent state.
@@ -0,0 +1,215 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { execFileSync } from 'node:child_process';
3
+ import { cpSync, existsSync, lstatSync, mkdtempSync, mkdirSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
4
+ import { homedir, tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { debug } from '../debug/log.js';
7
+
8
+ export interface GiftInstallOptions {
9
+ pack: string;
10
+ code?: string;
11
+ email?: string;
12
+ agree?: boolean;
13
+ endpoint?: string;
14
+ fetch?: typeof fetch;
15
+ targets?: string[];
16
+ /** Caps (defaults below); tests pass small values. */
17
+ maxDownloadBytes?: number;
18
+ maxUnpackedBytes?: number;
19
+ maxEntries?: number;
20
+ }
21
+
22
+ export type GiftInstallResult =
23
+ | { ok: true; names: string[]; paths: string[]; collisions: string[]; needsKeys: boolean; message: string }
24
+ | { ok: false; reason: string; message: string };
25
+
26
+ // GK1 public bundle: packs/elanous-essentials/plugin.json extensions.ai.elanous.bundle.
27
+ const ESSENTIAL_SKILLS = ['youtube-master', 'omni-crawl', 'omni-digest', 'diagram-master', 'lecture-note-digitizer'];
28
+ // A malicious or broken gift must not exhaust the temp disk: cap the download, the unpacked total and the entry count.
29
+ export const MAX_DOWNLOAD_BYTES = 20 * 1024 * 1024;
30
+ export const MAX_UNPACKED_BYTES = 100 * 1024 * 1024;
31
+ export const MAX_ENTRIES = 5000;
32
+ const CONSENT_NOTICE = '선물 스킬 전달과 안내 메일을 위해 이메일을 받습니다 · 보관은 알림 신청과 같은 기간 · 동의하면 --agree 를 붙여 다시 시도해 주세요.';
33
+ const ERROR_MESSAGES: Record<string, string> = {
34
+ 'email-required': CONSENT_NOTICE,
35
+ 'consent-required': CONSENT_NOTICE,
36
+ 'unknown-code': '코드를 다시 확인해 주세요.',
37
+ 'used-up': '이미 다 쓴 코드예요 · 업데이트 소식 신청을 확인해 주세요.',
38
+ expired: '만료된 코드예요.',
39
+ };
40
+
41
+ /** Redeem, verify and install a gift without modifying an existing skill directory. */
42
+ export async function installGiftPack({
43
+ pack, code, email, agree, endpoint = 'https://elanous.ai', fetch: fetchImpl = fetch,
44
+ targets = [join(homedir(), '.agents', 'skills'), join(homedir(), '.claude', 'skills')],
45
+ maxDownloadBytes = MAX_DOWNLOAD_BYTES, maxUnpackedBytes = MAX_UNPACKED_BYTES, maxEntries = MAX_ENTRIES,
46
+ }: GiftInstallOptions): Promise<GiftInstallResult> {
47
+ const codePrefix = code && code.length > 4 ? code.slice(0, 4) : undefined;
48
+ const reject = (reason: string, message: string): GiftInstallResult => {
49
+ debug.log('skills.gift', 'rejected', { pack, reason, count: 0, codePrefix });
50
+ return { ok: false, reason, message };
51
+ };
52
+ if (!email?.trim() || !agree) return reject(!email?.trim() ? 'email-required' : 'consent-required', CONSENT_NOTICE);
53
+ if (!code?.trim()) return reject('unknown-code', ERROR_MESSAGES['unknown-code']!);
54
+ let gift: Record<string, unknown>;
55
+ try {
56
+ const response = await fetchImpl(`${endpoint.replace(/\/$/, '')}/api/gift`, {
57
+ method: 'POST', headers: { 'content-type': 'application/json' },
58
+ body: JSON.stringify({ code, email, consent: true }),
59
+ });
60
+ // The gift API assigns these reasons by HTTP status; error bodies may be absent or malformed.
61
+ const statusReason = ({ 404: 'unknown-code', 409: 'used-up', 410: 'expired' } as Record<number, string | undefined>)[response.status];
62
+ if (statusReason) return reject(statusReason, ERROR_MESSAGES[statusReason]!);
63
+ const payload: unknown = await response.json();
64
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
65
+ return reject('invalid-response', '선물 응답을 확인할 수 없습니다. 다시 시도해 주세요.');
66
+ }
67
+ gift = payload as Record<string, unknown>;
68
+ if (!response.ok) {
69
+ const reason = response.status === 400 && (gift.reason === 'email-required' || gift.reason === 'consent-required')
70
+ ? gift.reason : 'request-rejected';
71
+ return reject(reason, ERROR_MESSAGES[reason] ?? '선물을 받을 수 없습니다. 다시 시도해 주세요.');
72
+ }
73
+ } catch {
74
+ return reject('network-error', '네트워크에 연결할 수 없습니다. 다시 시도해 주세요.');
75
+ }
76
+ if (gift.ok !== true || gift.pack !== pack || typeof gift.zipUrl !== 'string' || !/^https?:\/\//.test(gift.zipUrl)
77
+ || typeof gift.sha256 !== 'string' || !/^[a-f0-9]{64}$/i.test(gift.sha256)) {
78
+ return reject('invalid-response', '선물 응답을 확인할 수 없습니다. 다시 시도해 주세요.');
79
+ }
80
+ let zip: Buffer;
81
+ const tooLarge = () => reject('too-large', '선물 zip 이 허용 크기를 넘습니다. 설치하지 않았습니다.');
82
+ try {
83
+ const response = await fetchImpl(gift.zipUrl, { redirect: 'error' });
84
+ if (!response.ok) throw new Error('download-failed');
85
+ const declared = Number(response.headers.get('content-length'));
86
+ if (Number.isFinite(declared) && declared > maxDownloadBytes) return tooLarge();
87
+ // Count the bytes actually received too — a missing or lying Content-Length must not bypass the cap.
88
+ const chunks: Uint8Array[] = [];
89
+ let received = 0;
90
+ if (response.body) {
91
+ const reader = response.body.getReader();
92
+ for (;;) {
93
+ const { done, value } = await reader.read();
94
+ if (done) break;
95
+ received += value.byteLength;
96
+ if (received > maxDownloadBytes) { await reader.cancel().catch(() => {}); return tooLarge(); }
97
+ chunks.push(value);
98
+ }
99
+ }
100
+ zip = Buffer.concat(chunks);
101
+ } catch {
102
+ return reject('network-error', '다운로드에 실패했습니다. 다시 시도해 주세요.');
103
+ }
104
+ if (createHash('sha256').update(zip).digest('hex').toLowerCase() !== gift.sha256.toLowerCase()) {
105
+ debug.log('skills.gift', 'checksum-mismatch', { pack, reason: 'sha256', count: 0, codePrefix });
106
+ return { ok: false, reason: 'checksum-mismatch', message: '다운로드 검증(sha256)에 실패했습니다. 설치하지 않았습니다.' };
107
+ }
108
+ const temp = mkdtempSync(join(tmpdir(), 'elanous-gift-'));
109
+ const created: string[] = [];
110
+ let incomplete: string | undefined;
111
+ try {
112
+ const zipPath = join(temp, 'gift.zip');
113
+ const extracted = join(temp, 'extracted');
114
+ mkdirSync(extracted);
115
+ writeFileSync(zipPath, zip);
116
+ // Reject archive symlinks and traversal paths before extracting any entry.
117
+ execFileSync('python3', ['-c', `import os, stat, sys, zipfile
118
+ root = sys.argv[2]
119
+ max_unpacked = int(sys.argv[3]); max_entries = int(sys.argv[4])
120
+ with zipfile.ZipFile(sys.argv[1]) as archive:
121
+ entries = archive.infolist()
122
+ if len(entries) > max_entries or sum(entry.file_size for entry in entries) > max_unpacked:
123
+ sys.exit(3)
124
+ for entry in entries:
125
+ parts = entry.filename.split('/')
126
+ mode = entry.external_attr >> 16
127
+ if (not entry.filename or entry.filename.startswith('/') or '\\u005c' in entry.filename
128
+ or any(part in ('', '.', '..') for part in parts[:-1])
129
+ or parts[-1] in ('.', '..') or (not entry.is_dir() and not parts[-1])
130
+ or stat.S_ISLNK(mode)):
131
+ raise ValueError('invalid archive entry')
132
+ written = 0
133
+ for entry in entries:
134
+ path = os.path.join(root, entry.filename)
135
+ if entry.is_dir():
136
+ os.makedirs(path, exist_ok=True)
137
+ else:
138
+ os.makedirs(os.path.dirname(path), exist_ok=True)
139
+ with archive.open(entry) as source, open(path, 'xb') as target:
140
+ while True:
141
+ block = source.read(65536)
142
+ if not block:
143
+ break
144
+ written += len(block)
145
+ if written > max_unpacked:
146
+ sys.exit(3)
147
+ target.write(block)
148
+ `, zipPath, extracted, String(maxUnpackedBytes), String(maxEntries)], { stdio: 'pipe' });
149
+ const skillDirs = (root: string) => readdirSync(root, { withFileTypes: true })
150
+ // SKILL.md must be a regular file — a `SKILL.md/` directory or a symlink is not a skill.
151
+ .filter((entry) => entry.isDirectory() && lstatSync(join(root, entry.name, 'SKILL.md'), { throwIfNoEntry: false })?.isFile() === true)
152
+ .map((entry) => ({ name: entry.name, path: join(root, entry.name) }));
153
+ let skills = skillDirs(extracted);
154
+ if (!skills.length) {
155
+ const roots = readdirSync(extracted, { withFileTypes: true })
156
+ .filter((entry) => entry.isDirectory()).map((entry) => join(extracted, entry.name));
157
+ if (roots.length === 1) {
158
+ skills = skillDirs(roots[0]!);
159
+ if (!skills.length && existsSync(join(roots[0]!, 'skills'))) skills = skillDirs(join(roots[0]!, 'skills'));
160
+ }
161
+ }
162
+ // Directory listing order differs between filesystems — keep the bundle order, then names.
163
+ const rank = (name: string) => { const i = ESSENTIAL_SKILLS.indexOf(name); return i < 0 ? ESSENTIAL_SKILLS.length : i; };
164
+ skills = [...skills].sort((a, b) => rank(a.name) - rank(b.name) || a.name.localeCompare(b.name));
165
+ if (!skills.length || !targets.length) return reject('invalid-pack', '설치할 스킬 폴더나 대상 경로를 찾지 못했습니다.');
166
+ if (pack === 'essential' && (skills.length !== ESSENTIAL_SKILLS.length
167
+ || ESSENTIAL_SKILLS.some((name) => !skills.some((skill) => skill.name === name)))) {
168
+ return reject('invalid-pack', '필수 스킬 다섯이 모두 들어 있지 않습니다. 설치하지 않았습니다.');
169
+ }
170
+ const paths: string[] = [];
171
+ const collisions: string[] = [];
172
+ const date = new Date().toISOString().slice(0, 10);
173
+ for (const target of targets) {
174
+ mkdirSync(target, { recursive: true });
175
+ for (const skill of skills) {
176
+ const desired = join(target, skill.name);
177
+ let dest = desired;
178
+ if (lstatSync(desired, { throwIfNoEntry: false })) {
179
+ dest = `${desired}.gift-${date}`;
180
+ let n = 2;
181
+ while (lstatSync(dest, { throwIfNoEntry: false })) dest = `${desired}.gift-${date}-${n++}`;
182
+ collisions.push(`${skill.name} → ${dest}`);
183
+ }
184
+ // Reserve the destination exclusively: a concurrent installer must not be overwritten.
185
+ mkdirSync(dest);
186
+ incomplete = dest;
187
+ for (const entry of readdirSync(skill.path)) {
188
+ cpSync(join(skill.path, entry), join(dest, entry), { recursive: true, errorOnExist: true, force: false });
189
+ }
190
+ created.push(dest);
191
+ incomplete = undefined;
192
+ paths.push(dest);
193
+ }
194
+ }
195
+ const names = skills.map((skill) => skill.name);
196
+ const needsKeys = skills.some((skill) => existsSync(join(skill.path, '.env.example')));
197
+ const message = `${collisions.length ? `기존 폴더는 그대로 두고 옆에 설치했습니다: ${collisions.join(', ')}\n` : ''}${needsKeys ? '.env.example 이 있습니다 — 키는 가이드대로 설정해 주세요.\n' : ''}설치한 스킬: ${names.join(', ')}`;
198
+ debug.log('skills.gift', 'installed', { pack, reason: 'ok', count: paths.length, codePrefix });
199
+ return { ok: true, names, paths, collisions, needsKeys, message };
200
+ } catch (error) {
201
+ if ((error as { status?: number }).status === 3 && !created.length) return tooLarge();
202
+ const remaining: string[] = [];
203
+ for (const path of [...(incomplete ? [incomplete] : []), ...created.reverse()]) {
204
+ try { rmSync(path, { recursive: true }); }
205
+ catch { remaining.push(path); }
206
+ }
207
+ if (remaining.length) {
208
+ debug.log('skills.gift', 'rejected', { pack, reason: 'partial-install', count: remaining.length, codePrefix });
209
+ return { ok: false, reason: 'partial-install', message: `일부 설치된 스킬을 되돌리지 못했습니다: ${remaining.join(', ')}` };
210
+ }
211
+ return reject('invalid-pack', '선물 zip 을 안전하게 풀 수 없습니다. 설치하지 않았습니다.');
212
+ } finally {
213
+ rmSync(temp, { recursive: true, force: true });
214
+ }
215
+ }
@@ -101,7 +101,10 @@ export async function dispatchWebSearch(args: Record<string, unknown>, ctx: { si
101
101
  });
102
102
 
103
103
  const notePrefix = result.note ? `\n\n${result.note}\n` : '';
104
- const output = [header, notePrefix.trim(), ...hitLines].filter(Boolean).join('\n\n');
104
+ const xStatus = result.xSearch === 'unavailable'
105
+ ? 'X search unavailable: these results do not establish reactions on X; do not cite them as X posts.'
106
+ : '';
107
+ const output = [header, xStatus, notePrefix.trim(), ...hitLines].filter(Boolean).join('\n\n');
105
108
 
106
109
  return {
107
110
  output,