steroids-cli 0.4.47

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 (395) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +640 -0
  3. package/dist/cli/colors.d.ts +110 -0
  4. package/dist/cli/colors.d.ts.map +1 -0
  5. package/dist/cli/colors.js +228 -0
  6. package/dist/cli/colors.js.map +1 -0
  7. package/dist/cli/env.d.ts +159 -0
  8. package/dist/cli/env.d.ts.map +1 -0
  9. package/dist/cli/env.js +227 -0
  10. package/dist/cli/env.js.map +1 -0
  11. package/dist/cli/errors.d.ts +166 -0
  12. package/dist/cli/errors.d.ts.map +1 -0
  13. package/dist/cli/errors.js +244 -0
  14. package/dist/cli/errors.js.map +1 -0
  15. package/dist/cli/flags.d.ts +75 -0
  16. package/dist/cli/flags.d.ts.map +1 -0
  17. package/dist/cli/flags.js +232 -0
  18. package/dist/cli/flags.js.map +1 -0
  19. package/dist/cli/help.d.ts +97 -0
  20. package/dist/cli/help.d.ts.map +1 -0
  21. package/dist/cli/help.js +275 -0
  22. package/dist/cli/help.js.map +1 -0
  23. package/dist/cli/index.d.ts +13 -0
  24. package/dist/cli/index.d.ts.map +1 -0
  25. package/dist/cli/index.js +29 -0
  26. package/dist/cli/index.js.map +1 -0
  27. package/dist/cli/interactive.d.ts +58 -0
  28. package/dist/cli/interactive.d.ts.map +1 -0
  29. package/dist/cli/interactive.js +127 -0
  30. package/dist/cli/interactive.js.map +1 -0
  31. package/dist/cli/output.d.ts +116 -0
  32. package/dist/cli/output.d.ts.map +1 -0
  33. package/dist/cli/output.js +178 -0
  34. package/dist/cli/output.js.map +1 -0
  35. package/dist/commands/about.d.ts +7 -0
  36. package/dist/commands/about.d.ts.map +1 -0
  37. package/dist/commands/about.js +259 -0
  38. package/dist/commands/about.js.map +1 -0
  39. package/dist/commands/ai.d.ts +6 -0
  40. package/dist/commands/ai.d.ts.map +1 -0
  41. package/dist/commands/ai.js +382 -0
  42. package/dist/commands/ai.js.map +1 -0
  43. package/dist/commands/backup.d.ts +3 -0
  44. package/dist/commands/backup.d.ts.map +1 -0
  45. package/dist/commands/backup.js +528 -0
  46. package/dist/commands/backup.js.map +1 -0
  47. package/dist/commands/completion.d.ts +3 -0
  48. package/dist/commands/completion.d.ts.map +1 -0
  49. package/dist/commands/completion.js +405 -0
  50. package/dist/commands/completion.js.map +1 -0
  51. package/dist/commands/config.d.ts +3 -0
  52. package/dist/commands/config.d.ts.map +1 -0
  53. package/dist/commands/config.js +665 -0
  54. package/dist/commands/config.js.map +1 -0
  55. package/dist/commands/disputes.d.ts +3 -0
  56. package/dist/commands/disputes.d.ts.map +1 -0
  57. package/dist/commands/disputes.js +499 -0
  58. package/dist/commands/disputes.js.map +1 -0
  59. package/dist/commands/gc.d.ts +3 -0
  60. package/dist/commands/gc.d.ts.map +1 -0
  61. package/dist/commands/gc.js +300 -0
  62. package/dist/commands/gc.js.map +1 -0
  63. package/dist/commands/git.d.ts +3 -0
  64. package/dist/commands/git.d.ts.map +1 -0
  65. package/dist/commands/git.js +458 -0
  66. package/dist/commands/git.js.map +1 -0
  67. package/dist/commands/health.d.ts +3 -0
  68. package/dist/commands/health.d.ts.map +1 -0
  69. package/dist/commands/health.js +604 -0
  70. package/dist/commands/health.js.map +1 -0
  71. package/dist/commands/hooks.d.ts +6 -0
  72. package/dist/commands/hooks.d.ts.map +1 -0
  73. package/dist/commands/hooks.js +529 -0
  74. package/dist/commands/hooks.js.map +1 -0
  75. package/dist/commands/init.d.ts +6 -0
  76. package/dist/commands/init.d.ts.map +1 -0
  77. package/dist/commands/init.js +200 -0
  78. package/dist/commands/init.js.map +1 -0
  79. package/dist/commands/llm.d.ts +7 -0
  80. package/dist/commands/llm.d.ts.map +1 -0
  81. package/dist/commands/llm.js +285 -0
  82. package/dist/commands/llm.js.map +1 -0
  83. package/dist/commands/locks.d.ts +3 -0
  84. package/dist/commands/locks.d.ts.map +1 -0
  85. package/dist/commands/locks.js +431 -0
  86. package/dist/commands/locks.js.map +1 -0
  87. package/dist/commands/logs.d.ts +3 -0
  88. package/dist/commands/logs.d.ts.map +1 -0
  89. package/dist/commands/logs.js +487 -0
  90. package/dist/commands/logs.js.map +1 -0
  91. package/dist/commands/loop-phases.d.ts +11 -0
  92. package/dist/commands/loop-phases.d.ts.map +1 -0
  93. package/dist/commands/loop-phases.js +204 -0
  94. package/dist/commands/loop-phases.js.map +1 -0
  95. package/dist/commands/loop.d.ts +3 -0
  96. package/dist/commands/loop.d.ts.map +1 -0
  97. package/dist/commands/loop.js +396 -0
  98. package/dist/commands/loop.js.map +1 -0
  99. package/dist/commands/projects.d.ts +6 -0
  100. package/dist/commands/projects.d.ts.map +1 -0
  101. package/dist/commands/projects.js +362 -0
  102. package/dist/commands/projects.js.map +1 -0
  103. package/dist/commands/purge.d.ts +3 -0
  104. package/dist/commands/purge.d.ts.map +1 -0
  105. package/dist/commands/purge.js +516 -0
  106. package/dist/commands/purge.js.map +1 -0
  107. package/dist/commands/runners.d.ts +3 -0
  108. package/dist/commands/runners.d.ts.map +1 -0
  109. package/dist/commands/runners.js +1076 -0
  110. package/dist/commands/runners.js.map +1 -0
  111. package/dist/commands/scan.d.ts +3 -0
  112. package/dist/commands/scan.d.ts.map +1 -0
  113. package/dist/commands/scan.js +291 -0
  114. package/dist/commands/scan.js.map +1 -0
  115. package/dist/commands/sections-commands.d.ts +9 -0
  116. package/dist/commands/sections-commands.d.ts.map +1 -0
  117. package/dist/commands/sections-commands.js +282 -0
  118. package/dist/commands/sections-commands.js.map +1 -0
  119. package/dist/commands/sections-graph.d.ts +25 -0
  120. package/dist/commands/sections-graph.d.ts.map +1 -0
  121. package/dist/commands/sections-graph.js +180 -0
  122. package/dist/commands/sections-graph.js.map +1 -0
  123. package/dist/commands/sections.d.ts +3 -0
  124. package/dist/commands/sections.d.ts.map +1 -0
  125. package/dist/commands/sections.js +376 -0
  126. package/dist/commands/sections.js.map +1 -0
  127. package/dist/commands/stats.d.ts +6 -0
  128. package/dist/commands/stats.d.ts.map +1 -0
  129. package/dist/commands/stats.js +324 -0
  130. package/dist/commands/stats.js.map +1 -0
  131. package/dist/commands/tasks.d.ts +3 -0
  132. package/dist/commands/tasks.d.ts.map +1 -0
  133. package/dist/commands/tasks.js +1115 -0
  134. package/dist/commands/tasks.js.map +1 -0
  135. package/dist/commands/web.d.ts +7 -0
  136. package/dist/commands/web.d.ts.map +1 -0
  137. package/dist/commands/web.js +204 -0
  138. package/dist/commands/web.js.map +1 -0
  139. package/dist/config/ai-setup.d.ts +27 -0
  140. package/dist/config/ai-setup.d.ts.map +1 -0
  141. package/dist/config/ai-setup.js +432 -0
  142. package/dist/config/ai-setup.js.map +1 -0
  143. package/dist/config/browser.d.ts +9 -0
  144. package/dist/config/browser.d.ts.map +1 -0
  145. package/dist/config/browser.js +200 -0
  146. package/dist/config/browser.js.map +1 -0
  147. package/dist/config/json-schema.d.ts +28 -0
  148. package/dist/config/json-schema.d.ts.map +1 -0
  149. package/dist/config/json-schema.js +84 -0
  150. package/dist/config/json-schema.js.map +1 -0
  151. package/dist/config/loader.d.ts +152 -0
  152. package/dist/config/loader.d.ts.map +1 -0
  153. package/dist/config/loader.js +270 -0
  154. package/dist/config/loader.js.map +1 -0
  155. package/dist/config/schema.d.ts +34 -0
  156. package/dist/config/schema.d.ts.map +1 -0
  157. package/dist/config/schema.js +437 -0
  158. package/dist/config/schema.js.map +1 -0
  159. package/dist/config/validator.d.ts +32 -0
  160. package/dist/config/validator.d.ts.map +1 -0
  161. package/dist/config/validator.js +187 -0
  162. package/dist/config/validator.js.map +1 -0
  163. package/dist/database/connection.d.ts +35 -0
  164. package/dist/database/connection.d.ts.map +1 -0
  165. package/dist/database/connection.js +208 -0
  166. package/dist/database/connection.js.map +1 -0
  167. package/dist/database/queries.d.ts +218 -0
  168. package/dist/database/queries.d.ts.map +1 -0
  169. package/dist/database/queries.js +613 -0
  170. package/dist/database/queries.js.map +1 -0
  171. package/dist/database/schema.d.ts +8 -0
  172. package/dist/database/schema.d.ts.map +1 -0
  173. package/dist/database/schema.js +160 -0
  174. package/dist/database/schema.js.map +1 -0
  175. package/dist/disputes/behavior.d.ts +106 -0
  176. package/dist/disputes/behavior.d.ts.map +1 -0
  177. package/dist/disputes/behavior.js +150 -0
  178. package/dist/disputes/behavior.js.map +1 -0
  179. package/dist/disputes/create.d.ts +59 -0
  180. package/dist/disputes/create.d.ts.map +1 -0
  181. package/dist/disputes/create.js +222 -0
  182. package/dist/disputes/create.js.map +1 -0
  183. package/dist/disputes/index.d.ts +21 -0
  184. package/dist/disputes/index.d.ts.map +1 -0
  185. package/dist/disputes/index.js +76 -0
  186. package/dist/disputes/index.js.map +1 -0
  187. package/dist/disputes/markdown.d.ts +41 -0
  188. package/dist/disputes/markdown.d.ts.map +1 -0
  189. package/dist/disputes/markdown.js +261 -0
  190. package/dist/disputes/markdown.js.map +1 -0
  191. package/dist/disputes/queries.d.ts +83 -0
  192. package/dist/disputes/queries.d.ts.map +1 -0
  193. package/dist/disputes/queries.js +180 -0
  194. package/dist/disputes/queries.js.map +1 -0
  195. package/dist/disputes/resolve.d.ts +57 -0
  196. package/dist/disputes/resolve.d.ts.map +1 -0
  197. package/dist/disputes/resolve.js +171 -0
  198. package/dist/disputes/resolve.js.map +1 -0
  199. package/dist/disputes/stale.d.ts +98 -0
  200. package/dist/disputes/stale.d.ts.map +1 -0
  201. package/dist/disputes/stale.js +205 -0
  202. package/dist/disputes/stale.js.map +1 -0
  203. package/dist/disputes/types.d.ts +92 -0
  204. package/dist/disputes/types.d.ts.map +1 -0
  205. package/dist/disputes/types.js +100 -0
  206. package/dist/disputes/types.js.map +1 -0
  207. package/dist/git/push.d.ts +26 -0
  208. package/dist/git/push.d.ts.map +1 -0
  209. package/dist/git/push.js +97 -0
  210. package/dist/git/push.js.map +1 -0
  211. package/dist/git/status.d.ts +61 -0
  212. package/dist/git/status.d.ts.map +1 -0
  213. package/dist/git/status.js +251 -0
  214. package/dist/git/status.js.map +1 -0
  215. package/dist/hooks/events.d.ts +72 -0
  216. package/dist/hooks/events.d.ts.map +1 -0
  217. package/dist/hooks/events.js +120 -0
  218. package/dist/hooks/events.js.map +1 -0
  219. package/dist/hooks/index.d.ts +19 -0
  220. package/dist/hooks/index.d.ts.map +1 -0
  221. package/dist/hooks/index.js +48 -0
  222. package/dist/hooks/index.js.map +1 -0
  223. package/dist/hooks/integration.d.ts +69 -0
  224. package/dist/hooks/integration.d.ts.map +1 -0
  225. package/dist/hooks/integration.js +179 -0
  226. package/dist/hooks/integration.js.map +1 -0
  227. package/dist/hooks/merge.d.ts +115 -0
  228. package/dist/hooks/merge.d.ts.map +1 -0
  229. package/dist/hooks/merge.js +161 -0
  230. package/dist/hooks/merge.js.map +1 -0
  231. package/dist/hooks/orchestrator.d.ts +115 -0
  232. package/dist/hooks/orchestrator.d.ts.map +1 -0
  233. package/dist/hooks/orchestrator.js +226 -0
  234. package/dist/hooks/orchestrator.js.map +1 -0
  235. package/dist/hooks/payload.d.ts +294 -0
  236. package/dist/hooks/payload.d.ts.map +1 -0
  237. package/dist/hooks/payload.js +267 -0
  238. package/dist/hooks/payload.js.map +1 -0
  239. package/dist/hooks/script-runner.d.ts +63 -0
  240. package/dist/hooks/script-runner.d.ts.map +1 -0
  241. package/dist/hooks/script-runner.js +221 -0
  242. package/dist/hooks/script-runner.js.map +1 -0
  243. package/dist/hooks/templates.d.ts +104 -0
  244. package/dist/hooks/templates.d.ts.map +1 -0
  245. package/dist/hooks/templates.js +327 -0
  246. package/dist/hooks/templates.js.map +1 -0
  247. package/dist/hooks/webhook-runner.d.ts +69 -0
  248. package/dist/hooks/webhook-runner.d.ts.map +1 -0
  249. package/dist/hooks/webhook-runner.js +208 -0
  250. package/dist/hooks/webhook-runner.js.map +1 -0
  251. package/dist/index.d.ts +7 -0
  252. package/dist/index.d.ts.map +1 -0
  253. package/dist/index.js +281 -0
  254. package/dist/index.js.map +1 -0
  255. package/dist/locking/cleanup.d.ts +70 -0
  256. package/dist/locking/cleanup.d.ts.map +1 -0
  257. package/dist/locking/cleanup.js +157 -0
  258. package/dist/locking/cleanup.js.map +1 -0
  259. package/dist/locking/queries.d.ts +116 -0
  260. package/dist/locking/queries.d.ts.map +1 -0
  261. package/dist/locking/queries.js +255 -0
  262. package/dist/locking/queries.js.map +1 -0
  263. package/dist/locking/section-lock.d.ts +74 -0
  264. package/dist/locking/section-lock.d.ts.map +1 -0
  265. package/dist/locking/section-lock.js +207 -0
  266. package/dist/locking/section-lock.js.map +1 -0
  267. package/dist/locking/task-lock.d.ts +92 -0
  268. package/dist/locking/task-lock.d.ts.map +1 -0
  269. package/dist/locking/task-lock.js +246 -0
  270. package/dist/locking/task-lock.js.map +1 -0
  271. package/dist/migrations/index.d.ts +7 -0
  272. package/dist/migrations/index.d.ts.map +1 -0
  273. package/dist/migrations/index.js +37 -0
  274. package/dist/migrations/index.js.map +1 -0
  275. package/dist/migrations/manifest.d.ts +92 -0
  276. package/dist/migrations/manifest.d.ts.map +1 -0
  277. package/dist/migrations/manifest.js +270 -0
  278. package/dist/migrations/manifest.js.map +1 -0
  279. package/dist/migrations/runner.d.ts +84 -0
  280. package/dist/migrations/runner.d.ts.map +1 -0
  281. package/dist/migrations/runner.js +351 -0
  282. package/dist/migrations/runner.js.map +1 -0
  283. package/dist/orchestrator/coder.d.ts +32 -0
  284. package/dist/orchestrator/coder.d.ts.map +1 -0
  285. package/dist/orchestrator/coder.js +174 -0
  286. package/dist/orchestrator/coder.js.map +1 -0
  287. package/dist/orchestrator/coordinator.d.ts +28 -0
  288. package/dist/orchestrator/coordinator.d.ts.map +1 -0
  289. package/dist/orchestrator/coordinator.js +256 -0
  290. package/dist/orchestrator/coordinator.js.map +1 -0
  291. package/dist/orchestrator/reviewer.d.ts +35 -0
  292. package/dist/orchestrator/reviewer.d.ts.map +1 -0
  293. package/dist/orchestrator/reviewer.js +241 -0
  294. package/dist/orchestrator/reviewer.js.map +1 -0
  295. package/dist/orchestrator/task-selector.d.ts +102 -0
  296. package/dist/orchestrator/task-selector.d.ts.map +1 -0
  297. package/dist/orchestrator/task-selector.js +341 -0
  298. package/dist/orchestrator/task-selector.js.map +1 -0
  299. package/dist/prompts/coder.d.ts +36 -0
  300. package/dist/prompts/coder.d.ts.map +1 -0
  301. package/dist/prompts/coder.js +315 -0
  302. package/dist/prompts/coder.js.map +1 -0
  303. package/dist/prompts/prompt-helpers.d.ts +51 -0
  304. package/dist/prompts/prompt-helpers.d.ts.map +1 -0
  305. package/dist/prompts/prompt-helpers.js +312 -0
  306. package/dist/prompts/prompt-helpers.js.map +1 -0
  307. package/dist/prompts/reviewer.d.ts +40 -0
  308. package/dist/prompts/reviewer.d.ts.map +1 -0
  309. package/dist/prompts/reviewer.js +438 -0
  310. package/dist/prompts/reviewer.js.map +1 -0
  311. package/dist/providers/api-models.d.ts +65 -0
  312. package/dist/providers/api-models.d.ts.map +1 -0
  313. package/dist/providers/api-models.js +323 -0
  314. package/dist/providers/api-models.js.map +1 -0
  315. package/dist/providers/claude.d.ts +53 -0
  316. package/dist/providers/claude.d.ts.map +1 -0
  317. package/dist/providers/claude.js +229 -0
  318. package/dist/providers/claude.js.map +1 -0
  319. package/dist/providers/codex.d.ts +53 -0
  320. package/dist/providers/codex.d.ts.map +1 -0
  321. package/dist/providers/codex.js +214 -0
  322. package/dist/providers/codex.js.map +1 -0
  323. package/dist/providers/gemini.d.ts +58 -0
  324. package/dist/providers/gemini.d.ts.map +1 -0
  325. package/dist/providers/gemini.js +242 -0
  326. package/dist/providers/gemini.js.map +1 -0
  327. package/dist/providers/index.d.ts +13 -0
  328. package/dist/providers/index.d.ts.map +1 -0
  329. package/dist/providers/index.js +49 -0
  330. package/dist/providers/index.js.map +1 -0
  331. package/dist/providers/interface.d.ts +173 -0
  332. package/dist/providers/interface.d.ts.map +1 -0
  333. package/dist/providers/interface.js +96 -0
  334. package/dist/providers/interface.js.map +1 -0
  335. package/dist/providers/invocation-logger.d.ts +114 -0
  336. package/dist/providers/invocation-logger.d.ts.map +1 -0
  337. package/dist/providers/invocation-logger.js +298 -0
  338. package/dist/providers/invocation-logger.js.map +1 -0
  339. package/dist/providers/openai.d.ts +53 -0
  340. package/dist/providers/openai.d.ts.map +1 -0
  341. package/dist/providers/openai.js +232 -0
  342. package/dist/providers/openai.js.map +1 -0
  343. package/dist/providers/registry.d.ts +100 -0
  344. package/dist/providers/registry.d.ts.map +1 -0
  345. package/dist/providers/registry.js +178 -0
  346. package/dist/providers/registry.js.map +1 -0
  347. package/dist/runners/activity-log.d.ts +65 -0
  348. package/dist/runners/activity-log.d.ts.map +1 -0
  349. package/dist/runners/activity-log.js +148 -0
  350. package/dist/runners/activity-log.js.map +1 -0
  351. package/dist/runners/cron.d.ts +26 -0
  352. package/dist/runners/cron.d.ts.map +1 -0
  353. package/dist/runners/cron.js +176 -0
  354. package/dist/runners/cron.js.map +1 -0
  355. package/dist/runners/daemon.d.ts +71 -0
  356. package/dist/runners/daemon.d.ts.map +1 -0
  357. package/dist/runners/daemon.js +245 -0
  358. package/dist/runners/daemon.js.map +1 -0
  359. package/dist/runners/global-db.d.ts +31 -0
  360. package/dist/runners/global-db.d.ts.map +1 -0
  361. package/dist/runners/global-db.js +230 -0
  362. package/dist/runners/global-db.js.map +1 -0
  363. package/dist/runners/hang-detector.d.ts +38 -0
  364. package/dist/runners/hang-detector.d.ts.map +1 -0
  365. package/dist/runners/hang-detector.js +136 -0
  366. package/dist/runners/hang-detector.js.map +1 -0
  367. package/dist/runners/heartbeat.d.ts +39 -0
  368. package/dist/runners/heartbeat.d.ts.map +1 -0
  369. package/dist/runners/heartbeat.js +79 -0
  370. package/dist/runners/heartbeat.js.map +1 -0
  371. package/dist/runners/lock.d.ts +47 -0
  372. package/dist/runners/lock.d.ts.map +1 -0
  373. package/dist/runners/lock.js +150 -0
  374. package/dist/runners/lock.js.map +1 -0
  375. package/dist/runners/orchestrator-loop.d.ts +20 -0
  376. package/dist/runners/orchestrator-loop.d.ts.map +1 -0
  377. package/dist/runners/orchestrator-loop.js +285 -0
  378. package/dist/runners/orchestrator-loop.js.map +1 -0
  379. package/dist/runners/projects.d.ts +96 -0
  380. package/dist/runners/projects.d.ts.map +1 -0
  381. package/dist/runners/projects.js +255 -0
  382. package/dist/runners/projects.js.map +1 -0
  383. package/dist/runners/wakeup.d.ts +34 -0
  384. package/dist/runners/wakeup.d.ts.map +1 -0
  385. package/dist/runners/wakeup.js +291 -0
  386. package/dist/runners/wakeup.js.map +1 -0
  387. package/migrations/001_initial_schema.sql +106 -0
  388. package/migrations/002_add_commit_sha.sql +12 -0
  389. package/migrations/003_add_section_priority.sql +13 -0
  390. package/migrations/004_add_section_dependencies.sql +18 -0
  391. package/migrations/005_add_audit_actor_model.sql +10 -0
  392. package/migrations/006_add_task_invocations.sql +33 -0
  393. package/migrations/007_add_file_anchor.sql +14 -0
  394. package/migrations/manifest.json +62 -0
  395. package/package.json +49 -0
@@ -0,0 +1,1115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tasksCommand = tasksCommand;
4
+ /**
5
+ * steroids tasks - Manage tasks
6
+ */
7
+ const node_util_1 = require("node:util");
8
+ const node_fs_1 = require("node:fs");
9
+ const node_path_1 = require("node:path");
10
+ const connection_js_1 = require("../database/connection.js");
11
+ const projects_js_1 = require("../runners/projects.js");
12
+ const activity_log_js_1 = require("../runners/activity-log.js");
13
+ const queries_js_1 = require("../database/queries.js");
14
+ const output_js_1 = require("../cli/output.js");
15
+ const errors_js_1 = require("../cli/errors.js");
16
+ const help_js_1 = require("../cli/help.js");
17
+ const integration_js_1 = require("../hooks/integration.js");
18
+ const status_js_1 = require("../git/status.js");
19
+ const HELP = (0, help_js_1.generateHelp)({
20
+ command: 'tasks',
21
+ description: 'Manage tasks in the automated development workflow',
22
+ details: `Tasks are units of work that flow through the coder/reviewer loop.
23
+ Each task has a specification file and tracks progress through various states.
24
+ Use this command to add, update, approve, reject, or skip tasks.`,
25
+ usage: [
26
+ 'steroids tasks [options]',
27
+ 'steroids tasks <subcommand> [args] [options]',
28
+ ],
29
+ subcommands: [
30
+ { name: 'list', description: 'List tasks (default subcommand)' },
31
+ { name: 'stats', description: 'Show task counts by status' },
32
+ { name: 'show', args: '<id|title>', description: 'Show task details with invocation logs' },
33
+ { name: 'add', args: '<title>', description: 'Add a new task' },
34
+ { name: 'update', args: '<id|title>', description: 'Update task status' },
35
+ { name: 'approve', args: '<id|title>', description: 'Approve a task (mark completed)' },
36
+ { name: 'reject', args: '<id|title>', description: 'Reject a task (back to in_progress)' },
37
+ { name: 'skip', args: '<id|title>', description: 'Skip a task (external/manual work)' },
38
+ { name: 'audit', args: '<id|title>', description: 'View task audit trail' },
39
+ ],
40
+ options: [
41
+ { short: 's', long: 'status', description: 'Filter by status', values: 'pending | in_progress | review | completed | disputed | failed | skipped | partial | active | all', default: 'pending' },
42
+ { short: 'g', long: 'global', description: 'List tasks across ALL registered projects' },
43
+ { long: 'section', description: 'Filter by section ID (local project only)', values: '<id>' },
44
+ { long: 'search', description: 'Search in task titles', values: '<query>' },
45
+ { long: 'reset-rejections', description: 'Reset rejection count to 0 (update subcommand)' },
46
+ { long: 'actor', description: 'Actor making the change', values: '<name>' },
47
+ { long: 'model', description: 'Model identifier (for LLM actors)', values: '<model>' },
48
+ { long: 'notes', description: 'Review notes/comments', values: '<text>' },
49
+ { long: 'source', description: 'Specification file (add subcommand)', values: '<file>' },
50
+ { long: 'file', description: 'Anchor task to a committed file (add subcommand)', values: '<path>' },
51
+ { long: 'line', description: 'Line number in anchored file (requires --file)', values: '<number>' },
52
+ { long: 'feedback', description: 'Add to "Needs User Input" section (skipped, for human review)' },
53
+ { long: 'partial', description: 'Mark as partial when skipping' },
54
+ { long: 'no-hooks', description: 'Skip hook execution (global flag)' },
55
+ ],
56
+ examples: [
57
+ { command: 'steroids tasks', description: 'List pending tasks' },
58
+ { command: 'steroids tasks --status all', description: 'List all tasks' },
59
+ { command: 'steroids tasks --status active', description: 'Show active tasks (in_progress + review)' },
60
+ { command: 'steroids tasks --status skipped', description: 'See what needs manual action' },
61
+ { command: 'steroids tasks --global --json', description: 'List tasks from all projects as JSON' },
62
+ { command: 'steroids tasks add "Implement login" --section abc123 --source docs/spec.md', description: 'Add new task' },
63
+ { command: 'steroids tasks add "Fix bug" --section abc123 --source spec.md --file src/utils.ts --line 42', description: 'Add task anchored to a file' },
64
+ { command: 'steroids tasks add "Review execSync usage" --feedback', description: 'Add feedback task (skipped section)' },
65
+ { command: 'steroids tasks update "Implement login" --status review', description: 'Update task status' },
66
+ { command: 'steroids tasks approve abc123 --model claude-sonnet-4', description: 'Approve a task' },
67
+ { command: 'steroids tasks approve abc123 --no-hooks', description: 'Approve without triggering hooks' },
68
+ { command: 'steroids tasks reject abc123 --model codex --notes "Missing tests"', description: 'Reject a task' },
69
+ { command: 'steroids tasks skip abc123 --notes "Cloud SQL - manual setup"', description: 'Skip a task' },
70
+ { command: 'steroids tasks audit abc123', description: 'View task history' },
71
+ { command: 'steroids tasks show abc123 --logs', description: 'Show task with LLM invocation logs' },
72
+ { command: 'steroids tasks stats --json', description: 'Get task statistics as JSON' },
73
+ ],
74
+ related: [
75
+ { command: 'steroids sections', description: 'Manage task sections' },
76
+ { command: 'steroids loop', description: 'Run automation on pending tasks' },
77
+ { command: 'steroids dispute', description: 'View coder/reviewer disputes' },
78
+ ],
79
+ sections: [
80
+ {
81
+ title: 'STATUS VALUES',
82
+ content: `pending [ ] Not started
83
+ in_progress [-] Being worked on by coder
84
+ review [o] Ready for reviewer
85
+ completed [x] Approved by reviewer
86
+ disputed [!] Coder/reviewer disagreement
87
+ failed [F] Exceeded 15 rejections (needs human)
88
+ skipped [S] Fully external (nothing to code)
89
+ partial [s] Coded what we could, rest external
90
+ active Combined: in_progress + review`,
91
+ },
92
+ ],
93
+ });
94
+ async function tasksCommand(args, flags) {
95
+ // Check global help flag (parsed by main CLI)
96
+ if (flags.help) {
97
+ console.log(HELP);
98
+ return;
99
+ }
100
+ if (args.length === 0) {
101
+ // Default: list pending tasks
102
+ await listAllTasks([], flags);
103
+ return;
104
+ }
105
+ // Check if first arg is a subcommand or a flag
106
+ const subcommand = args[0];
107
+ if (subcommand.startsWith('-')) {
108
+ // It's a flag, so this is a list command
109
+ await listAllTasks(args, flags);
110
+ return;
111
+ }
112
+ const subArgs = args.slice(1);
113
+ switch (subcommand) {
114
+ case 'stats':
115
+ await showStats(subArgs, flags);
116
+ break;
117
+ case 'show':
118
+ await showTask(subArgs, flags);
119
+ break;
120
+ case 'add':
121
+ await addTask(subArgs, flags);
122
+ break;
123
+ case 'update':
124
+ await updateTask(subArgs, flags);
125
+ break;
126
+ case 'approve':
127
+ await approveTaskCmd(subArgs, flags);
128
+ break;
129
+ case 'reject':
130
+ await rejectTaskCmd(subArgs, flags);
131
+ break;
132
+ case 'skip':
133
+ await skipTaskCmd(subArgs, flags);
134
+ break;
135
+ case 'audit':
136
+ await auditTask(subArgs, flags);
137
+ break;
138
+ case 'list':
139
+ await listAllTasks(subArgs, flags);
140
+ break;
141
+ default:
142
+ if (flags.json) {
143
+ (0, output_js_1.outputJsonError)('tasks', subcommand, errors_js_1.ErrorCode.INVALID_ARGUMENTS, `Unknown subcommand: ${subcommand}`);
144
+ }
145
+ else {
146
+ console.error(`Unknown subcommand: ${subcommand}`);
147
+ console.log(HELP);
148
+ }
149
+ process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.INVALID_ARGUMENTS));
150
+ }
151
+ }
152
+ async function listAllTasks(args, globalFlags) {
153
+ // Check for help flag first (parseArgs doesn't always handle -h well)
154
+ if (args.includes('-h') || args.includes('--help')) {
155
+ console.log(HELP);
156
+ return;
157
+ }
158
+ const { values } = (0, node_util_1.parseArgs)({
159
+ args,
160
+ options: {
161
+ help: { type: 'boolean', short: 'h', default: false },
162
+ json: { type: 'boolean', short: 'j', default: false },
163
+ status: { type: 'string', short: 's', default: 'pending' },
164
+ section: { type: 'string' },
165
+ search: { type: 'string' },
166
+ global: { type: 'boolean', short: 'g', default: false },
167
+ },
168
+ allowPositionals: false,
169
+ });
170
+ if (values.help) {
171
+ console.log(HELP);
172
+ return;
173
+ }
174
+ // Honor global --json flag or local -j/--json flag
175
+ const outputJson = values.json || globalFlags?.json;
176
+ const statusFilter = values.status;
177
+ const isGlobalQuery = values.global;
178
+ // For --global flag, query all registered projects
179
+ if (isGlobalQuery) {
180
+ const allTasks = [];
181
+ const projects = (0, projects_js_1.getRegisteredProjects)(false); // enabled only
182
+ for (const project of projects) {
183
+ const dbPath = `${project.path}/.steroids/steroids.db`;
184
+ if (!(0, node_fs_1.existsSync)(dbPath))
185
+ continue;
186
+ try {
187
+ const { db, close } = (0, connection_js_1.openDatabase)(project.path);
188
+ try {
189
+ let tasks;
190
+ if (statusFilter === 'active') {
191
+ const inProgress = (0, queries_js_1.listTasks)(db, { status: 'in_progress', search: values.search });
192
+ const review = (0, queries_js_1.listTasks)(db, { status: 'review', search: values.search });
193
+ tasks = [...inProgress, ...review];
194
+ }
195
+ else {
196
+ tasks = (0, queries_js_1.listTasks)(db, { status: statusFilter, search: values.search });
197
+ }
198
+ const projectName = project.name || (0, node_path_1.basename)(project.path);
199
+ for (const task of tasks) {
200
+ allTasks.push({
201
+ ...task,
202
+ project_path: project.path,
203
+ project_name: projectName,
204
+ });
205
+ }
206
+ }
207
+ finally {
208
+ close();
209
+ }
210
+ }
211
+ catch {
212
+ // Skip projects with inaccessible databases
213
+ }
214
+ }
215
+ if (outputJson) {
216
+ (0, output_js_1.outputJson)('tasks', 'list', {
217
+ tasks: allTasks,
218
+ total: allTasks.length,
219
+ filter: { status: statusFilter, global: true },
220
+ });
221
+ return;
222
+ }
223
+ if (allTasks.length === 0) {
224
+ console.log(`No ${statusFilter} tasks found across all projects.`);
225
+ return;
226
+ }
227
+ const statusLabel = statusFilter.toUpperCase();
228
+ console.log(`${statusLabel} TASKS (All Projects)`);
229
+ console.log('─'.repeat(100));
230
+ console.log('STATUS PROJECT TITLE REJ ID');
231
+ console.log('─'.repeat(100));
232
+ for (const task of allTasks) {
233
+ const marker = queries_js_1.STATUS_MARKERS[task.status];
234
+ const shortId = task.id.substring(0, 8);
235
+ const projectName = (task.project_name || 'unknown').substring(0, 22).padEnd(22);
236
+ const title = task.title.length > 40
237
+ ? task.title.substring(0, 37) + '...'
238
+ : task.title.padEnd(40);
239
+ const rej = task.rejection_count > 0
240
+ ? String(task.rejection_count).padStart(3)
241
+ : ' -';
242
+ console.log(`${marker} ${projectName} ${title} ${rej} ${shortId}`);
243
+ }
244
+ console.log('─'.repeat(100));
245
+ console.log(`Total: ${allTasks.length} active task(s) across ${projects.length} project(s)`);
246
+ // Multi-project warning
247
+ if (projects.length > 1) {
248
+ const currentProject = process.cwd();
249
+ console.log('');
250
+ console.log('─'.repeat(100));
251
+ console.log('⚠️ MULTI-PROJECT WARNING');
252
+ console.log(` Your current project: ${currentProject}`);
253
+ console.log(' DO NOT modify files in other projects. Each runner works only on its own project.');
254
+ console.log('─'.repeat(100));
255
+ }
256
+ return;
257
+ }
258
+ // Non-global query: use current project only
259
+ const { db, close } = (0, connection_js_1.openDatabase)();
260
+ try {
261
+ let sectionId;
262
+ if (values.section) {
263
+ const section = (0, queries_js_1.getSection)(db, values.section);
264
+ if (!section) {
265
+ if (outputJson) {
266
+ (0, output_js_1.outputJsonError)('tasks', 'list', errors_js_1.ErrorCode.SECTION_NOT_FOUND, `Section not found: ${values.section}`, {
267
+ sectionId: values.section,
268
+ hint: 'Use "steroids sections list" to see available sections.',
269
+ });
270
+ }
271
+ else {
272
+ console.error(`Section not found: ${values.section}`);
273
+ console.error('Use "steroids sections list" to see available sections.');
274
+ }
275
+ process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.SECTION_NOT_FOUND));
276
+ }
277
+ sectionId = section.id;
278
+ }
279
+ let tasks;
280
+ if (statusFilter === 'active') {
281
+ const inProgress = (0, queries_js_1.listTasks)(db, { status: 'in_progress', sectionId, search: values.search });
282
+ const review = (0, queries_js_1.listTasks)(db, { status: 'review', sectionId, search: values.search });
283
+ tasks = [...inProgress, ...review];
284
+ }
285
+ else {
286
+ tasks = (0, queries_js_1.listTasks)(db, {
287
+ status: statusFilter,
288
+ sectionId,
289
+ search: values.search,
290
+ });
291
+ }
292
+ if (outputJson) {
293
+ (0, output_js_1.outputJson)('tasks', 'list', {
294
+ tasks,
295
+ total: tasks.length,
296
+ filter: { status: statusFilter, section: sectionId, search: values.search },
297
+ });
298
+ return;
299
+ }
300
+ if (tasks.length === 0) {
301
+ console.log(`No ${values.status} tasks found.`);
302
+ return;
303
+ }
304
+ console.log(`TASKS (Project: ${process.cwd()})`);
305
+ console.log('─'.repeat(80));
306
+ console.log('STATUS TITLE REJ ID');
307
+ console.log('─'.repeat(80));
308
+ for (const task of tasks) {
309
+ const marker = queries_js_1.STATUS_MARKERS[task.status];
310
+ const shortId = task.id.substring(0, 8);
311
+ const title = task.title.length > 52
312
+ ? task.title.substring(0, 49) + '...'
313
+ : task.title.padEnd(52);
314
+ const rej = task.rejection_count > 0
315
+ ? String(task.rejection_count).padStart(3)
316
+ : ' -';
317
+ console.log(`${marker} ${title} ${rej} ${shortId}`);
318
+ }
319
+ console.log('─'.repeat(80));
320
+ console.log(`Total: ${tasks.length} tasks`);
321
+ }
322
+ finally {
323
+ close();
324
+ }
325
+ }
326
+ async function showStats(args, globalFlags) {
327
+ const { values } = (0, node_util_1.parseArgs)({
328
+ args,
329
+ options: {
330
+ help: { type: 'boolean', short: 'h', default: false },
331
+ json: { type: 'boolean', short: 'j', default: false },
332
+ },
333
+ allowPositionals: false,
334
+ });
335
+ if (values.help) {
336
+ console.log(`
337
+ steroids tasks stats - Show task counts by status
338
+
339
+ USAGE:
340
+ steroids tasks stats [options]
341
+
342
+ OPTIONS:
343
+ -j, --json Output as JSON
344
+ -h, --help Show help
345
+
346
+ EXAMPLE:
347
+ steroids tasks stats
348
+ `);
349
+ return;
350
+ }
351
+ const outputJson = values.json || globalFlags?.json;
352
+ const { db, close } = (0, connection_js_1.openDatabase)();
353
+ try {
354
+ const statuses = [
355
+ 'pending',
356
+ 'in_progress',
357
+ 'review',
358
+ 'completed',
359
+ 'disputed',
360
+ 'failed',
361
+ 'skipped',
362
+ 'partial',
363
+ ];
364
+ const counts = {};
365
+ let total = 0;
366
+ for (const status of statuses) {
367
+ const tasks = (0, queries_js_1.listTasks)(db, { status });
368
+ counts[status] = tasks.length;
369
+ total += tasks.length;
370
+ }
371
+ if (outputJson) {
372
+ (0, output_js_1.outputJson)('tasks', 'stats', { counts, total });
373
+ return;
374
+ }
375
+ console.log('TASK STATS');
376
+ console.log('─'.repeat(30));
377
+ for (const status of statuses) {
378
+ const count = counts[status];
379
+ const marker = queries_js_1.STATUS_MARKERS[status];
380
+ console.log(` ${marker} ${status.padEnd(12)}: ${count}`);
381
+ }
382
+ console.log('─'.repeat(30));
383
+ console.log(` Total: ${total}`);
384
+ }
385
+ finally {
386
+ close();
387
+ }
388
+ }
389
+ async function showTask(args, flags) {
390
+ const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'show', flags });
391
+ const { values, positionals } = (0, node_util_1.parseArgs)({
392
+ args,
393
+ options: {
394
+ help: { type: 'boolean', short: 'h', default: false },
395
+ logs: { type: 'boolean', default: false },
396
+ 'logs-full': { type: 'boolean', default: false },
397
+ limit: { type: 'string', default: '5' },
398
+ },
399
+ allowPositionals: true,
400
+ });
401
+ if (values.help || flags.help || positionals.length === 0) {
402
+ out.log(`
403
+ steroids tasks show <id|title> - Show task details with invocation logs
404
+
405
+ USAGE:
406
+ steroids tasks show <id|title> [options]
407
+
408
+ OPTIONS:
409
+ --logs Show LLM invocation history (prompts/responses)
410
+ --logs-full Show full prompts and responses (verbose)
411
+ --limit <n> Limit number of invocations shown (default: 5)
412
+ -h, --help Show help
413
+
414
+ DESCRIPTION:
415
+ Shows detailed information about a task including:
416
+ - Task metadata (ID, title, status, section, spec file)
417
+ - Rejection count and history
418
+ - LLM invocation logs (with --logs flag)
419
+
420
+ EXAMPLES:
421
+ steroids tasks show abc123 # Basic task info
422
+ steroids tasks show abc123 --logs # Include LLM invocation summary
423
+ steroids tasks show abc123 --logs-full # Full prompts and responses
424
+ steroids tasks show abc123 --logs --limit 10 # Show last 10 invocations
425
+ `);
426
+ return;
427
+ }
428
+ const identifier = positionals[0];
429
+ const showLogs = values.logs || values['logs-full'];
430
+ const showFullLogs = values['logs-full'];
431
+ const limit = parseInt(values.limit, 10) || 5;
432
+ const { db, close } = (0, connection_js_1.openDatabase)();
433
+ try {
434
+ let task = (0, queries_js_1.getTask)(db, identifier);
435
+ if (!task) {
436
+ task = (0, queries_js_1.getTaskByTitle)(db, identifier);
437
+ }
438
+ if (!task) {
439
+ out.error(errors_js_1.ErrorCode.TASK_NOT_FOUND, `Task not found: ${identifier}`, { identifier });
440
+ process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.TASK_NOT_FOUND));
441
+ }
442
+ // Get section info
443
+ const section = task.section_id ? (0, queries_js_1.getSection)(db, task.section_id) : null;
444
+ // Get invocation counts
445
+ const invocationCounts = (0, queries_js_1.getInvocationCount)(db, task.id);
446
+ // Get invocations if requested
447
+ let invocations = [];
448
+ if (showLogs) {
449
+ invocations = (0, queries_js_1.getTaskInvocations)(db, task.id).slice(-limit);
450
+ }
451
+ // Get audit trail for rejection history
452
+ const auditEntries = (0, queries_js_1.getTaskAudit)(db, task.id);
453
+ if (flags.json) {
454
+ out.success({
455
+ task,
456
+ section: section ? { id: section.id, name: section.name } : null,
457
+ invocationCounts,
458
+ invocations: showLogs ? invocations : undefined,
459
+ auditTrail: auditEntries,
460
+ });
461
+ return;
462
+ }
463
+ // Display task details
464
+ console.log('─'.repeat(80));
465
+ console.log('TASK DETAILS');
466
+ console.log('─'.repeat(80));
467
+ console.log(`ID: ${task.id}`);
468
+ console.log(`Title: ${task.title}`);
469
+ console.log(`Status: ${queries_js_1.STATUS_MARKERS[task.status]} ${task.status}`);
470
+ console.log(`Section: ${section ? section.name : '(none)'}`);
471
+ console.log(`Spec File: ${task.source_file ?? '(not set)'}`);
472
+ if (task.file_path) {
473
+ const lineStr = task.file_line ? `:${task.file_line}` : '';
474
+ console.log(`File Anchor: ${task.file_path}${lineStr}`);
475
+ console.log(`File Commit: ${task.file_commit_sha?.substring(0, 7) ?? '(unknown)'}`);
476
+ console.log(`Content Hash: ${task.file_content_hash?.substring(0, 12) ?? '(unknown)'}`);
477
+ }
478
+ console.log(`Rejections: ${task.rejection_count}/15`);
479
+ console.log(`Created: ${task.created_at}`);
480
+ console.log(`Updated: ${task.updated_at}`);
481
+ console.log('');
482
+ console.log(`Invocations: ${invocationCounts.coder} coder, ${invocationCounts.reviewer} reviewer (${invocationCounts.total} total)`);
483
+ console.log('─'.repeat(80));
484
+ if (showLogs && invocations.length > 0) {
485
+ console.log('');
486
+ console.log('LLM INVOCATIONS (most recent)');
487
+ console.log('─'.repeat(80));
488
+ for (const inv of invocations) {
489
+ const ts = inv.created_at.substring(0, 19).replace('T', ' ');
490
+ const status = inv.success ? 'OK' : (inv.timed_out ? 'TIMEOUT' : 'FAIL');
491
+ const duration = (inv.duration_ms / 1000).toFixed(1) + 's';
492
+ const rejNum = inv.rejection_number ? ` (rejection #${inv.rejection_number})` : '';
493
+ console.log(`\n[${ts}] ${inv.role.toUpperCase()} - ${inv.provider}/${inv.model} - ${status} - ${duration}${rejNum}`);
494
+ if (showFullLogs) {
495
+ console.log('\n--- PROMPT ---');
496
+ console.log(inv.prompt.substring(0, 5000) + (inv.prompt.length > 5000 ? '\n\n[...truncated...]' : ''));
497
+ console.log('\n--- RESPONSE ---');
498
+ const response = inv.response || '(no response)';
499
+ console.log(response.substring(0, 3000) + (response.length > 3000 ? '\n\n[...truncated...]' : ''));
500
+ if (inv.error) {
501
+ console.log('\n--- ERROR ---');
502
+ console.log(inv.error.substring(0, 1000));
503
+ }
504
+ }
505
+ else {
506
+ // Summary view
507
+ const promptLines = inv.prompt.split('\n').length;
508
+ const responseLines = (inv.response || '').split('\n').length;
509
+ console.log(` Prompt: ${promptLines} lines, ${inv.prompt.length} chars`);
510
+ console.log(` Response: ${responseLines} lines, ${(inv.response || '').length} chars`);
511
+ if (inv.error) {
512
+ console.log(` Error: ${inv.error.substring(0, 100)}...`);
513
+ }
514
+ }
515
+ }
516
+ console.log('');
517
+ console.log('─'.repeat(80));
518
+ console.log(`Tip: Use --logs-full to see complete prompts and responses`);
519
+ }
520
+ else if (showLogs && invocations.length === 0) {
521
+ console.log('');
522
+ console.log('No invocations recorded for this task yet.');
523
+ console.log('Invocations are recorded when coder/reviewer processes run.');
524
+ }
525
+ if (!showLogs && invocationCounts.total > 0) {
526
+ console.log('');
527
+ console.log(`Tip: Use --logs to see invocation history, --logs-full for full prompts`);
528
+ }
529
+ }
530
+ finally {
531
+ close();
532
+ }
533
+ }
534
+ async function addTask(args, flags) {
535
+ const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'add', flags });
536
+ const { values, positionals } = (0, node_util_1.parseArgs)({
537
+ args,
538
+ options: {
539
+ help: { type: 'boolean', short: 'h', default: false },
540
+ section: { type: 'string' },
541
+ source: { type: 'string' },
542
+ file: { type: 'string' },
543
+ line: { type: 'string' },
544
+ feedback: { type: 'boolean', default: false },
545
+ },
546
+ allowPositionals: true,
547
+ });
548
+ if (values.help || flags.help) {
549
+ out.log(`
550
+ steroids tasks add <title> - Add a new task
551
+
552
+ USAGE:
553
+ steroids tasks add <title> --section <id> --source <file> [options]
554
+ steroids tasks add <title> --feedback [--file <path> --line <n>]
555
+
556
+ OPTIONS:
557
+ --section <id> Section ID (required unless --feedback)
558
+ --source <file> Specification file (required unless --feedback)
559
+ --feedback Add to "Needs User Input" section (skipped, for human review)
560
+ --file <path> Anchor task to a committed file
561
+ --line <number> Line number in the anchored file (requires --file)
562
+ -h, --help Show help
563
+
564
+ EXAMPLES:
565
+ steroids tasks add "Implement login" --section abc123 --source docs/login-spec.md
566
+ steroids tasks add "Fix null check" --section abc123 --source spec.md --file src/utils.ts --line 42
567
+ steroids tasks add "Pre-existing execSync in queries.ts needs review" --feedback
568
+ steroids tasks add "Should we use Redis or in-memory cache?" --feedback
569
+ `);
570
+ return;
571
+ }
572
+ if (positionals.length === 0) {
573
+ out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, 'task title required', {
574
+ usage: 'steroids tasks add <title> --section <id> --source <file>',
575
+ });
576
+ process.exit(2);
577
+ }
578
+ if (!values.feedback && !values.section) {
579
+ out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--section <id> is required (or use --feedback)', {
580
+ usage: 'steroids tasks add <title> --section <id> --source <file>',
581
+ hint: 'Every task must belong to a section. Use --feedback for advisory items.',
582
+ });
583
+ process.exit(2);
584
+ }
585
+ if (!values.feedback && !values.source) {
586
+ out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--source <file> is required (or use --feedback)', {
587
+ usage: 'steroids tasks add <title> --section <id> --source <file>',
588
+ hint: 'Every task must reference a specification file. Use --feedback for advisory items.',
589
+ });
590
+ process.exit(2);
591
+ }
592
+ // Validate --line requires --file
593
+ if (values.line && !values.file) {
594
+ out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--line requires --file', {
595
+ usage: 'steroids tasks add <title> --file <path> --line <number>',
596
+ hint: 'You cannot specify a line number without a file.',
597
+ });
598
+ process.exit(2);
599
+ }
600
+ // Parse and validate line number
601
+ let fileLine;
602
+ if (values.line) {
603
+ fileLine = parseInt(values.line, 10);
604
+ if (isNaN(fileLine) || fileLine < 1) {
605
+ out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--line must be a positive integer', {
606
+ provided: values.line,
607
+ });
608
+ process.exit(2);
609
+ }
610
+ }
611
+ // Resolve file anchor if --file is provided
612
+ let filePath;
613
+ let fileCommitSha;
614
+ let fileContentHash;
615
+ if (values.file) {
616
+ const fullPath = (0, node_path_1.resolve)(process.cwd(), values.file);
617
+ const normalizedPath = (0, node_path_1.relative)(process.cwd(), fullPath);
618
+ if (normalizedPath.startsWith('..')) {
619
+ out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, `File is outside the project directory: ${values.file}`, {
620
+ file: values.file,
621
+ hint: 'The file must be within the project directory.',
622
+ });
623
+ process.exit(2);
624
+ }
625
+ if (!(0, node_fs_1.existsSync)(fullPath)) {
626
+ out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, `File not found: ${normalizedPath}`, {
627
+ file: normalizedPath,
628
+ hint: 'The file must exist in the project directory.',
629
+ });
630
+ process.exit(2);
631
+ }
632
+ if (!(0, status_js_1.isFileTracked)(normalizedPath)) {
633
+ out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, `File is not tracked by git: ${normalizedPath}`, {
634
+ file: normalizedPath,
635
+ hint: 'The file must be committed to the repository. Run: git add && git commit',
636
+ });
637
+ process.exit(2);
638
+ }
639
+ if ((0, status_js_1.isFileDirty)(normalizedPath)) {
640
+ out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, `File has uncommitted changes: ${normalizedPath}`, {
641
+ file: normalizedPath,
642
+ hint: 'Commit your changes before anchoring a task to this file.',
643
+ });
644
+ process.exit(2);
645
+ }
646
+ filePath = normalizedPath;
647
+ fileCommitSha = (0, status_js_1.getFileLastCommit)(normalizedPath) ?? undefined;
648
+ fileContentHash = (0, status_js_1.getFileContentHash)(normalizedPath) ?? undefined;
649
+ }
650
+ const title = positionals.join(' ');
651
+ const { db, close } = (0, connection_js_1.openDatabase)();
652
+ try {
653
+ let section;
654
+ if (values.feedback) {
655
+ section = (0, queries_js_1.getOrCreateFeedbackSection)(db);
656
+ }
657
+ else {
658
+ section = (0, queries_js_1.getSection)(db, values.section);
659
+ if (!section) {
660
+ out.error(errors_js_1.ErrorCode.SECTION_NOT_FOUND, `Section not found: ${values.section}`, {
661
+ sectionId: values.section,
662
+ hint: 'Use "steroids sections list" to see available sections.',
663
+ });
664
+ process.exit(1);
665
+ }
666
+ }
667
+ const task = (0, queries_js_1.createTask)(db, title, {
668
+ sectionId: section.id,
669
+ sourceFile: values.source ?? undefined,
670
+ filePath,
671
+ fileLine,
672
+ fileCommitSha,
673
+ fileContentHash,
674
+ });
675
+ // Trigger task.created hooks
676
+ if (!(0, integration_js_1.shouldSkipHooks)(flags)) {
677
+ await (0, integration_js_1.triggerHooksSafely)(() => (0, integration_js_1.triggerTaskCreated)(task, { verbose: flags.verbose }), { verbose: flags.verbose });
678
+ }
679
+ out.success({ task, feedback: !!values.feedback });
680
+ if (!flags.json) {
681
+ out.log(`Task created: ${task.title}`);
682
+ out.log(` ID: ${task.id}`);
683
+ out.log(` Status: ${task.status}`);
684
+ if (values.feedback) {
685
+ out.log(` Section: Needs User Input (skipped - for human review)`);
686
+ }
687
+ if (task.source_file) {
688
+ out.log(` Source: ${task.source_file}`);
689
+ }
690
+ if (task.file_path) {
691
+ const lineStr = task.file_line ? `:${task.file_line}` : '';
692
+ out.log(` File: ${task.file_path}${lineStr}`);
693
+ out.log(` Commit: ${task.file_commit_sha?.substring(0, 7) ?? 'unknown'}`);
694
+ }
695
+ }
696
+ }
697
+ finally {
698
+ close();
699
+ }
700
+ }
701
+ async function updateTask(args, flags) {
702
+ const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'update', flags });
703
+ const { values, positionals } = (0, node_util_1.parseArgs)({
704
+ args,
705
+ options: {
706
+ help: { type: 'boolean', short: 'h', default: false },
707
+ status: { type: 'string' },
708
+ actor: { type: 'string', default: 'human:cli' },
709
+ model: { type: 'string' },
710
+ notes: { type: 'string' },
711
+ 'reset-rejections': { type: 'boolean', default: false },
712
+ },
713
+ allowPositionals: true,
714
+ });
715
+ if (values.help || flags.help || positionals.length === 0) {
716
+ out.log(`
717
+ steroids tasks update <title|id> - Update task status
718
+
719
+ USAGE:
720
+ steroids tasks update <title|id> [options]
721
+
722
+ OPTIONS:
723
+ --status <status> New status: pending | in_progress | review | completed
724
+ --reset-rejections Reset rejection count to 0 (keeps audit history)
725
+ --actor <actor> Actor making the change (default: human:cli)
726
+ --model <model> Model identifier (for LLM actors)
727
+ --notes <text> Notes for the reviewer (useful when submitting for review)
728
+ -h, --help Show help
729
+
730
+ EXAMPLES:
731
+ steroids tasks update abc123 --status review
732
+ steroids tasks update abc123 --status pending --reset-rejections
733
+ steroids tasks update abc123 --status review --notes "Found existing implementation at commit xyz"
734
+ `);
735
+ return;
736
+ }
737
+ if (!values.status && !values['reset-rejections']) {
738
+ if (flags.json) {
739
+ out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--status or --reset-rejections required');
740
+ }
741
+ else {
742
+ console.error('Error: --status or --reset-rejections required');
743
+ }
744
+ process.exit(1);
745
+ }
746
+ const identifier = positionals.join(' ');
747
+ const { db, close } = (0, connection_js_1.openDatabase)();
748
+ try {
749
+ // Try to find task by ID or title
750
+ let task = (0, queries_js_1.getTask)(db, identifier);
751
+ if (!task) {
752
+ task = (0, queries_js_1.getTaskByTitle)(db, identifier);
753
+ }
754
+ if (!task) {
755
+ if (flags.json) {
756
+ out.error(errors_js_1.ErrorCode.TASK_NOT_FOUND, `Task not found: ${identifier}`, { identifier });
757
+ }
758
+ else {
759
+ console.error(`Task not found: ${identifier}`);
760
+ }
761
+ process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.TASK_NOT_FOUND));
762
+ }
763
+ const actor = values.model
764
+ ? `model:${values.model}`
765
+ : values.actor ?? 'human:cli';
766
+ let oldRejectionCount;
767
+ // Reset rejections if requested
768
+ if (values['reset-rejections']) {
769
+ oldRejectionCount = (0, queries_js_1.resetRejectionCount)(db, task.id, actor, values.notes);
770
+ }
771
+ // Update status if provided
772
+ const previousStatus = task.status;
773
+ if (values.status) {
774
+ (0, queries_js_1.updateTaskStatus)(db, task.id, values.status, actor, values.notes);
775
+ }
776
+ const updated = (0, queries_js_1.getTask)(db, task.id);
777
+ // Trigger task.updated hooks
778
+ if (!(0, integration_js_1.shouldSkipHooks)(flags) && updated) {
779
+ await (0, integration_js_1.triggerHooksSafely)(() => (0, integration_js_1.triggerTaskUpdated)(updated, previousStatus, { verbose: flags.verbose }), { verbose: flags.verbose });
780
+ }
781
+ if (flags.json) {
782
+ out.success({ task: updated, rejectionReset: oldRejectionCount !== undefined, oldRejectionCount });
783
+ }
784
+ else {
785
+ console.log(`Task updated: ${task.title}`);
786
+ if (values.status) {
787
+ console.log(` Status: ${task.status} → ${values.status}`);
788
+ }
789
+ if (oldRejectionCount !== undefined) {
790
+ console.log(` Rejections: ${oldRejectionCount} → 0 (reset)`);
791
+ }
792
+ }
793
+ }
794
+ finally {
795
+ close();
796
+ }
797
+ }
798
+ /**
799
+ * Check if a section is completed after a task is marked complete
800
+ */
801
+ async function checkSectionCompletion(db, sectionId, flags) {
802
+ if (!sectionId || (0, integration_js_1.shouldSkipHooks)(flags)) {
803
+ return;
804
+ }
805
+ // Get section info
806
+ const section = (0, queries_js_1.getSection)(db, sectionId);
807
+ if (!section) {
808
+ return;
809
+ }
810
+ // Check if all tasks in section are completed
811
+ const sectionTasks = (0, queries_js_1.listTasks)(db, { sectionId });
812
+ const allCompleted = sectionTasks.every((t) => t.status === 'completed');
813
+ if (allCompleted && sectionTasks.length > 0) {
814
+ // Trigger section.completed hooks
815
+ await (0, integration_js_1.triggerHooksSafely)(() => (0, integration_js_1.triggerSectionCompleted)({
816
+ id: section.id,
817
+ name: section.name,
818
+ taskCount: sectionTasks.length,
819
+ }, sectionTasks.map((t) => ({ id: t.id, title: t.title })), { verbose: flags.verbose }), { verbose: flags.verbose });
820
+ }
821
+ }
822
+ /**
823
+ * Check if the entire project is completed after a task is marked complete
824
+ */
825
+ async function checkProjectCompletion(db, flags) {
826
+ if ((0, integration_js_1.shouldSkipHooks)(flags)) {
827
+ return;
828
+ }
829
+ // Get all tasks
830
+ const allTasks = (0, queries_js_1.listTasks)(db, { status: 'all' });
831
+ // Check if all tasks are completed
832
+ const allCompleted = allTasks.every((t) => t.status === 'completed');
833
+ if (allCompleted && allTasks.length > 0) {
834
+ // Get sections
835
+ const sections = (0, queries_js_1.listSections)(db);
836
+ // Get unique source files
837
+ const files = Array.from(new Set(allTasks.map((t) => t.source_file).filter(Boolean)));
838
+ // Trigger project.completed hooks
839
+ await (0, integration_js_1.triggerHooksSafely)(() => (0, integration_js_1.triggerProjectCompleted)({
840
+ totalTasks: allTasks.length,
841
+ files,
842
+ sectionCount: sections.length,
843
+ }, { verbose: flags.verbose }), { verbose: flags.verbose });
844
+ }
845
+ }
846
+ async function approveTaskCmd(args, flags) {
847
+ const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'approve', flags });
848
+ const { values, positionals } = (0, node_util_1.parseArgs)({
849
+ args,
850
+ options: {
851
+ help: { type: 'boolean', short: 'h', default: false },
852
+ model: { type: 'string' },
853
+ notes: { type: 'string' },
854
+ },
855
+ allowPositionals: true,
856
+ });
857
+ if (values.help || flags.help || positionals.length === 0) {
858
+ out.log(`
859
+ steroids tasks approve <id> - Approve a task
860
+
861
+ USAGE:
862
+ steroids tasks approve <id> [options]
863
+
864
+ OPTIONS:
865
+ --model <model> Model performing the review (required)
866
+ --notes <text> Approval notes
867
+ -h, --help Show help
868
+ `);
869
+ return;
870
+ }
871
+ if (!values.model) {
872
+ out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--model required');
873
+ process.exit(1);
874
+ }
875
+ const identifier = positionals[0];
876
+ const { db, close } = (0, connection_js_1.openDatabase)();
877
+ try {
878
+ let task = (0, queries_js_1.getTask)(db, identifier);
879
+ if (!task) {
880
+ task = (0, queries_js_1.getTaskByTitle)(db, identifier);
881
+ }
882
+ if (!task) {
883
+ out.error(errors_js_1.ErrorCode.TASK_NOT_FOUND, `Task not found: ${identifier}`, { identifier });
884
+ process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.TASK_NOT_FOUND));
885
+ }
886
+ (0, queries_js_1.approveTask)(db, task.id, values.model, values.notes);
887
+ const updated = (0, queries_js_1.getTask)(db, task.id);
888
+ // Trigger task.completed hooks
889
+ if (!(0, integration_js_1.shouldSkipHooks)(flags) && updated) {
890
+ await (0, integration_js_1.triggerHooksSafely)(() => (0, integration_js_1.triggerTaskCompleted)(updated, { verbose: flags.verbose }), { verbose: flags.verbose });
891
+ // Check if section is now complete
892
+ await checkSectionCompletion(db, updated.section_id, flags);
893
+ // Check if entire project is now complete
894
+ await checkProjectCompletion(db, flags);
895
+ }
896
+ out.success({ task: updated });
897
+ if (!flags.json) {
898
+ out.log(`Task approved: ${task.title}`);
899
+ out.log(` Status: completed`);
900
+ out.log(` Reviewer: ${values.model}`);
901
+ }
902
+ }
903
+ finally {
904
+ close();
905
+ }
906
+ }
907
+ async function rejectTaskCmd(args, flags) {
908
+ const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'reject', flags });
909
+ const { values, positionals } = (0, node_util_1.parseArgs)({
910
+ args,
911
+ options: {
912
+ help: { type: 'boolean', short: 'h', default: false },
913
+ model: { type: 'string' },
914
+ notes: { type: 'string' },
915
+ },
916
+ allowPositionals: true,
917
+ });
918
+ if (values.help || flags.help || positionals.length === 0) {
919
+ out.log(`
920
+ steroids tasks reject <id> - Reject a task
921
+
922
+ USAGE:
923
+ steroids tasks reject <id> [options]
924
+
925
+ OPTIONS:
926
+ --model <model> Model performing the review (required)
927
+ --notes <text> Rejection reason (important for coder)
928
+ -h, --help Show help
929
+ `);
930
+ return;
931
+ }
932
+ if (!values.model) {
933
+ out.error(errors_js_1.ErrorCode.INVALID_ARGUMENTS, '--model required');
934
+ process.exit(1);
935
+ }
936
+ const identifier = positionals[0];
937
+ const { db, close } = (0, connection_js_1.openDatabase)();
938
+ try {
939
+ let task = (0, queries_js_1.getTask)(db, identifier);
940
+ if (!task) {
941
+ task = (0, queries_js_1.getTaskByTitle)(db, identifier);
942
+ }
943
+ if (!task) {
944
+ out.error(errors_js_1.ErrorCode.TASK_NOT_FOUND, `Task not found: ${identifier}`, { identifier });
945
+ process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.TASK_NOT_FOUND));
946
+ }
947
+ const result = (0, queries_js_1.rejectTask)(db, task.id, values.model, values.notes);
948
+ // Trigger task.failed hooks if task failed
949
+ if (result.status === 'failed') {
950
+ const failedTask = (0, queries_js_1.getTask)(db, task.id);
951
+ if (!(0, integration_js_1.shouldSkipHooks)(flags) && failedTask) {
952
+ await (0, integration_js_1.triggerHooksSafely)(() => (0, integration_js_1.triggerTaskFailed)(failedTask, 15, { verbose: flags.verbose }), { verbose: flags.verbose });
953
+ }
954
+ }
955
+ out.success({ task, result });
956
+ if (!flags.json) {
957
+ if (result.status === 'failed') {
958
+ out.log(`Task FAILED: ${task.title}`);
959
+ out.log(` Exceeded 15 rejections. Requires human intervention.`);
960
+ }
961
+ else {
962
+ out.log(`Task rejected: ${task.title}`);
963
+ out.log(` Status: in_progress (rejection ${result.rejectionCount}/15)`);
964
+ out.log(` Reviewer: ${values.model}`);
965
+ if (values.notes) {
966
+ out.log(` Notes: ${values.notes}`);
967
+ }
968
+ }
969
+ }
970
+ }
971
+ finally {
972
+ close();
973
+ }
974
+ }
975
+ async function skipTaskCmd(args, flags) {
976
+ const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'skip', flags });
977
+ const { values, positionals } = (0, node_util_1.parseArgs)({
978
+ args,
979
+ options: {
980
+ help: { type: 'boolean', short: 'h', default: false },
981
+ model: { type: 'string' },
982
+ notes: { type: 'string' },
983
+ partial: { type: 'boolean', default: false },
984
+ },
985
+ allowPositionals: true,
986
+ });
987
+ if (values.help || flags.help || positionals.length === 0) {
988
+ out.log(`
989
+ steroids tasks skip <id> - Skip a task (external setup required)
990
+
991
+ USAGE:
992
+ steroids tasks skip <id> [options]
993
+
994
+ OPTIONS:
995
+ --model <model> Model identifying the skip (required for LLM actors)
996
+ --notes <text> Reason for skipping (e.g., "Cloud SQL - marked SKIP in spec")
997
+ --partial Mark as partial (coded what we could, rest is external)
998
+ -h, --help Show help
999
+
1000
+ DESCRIPTION:
1001
+ Use this when a task requires external/manual setup that cannot be automated:
1002
+ - Cloud infrastructure (Cloud SQL, GKE, etc.)
1003
+ - Manual account creation
1004
+ - License procurement
1005
+ - Hardware setup
1006
+
1007
+ The task spec should indicate "SKIP" or "MANUAL" for these tasks.
1008
+
1009
+ --partial: Use when you've implemented what you can, but the rest requires
1010
+ external action (e.g., created deployment YAML but can't provision).
1011
+
1012
+ EXAMPLES:
1013
+ steroids tasks skip abc123 --notes "Cloud SQL - spec says SKIP"
1014
+ steroids tasks skip abc123 --partial --notes "Created deployment, needs manual GKE setup"
1015
+ `);
1016
+ return;
1017
+ }
1018
+ const identifier = positionals[0];
1019
+ const newStatus = values.partial ? 'partial' : 'skipped';
1020
+ const { db, close } = (0, connection_js_1.openDatabase)();
1021
+ try {
1022
+ let task = (0, queries_js_1.getTask)(db, identifier);
1023
+ if (!task) {
1024
+ task = (0, queries_js_1.getTaskByTitle)(db, identifier);
1025
+ }
1026
+ if (!task) {
1027
+ out.error(errors_js_1.ErrorCode.TASK_NOT_FOUND, `Task not found: ${identifier}`, { identifier });
1028
+ process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.TASK_NOT_FOUND));
1029
+ }
1030
+ const actor = values.model
1031
+ ? `model:${values.model}`
1032
+ : 'human:cli';
1033
+ // Get section name before updating
1034
+ const section = task.section_id ? (0, queries_js_1.getSection)(db, task.section_id) : null;
1035
+ const sectionName = section?.name ?? null;
1036
+ const taskTitle = task.title;
1037
+ (0, queries_js_1.updateTaskStatus)(db, task.id, newStatus, actor, values.notes);
1038
+ // Refresh task to get updated status
1039
+ task = (0, queries_js_1.getTask)(db, task.id);
1040
+ // Log activity for skipped/partial task
1041
+ const projectPath = (0, node_path_1.resolve)(process.cwd());
1042
+ (0, activity_log_js_1.logActivity)(projectPath, 'cli', // CLI operations use 'cli' as runner ID
1043
+ task.id, taskTitle, sectionName, values.partial ? 'partial' : 'skipped');
1044
+ out.success({ task, skipped: true, partial: values.partial });
1045
+ if (!flags.json) {
1046
+ const statusLabel = values.partial ? 'PARTIAL' : 'SKIPPED';
1047
+ out.log(`Task ${statusLabel}: ${task.title}`);
1048
+ out.log(` Status: ${newStatus}`);
1049
+ out.log(` Actor: ${actor}`);
1050
+ if (values.notes) {
1051
+ out.log(` Notes: ${values.notes}`);
1052
+ }
1053
+ out.log('');
1054
+ out.log(' Task will not block the runner. Move on to the next task.');
1055
+ }
1056
+ }
1057
+ finally {
1058
+ close();
1059
+ }
1060
+ }
1061
+ async function auditTask(args, flags) {
1062
+ const out = (0, output_js_1.createOutput)({ command: 'tasks', subcommand: 'audit', flags });
1063
+ const { values, positionals } = (0, node_util_1.parseArgs)({
1064
+ args,
1065
+ options: {
1066
+ help: { type: 'boolean', short: 'h', default: false },
1067
+ },
1068
+ allowPositionals: true,
1069
+ });
1070
+ if (values.help || flags.help || positionals.length === 0) {
1071
+ out.log(`
1072
+ steroids tasks audit <id> - View task audit trail
1073
+
1074
+ USAGE:
1075
+ steroids tasks audit <id> [options]
1076
+
1077
+ OPTIONS:
1078
+ -h, --help Show help
1079
+ `);
1080
+ return;
1081
+ }
1082
+ const identifier = positionals[0];
1083
+ const { db, close } = (0, connection_js_1.openDatabase)();
1084
+ try {
1085
+ let task = (0, queries_js_1.getTask)(db, identifier);
1086
+ if (!task) {
1087
+ task = (0, queries_js_1.getTaskByTitle)(db, identifier);
1088
+ }
1089
+ if (!task) {
1090
+ out.error(errors_js_1.ErrorCode.TASK_NOT_FOUND, `Task not found: ${identifier}`, { identifier });
1091
+ process.exit((0, errors_js_1.getExitCode)(errors_js_1.ErrorCode.TASK_NOT_FOUND));
1092
+ }
1093
+ const entries = (0, queries_js_1.getTaskAudit)(db, task.id);
1094
+ out.success({ task: { id: task.id, title: task.title }, auditTrail: entries });
1095
+ if (!flags.json) {
1096
+ out.log(`Audit trail for: ${task.title}`);
1097
+ out.log(`Task ID: ${task.id}`);
1098
+ out.log('─'.repeat(80));
1099
+ out.log('TIMESTAMP FROM TO ACTOR NOTES');
1100
+ out.log('─'.repeat(80));
1101
+ for (const entry of entries) {
1102
+ const ts = entry.created_at.substring(0, 19).replace('T', ' ');
1103
+ const from = (entry.from_status ?? '-').padEnd(12);
1104
+ const to = entry.to_status.padEnd(12);
1105
+ const actor = entry.actor.padEnd(18);
1106
+ const notes = entry.notes ?? '-';
1107
+ out.log(`${ts} ${from} ${to} ${actor} ${notes}`);
1108
+ }
1109
+ }
1110
+ }
1111
+ finally {
1112
+ close();
1113
+ }
1114
+ }
1115
+ //# sourceMappingURL=tasks.js.map