@ashlr/hub 2.2.0

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 (477) hide show
  1. package/CHANGELOG.md +1158 -0
  2. package/LICENSE +21 -0
  3. package/README.md +833 -0
  4. package/bin/ashlr +8 -0
  5. package/dist/api/core.d.ts +28 -0
  6. package/dist/api/core.js +42 -0
  7. package/dist/api/core.js.map +1 -0
  8. package/dist/api/index.d.ts +9 -0
  9. package/dist/api/index.js +9 -0
  10. package/dist/api/index.js.map +1 -0
  11. package/dist/api/plugin.d.ts +12 -0
  12. package/dist/api/plugin.js +13 -0
  13. package/dist/api/plugin.js.map +1 -0
  14. package/dist/api/types.d.ts +9 -0
  15. package/dist/api/types.js +9 -0
  16. package/dist/api/types.js.map +1 -0
  17. package/dist/cli/args.d.ts +20 -0
  18. package/dist/cli/args.js +23 -0
  19. package/dist/cli/args.js.map +1 -0
  20. package/dist/cli/ask.d.ts +22 -0
  21. package/dist/cli/ask.js +240 -0
  22. package/dist/cli/ask.js.map +1 -0
  23. package/dist/cli/audit.d.ts +26 -0
  24. package/dist/cli/audit.js +199 -0
  25. package/dist/cli/audit.js.map +1 -0
  26. package/dist/cli/backlog.d.ts +26 -0
  27. package/dist/cli/backlog.js +322 -0
  28. package/dist/cli/backlog.js.map +1 -0
  29. package/dist/cli/completions.d.ts +18 -0
  30. package/dist/cli/completions.js +156 -0
  31. package/dist/cli/completions.js.map +1 -0
  32. package/dist/cli/daemon.d.ts +31 -0
  33. package/dist/cli/daemon.js +334 -0
  34. package/dist/cli/daemon.js.map +1 -0
  35. package/dist/cli/demo-sandbox.d.ts +78 -0
  36. package/dist/cli/demo-sandbox.js +227 -0
  37. package/dist/cli/demo-sandbox.js.map +1 -0
  38. package/dist/cli/demo.d.ts +76 -0
  39. package/dist/cli/demo.js +285 -0
  40. package/dist/cli/demo.js.map +1 -0
  41. package/dist/cli/digest.d.ts +46 -0
  42. package/dist/cli/digest.js +352 -0
  43. package/dist/cli/digest.js.map +1 -0
  44. package/dist/cli/doctor-init.d.ts +28 -0
  45. package/dist/cli/doctor-init.js +335 -0
  46. package/dist/cli/doctor-init.js.map +1 -0
  47. package/dist/cli/genome.d.ts +62 -0
  48. package/dist/cli/genome.js +1061 -0
  49. package/dist/cli/genome.js.map +1 -0
  50. package/dist/cli/gh.d.ts +27 -0
  51. package/dist/cli/gh.js +322 -0
  52. package/dist/cli/gh.js.map +1 -0
  53. package/dist/cli/goals.d.ts +55 -0
  54. package/dist/cli/goals.js +667 -0
  55. package/dist/cli/goals.js.map +1 -0
  56. package/dist/cli/health.d.ts +56 -0
  57. package/dist/cli/health.js +407 -0
  58. package/dist/cli/health.js.map +1 -0
  59. package/dist/cli/help.d.ts +51 -0
  60. package/dist/cli/help.js +436 -0
  61. package/dist/cli/help.js.map +1 -0
  62. package/dist/cli/inbox.d.ts +28 -0
  63. package/dist/cli/inbox.js +589 -0
  64. package/dist/cli/inbox.js.map +1 -0
  65. package/dist/cli/index.d.ts +41 -0
  66. package/dist/cli/index.js +1208 -0
  67. package/dist/cli/index.js.map +1 -0
  68. package/dist/cli/knowledge.d.ts +33 -0
  69. package/dist/cli/knowledge.js +575 -0
  70. package/dist/cli/knowledge.js.map +1 -0
  71. package/dist/cli/mcp.d.ts +26 -0
  72. package/dist/cli/mcp.js +487 -0
  73. package/dist/cli/mcp.js.map +1 -0
  74. package/dist/cli/models.d.ts +26 -0
  75. package/dist/cli/models.js +435 -0
  76. package/dist/cli/models.js.map +1 -0
  77. package/dist/cli/new.d.ts +32 -0
  78. package/dist/cli/new.js +565 -0
  79. package/dist/cli/new.js.map +1 -0
  80. package/dist/cli/notify.d.ts +20 -0
  81. package/dist/cli/notify.js +188 -0
  82. package/dist/cli/notify.js.map +1 -0
  83. package/dist/cli/onboard.d.ts +85 -0
  84. package/dist/cli/onboard.js +305 -0
  85. package/dist/cli/onboard.js.map +1 -0
  86. package/dist/cli/open.d.ts +38 -0
  87. package/dist/cli/open.js +86 -0
  88. package/dist/cli/open.js.map +1 -0
  89. package/dist/cli/orient.d.ts +14 -0
  90. package/dist/cli/orient.js +110 -0
  91. package/dist/cli/orient.js.map +1 -0
  92. package/dist/cli/picker.d.ts +21 -0
  93. package/dist/cli/picker.js +177 -0
  94. package/dist/cli/picker.js.map +1 -0
  95. package/dist/cli/plugins.d.ts +21 -0
  96. package/dist/cli/plugins.js +394 -0
  97. package/dist/cli/plugins.js.map +1 -0
  98. package/dist/cli/preflight.d.ts +29 -0
  99. package/dist/cli/preflight.js +131 -0
  100. package/dist/cli/preflight.js.map +1 -0
  101. package/dist/cli/pulse.d.ts +26 -0
  102. package/dist/cli/pulse.js +482 -0
  103. package/dist/cli/pulse.js.map +1 -0
  104. package/dist/cli/reflect.d.ts +42 -0
  105. package/dist/cli/reflect.js +381 -0
  106. package/dist/cli/reflect.js.map +1 -0
  107. package/dist/cli/run.d.ts +29 -0
  108. package/dist/cli/run.js +687 -0
  109. package/dist/cli/run.js.map +1 -0
  110. package/dist/cli/sandbox.d.ts +20 -0
  111. package/dist/cli/sandbox.js +345 -0
  112. package/dist/cli/sandbox.js.map +1 -0
  113. package/dist/cli/seams.d.ts +41 -0
  114. package/dist/cli/seams.js +167 -0
  115. package/dist/cli/seams.js.map +1 -0
  116. package/dist/cli/serve.d.ts +25 -0
  117. package/dist/cli/serve.js +249 -0
  118. package/dist/cli/serve.js.map +1 -0
  119. package/dist/cli/ship.d.ts +33 -0
  120. package/dist/cli/ship.js +433 -0
  121. package/dist/cli/ship.js.map +1 -0
  122. package/dist/cli/spec.d.ts +19 -0
  123. package/dist/cli/spec.js +478 -0
  124. package/dist/cli/spec.js.map +1 -0
  125. package/dist/cli/swarm.d.ts +42 -0
  126. package/dist/cli/swarm.js +1365 -0
  127. package/dist/cli/swarm.js.map +1 -0
  128. package/dist/cli/telemetry.d.ts +26 -0
  129. package/dist/cli/telemetry.js +364 -0
  130. package/dist/cli/telemetry.js.map +1 -0
  131. package/dist/cli/tui.d.ts +17 -0
  132. package/dist/cli/tui.js +52 -0
  133. package/dist/cli/tui.js.map +1 -0
  134. package/dist/cli/ui.d.ts +53 -0
  135. package/dist/cli/ui.js +66 -0
  136. package/dist/cli/ui.js.map +1 -0
  137. package/dist/cli/update.d.ts +38 -0
  138. package/dist/cli/update.js +682 -0
  139. package/dist/cli/update.js.map +1 -0
  140. package/dist/cli/vercel.d.ts +20 -0
  141. package/dist/cli/vercel.js +193 -0
  142. package/dist/cli/vercel.js.map +1 -0
  143. package/dist/cli/verify-safety.d.ts +96 -0
  144. package/dist/cli/verify-safety.js +509 -0
  145. package/dist/cli/verify-safety.js.map +1 -0
  146. package/dist/cli/wire.d.ts +22 -0
  147. package/dist/cli/wire.js +205 -0
  148. package/dist/cli/wire.js.map +1 -0
  149. package/dist/core/classify.d.ts +51 -0
  150. package/dist/core/classify.js +450 -0
  151. package/dist/core/classify.js.map +1 -0
  152. package/dist/core/config.d.ts +63 -0
  153. package/dist/core/config.js +474 -0
  154. package/dist/core/config.js.map +1 -0
  155. package/dist/core/daemon/loop.d.ts +74 -0
  156. package/dist/core/daemon/loop.js +618 -0
  157. package/dist/core/daemon/loop.js.map +1 -0
  158. package/dist/core/daemon/state.d.ts +66 -0
  159. package/dist/core/daemon/state.js +197 -0
  160. package/dist/core/daemon/state.js.map +1 -0
  161. package/dist/core/dashboard.d.ts +40 -0
  162. package/dist/core/dashboard.js +463 -0
  163. package/dist/core/dashboard.js.map +1 -0
  164. package/dist/core/digest/build.d.ts +51 -0
  165. package/dist/core/digest/build.js +230 -0
  166. package/dist/core/digest/build.js.map +1 -0
  167. package/dist/core/digest/deliver.d.ts +47 -0
  168. package/dist/core/digest/deliver.js +230 -0
  169. package/dist/core/digest/deliver.js.map +1 -0
  170. package/dist/core/digest/store.d.ts +57 -0
  171. package/dist/core/digest/store.js +223 -0
  172. package/dist/core/digest/store.js.map +1 -0
  173. package/dist/core/doctor-fix.d.ts +21 -0
  174. package/dist/core/doctor-fix.js +440 -0
  175. package/dist/core/doctor-fix.js.map +1 -0
  176. package/dist/core/doctor.d.ts +18 -0
  177. package/dist/core/doctor.js +806 -0
  178. package/dist/core/doctor.js.map +1 -0
  179. package/dist/core/env-bridge.d.ts +64 -0
  180. package/dist/core/env-bridge.js +103 -0
  181. package/dist/core/env-bridge.js.map +1 -0
  182. package/dist/core/genome/capture.d.ts +49 -0
  183. package/dist/core/genome/capture.js +352 -0
  184. package/dist/core/genome/capture.js.map +1 -0
  185. package/dist/core/genome/consolidate.d.ts +38 -0
  186. package/dist/core/genome/consolidate.js +426 -0
  187. package/dist/core/genome/consolidate.js.map +1 -0
  188. package/dist/core/genome/export.d.ts +29 -0
  189. package/dist/core/genome/export.js +102 -0
  190. package/dist/core/genome/export.js.map +1 -0
  191. package/dist/core/genome/playbook.d.ts +33 -0
  192. package/dist/core/genome/playbook.js +320 -0
  193. package/dist/core/genome/playbook.js.map +1 -0
  194. package/dist/core/genome/recall.d.ts +45 -0
  195. package/dist/core/genome/recall.js +293 -0
  196. package/dist/core/genome/recall.js.map +1 -0
  197. package/dist/core/genome/store.d.ts +62 -0
  198. package/dist/core/genome/store.js +711 -0
  199. package/dist/core/genome/store.js.map +1 -0
  200. package/dist/core/git.d.ts +34 -0
  201. package/dist/core/git.js +115 -0
  202. package/dist/core/git.js.map +1 -0
  203. package/dist/core/goals/advance.d.ts +82 -0
  204. package/dist/core/goals/advance.js +263 -0
  205. package/dist/core/goals/advance.js.map +1 -0
  206. package/dist/core/goals/planner.d.ts +58 -0
  207. package/dist/core/goals/planner.js +233 -0
  208. package/dist/core/goals/planner.js.map +1 -0
  209. package/dist/core/goals/store.d.ts +128 -0
  210. package/dist/core/goals/store.js +442 -0
  211. package/dist/core/goals/store.js.map +1 -0
  212. package/dist/core/inbox/apply.d.ts +36 -0
  213. package/dist/core/inbox/apply.js +407 -0
  214. package/dist/core/inbox/apply.js.map +1 -0
  215. package/dist/core/inbox/notify-proposal.d.ts +13 -0
  216. package/dist/core/inbox/notify-proposal.js +21 -0
  217. package/dist/core/inbox/notify-proposal.js.map +1 -0
  218. package/dist/core/inbox/store.d.ts +67 -0
  219. package/dist/core/inbox/store.js +258 -0
  220. package/dist/core/inbox/store.js.map +1 -0
  221. package/dist/core/index-engine.d.ts +62 -0
  222. package/dist/core/index-engine.js +486 -0
  223. package/dist/core/index-engine.js.map +1 -0
  224. package/dist/core/integrations/desktop-notify.d.ts +18 -0
  225. package/dist/core/integrations/desktop-notify.js +42 -0
  226. package/dist/core/integrations/desktop-notify.js.map +1 -0
  227. package/dist/core/integrations/editors.d.ts +50 -0
  228. package/dist/core/integrations/editors.js +216 -0
  229. package/dist/core/integrations/editors.js.map +1 -0
  230. package/dist/core/integrations/github.d.ts +66 -0
  231. package/dist/core/integrations/github.js +332 -0
  232. package/dist/core/integrations/github.js.map +1 -0
  233. package/dist/core/integrations/identity.d.ts +29 -0
  234. package/dist/core/integrations/identity.js +359 -0
  235. package/dist/core/integrations/identity.js.map +1 -0
  236. package/dist/core/integrations/notify.d.ts +23 -0
  237. package/dist/core/integrations/notify.js +96 -0
  238. package/dist/core/integrations/notify.js.map +1 -0
  239. package/dist/core/integrations/vercel.d.ts +37 -0
  240. package/dist/core/integrations/vercel.js +192 -0
  241. package/dist/core/integrations/vercel.js.map +1 -0
  242. package/dist/core/knowledge/ask.d.ts +34 -0
  243. package/dist/core/knowledge/ask.js +325 -0
  244. package/dist/core/knowledge/ask.js.map +1 -0
  245. package/dist/core/knowledge/graph.d.ts +51 -0
  246. package/dist/core/knowledge/graph.js +564 -0
  247. package/dist/core/knowledge/graph.js.map +1 -0
  248. package/dist/core/knowledge/index.d.ts +74 -0
  249. package/dist/core/knowledge/index.js +586 -0
  250. package/dist/core/knowledge/index.js.map +1 -0
  251. package/dist/core/learn/playbooks.d.ts +84 -0
  252. package/dist/core/learn/playbooks.js +241 -0
  253. package/dist/core/learn/playbooks.js.map +1 -0
  254. package/dist/core/learn/reflect.d.ts +87 -0
  255. package/dist/core/learn/reflect.js +435 -0
  256. package/dist/core/learn/reflect.js.map +1 -0
  257. package/dist/core/learn/store.d.ts +51 -0
  258. package/dist/core/learn/store.js +165 -0
  259. package/dist/core/learn/store.js.map +1 -0
  260. package/dist/core/learn/tuning.d.ts +48 -0
  261. package/dist/core/learn/tuning.js +201 -0
  262. package/dist/core/learn/tuning.js.map +1 -0
  263. package/dist/core/lifecycle/scaffold.d.ts +43 -0
  264. package/dist/core/lifecycle/scaffold.js +260 -0
  265. package/dist/core/lifecycle/scaffold.js.map +1 -0
  266. package/dist/core/lifecycle/ship.d.ts +48 -0
  267. package/dist/core/lifecycle/ship.js +513 -0
  268. package/dist/core/lifecycle/ship.js.map +1 -0
  269. package/dist/core/lifecycle/templates.d.ts +20 -0
  270. package/dist/core/lifecycle/templates.js +605 -0
  271. package/dist/core/lifecycle/templates.js.map +1 -0
  272. package/dist/core/mcp-gateway.d.ts +59 -0
  273. package/dist/core/mcp-gateway.js +385 -0
  274. package/dist/core/mcp-gateway.js.map +1 -0
  275. package/dist/core/mcp-native.d.ts +53 -0
  276. package/dist/core/mcp-native.js +507 -0
  277. package/dist/core/mcp-native.js.map +1 -0
  278. package/dist/core/mcp-registry.d.ts +36 -0
  279. package/dist/core/mcp-registry.js +180 -0
  280. package/dist/core/mcp-registry.js.map +1 -0
  281. package/dist/core/observability/budget-alert.d.ts +18 -0
  282. package/dist/core/observability/budget-alert.js +90 -0
  283. package/dist/core/observability/budget-alert.js.map +1 -0
  284. package/dist/core/observability/estimate.d.ts +27 -0
  285. package/dist/core/observability/estimate.js +188 -0
  286. package/dist/core/observability/estimate.js.map +1 -0
  287. package/dist/core/observability/forecast.d.ts +19 -0
  288. package/dist/core/observability/forecast.js +101 -0
  289. package/dist/core/observability/forecast.js.map +1 -0
  290. package/dist/core/observability/governance.d.ts +28 -0
  291. package/dist/core/observability/governance.js +101 -0
  292. package/dist/core/observability/governance.js.map +1 -0
  293. package/dist/core/observability/otlp.d.ts +88 -0
  294. package/dist/core/observability/otlp.js +217 -0
  295. package/dist/core/observability/otlp.js.map +1 -0
  296. package/dist/core/observability/rollup.d.ts +35 -0
  297. package/dist/core/observability/rollup.js +311 -0
  298. package/dist/core/observability/rollup.js.map +1 -0
  299. package/dist/core/observability/telemetry-sink.d.ts +63 -0
  300. package/dist/core/observability/telemetry-sink.js +350 -0
  301. package/dist/core/observability/telemetry-sink.js.map +1 -0
  302. package/dist/core/observability/usage-source.d.ts +60 -0
  303. package/dist/core/observability/usage-source.js +347 -0
  304. package/dist/core/observability/usage-source.js.map +1 -0
  305. package/dist/core/onboard.d.ts +27 -0
  306. package/dist/core/onboard.js +288 -0
  307. package/dist/core/onboard.js.map +1 -0
  308. package/dist/core/orient.d.ts +23 -0
  309. package/dist/core/orient.js +135 -0
  310. package/dist/core/orient.js.map +1 -0
  311. package/dist/core/phantom.d.ts +23 -0
  312. package/dist/core/phantom.js +279 -0
  313. package/dist/core/phantom.js.map +1 -0
  314. package/dist/core/plugins/host-api.d.ts +29 -0
  315. package/dist/core/plugins/host-api.js +113 -0
  316. package/dist/core/plugins/host-api.js.map +1 -0
  317. package/dist/core/plugins/integrity.d.ts +36 -0
  318. package/dist/core/plugins/integrity.js +73 -0
  319. package/dist/core/plugins/integrity.js.map +1 -0
  320. package/dist/core/plugins/manifest.d.ts +31 -0
  321. package/dist/core/plugins/manifest.js +316 -0
  322. package/dist/core/plugins/manifest.js.map +1 -0
  323. package/dist/core/plugins/registry.d.ts +87 -0
  324. package/dist/core/plugins/registry.js +415 -0
  325. package/dist/core/plugins/registry.js.map +1 -0
  326. package/dist/core/plugins/types.d.ts +182 -0
  327. package/dist/core/plugins/types.js +40 -0
  328. package/dist/core/plugins/types.js.map +1 -0
  329. package/dist/core/plugins/wrappers.d.ts +40 -0
  330. package/dist/core/plugins/wrappers.js +229 -0
  331. package/dist/core/plugins/wrappers.js.map +1 -0
  332. package/dist/core/portfolio/backlog.d.ts +40 -0
  333. package/dist/core/portfolio/backlog.js +177 -0
  334. package/dist/core/portfolio/backlog.js.map +1 -0
  335. package/dist/core/portfolio/scanners.d.ts +21 -0
  336. package/dist/core/portfolio/scanners.js +600 -0
  337. package/dist/core/portfolio/scanners.js.map +1 -0
  338. package/dist/core/providers.d.ts +34 -0
  339. package/dist/core/providers.js +250 -0
  340. package/dist/core/providers.js.map +1 -0
  341. package/dist/core/quality/conventions.d.ts +35 -0
  342. package/dist/core/quality/conventions.js +267 -0
  343. package/dist/core/quality/conventions.js.map +1 -0
  344. package/dist/core/quality/fixes.d.ts +56 -0
  345. package/dist/core/quality/fixes.js +209 -0
  346. package/dist/core/quality/fixes.js.map +1 -0
  347. package/dist/core/quality/health.d.ts +69 -0
  348. package/dist/core/quality/health.js +350 -0
  349. package/dist/core/quality/health.js.map +1 -0
  350. package/dist/core/quality/store.d.ts +56 -0
  351. package/dist/core/quality/store.js +195 -0
  352. package/dist/core/quality/store.js.map +1 -0
  353. package/dist/core/readiness.d.ts +112 -0
  354. package/dist/core/readiness.js +431 -0
  355. package/dist/core/readiness.js.map +1 -0
  356. package/dist/core/run/agent-loop.d.ts +40 -0
  357. package/dist/core/run/agent-loop.js +291 -0
  358. package/dist/core/run/agent-loop.js.map +1 -0
  359. package/dist/core/run/budget.d.ts +47 -0
  360. package/dist/core/run/budget.js +113 -0
  361. package/dist/core/run/budget.js.map +1 -0
  362. package/dist/core/run/engines.d.ts +75 -0
  363. package/dist/core/run/engines.js +199 -0
  364. package/dist/core/run/engines.js.map +1 -0
  365. package/dist/core/run/model-manager.d.ts +64 -0
  366. package/dist/core/run/model-manager.js +339 -0
  367. package/dist/core/run/model-manager.js.map +1 -0
  368. package/dist/core/run/orchestrator.d.ts +100 -0
  369. package/dist/core/run/orchestrator.js +1515 -0
  370. package/dist/core/run/orchestrator.js.map +1 -0
  371. package/dist/core/run/provider-client.d.ts +46 -0
  372. package/dist/core/run/provider-client.js +796 -0
  373. package/dist/core/run/provider-client.js.map +1 -0
  374. package/dist/core/run/retry.d.ts +19 -0
  375. package/dist/core/run/retry.js +68 -0
  376. package/dist/core/run/retry.js.map +1 -0
  377. package/dist/core/run/router.d.ts +50 -0
  378. package/dist/core/run/router.js +257 -0
  379. package/dist/core/run/router.js.map +1 -0
  380. package/dist/core/run/self-heal.d.ts +52 -0
  381. package/dist/core/run/self-heal.js +181 -0
  382. package/dist/core/run/self-heal.js.map +1 -0
  383. package/dist/core/run/streaming.d.ts +31 -0
  384. package/dist/core/run/streaming.js +122 -0
  385. package/dist/core/run/streaming.js.map +1 -0
  386. package/dist/core/run/verify.d.ts +30 -0
  387. package/dist/core/run/verify.js +204 -0
  388. package/dist/core/run/verify.js.map +1 -0
  389. package/dist/core/sandbox/audit.d.ts +31 -0
  390. package/dist/core/sandbox/audit.js +169 -0
  391. package/dist/core/sandbox/audit.js.map +1 -0
  392. package/dist/core/sandbox/policy.d.ts +61 -0
  393. package/dist/core/sandbox/policy.js +211 -0
  394. package/dist/core/sandbox/policy.js.map +1 -0
  395. package/dist/core/sandbox/worktree.d.ts +175 -0
  396. package/dist/core/sandbox/worktree.js +673 -0
  397. package/dist/core/sandbox/worktree.js.map +1 -0
  398. package/dist/core/seams/backlog.d.ts +47 -0
  399. package/dist/core/seams/backlog.js +47 -0
  400. package/dist/core/seams/backlog.js.map +1 -0
  401. package/dist/core/seams/daemon-coordinator.d.ts +72 -0
  402. package/dist/core/seams/daemon-coordinator.js +76 -0
  403. package/dist/core/seams/daemon-coordinator.js.map +1 -0
  404. package/dist/core/seams/genome.d.ts +45 -0
  405. package/dist/core/seams/genome.js +53 -0
  406. package/dist/core/seams/genome.js.map +1 -0
  407. package/dist/core/seams/identity.d.ts +40 -0
  408. package/dist/core/seams/identity.js +44 -0
  409. package/dist/core/seams/identity.js.map +1 -0
  410. package/dist/core/seams/inbox.d.ts +60 -0
  411. package/dist/core/seams/inbox.js +66 -0
  412. package/dist/core/seams/inbox.js.map +1 -0
  413. package/dist/core/seams/index.d.ts +20 -0
  414. package/dist/core/seams/index.js +21 -0
  415. package/dist/core/seams/index.js.map +1 -0
  416. package/dist/core/seams/portfolio.d.ts +50 -0
  417. package/dist/core/seams/portfolio.js +61 -0
  418. package/dist/core/seams/portfolio.js.map +1 -0
  419. package/dist/core/seams/registry.d.ts +42 -0
  420. package/dist/core/seams/registry.js +128 -0
  421. package/dist/core/seams/registry.js.map +1 -0
  422. package/dist/core/seams/run-swarm.d.ts +66 -0
  423. package/dist/core/seams/run-swarm.js +74 -0
  424. package/dist/core/seams/run-swarm.js.map +1 -0
  425. package/dist/core/seams/types.d.ts +123 -0
  426. package/dist/core/seams/types.js +35 -0
  427. package/dist/core/seams/types.js.map +1 -0
  428. package/dist/core/spec/spec-store.d.ts +62 -0
  429. package/dist/core/spec/spec-store.js +359 -0
  430. package/dist/core/spec/spec-store.js.map +1 -0
  431. package/dist/core/swarm/gate.d.ts +45 -0
  432. package/dist/core/swarm/gate.js +114 -0
  433. package/dist/core/swarm/gate.js.map +1 -0
  434. package/dist/core/swarm/planner.d.ts +32 -0
  435. package/dist/core/swarm/planner.js +293 -0
  436. package/dist/core/swarm/planner.js.map +1 -0
  437. package/dist/core/swarm/rollback.d.ts +56 -0
  438. package/dist/core/swarm/rollback.js +266 -0
  439. package/dist/core/swarm/rollback.js.map +1 -0
  440. package/dist/core/swarm/runner.d.ts +62 -0
  441. package/dist/core/swarm/runner.js +1263 -0
  442. package/dist/core/swarm/runner.js.map +1 -0
  443. package/dist/core/swarm/sign.d.ts +71 -0
  444. package/dist/core/swarm/sign.js +362 -0
  445. package/dist/core/swarm/sign.js.map +1 -0
  446. package/dist/core/swarm/store.d.ts +52 -0
  447. package/dist/core/swarm/store.js +195 -0
  448. package/dist/core/swarm/store.js.map +1 -0
  449. package/dist/core/tidy.d.ts +32 -0
  450. package/dist/core/tidy.js +354 -0
  451. package/dist/core/tidy.js.map +1 -0
  452. package/dist/core/tools-registry.d.ts +16 -0
  453. package/dist/core/tools-registry.js +308 -0
  454. package/dist/core/tools-registry.js.map +1 -0
  455. package/dist/core/types.d.ts +2545 -0
  456. package/dist/core/types.js +9 -0
  457. package/dist/core/types.js.map +1 -0
  458. package/dist/core/web/api.d.ts +53 -0
  459. package/dist/core/web/api.js +698 -0
  460. package/dist/core/web/api.js.map +1 -0
  461. package/dist/core/web/public/app.js +1906 -0
  462. package/dist/core/web/public/index.html +721 -0
  463. package/dist/core/web/public/styles.css +2007 -0
  464. package/dist/core/web/server.d.ts +18 -0
  465. package/dist/core/web/server.js +122 -0
  466. package/dist/core/web/server.js.map +1 -0
  467. package/dist/core/web/static.d.ts +18 -0
  468. package/dist/core/web/static.js +122 -0
  469. package/dist/core/web/static.js.map +1 -0
  470. package/dist/tui/app.d.ts +33 -0
  471. package/dist/tui/app.js +350 -0
  472. package/dist/tui/app.js.map +1 -0
  473. package/dist/tui/render.d.ts +20 -0
  474. package/dist/tui/render.js +558 -0
  475. package/dist/tui/render.js.map +1 -0
  476. package/package.json +80 -0
  477. package/schema/config.schema.json +223 -0
@@ -0,0 +1,682 @@
1
+ /**
2
+ * `ashlr update` — safe self-update for ashlr-hub.
3
+ *
4
+ * Usage:
5
+ * ashlr update [--check] [--json]
6
+ *
7
+ * Flags:
8
+ * --check Report whether an update is available (git fetch + compare) without applying.
9
+ * --json Emit UpdateResult JSON on stdout; human text goes to stderr.
10
+ *
11
+ * Behaviour:
12
+ * 1. Detect the repo root from this file's location (import.meta.url).
13
+ * 2. If no git remote is configured, report "no remote configured — update is a no-op" (exit 0).
14
+ * 3. Abort on a dirty working tree — tell the user to commit first (exit 1).
15
+ * 4. --check: git fetch + compare HEAD..origin/<branch>; exit 0 if up-to-date, exit 0 if
16
+ * updates are available (it's informational only; never modifies anything).
17
+ * 5. Normal update: git pull --ff-only, npm install, npm run build, then verify
18
+ * `ashlr --version` / `ashlr help` works and the ~/.local/bin symlink points here.
19
+ * 6. Print a clear before/after version line.
20
+ *
21
+ * Safety guarantees:
22
+ * - NEVER force-push, reset, rebase, or do any other destructive git operation.
23
+ * - NEVER pushes to the remote.
24
+ * - Aborts immediately on dirty working tree.
25
+ * - Only uses git pull --ff-only (fails if the remote diverged; user must resolve).
26
+ *
27
+ * Exit codes:
28
+ * 0 success (or up-to-date / no-remote)
29
+ * 1 update failed, dirty tree, or other error
30
+ * 2 bad usage
31
+ */
32
+ import { spawnSync, execFileSync } from 'node:child_process';
33
+ import { existsSync, readlinkSync, lstatSync } from 'node:fs';
34
+ import { dirname, resolve, join, sep } from 'node:path';
35
+ import { fileURLToPath } from 'node:url';
36
+ import { homedir } from 'node:os';
37
+ import { createRequire } from 'node:module';
38
+ // ---------------------------------------------------------------------------
39
+ // ANSI helpers (non-TTY safe)
40
+ // ---------------------------------------------------------------------------
41
+ import { makeColors, isTty } from './ui.js';
42
+ const { bold, dim, red, green, yellow, cyan, gray } = makeColors(isTty());
43
+ // ---------------------------------------------------------------------------
44
+ // Repo / version helpers
45
+ // ---------------------------------------------------------------------------
46
+ /** Detect the repo root by walking up from this file's compiled location. */
47
+ function detectRepoRoot() {
48
+ // In production: __filename is dist/cli/update.js → go up two levels to reach repo root.
49
+ // In source: src/cli/update.ts → same structure.
50
+ const here = dirname(fileURLToPath(import.meta.url));
51
+ // dist/cli → dist → repo root
52
+ return resolve(here, '..', '..');
53
+ }
54
+ /** Read the version from package.json in the repo root. Returns null on error. */
55
+ function readPackageVersion(repoRoot) {
56
+ try {
57
+ // Use createRequire to load the JSON; avoids import assertions that vary by Node version.
58
+ const req = createRequire(import.meta.url);
59
+ const pkg = req(join(repoRoot, 'package.json'));
60
+ return typeof pkg.version === 'string' ? pkg.version : null;
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ }
66
+ function gitRun(args, cwd) {
67
+ const result = spawnSync('git', args, {
68
+ cwd,
69
+ encoding: 'utf8',
70
+ timeout: 30_000,
71
+ // Do NOT inherit stdio — we capture for JSON/clean output.
72
+ });
73
+ return {
74
+ ok: result.status === 0 && !result.error,
75
+ stdout: (result.stdout ?? '').trim(),
76
+ stderr: (result.stderr ?? '').trim(),
77
+ status: result.status,
78
+ };
79
+ }
80
+ /** Return true when git is on PATH. */
81
+ function gitAvailable() {
82
+ const r = spawnSync('git', ['--version'], { encoding: 'utf8', timeout: 5_000 });
83
+ return r.status === 0;
84
+ }
85
+ /** Return the list of configured remote names (empty array if none). */
86
+ function getRemoteNames(cwd) {
87
+ const r = gitRun(['remote'], cwd);
88
+ if (!r.ok || !r.stdout)
89
+ return [];
90
+ return r.stdout.split('\n').map(l => l.trim()).filter(Boolean);
91
+ }
92
+ /** Return true when the working tree has uncommitted changes. */
93
+ function isWorkingTreeDirty(cwd) {
94
+ // --porcelain exits 0 and produces output when dirty.
95
+ const r = gitRun(['status', '--porcelain'], cwd);
96
+ return r.ok && r.stdout.length > 0;
97
+ }
98
+ /** Return the current branch name. Falls back to 'HEAD' on detached HEAD. */
99
+ function getCurrentBranch(cwd) {
100
+ const r = gitRun(['symbolic-ref', '--short', 'HEAD'], cwd);
101
+ if (r.ok && r.stdout)
102
+ return r.stdout;
103
+ // Detached HEAD
104
+ const rev = gitRun(['rev-parse', '--short', 'HEAD'], cwd);
105
+ return rev.ok ? rev.stdout : 'HEAD';
106
+ }
107
+ /** Return the short commit hash for a ref. */
108
+ function getCommitHash(ref, cwd) {
109
+ const r = gitRun(['rev-parse', '--short', ref], cwd);
110
+ return r.ok ? r.stdout : null;
111
+ }
112
+ /** Fetch from the default remote (non-destructive). */
113
+ function gitFetch(remote, cwd) {
114
+ const r = gitRun(['fetch', remote], cwd);
115
+ return { ok: r.ok, error: r.stderr };
116
+ }
117
+ /**
118
+ * Count commits in <a>..<b>.
119
+ * Returns { count, error } where count is null on failure.
120
+ */
121
+ function countCommitsBetween(from, to, cwd) {
122
+ const r = gitRun(['rev-list', '--count', `${from}..${to}`], cwd);
123
+ if (!r.ok)
124
+ return { count: null, error: r.stderr };
125
+ const n = parseInt(r.stdout, 10);
126
+ return { count: isNaN(n) ? 0 : n, error: '' };
127
+ }
128
+ function npmRun(args, cwd) {
129
+ try {
130
+ const out = execFileSync('npm', args, {
131
+ cwd,
132
+ encoding: 'utf8',
133
+ timeout: 120_000,
134
+ stdio: ['ignore', 'pipe', 'pipe'],
135
+ });
136
+ return { ok: true, output: out };
137
+ }
138
+ catch (err) {
139
+ const e = err;
140
+ const combined = [e.stdout ?? '', e.stderr ?? '', e.message ?? ''].join('\n');
141
+ return { ok: false, output: combined };
142
+ }
143
+ }
144
+ /**
145
+ * Verify that ~/.local/bin/ashlr is a symlink that resolves to the bin/ashlr
146
+ * in this repo root.
147
+ */
148
+ function checkSymlink(repoRoot) {
149
+ const symlinkPath = join(homedir(), '.local', 'bin', 'ashlr');
150
+ const expectedTarget = join(repoRoot, 'bin', 'ashlr');
151
+ if (!existsSync(symlinkPath)) {
152
+ return {
153
+ path: symlinkPath,
154
+ ok: false,
155
+ pointsHere: false,
156
+ target: null,
157
+ detail: `symlink not found at ${symlinkPath}`,
158
+ };
159
+ }
160
+ let target = null;
161
+ try {
162
+ const stat = lstatSync(symlinkPath);
163
+ if (stat.isSymbolicLink()) {
164
+ // readlinkSync gives the raw link target (may be relative).
165
+ const rawTarget = readlinkSync(symlinkPath);
166
+ // Resolve relative to the symlink's directory.
167
+ target = resolve(dirname(symlinkPath), rawTarget);
168
+ }
169
+ else {
170
+ // Not a symlink — could be a copy or an install script wrote it directly.
171
+ return {
172
+ path: symlinkPath,
173
+ ok: true, // it exists and is executable; not our concern
174
+ pointsHere: false,
175
+ target: null,
176
+ detail: `${symlinkPath} exists but is not a symlink (real file)`,
177
+ };
178
+ }
179
+ }
180
+ catch (err) {
181
+ return {
182
+ path: symlinkPath,
183
+ ok: false,
184
+ pointsHere: false,
185
+ target: null,
186
+ detail: `failed to read symlink: ${err instanceof Error ? err.message : String(err)}`,
187
+ };
188
+ }
189
+ const pointsHere = target === expectedTarget;
190
+ return {
191
+ path: symlinkPath,
192
+ ok: true,
193
+ pointsHere,
194
+ target,
195
+ detail: pointsHere
196
+ ? `${symlinkPath} → ${target}`
197
+ : `${symlinkPath} → ${target} (expected ${expectedTarget})`,
198
+ };
199
+ }
200
+ function smokeTest(repoRoot) {
201
+ const binPath = join(repoRoot, 'bin', 'ashlr');
202
+ const node = process.execPath;
203
+ // Try `ashlr help` via absolute bin path.
204
+ const r = spawnSync(node, [binPath, 'help'], {
205
+ encoding: 'utf8',
206
+ timeout: 15_000,
207
+ });
208
+ if (r.status === 0) {
209
+ return { ok: true, detail: 'ashlr help exited 0' };
210
+ }
211
+ return {
212
+ ok: false,
213
+ detail: `ashlr help exited ${r.status ?? '?'}: ${(r.stderr ?? '').slice(0, 200)}`,
214
+ };
215
+ }
216
+ function parseUpdateArgs(args) {
217
+ const result = { check: false, json: false, yes: false };
218
+ for (let i = 0; i < args.length; i++) {
219
+ const arg = args[i];
220
+ if (arg === '--check') {
221
+ result.check = true;
222
+ }
223
+ else if (arg === '--json') {
224
+ result.json = true;
225
+ }
226
+ else if (arg === '--yes') {
227
+ result.yes = true;
228
+ }
229
+ else if (arg === '--channel') {
230
+ const val = args[++i];
231
+ if (val !== 'git' && val !== 'npm') {
232
+ result.usageError = `--channel must be git or npm; got: ${val ?? '(missing)'}`;
233
+ return result;
234
+ }
235
+ result.channel = val;
236
+ }
237
+ else if (arg === '--help' || arg === '-h') {
238
+ // handled upstream
239
+ }
240
+ else {
241
+ result.usageError = `unknown flag: ${arg}`;
242
+ return result;
243
+ }
244
+ }
245
+ return result;
246
+ }
247
+ // ---------------------------------------------------------------------------
248
+ // M33: channel detection + npm channel flow
249
+ // ---------------------------------------------------------------------------
250
+ /**
251
+ * 'npm' when the compiled module runs from inside a node_modules install
252
+ * (global or local `npm install @ashlr/hub`); 'git' for a repo checkout.
253
+ * Exported for tests.
254
+ */
255
+ export function detectChannel(moduleUrl = import.meta.url) {
256
+ const here = fileURLToPath(moduleUrl);
257
+ return here.includes(`${sep}node_modules${sep}`) ? 'npm' : 'git';
258
+ }
259
+ /**
260
+ * npm-channel update: check the registry for a newer version (bounded network
261
+ * call, degrades offline to "unknown"); the actual install runs ONLY with
262
+ * --yes — otherwise the command to run is printed.
263
+ */
264
+ async function runNpmChannel(parsed, repoRoot) {
265
+ const jsonMode = parsed.json;
266
+ const localVersion = readPackageVersion(repoRoot);
267
+ // `npm view` is a network call — explicit, bounded, read-only.
268
+ let latest = null;
269
+ try {
270
+ const res = spawnSync('npm', ['view', '@ashlr/hub', 'version'], {
271
+ encoding: 'utf8',
272
+ timeout: 10_000,
273
+ });
274
+ latest = res.status === 0 ? res.stdout.trim() || null : null;
275
+ }
276
+ catch {
277
+ latest = null;
278
+ }
279
+ const upToDate = latest !== null && localVersion !== null && latest === localVersion;
280
+ if (jsonMode) {
281
+ process.stdout.write(JSON.stringify({
282
+ channel: 'npm',
283
+ versionLocal: localVersion,
284
+ versionLatest: latest,
285
+ upToDate: latest === null ? null : upToDate,
286
+ updated: false,
287
+ message: latest === null
288
+ ? 'registry unreachable — could not check for updates'
289
+ : upToDate
290
+ ? 'up to date'
291
+ : `update available: ${localVersion ?? '?'} → ${latest}`,
292
+ }, null, 2) + '\n');
293
+ }
294
+ else {
295
+ out('', false);
296
+ out(bold(' ashlr update') + gray(' — npm channel'), false);
297
+ out(` installed: ${cyan(localVersion ?? 'unknown')}`, false);
298
+ out(` latest: ${latest === null ? yellow('unknown (registry unreachable)') : cyan(latest)}`, false);
299
+ out('', false);
300
+ }
301
+ if (latest === null || upToDate || parsed.check)
302
+ return 0;
303
+ if (!parsed.yes) {
304
+ if (!jsonMode) {
305
+ out(` Update available. Run: ${cyan('npm install -g @ashlr/hub@latest')}`, false);
306
+ out(dim(' (or re-run `ashlr update --yes` to install it now)'), false);
307
+ out('', false);
308
+ }
309
+ return 0;
310
+ }
311
+ const install = spawnSync('npm', ['install', '-g', '@ashlr/hub@latest'], {
312
+ encoding: 'utf8',
313
+ stdio: jsonMode ? 'pipe' : 'inherit',
314
+ timeout: 300_000,
315
+ });
316
+ const ok = install.status === 0;
317
+ if (jsonMode) {
318
+ process.stdout.write(JSON.stringify({ channel: 'npm', updated: ok, versionLatest: latest }, null, 2) + '\n');
319
+ }
320
+ else {
321
+ out(ok ? green(` ✓ updated to ${latest}`) : red(' ✗ npm install failed'), false);
322
+ out('', false);
323
+ }
324
+ return ok ? 0 : 1;
325
+ }
326
+ // ---------------------------------------------------------------------------
327
+ // Human output helpers
328
+ // ---------------------------------------------------------------------------
329
+ function out(line, jsonMode) {
330
+ // In JSON mode human text goes to stderr; stdout is reserved for the JSON payload.
331
+ if (jsonMode) {
332
+ process.stderr.write(line + '\n');
333
+ }
334
+ else {
335
+ process.stdout.write(line + '\n');
336
+ }
337
+ }
338
+ function printHelp() {
339
+ console.log('');
340
+ console.log(bold(' ashlr update') + dim(' — safe self-update'));
341
+ console.log('');
342
+ console.log(' ' + bold('Usage:'));
343
+ console.log('');
344
+ console.log(` ashlr update ${cyan('[--check]')} ${cyan('[--json]')} ${cyan('[--channel git|npm]')} ${cyan('[--yes]')}`);
345
+ console.log('');
346
+ console.log(' ' + bold('Flags:'));
347
+ console.log('');
348
+ console.log(` ${cyan('--check')} Report if an update is available without applying it.`);
349
+ console.log(` ${cyan('--json')} Emit UpdateResult JSON on stdout; human text to stderr.`);
350
+ console.log('');
351
+ console.log(' ' + bold('Safety:'));
352
+ console.log('');
353
+ console.log(` ${dim('• Only uses git pull --ff-only — never force-push/reset/rebase.')}`);
354
+ console.log(` ${dim('• Aborts on a dirty working tree (commit your changes first).')}`);
355
+ console.log(` ${dim('• If no remote is configured, reports no-op and exits 0.')}`);
356
+ console.log('');
357
+ console.log(' ' + bold('Exit codes:'));
358
+ console.log('');
359
+ console.log(` ${dim('0 success (or already up-to-date / no remote)')}`);
360
+ console.log(` ${dim('1 update failed, dirty tree, or other error')}`);
361
+ console.log(` ${dim('2 bad usage')}`);
362
+ console.log('');
363
+ }
364
+ // ---------------------------------------------------------------------------
365
+ // cmdUpdate — main entry point
366
+ // ---------------------------------------------------------------------------
367
+ export async function cmdUpdate(args) {
368
+ // Help shortcircuit
369
+ if (args[0] === '--help' || args[0] === '-h' || args[0] === 'help') {
370
+ printHelp();
371
+ return 0;
372
+ }
373
+ const parsed = parseUpdateArgs(args);
374
+ if (parsed.usageError) {
375
+ process.stderr.write(red('error: ') + parsed.usageError + '\n');
376
+ return 2;
377
+ }
378
+ const jsonMode = parsed.json;
379
+ // ── Detect repo root ───────────────────────────────────────────────────────
380
+ const repoRoot = detectRepoRoot();
381
+ // ── M33: channel routing — npm installs update via the registry, not git ──
382
+ const channel = parsed.channel ?? detectChannel();
383
+ if (channel === 'npm') {
384
+ return runNpmChannel(parsed, repoRoot);
385
+ }
386
+ out('', jsonMode);
387
+ out(bold(' ashlr update') + gray(` — ${repoRoot}`), jsonMode);
388
+ out('', jsonMode);
389
+ // ── Check git availability ─────────────────────────────────────────────────
390
+ if (!gitAvailable()) {
391
+ const msg = 'git not found on PATH — update requires git';
392
+ process.stderr.write(red('error: ') + msg + '\n');
393
+ if (jsonMode) {
394
+ const result = {
395
+ updated: false, upToDate: null, newCommits: null,
396
+ versionBefore: null, versionAfter: null,
397
+ commitBefore: null, commitAfter: null,
398
+ remotes: [], branch: '',
399
+ symlink: null, smokeOk: null,
400
+ message: msg, error: msg,
401
+ };
402
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
403
+ }
404
+ return 1;
405
+ }
406
+ // ── Check for git repo ─────────────────────────────────────────────────────
407
+ const gitCheck = gitRun(['rev-parse', '--is-inside-work-tree'], repoRoot);
408
+ if (!gitCheck.ok) {
409
+ const msg = `${repoRoot} is not a git repository — update requires a git checkout`;
410
+ process.stderr.write(red('error: ') + msg + '\n');
411
+ if (jsonMode) {
412
+ const result = {
413
+ updated: false, upToDate: null, newCommits: null,
414
+ versionBefore: null, versionAfter: null,
415
+ commitBefore: null, commitAfter: null,
416
+ remotes: [], branch: '',
417
+ symlink: null, smokeOk: null,
418
+ message: msg, error: msg,
419
+ };
420
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
421
+ }
422
+ return 1;
423
+ }
424
+ // ── Check for remote ──────────────────────────────────────────────────────
425
+ const remotes = getRemoteNames(repoRoot);
426
+ const branch = getCurrentBranch(repoRoot);
427
+ if (remotes.length === 0) {
428
+ const msg = 'no remote configured — update is a no-op';
429
+ out(` ${yellow('!')} ${msg}`, jsonMode);
430
+ out('', jsonMode);
431
+ if (jsonMode) {
432
+ const result = {
433
+ updated: false, upToDate: true, newCommits: 0,
434
+ versionBefore: readPackageVersion(repoRoot), versionAfter: null,
435
+ commitBefore: getCommitHash('HEAD', repoRoot), commitAfter: null,
436
+ remotes, branch,
437
+ symlink: null, smokeOk: null,
438
+ message: msg, error: null,
439
+ };
440
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
441
+ }
442
+ return 0;
443
+ }
444
+ const remote = remotes[0]; // prefer 'origin' if present, else first
445
+ // Prefer 'origin' if it exists in the list.
446
+ const preferredRemote = remotes.includes('origin') ? 'origin' : remote;
447
+ // ── Snapshot before-state ─────────────────────────────────────────────────
448
+ const versionBefore = readPackageVersion(repoRoot);
449
+ const commitBefore = getCommitHash('HEAD', repoRoot);
450
+ out(` ${dim('remote:')} ${cyan(preferredRemote)}`, jsonMode);
451
+ out(` ${dim('branch:')} ${cyan(branch)}`, jsonMode);
452
+ if (versionBefore) {
453
+ out(` ${dim('version:')} ${cyan(versionBefore)} ${dim(`(${commitBefore ?? '?'})`)}`, jsonMode);
454
+ }
455
+ out('', jsonMode);
456
+ // ── Dirty working tree check ───────────────────────────────────────────────
457
+ if (isWorkingTreeDirty(repoRoot)) {
458
+ const msg = 'working tree has uncommitted changes — please commit or stash them before updating';
459
+ out(` ${red('✗')} ${bold('Dirty working tree')}`, jsonMode);
460
+ out(` ${dim(msg)}`, jsonMode);
461
+ out(` ${dim('Run:')} ${cyan('git status')} ${dim('to see what changed.')}`, jsonMode);
462
+ out('', jsonMode);
463
+ if (jsonMode) {
464
+ const result = {
465
+ updated: false, upToDate: null, newCommits: null,
466
+ versionBefore, versionAfter: null,
467
+ commitBefore, commitAfter: null,
468
+ remotes, branch,
469
+ symlink: null, smokeOk: null,
470
+ message: msg, error: msg,
471
+ };
472
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
473
+ }
474
+ return 1;
475
+ }
476
+ // ── Fetch ──────────────────────────────────────────────────────────────────
477
+ out(` ${dim('Fetching from')} ${cyan(preferredRemote)}${dim('…')}`, jsonMode);
478
+ const fetchResult = gitFetch(preferredRemote, repoRoot);
479
+ if (!fetchResult.ok) {
480
+ const msg = `git fetch failed: ${fetchResult.error}`;
481
+ out(` ${yellow('!')} ${msg}`, jsonMode);
482
+ out(` ${dim('Continuing with local state only.')}`, jsonMode);
483
+ out('', jsonMode);
484
+ // Non-fatal: we can still check / pull if network was flaky but the ref exists.
485
+ // For --check we need fetch to succeed to give accurate info; report the error.
486
+ }
487
+ // ── Determine remote tracking ref ─────────────────────────────────────────
488
+ const remoteRef = `${preferredRemote}/${branch}`;
489
+ // Check the remote ref exists (may not if the remote is bare or branch name differs).
490
+ const remoteRefCheck = gitRun(['rev-parse', '--verify', remoteRef], repoRoot);
491
+ const hasRemoteRef = remoteRefCheck.ok;
492
+ // ── --check mode: just report availability ────────────────────────────────
493
+ if (parsed.check) {
494
+ let upToDate = true;
495
+ let newCommits = null;
496
+ if (hasRemoteRef) {
497
+ const { count, error } = countCommitsBetween('HEAD', remoteRef, repoRoot);
498
+ if (error) {
499
+ out(` ${yellow('!')} Could not count commits: ${error}`, jsonMode);
500
+ }
501
+ else {
502
+ newCommits = count;
503
+ upToDate = (count ?? 0) === 0;
504
+ }
505
+ }
506
+ else {
507
+ out(` ${yellow('!')} Remote ref ${cyan(remoteRef)} not found — cannot compare`, jsonMode);
508
+ }
509
+ if (upToDate) {
510
+ out(` ${green('✓')} ${bold('Up to date')} ${dim(`(${commitBefore ?? '?'})`)}`, jsonMode);
511
+ }
512
+ else {
513
+ out(` ${yellow('!')} ${bold(`${newCommits} new commit(s) available`)}` +
514
+ ` ${dim(`on ${remoteRef}`)}`, jsonMode);
515
+ out(` ${dim('Run')} ${cyan('ashlr update')} ${dim('to apply.')}`, jsonMode);
516
+ }
517
+ out('', jsonMode);
518
+ if (jsonMode) {
519
+ const result = {
520
+ updated: false, upToDate, newCommits,
521
+ versionBefore, versionAfter: null,
522
+ commitBefore, commitAfter: null,
523
+ remotes, branch,
524
+ symlink: null, smokeOk: null,
525
+ message: upToDate
526
+ ? 'already up to date'
527
+ : `${newCommits ?? '?'} new commit(s) available`,
528
+ error: null,
529
+ };
530
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
531
+ }
532
+ return 0;
533
+ }
534
+ // ── Apply update: git pull --ff-only ──────────────────────────────────────
535
+ out(` ${dim('Running')} ${cyan('git pull --ff-only')}${dim('…')}`, jsonMode);
536
+ const pullResult = gitRun(['pull', '--ff-only', preferredRemote, branch], repoRoot);
537
+ if (!pullResult.ok) {
538
+ const msg = `git pull --ff-only failed: ${pullResult.stderr || pullResult.stdout}\n` +
539
+ `This means the remote has diverged from your local branch.\n` +
540
+ `Resolve manually: git pull (merge/rebase) or git reset`;
541
+ out(` ${red('✗')} ${bold('Pull failed')}`, jsonMode);
542
+ out(` ${dim(pullResult.stderr || pullResult.stdout)}`, jsonMode);
543
+ out('', jsonMode);
544
+ if (jsonMode) {
545
+ const result = {
546
+ updated: false, upToDate: null, newCommits: null,
547
+ versionBefore, versionAfter: null,
548
+ commitBefore, commitAfter: null,
549
+ remotes, branch,
550
+ symlink: null, smokeOk: null,
551
+ message: 'pull failed', error: msg,
552
+ };
553
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
554
+ }
555
+ return 1;
556
+ }
557
+ const alreadyUpToDate = pullResult.stdout.toLowerCase().includes('already up to date') ||
558
+ pullResult.stdout.toLowerCase().includes('already up-to-date');
559
+ if (alreadyUpToDate) {
560
+ out(` ${green('✓')} ${bold('Already up to date')} ${dim(`(${commitBefore ?? '?'})`)}`, jsonMode);
561
+ out('', jsonMode);
562
+ // Even when already up-to-date, verify the symlink and smoke-test.
563
+ const symlinkInfo = checkSymlink(repoRoot);
564
+ const smoke = smokeTest(repoRoot);
565
+ if (symlinkInfo.ok && symlinkInfo.pointsHere) {
566
+ out(` ${green('✓')} symlink ${dim(symlinkInfo.detail)}`, jsonMode);
567
+ }
568
+ else {
569
+ out(` ${yellow('!')} symlink ${dim(symlinkInfo.detail)}`, jsonMode);
570
+ }
571
+ out(` ${smoke.ok ? green('✓') : yellow('!')} smoke ${dim(smoke.detail)}`, jsonMode);
572
+ out('', jsonMode);
573
+ if (jsonMode) {
574
+ const result = {
575
+ updated: false, upToDate: true, newCommits: 0,
576
+ versionBefore, versionAfter: versionBefore,
577
+ commitBefore, commitAfter: commitBefore,
578
+ remotes, branch,
579
+ symlink: symlinkInfo, smokeOk: smoke.ok,
580
+ message: 'already up to date', error: null,
581
+ };
582
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
583
+ }
584
+ return 0;
585
+ }
586
+ out(` ${green('✓')} ${bold('Pulled')} ${dim(pullResult.stdout.split('\n')[0] ?? '')}`, jsonMode);
587
+ out('', jsonMode);
588
+ // ── npm install ────────────────────────────────────────────────────────────
589
+ out(` ${dim('Running')} ${cyan('npm install')}${dim('…')}`, jsonMode);
590
+ const installResult = npmRun(['install'], repoRoot);
591
+ if (!installResult.ok) {
592
+ const msg = `npm install failed: ${installResult.output.slice(0, 500)}`;
593
+ out(` ${red('✗')} ${bold('npm install failed')}`, jsonMode);
594
+ out(` ${dim(installResult.output.slice(0, 300))}`, jsonMode);
595
+ out('', jsonMode);
596
+ if (jsonMode) {
597
+ const result = {
598
+ updated: false, upToDate: null, newCommits: null,
599
+ versionBefore, versionAfter: null,
600
+ commitBefore, commitAfter: getCommitHash('HEAD', repoRoot),
601
+ remotes, branch,
602
+ symlink: null, smokeOk: null,
603
+ message: 'npm install failed', error: msg,
604
+ };
605
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
606
+ }
607
+ return 1;
608
+ }
609
+ out(` ${green('✓')} npm install`, jsonMode);
610
+ // ── npm run build ─────────────────────────────────────────────────────────
611
+ out(` ${dim('Running')} ${cyan('npm run build')}${dim('…')}`, jsonMode);
612
+ const buildResult = npmRun(['run', 'build'], repoRoot);
613
+ if (!buildResult.ok) {
614
+ const msg = `npm run build failed: ${buildResult.output.slice(0, 500)}`;
615
+ out(` ${red('✗')} ${bold('npm run build failed')}`, jsonMode);
616
+ out(` ${dim(buildResult.output.slice(0, 300))}`, jsonMode);
617
+ out('', jsonMode);
618
+ if (jsonMode) {
619
+ const result = {
620
+ updated: false, upToDate: null, newCommits: null,
621
+ versionBefore, versionAfter: null,
622
+ commitBefore, commitAfter: getCommitHash('HEAD', repoRoot),
623
+ remotes, branch,
624
+ symlink: null, smokeOk: null,
625
+ message: 'build failed', error: msg,
626
+ };
627
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
628
+ }
629
+ return 1;
630
+ }
631
+ out(` ${green('✓')} npm run build → dist/`, jsonMode);
632
+ out('', jsonMode);
633
+ // ── After-state ────────────────────────────────────────────────────────────
634
+ const commitAfter = getCommitHash('HEAD', repoRoot);
635
+ const versionAfter = readPackageVersion(repoRoot);
636
+ // ── Symlink verification ───────────────────────────────────────────────────
637
+ const symlinkInfo = checkSymlink(repoRoot);
638
+ if (symlinkInfo.ok && symlinkInfo.pointsHere) {
639
+ out(` ${green('✓')} symlink ${dim(symlinkInfo.detail)}`, jsonMode);
640
+ }
641
+ else if (symlinkInfo.ok) {
642
+ out(` ${yellow('!')} symlink ${dim(symlinkInfo.detail)}`, jsonMode);
643
+ out(` ${dim('Run')} ${cyan('./install.sh')} ${dim('to re-point the symlink.')}`, jsonMode);
644
+ }
645
+ else {
646
+ out(` ${yellow('!')} symlink ${dim(symlinkInfo.detail)}`, jsonMode);
647
+ out(` ${dim('Run')} ${cyan('./install.sh')} ${dim('to create the symlink.')}`, jsonMode);
648
+ }
649
+ // ── Smoke test ─────────────────────────────────────────────────────────────
650
+ const smoke = smokeTest(repoRoot);
651
+ out(` ${smoke.ok ? green('✓') : red('✗')} smoke ${dim(smoke.detail)}`, jsonMode);
652
+ out('', jsonMode);
653
+ // ── Version summary ────────────────────────────────────────────────────────
654
+ if (versionBefore !== versionAfter && versionAfter) {
655
+ out(` ${bold('Updated:')} ${yellow(versionBefore ?? '?')} → ${green(versionAfter)} ` +
656
+ `${dim(`(${commitBefore ?? '?'} → ${commitAfter ?? '?'})`)}`, jsonMode);
657
+ }
658
+ else if (versionAfter) {
659
+ out(` ${bold('Updated:')} ${green(versionAfter)} ` +
660
+ `${dim(`(${commitBefore ?? '?'} → ${commitAfter ?? '?'})`)}`, jsonMode);
661
+ }
662
+ if (!smoke.ok) {
663
+ out(` ${red('!')} Smoke test failed — the build may be broken.`, jsonMode);
664
+ out(` ${dim('Try running')} ${cyan('npm run build')} ${dim('manually from')} ${gray(repoRoot)}`, jsonMode);
665
+ }
666
+ out('', jsonMode);
667
+ // ── JSON output ────────────────────────────────────────────────────────────
668
+ if (jsonMode) {
669
+ const result = {
670
+ updated: true, upToDate: false, newCommits: null,
671
+ versionBefore, versionAfter,
672
+ commitBefore, commitAfter,
673
+ remotes, branch,
674
+ symlink: symlinkInfo, smokeOk: smoke.ok,
675
+ message: smoke.ok ? 'update applied successfully' : 'update applied but smoke test failed',
676
+ error: smoke.ok ? null : smoke.detail,
677
+ };
678
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
679
+ }
680
+ return smoke.ok ? 0 : 1;
681
+ }
682
+ //# sourceMappingURL=update.js.map