@proletariat/cli 0.3.21 → 0.3.23

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 (335) hide show
  1. package/LICENSE +190 -21
  2. package/README.md +7 -7
  3. package/dist/commands/action/create.d.ts +0 -1
  4. package/dist/commands/action/delete.d.ts +0 -1
  5. package/dist/commands/action/index.d.ts +0 -1
  6. package/dist/commands/action/list.d.ts +0 -1
  7. package/dist/commands/action/list.js +2 -0
  8. package/dist/commands/action/run.d.ts +0 -1
  9. package/dist/commands/action/show.d.ts +0 -1
  10. package/dist/commands/action/update.d.ts +0 -1
  11. package/dist/commands/agent/auth.d.ts +0 -1
  12. package/dist/commands/agent/auth.js +3 -7
  13. package/dist/commands/agent/discover.d.ts +0 -1
  14. package/dist/commands/agent/discover.js +3 -7
  15. package/dist/commands/agent/index.d.ts +0 -1
  16. package/dist/commands/agent/index.js +2 -0
  17. package/dist/commands/agent/list.d.ts +0 -1
  18. package/dist/commands/agent/list.js +30 -1
  19. package/dist/commands/agent/login.d.ts +0 -1
  20. package/dist/commands/agent/login.js +2 -0
  21. package/dist/commands/agent/rebuild.d.ts +0 -1
  22. package/dist/commands/agent/rebuild.js +2 -0
  23. package/dist/commands/agent/remove.d.ts +0 -1
  24. package/dist/commands/agent/remove.js +2 -0
  25. package/dist/commands/agent/restart.d.ts +0 -1
  26. package/dist/commands/agent/restart.js +2 -0
  27. package/dist/commands/agent/shell.d.ts +0 -1
  28. package/dist/commands/agent/shell.js +2 -0
  29. package/dist/commands/agent/staff/add.d.ts +0 -1
  30. package/dist/commands/agent/staff/add.js +3 -7
  31. package/dist/commands/agent/staff/index.d.ts +0 -1
  32. package/dist/commands/agent/staff/index.js +2 -0
  33. package/dist/commands/agent/staff/remove.d.ts +0 -1
  34. package/dist/commands/agent/staff/remove.js +2 -0
  35. package/dist/commands/agent/status.d.ts +0 -1
  36. package/dist/commands/agent/status.js +2 -0
  37. package/dist/commands/agent/temp/cleanup.d.ts +0 -1
  38. package/dist/commands/agent/temp/cleanup.js +2 -0
  39. package/dist/commands/agent/temp/index.d.ts +0 -1
  40. package/dist/commands/agent/temp/index.js +2 -0
  41. package/dist/commands/agent/themes/index.d.ts +0 -1
  42. package/dist/commands/agent/themes/index.js +3 -7
  43. package/dist/commands/agent/themes/set.d.ts +0 -1
  44. package/dist/commands/agent/themes/set.js +3 -7
  45. package/dist/commands/agent/visit.d.ts +0 -1
  46. package/dist/commands/agent/visit.js +2 -0
  47. package/dist/commands/autocomplete/setup.d.ts +0 -1
  48. package/dist/commands/board/index.d.ts +0 -1
  49. package/dist/commands/board/view.d.ts +0 -1
  50. package/dist/commands/board/watch.d.ts +0 -1
  51. package/dist/commands/branch/create.d.ts +0 -1
  52. package/dist/commands/branch/create.js +2 -0
  53. package/dist/commands/branch/index.d.ts +0 -1
  54. package/dist/commands/branch/index.js +2 -0
  55. package/dist/commands/branch/list.d.ts +0 -1
  56. package/dist/commands/branch/validate.d.ts +0 -1
  57. package/dist/commands/branch/where.d.ts +0 -1
  58. package/dist/commands/branch/where.js +2 -0
  59. package/dist/commands/category/create.d.ts +18 -0
  60. package/dist/commands/category/create.js +108 -0
  61. package/dist/commands/category/delete.d.ts +17 -0
  62. package/dist/commands/category/delete.js +103 -0
  63. package/dist/commands/category/index.d.ts +15 -0
  64. package/dist/commands/category/index.js +87 -0
  65. package/dist/commands/category/list.d.ts +17 -0
  66. package/dist/commands/category/list.js +85 -0
  67. package/dist/commands/category/rename.d.ts +18 -0
  68. package/dist/commands/category/rename.js +127 -0
  69. package/dist/commands/claude.js +2 -0
  70. package/dist/commands/commit.js +2 -0
  71. package/dist/commands/config/index.js +2 -0
  72. package/dist/commands/docker/clean.d.ts +0 -1
  73. package/dist/commands/docker/index.d.ts +0 -1
  74. package/dist/commands/docker/prune.d.ts +0 -1
  75. package/dist/commands/docker/restart.d.ts +0 -1
  76. package/dist/commands/docker/stop.d.ts +0 -1
  77. package/dist/commands/epic/activate.d.ts +0 -1
  78. package/dist/commands/epic/activate.js +2 -0
  79. package/dist/commands/epic/archive.d.ts +0 -1
  80. package/dist/commands/epic/archive.js +2 -0
  81. package/dist/commands/epic/create.d.ts +0 -1
  82. package/dist/commands/epic/create.js +3 -1
  83. package/dist/commands/epic/delete.d.ts +14 -0
  84. package/dist/commands/epic/delete.js +129 -0
  85. package/dist/commands/epic/index.d.ts +0 -1
  86. package/dist/commands/epic/index.js +6 -0
  87. package/dist/commands/epic/link/block.d.ts +0 -1
  88. package/dist/commands/epic/link/block.js +2 -0
  89. package/dist/commands/epic/link/duplicates.d.ts +0 -1
  90. package/dist/commands/epic/link/duplicates.js +2 -0
  91. package/dist/commands/epic/link/index.d.ts +0 -1
  92. package/dist/commands/epic/link/index.js +2 -0
  93. package/dist/commands/epic/link/relates.d.ts +0 -1
  94. package/dist/commands/epic/link/relates.js +2 -0
  95. package/dist/commands/epic/link/remove.d.ts +0 -1
  96. package/dist/commands/epic/link/remove.js +2 -0
  97. package/dist/commands/epic/list.d.ts +0 -1
  98. package/dist/commands/epic/move.d.ts +0 -1
  99. package/dist/commands/epic/move.js +2 -0
  100. package/dist/commands/epic/progress.d.ts +0 -1
  101. package/dist/commands/epic/progress.js +2 -0
  102. package/dist/commands/epic/project.d.ts +0 -1
  103. package/dist/commands/epic/project.js +2 -0
  104. package/dist/commands/epic/reorder.d.ts +0 -1
  105. package/dist/commands/epic/reorder.js +2 -0
  106. package/dist/commands/epic/spec.d.ts +0 -1
  107. package/dist/commands/epic/spec.js +2 -0
  108. package/dist/commands/epic/ticket.d.ts +0 -1
  109. package/dist/commands/epic/ticket.js +2 -0
  110. package/dist/commands/epic/view.d.ts +0 -1
  111. package/dist/commands/epic/view.js +2 -0
  112. package/dist/commands/execution/config.d.ts +0 -1
  113. package/dist/commands/execution/config.js +2 -0
  114. package/dist/commands/execution/index.d.ts +0 -1
  115. package/dist/commands/execution/index.js +3 -1
  116. package/dist/commands/execution/list.d.ts +0 -1
  117. package/dist/commands/execution/logs.d.ts +0 -1
  118. package/dist/commands/execution/logs.js +3 -1
  119. package/dist/commands/execution/stop.d.ts +0 -1
  120. package/dist/commands/execution/stop.js +3 -1
  121. package/dist/commands/execution/view.d.ts +0 -1
  122. package/dist/commands/execution/view.js +3 -1
  123. package/dist/commands/gh/index.d.ts +0 -1
  124. package/dist/commands/gh/login.d.ts +0 -1
  125. package/dist/commands/gh/status.d.ts +0 -1
  126. package/dist/commands/gh/token.d.ts +0 -1
  127. package/dist/commands/init.js +2 -0
  128. package/dist/commands/phase/create.d.ts +0 -1
  129. package/dist/commands/phase/create.js +1 -2
  130. package/dist/commands/phase/delete.d.ts +0 -1
  131. package/dist/commands/phase/delete.js +1 -1
  132. package/dist/commands/phase/list.d.ts +0 -1
  133. package/dist/commands/phase/move.d.ts +0 -1
  134. package/dist/commands/phase/move.js +2 -2
  135. package/dist/commands/phase/template/apply.d.ts +0 -1
  136. package/dist/commands/phase/template/apply.js +2 -0
  137. package/dist/commands/phase/template/create.d.ts +0 -1
  138. package/dist/commands/phase/template/create.js +0 -1
  139. package/dist/commands/phase/template/delete.d.ts +0 -1
  140. package/dist/commands/phase/template/delete.js +2 -0
  141. package/dist/commands/phase/template/index.d.ts +0 -1
  142. package/dist/commands/phase/template/index.js +2 -0
  143. package/dist/commands/phase/template/list.d.ts +0 -1
  144. package/dist/commands/phase/template/list.js +2 -0
  145. package/dist/commands/phase/template/update.d.ts +0 -1
  146. package/dist/commands/phase/update.d.ts +0 -1
  147. package/dist/commands/phase/update.js +2 -2
  148. package/dist/commands/pmo/init.js +2 -0
  149. package/dist/commands/pr/create.d.ts +0 -1
  150. package/dist/commands/pr/index.d.ts +0 -1
  151. package/dist/commands/pr/link.d.ts +0 -1
  152. package/dist/commands/pr/list.d.ts +0 -1
  153. package/dist/commands/pr/status.d.ts +0 -1
  154. package/dist/commands/project/archive.d.ts +0 -1
  155. package/dist/commands/project/create.d.ts +0 -1
  156. package/dist/commands/project/delete.d.ts +0 -1
  157. package/dist/commands/project/index.d.ts +0 -1
  158. package/dist/commands/project/list.d.ts +0 -1
  159. package/dist/commands/project/spec.d.ts +0 -1
  160. package/dist/commands/project/unarchive.d.ts +0 -1
  161. package/dist/commands/project/update.d.ts +0 -1
  162. package/dist/commands/project/view.d.ts +0 -1
  163. package/dist/commands/repo/add.d.ts +0 -1
  164. package/dist/commands/repo/add.js +2 -0
  165. package/dist/commands/repo/index.d.ts +0 -1
  166. package/dist/commands/repo/list.d.ts +0 -1
  167. package/dist/commands/repo/remove.d.ts +0 -1
  168. package/dist/commands/repo/view.d.ts +0 -1
  169. package/dist/commands/roadmap/add-project.d.ts +0 -1
  170. package/dist/commands/roadmap/add-project.js +2 -0
  171. package/dist/commands/roadmap/create.d.ts +0 -1
  172. package/dist/commands/roadmap/create.js +2 -0
  173. package/dist/commands/roadmap/delete.d.ts +0 -1
  174. package/dist/commands/roadmap/delete.js +12 -1
  175. package/dist/commands/roadmap/generate.d.ts +0 -1
  176. package/dist/commands/roadmap/generate.js +2 -0
  177. package/dist/commands/roadmap/index.d.ts +0 -1
  178. package/dist/commands/roadmap/index.js +2 -0
  179. package/dist/commands/roadmap/list.d.ts +0 -1
  180. package/dist/commands/roadmap/remove-project.d.ts +0 -1
  181. package/dist/commands/roadmap/remove-project.js +2 -0
  182. package/dist/commands/roadmap/reorder.d.ts +0 -1
  183. package/dist/commands/roadmap/reorder.js +2 -0
  184. package/dist/commands/roadmap/update.d.ts +0 -1
  185. package/dist/commands/roadmap/update.js +2 -0
  186. package/dist/commands/roadmap/view.d.ts +0 -1
  187. package/dist/commands/roadmap/view.js +2 -0
  188. package/dist/commands/session/attach.d.ts +0 -1
  189. package/dist/commands/session/attach.js +9 -0
  190. package/dist/commands/session/index.d.ts +0 -1
  191. package/dist/commands/session/index.js +2 -0
  192. package/dist/commands/session/list.d.ts +0 -1
  193. package/dist/commands/spec/create.d.ts +0 -1
  194. package/dist/commands/spec/create.js +1 -1
  195. package/dist/commands/spec/delete.d.ts +0 -1
  196. package/dist/commands/spec/edit.d.ts +0 -1
  197. package/dist/commands/spec/index.d.ts +0 -1
  198. package/dist/commands/spec/link/depends.d.ts +0 -1
  199. package/dist/commands/spec/link/duplicates.d.ts +0 -1
  200. package/dist/commands/spec/link/index.d.ts +0 -1
  201. package/dist/commands/spec/link/relates.d.ts +0 -1
  202. package/dist/commands/spec/link/remove.d.ts +0 -1
  203. package/dist/commands/spec/list.d.ts +0 -1
  204. package/dist/commands/spec/plan.d.ts +0 -1
  205. package/dist/commands/spec/ticket.d.ts +0 -3
  206. package/dist/commands/spec/ticket.js +7 -38
  207. package/dist/commands/spec/view.d.ts +0 -1
  208. package/dist/commands/status/category.d.ts +14 -0
  209. package/dist/commands/status/category.js +63 -0
  210. package/dist/commands/status/create.d.ts +0 -1
  211. package/dist/commands/status/create.js +1 -1
  212. package/dist/commands/status/delete.d.ts +0 -1
  213. package/dist/commands/status/index.d.ts +0 -1
  214. package/dist/commands/status/list.d.ts +0 -1
  215. package/dist/commands/status/move.d.ts +0 -1
  216. package/dist/commands/status/update.d.ts +0 -1
  217. package/dist/commands/template/delete.d.ts +0 -1
  218. package/dist/commands/template/delete.js +2 -0
  219. package/dist/commands/template/index.d.ts +0 -1
  220. package/dist/commands/template/list.d.ts +0 -1
  221. package/dist/commands/template/list.js +2 -0
  222. package/dist/commands/template/phase/apply.js +2 -0
  223. package/dist/commands/template/phase/create.d.ts +0 -1
  224. package/dist/commands/template/phase/create.js +3 -9
  225. package/dist/commands/template/phase/delete.js +2 -0
  226. package/dist/commands/template/phase/index.d.ts +0 -1
  227. package/dist/commands/template/phase/list.js +2 -0
  228. package/dist/commands/template/phase/update.js +2 -0
  229. package/dist/commands/template/ticket/apply.js +2 -0
  230. package/dist/commands/template/ticket/create.js +2 -0
  231. package/dist/commands/template/ticket/delete.js +2 -0
  232. package/dist/commands/template/ticket/index.d.ts +0 -1
  233. package/dist/commands/template/ticket/list.js +2 -0
  234. package/dist/commands/template/ticket/save.d.ts +0 -1
  235. package/dist/commands/template/ticket/save.js +0 -6
  236. package/dist/commands/terminal/title.d.ts +0 -1
  237. package/dist/commands/ticket/bulk.d.ts +0 -1
  238. package/dist/commands/ticket/bulk.js +2 -0
  239. package/dist/commands/ticket/category.d.ts +14 -0
  240. package/dist/commands/ticket/category.js +63 -0
  241. package/dist/commands/ticket/complete.d.ts +0 -1
  242. package/dist/commands/ticket/complete.js +2 -0
  243. package/dist/commands/ticket/create.d.ts +0 -1
  244. package/dist/commands/ticket/create.js +6 -4
  245. package/dist/commands/ticket/delete.d.ts +0 -1
  246. package/dist/commands/ticket/delete.js +2 -0
  247. package/dist/commands/ticket/edit.d.ts +0 -1
  248. package/dist/commands/ticket/edit.js +4 -2
  249. package/dist/commands/ticket/epic.d.ts +0 -1
  250. package/dist/commands/ticket/epic.js +2 -0
  251. package/dist/commands/ticket/index.d.ts +0 -1
  252. package/dist/commands/ticket/index.js +2 -0
  253. package/dist/commands/ticket/link/block.d.ts +0 -1
  254. package/dist/commands/ticket/link/block.js +2 -0
  255. package/dist/commands/ticket/link/duplicates.d.ts +0 -1
  256. package/dist/commands/ticket/link/duplicates.js +2 -0
  257. package/dist/commands/ticket/link/index.d.ts +0 -1
  258. package/dist/commands/ticket/link/index.js +2 -0
  259. package/dist/commands/ticket/link/relates.d.ts +0 -1
  260. package/dist/commands/ticket/link/relates.js +2 -0
  261. package/dist/commands/ticket/link/remove.d.ts +0 -1
  262. package/dist/commands/ticket/link/remove.js +2 -0
  263. package/dist/commands/ticket/list.d.ts +0 -1
  264. package/dist/commands/ticket/move.d.ts +0 -1
  265. package/dist/commands/ticket/move.js +2 -0
  266. package/dist/commands/ticket/project.d.ts +0 -1
  267. package/dist/commands/ticket/project.js +2 -0
  268. package/dist/commands/ticket/reassign.d.ts +0 -1
  269. package/dist/commands/ticket/reassign.js +29 -0
  270. package/dist/commands/ticket/spec.d.ts +0 -1
  271. package/dist/commands/ticket/spec.js +2 -0
  272. package/dist/commands/ticket/status.d.ts +0 -1
  273. package/dist/commands/ticket/status.js +2 -0
  274. package/dist/commands/ticket/template/apply.d.ts +0 -1
  275. package/dist/commands/ticket/template/apply.js +2 -0
  276. package/dist/commands/ticket/template/create.d.ts +0 -1
  277. package/dist/commands/ticket/template/create.js +4 -2
  278. package/dist/commands/ticket/template/delete.d.ts +0 -1
  279. package/dist/commands/ticket/template/delete.js +2 -0
  280. package/dist/commands/ticket/template/index.d.ts +0 -1
  281. package/dist/commands/ticket/template/index.js +2 -0
  282. package/dist/commands/ticket/template/list.d.ts +0 -1
  283. package/dist/commands/ticket/template/list.js +2 -0
  284. package/dist/commands/ticket/template/save.d.ts +0 -1
  285. package/dist/commands/ticket/template/save.js +2 -0
  286. package/dist/commands/ticket/update.d.ts +0 -1
  287. package/dist/commands/ticket/update.js +2 -0
  288. package/dist/commands/ticket/view.d.ts +0 -1
  289. package/dist/commands/ticket/view.js +2 -0
  290. package/dist/commands/work/complete.d.ts +0 -1
  291. package/dist/commands/work/complete.js +2 -0
  292. package/dist/commands/work/index.d.ts +0 -1
  293. package/dist/commands/work/index.js +2 -0
  294. package/dist/commands/work/ready.d.ts +1 -2
  295. package/dist/commands/work/ready.js +11 -5
  296. package/dist/commands/work/revise.d.ts +0 -1
  297. package/dist/commands/work/revise.js +3 -1
  298. package/dist/commands/work/spawn-all.d.ts +0 -1
  299. package/dist/commands/work/spawn-all.js +2 -0
  300. package/dist/commands/work/spawn.d.ts +0 -1
  301. package/dist/commands/work/spawn.js +2 -0
  302. package/dist/commands/work/start.d.ts +0 -1
  303. package/dist/commands/work/start.js +6 -0
  304. package/dist/commands/work/watch.d.ts +0 -1
  305. package/dist/commands/work/watch.js +3 -1
  306. package/dist/commands/workflow/create.d.ts +0 -1
  307. package/dist/commands/workflow/delete.d.ts +0 -1
  308. package/dist/commands/workflow/index.d.ts +0 -1
  309. package/dist/commands/workflow/index.js +2 -0
  310. package/dist/commands/workflow/list.d.ts +0 -1
  311. package/dist/commands/workflow/switch.d.ts +0 -1
  312. package/dist/commands/workflow/view.d.ts +0 -1
  313. package/dist/commands/workspace/list.js +2 -0
  314. package/dist/commands/workspace/prune.d.ts +13 -0
  315. package/dist/commands/workspace/prune.js +186 -0
  316. package/dist/commands/workspace/remove.js +2 -0
  317. package/dist/commands/workspace/use.js +2 -0
  318. package/dist/lib/pmo/base-command.d.ts +2 -4
  319. package/dist/lib/pmo/base-command.js +8 -10
  320. package/dist/lib/pmo/schema.d.ts +2 -0
  321. package/dist/lib/pmo/schema.js +17 -0
  322. package/dist/lib/pmo/storage/base.d.ts +4 -0
  323. package/dist/lib/pmo/storage/base.js +31 -0
  324. package/dist/lib/pmo/storage/categories.d.ts +50 -0
  325. package/dist/lib/pmo/storage/categories.js +205 -0
  326. package/dist/lib/pmo/storage/index.d.ts +14 -1
  327. package/dist/lib/pmo/storage/index.js +35 -1
  328. package/dist/lib/pmo/storage/tickets.d.ts +5 -0
  329. package/dist/lib/pmo/storage/tickets.js +31 -3
  330. package/dist/lib/pmo/storage/types.d.ts +10 -0
  331. package/dist/lib/pmo/types.d.ts +25 -0
  332. package/dist/lib/prompt-json.d.ts +10 -16
  333. package/dist/lib/prompt-json.js +8 -16
  334. package/oclif.manifest.json +4037 -4099
  335. package/package.json +5 -4
@@ -3,6 +3,7 @@
3
3
  * This module handles database setup and provides shared utilities.
4
4
  */
5
5
  import { PMO_TABLES, PMO_SCHEMA_SQL, validateTicketSchema } from '../schema.js';
6
+ import { TICKET_CATEGORIES, STATE_CATEGORY_ORDER } from '../types.js';
6
7
  import { BUILTIN_TEMPLATES } from '../templates-builtin.js';
7
8
  const T = PMO_TABLES;
8
9
  /**
@@ -802,6 +803,36 @@ Why is this refactor needed?
802
803
  insertTemplate.run(template.id, template.name, template.description || null, template.titlePattern || null, template.descriptionTemplate || null, template.defaultPriority || null, template.defaultCategory || null, null, null, null, '[]', JSON.stringify(template.suggestedSubtasks || []), now);
803
804
  }
804
805
  }
806
+ /**
807
+ * Seed built-in categories from TICKET_CATEGORIES and STATE_CATEGORY_ORDER.
808
+ */
809
+ export function seedBuiltinCategories(db) {
810
+ const insertCategory = db.prepare(`
811
+ INSERT OR IGNORE INTO ${T.categories} (id, name, type, description, position, is_builtin, created_at)
812
+ VALUES (?, ?, ?, ?, ?, 1, ?)
813
+ `);
814
+ const now = new Date().toISOString();
815
+ // Seed ticket categories from TICKET_CATEGORIES
816
+ for (let i = 0; i < TICKET_CATEGORIES.length; i++) {
817
+ const category = TICKET_CATEGORIES[i];
818
+ const id = `ticket-${category}`;
819
+ insertCategory.run(id, category, 'ticket', null, i, now);
820
+ }
821
+ // Seed status categories from STATE_CATEGORY_ORDER
822
+ const statusCategoryDescriptions = {
823
+ triage: 'Inbox - needs review before entering workflow',
824
+ backlog: 'Not yet scheduled for work',
825
+ unstarted: 'Scheduled but work has not begun',
826
+ started: 'Work is actively in progress',
827
+ completed: 'Work finished successfully',
828
+ canceled: 'Work will not be done',
829
+ };
830
+ for (let i = 0; i < STATE_CATEGORY_ORDER.length; i++) {
831
+ const category = STATE_CATEGORY_ORDER[i];
832
+ const id = `status-${category}`;
833
+ insertCategory.run(id, category, 'status', statusCategoryDescriptions[category] || null, i, now);
834
+ }
835
+ }
805
836
  /**
806
837
  * Update board timestamp for a project.
807
838
  */
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Category operations.
3
+ * Manages ticket and status categories.
4
+ */
5
+ import { Category, CategoryFilter, CategoryType } from '../types.js';
6
+ import { StorageContext } from './types.js';
7
+ export declare class CategoryStorage {
8
+ private ctx;
9
+ constructor(ctx: StorageContext);
10
+ /**
11
+ * List categories.
12
+ */
13
+ listCategories(filter?: CategoryFilter): Promise<Category[]>;
14
+ /**
15
+ * Get a category by ID.
16
+ */
17
+ getCategory(id: string): Promise<Category | null>;
18
+ /**
19
+ * Get a category by name and type.
20
+ */
21
+ getCategoryByName(name: string, type: CategoryType): Promise<Category | null>;
22
+ /**
23
+ * Create a new category.
24
+ */
25
+ createCategory(category: Partial<Category> & {
26
+ name: string;
27
+ type: CategoryType;
28
+ }): Promise<Category>;
29
+ /**
30
+ * Update a category.
31
+ */
32
+ updateCategory(id: string, changes: Partial<Category>): Promise<Category>;
33
+ /**
34
+ * Rename a category.
35
+ */
36
+ renameCategory(id: string, newName: string): Promise<Category>;
37
+ /**
38
+ * Delete a category.
39
+ */
40
+ deleteCategory(id: string): Promise<void>;
41
+ /**
42
+ * Get category names for a type (for validation and autocomplete).
43
+ */
44
+ getCategoryNames(type: CategoryType): Promise<string[]>;
45
+ /**
46
+ * Check if a category name is valid for a type.
47
+ */
48
+ isValidCategory(name: string, type: CategoryType): Promise<boolean>;
49
+ private rowToCategory;
50
+ }
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Category operations.
3
+ * Manages ticket and status categories.
4
+ */
5
+ import { PMO_TABLES } from '../schema.js';
6
+ import { PMOError } from '../types.js';
7
+ import { slugify } from '../utils.js';
8
+ const T = PMO_TABLES;
9
+ export class CategoryStorage {
10
+ ctx;
11
+ constructor(ctx) {
12
+ this.ctx = ctx;
13
+ }
14
+ /**
15
+ * List categories.
16
+ */
17
+ async listCategories(filter) {
18
+ let sql = `SELECT * FROM ${T.categories}`;
19
+ const conditions = [];
20
+ const params = [];
21
+ if (filter?.type) {
22
+ conditions.push('type = ?');
23
+ params.push(filter.type);
24
+ }
25
+ if (filter?.isBuiltin !== undefined) {
26
+ conditions.push('is_builtin = ?');
27
+ params.push(filter.isBuiltin ? 1 : 0);
28
+ }
29
+ if (filter?.search) {
30
+ conditions.push('(name LIKE ? OR description LIKE ?)');
31
+ params.push(`%${filter.search}%`, `%${filter.search}%`);
32
+ }
33
+ if (conditions.length > 0) {
34
+ sql += ` WHERE ${conditions.join(' AND ')}`;
35
+ }
36
+ sql += ' ORDER BY type, position ASC, name ASC';
37
+ const rows = this.ctx.db.prepare(sql).all(...params);
38
+ return rows.map((row) => this.rowToCategory(row));
39
+ }
40
+ /**
41
+ * Get a category by ID.
42
+ */
43
+ async getCategory(id) {
44
+ const row = this.ctx.db.prepare(`SELECT * FROM ${T.categories} WHERE id = ?`).get(id);
45
+ if (!row)
46
+ return null;
47
+ return this.rowToCategory(row);
48
+ }
49
+ /**
50
+ * Get a category by name and type.
51
+ */
52
+ async getCategoryByName(name, type) {
53
+ const row = this.ctx.db.prepare(`
54
+ SELECT * FROM ${T.categories} WHERE LOWER(name) = LOWER(?) AND type = ?
55
+ `).get(name, type);
56
+ if (!row)
57
+ return null;
58
+ return this.rowToCategory(row);
59
+ }
60
+ /**
61
+ * Create a new category.
62
+ */
63
+ async createCategory(category) {
64
+ if (!category.name) {
65
+ throw new PMOError('INVALID', 'Category name is required');
66
+ }
67
+ if (!category.type) {
68
+ throw new PMOError('INVALID', 'Category type is required');
69
+ }
70
+ const id = category.id || slugify(category.name);
71
+ // Check for duplicate name within the same type
72
+ const existing = this.ctx.db.prepare(`
73
+ SELECT id FROM ${T.categories} WHERE LOWER(name) = LOWER(?) AND type = ?
74
+ `).get(category.name, category.type);
75
+ if (existing) {
76
+ throw new PMOError('CONFLICT', `Category "${category.name}" already exists for type "${category.type}"`);
77
+ }
78
+ // Get the next position
79
+ const maxPos = this.ctx.db.prepare(`
80
+ SELECT MAX(position) as max FROM ${T.categories} WHERE type = ?
81
+ `).get(category.type);
82
+ const position = category.position ?? (maxPos.max ?? -1) + 1;
83
+ const now = new Date().toISOString();
84
+ this.ctx.db.prepare(`
85
+ INSERT INTO ${T.categories} (id, name, type, description, color, position, is_builtin, created_at)
86
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
87
+ `).run(id, category.name, category.type, category.description || null, category.color || null, position, category.isBuiltin ? 1 : 0, now);
88
+ return {
89
+ id,
90
+ name: category.name,
91
+ type: category.type,
92
+ description: category.description,
93
+ color: category.color,
94
+ position,
95
+ isBuiltin: category.isBuiltin || false,
96
+ createdAt: new Date(now),
97
+ };
98
+ }
99
+ /**
100
+ * Update a category.
101
+ */
102
+ async updateCategory(id, changes) {
103
+ const existing = await this.getCategory(id);
104
+ if (!existing) {
105
+ throw new PMOError('NOT_FOUND', `Category not found: ${id}`);
106
+ }
107
+ if (existing.isBuiltin) {
108
+ throw new PMOError('INVALID', 'Cannot modify built-in categories');
109
+ }
110
+ // Check for duplicate name if name is changing
111
+ if (changes.name && changes.name.toLowerCase() !== existing.name.toLowerCase()) {
112
+ const dup = this.ctx.db.prepare(`
113
+ SELECT id FROM ${T.categories} WHERE LOWER(name) = LOWER(?) AND type = ? AND id != ?
114
+ `).get(changes.name, existing.type, id);
115
+ if (dup) {
116
+ throw new PMOError('CONFLICT', `Category "${changes.name}" already exists for type "${existing.type}"`);
117
+ }
118
+ }
119
+ const updates = [];
120
+ const params = [];
121
+ if (changes.name !== undefined) {
122
+ updates.push('name = ?');
123
+ params.push(changes.name);
124
+ }
125
+ if (changes.description !== undefined) {
126
+ updates.push('description = ?');
127
+ params.push(changes.description || null);
128
+ }
129
+ if (changes.color !== undefined) {
130
+ updates.push('color = ?');
131
+ params.push(changes.color || null);
132
+ }
133
+ if (changes.position !== undefined) {
134
+ updates.push('position = ?');
135
+ params.push(changes.position);
136
+ }
137
+ if (updates.length > 0) {
138
+ params.push(id);
139
+ this.ctx.db.prepare(`UPDATE ${T.categories} SET ${updates.join(', ')} WHERE id = ?`).run(...params);
140
+ }
141
+ return (await this.getCategory(id));
142
+ }
143
+ /**
144
+ * Rename a category.
145
+ */
146
+ async renameCategory(id, newName) {
147
+ return this.updateCategory(id, { name: newName });
148
+ }
149
+ /**
150
+ * Delete a category.
151
+ */
152
+ async deleteCategory(id) {
153
+ const existing = await this.getCategory(id);
154
+ if (!existing) {
155
+ throw new PMOError('NOT_FOUND', `Category not found: ${id}`);
156
+ }
157
+ if (existing.isBuiltin) {
158
+ throw new PMOError('INVALID', 'Cannot delete built-in categories');
159
+ }
160
+ // Check if the category is in use
161
+ if (existing.type === 'ticket') {
162
+ const ticketsUsing = this.ctx.db.prepare(`
163
+ SELECT COUNT(*) as count FROM ${T.tickets} WHERE category = ?
164
+ `).get(existing.name);
165
+ if (ticketsUsing.count > 0) {
166
+ throw new PMOError('INVALID', `Cannot delete category "${existing.name}": ${ticketsUsing.count} ticket(s) are using it. Reassign tickets first.`);
167
+ }
168
+ }
169
+ else if (existing.type === 'status') {
170
+ const statusesUsing = this.ctx.db.prepare(`
171
+ SELECT COUNT(*) as count FROM ${T.workflow_statuses} WHERE category = ?
172
+ `).get(existing.name);
173
+ if (statusesUsing.count > 0) {
174
+ throw new PMOError('INVALID', `Cannot delete category "${existing.name}": ${statusesUsing.count} status(es) are using it. Reassign statuses first.`);
175
+ }
176
+ }
177
+ this.ctx.db.prepare(`DELETE FROM ${T.categories} WHERE id = ?`).run(id);
178
+ }
179
+ /**
180
+ * Get category names for a type (for validation and autocomplete).
181
+ */
182
+ async getCategoryNames(type) {
183
+ const categories = await this.listCategories({ type });
184
+ return categories.map(c => c.name);
185
+ }
186
+ /**
187
+ * Check if a category name is valid for a type.
188
+ */
189
+ async isValidCategory(name, type) {
190
+ const category = await this.getCategoryByName(name, type);
191
+ return category !== null;
192
+ }
193
+ rowToCategory(row) {
194
+ return {
195
+ id: row.id,
196
+ name: row.name,
197
+ type: row.type,
198
+ description: row.description || undefined,
199
+ color: row.color || undefined,
200
+ position: row.position,
201
+ isBuiltin: row.is_builtin === 1,
202
+ createdAt: new Date(row.created_at),
203
+ };
204
+ }
205
+ }
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import Database from 'better-sqlite3';
11
11
  import { DrizzleDB } from '../../database/drizzle.js';
12
- import { AcceptanceCriterion, Board, BoardConfig, BoardView, BoardViewFilter, BoardViewFilters, Column, CreateTicketInput, Epic, EpicDependency, EpicDependencyType, EpicFilter, PhaseFilter, PhaseTemplate, PhaseTemplateFilter, PMOStorage, Project, ProjectFilter, ProjectPhase, Roadmap, RoadmapFilter, RoadmapProject, Spec, SpecDependency, SpecDependencyType, SpecFilter, StateCategory, Subtask, SyncResult, SyncStatus, Ticket, TicketDependency, TicketDependencyType, TicketFilter, TicketTemplate, TicketTemplateFilter, WorkAction, WorkActionFilter, Workflow, WorkflowFilter, WorkflowStatus } from '../types.js';
12
+ import { AcceptanceCriterion, Board, BoardConfig, BoardView, BoardViewFilter, BoardViewFilters, Category, CategoryFilter, CategoryType, Column, CreateTicketInput, Epic, EpicDependency, EpicDependencyType, EpicFilter, PhaseFilter, PhaseTemplate, PhaseTemplateFilter, PMOStorage, Project, ProjectFilter, ProjectPhase, Roadmap, RoadmapFilter, RoadmapProject, Spec, SpecDependency, SpecDependencyType, SpecFilter, StateCategory, Subtask, SyncResult, SyncStatus, Ticket, TicketDependency, TicketDependencyType, TicketFilter, TicketTemplate, TicketTemplateFilter, WorkAction, WorkActionFilter, Workflow, WorkflowFilter, WorkflowStatus } from '../types.js';
13
13
  export declare class SQLiteStorage implements PMOStorage {
14
14
  readonly type: "sqlite";
15
15
  private db;
@@ -28,6 +28,7 @@ export declare class SQLiteStorage implements PMOStorage {
28
28
  private actionStorage;
29
29
  private viewStorage;
30
30
  private roadmapStorage;
31
+ private categoryStorage;
31
32
  constructor(dbPath: string);
32
33
  /**
33
34
  * Get the underlying database connection.
@@ -186,6 +187,18 @@ export declare class SQLiteStorage implements PMOStorage {
186
187
  removeProjectFromRoadmap(roadmapId: string, projectId: string): Promise<void>;
187
188
  reorderRoadmapProject(roadmapId: string, projectId: string, newPosition: number): Promise<RoadmapProject>;
188
189
  getRoadmapsForProject(projectId: string): Promise<Roadmap[]>;
190
+ listCategories(filter?: CategoryFilter): Promise<Category[]>;
191
+ getCategory(id: string): Promise<Category | null>;
192
+ getCategoryByName(name: string, type: CategoryType): Promise<Category | null>;
193
+ createCategory(category: Partial<Category> & {
194
+ name: string;
195
+ type: CategoryType;
196
+ }): Promise<Category>;
197
+ updateCategory(id: string, changes: Partial<Category>): Promise<Category>;
198
+ renameCategory(id: string, newName: string): Promise<Category>;
199
+ deleteCategory(id: string): Promise<void>;
200
+ getCategoryNames(type: CategoryType): Promise<string[]>;
201
+ isValidCategory(name: string, type: CategoryType): Promise<boolean>;
189
202
  pull(): Promise<SyncResult>;
190
203
  push(): Promise<SyncResult>;
191
204
  status(): Promise<SyncStatus>;
@@ -10,7 +10,7 @@
10
10
  import Database from 'better-sqlite3';
11
11
  import { createDrizzleConnection } from '../../database/drizzle.js';
12
12
  import { PMO_TABLES, PMO_SCHEMA_SQL, validateTicketSchema } from '../schema.js';
13
- import { runMigrations, seedBuiltinWorkflows, seedBuiltinPhases, seedBuiltinPhaseTemplates, seedBuiltinActions, seedBuiltinTicketTemplates, updateBoardTimestamp, } from './base.js';
13
+ import { runMigrations, seedBuiltinWorkflows, seedBuiltinPhases, seedBuiltinPhaseTemplates, seedBuiltinActions, seedBuiltinTicketTemplates, seedBuiltinCategories, updateBoardTimestamp, } from './base.js';
14
14
  import { ProjectStorage } from './projects.js';
15
15
  import { TicketStorage } from './tickets.js';
16
16
  import { SubtaskStorage, AcceptanceCriteriaStorage } from './subtasks.js';
@@ -23,6 +23,7 @@ import { PhaseStorage } from './phases.js';
23
23
  import { ActionStorage } from './actions.js';
24
24
  import { ViewStorage } from './views.js';
25
25
  import { RoadmapStorage } from './roadmaps.js';
26
+ import { CategoryStorage } from './categories.js';
26
27
  const T = PMO_TABLES;
27
28
  export class SQLiteStorage {
28
29
  type = 'sqlite';
@@ -43,6 +44,7 @@ export class SQLiteStorage {
43
44
  actionStorage;
44
45
  viewStorage;
45
46
  roadmapStorage;
47
+ categoryStorage;
46
48
  constructor(dbPath) {
47
49
  this.dbPath = dbPath;
48
50
  // Open database (creates if doesn't exist)
@@ -71,6 +73,7 @@ export class SQLiteStorage {
71
73
  this.actionStorage = new ActionStorage(ctx);
72
74
  this.viewStorage = new ViewStorage(ctx);
73
75
  this.roadmapStorage = new RoadmapStorage(ctx);
76
+ this.categoryStorage = new CategoryStorage(ctx);
74
77
  // Ensure PMO tables exist
75
78
  this.ensurePMOTables();
76
79
  }
@@ -100,6 +103,7 @@ export class SQLiteStorage {
100
103
  seedBuiltinPhaseTemplates(this.db);
101
104
  seedBuiltinActions(this.db);
102
105
  seedBuiltinTicketTemplates(this.db);
106
+ seedBuiltinCategories(this.db);
103
107
  // Validate schema
104
108
  validateTicketSchema(this.db);
105
109
  }
@@ -579,6 +583,36 @@ export class SQLiteStorage {
579
583
  return this.roadmapStorage.getRoadmapsForProject(projectId);
580
584
  }
581
585
  // ===========================================================================
586
+ // Category Operations
587
+ // ===========================================================================
588
+ async listCategories(filter) {
589
+ return this.categoryStorage.listCategories(filter);
590
+ }
591
+ async getCategory(id) {
592
+ return this.categoryStorage.getCategory(id);
593
+ }
594
+ async getCategoryByName(name, type) {
595
+ return this.categoryStorage.getCategoryByName(name, type);
596
+ }
597
+ async createCategory(category) {
598
+ return this.categoryStorage.createCategory(category);
599
+ }
600
+ async updateCategory(id, changes) {
601
+ return this.categoryStorage.updateCategory(id, changes);
602
+ }
603
+ async renameCategory(id, newName) {
604
+ return this.categoryStorage.renameCategory(id, newName);
605
+ }
606
+ async deleteCategory(id) {
607
+ return this.categoryStorage.deleteCategory(id);
608
+ }
609
+ async getCategoryNames(type) {
610
+ return this.categoryStorage.getCategoryNames(type);
611
+ }
612
+ async isValidCategory(name, type) {
613
+ return this.categoryStorage.isValidCategory(name, type);
614
+ }
615
+ // ===========================================================================
582
616
  // Sync Operations (no-op for pure SQLite)
583
617
  // ===========================================================================
584
618
  async pull() {
@@ -8,6 +8,11 @@ import { StorageContext } from './types.js';
8
8
  export declare class TicketStorage {
9
9
  private ctx;
10
10
  constructor(ctx: StorageContext);
11
+ /**
12
+ * Validate a category against the DB.
13
+ * Returns the valid category name if found, throws error if invalid.
14
+ */
15
+ private validateCategory;
11
16
  /**
12
17
  * Resolve a project identifier to its actual ID.
13
18
  * Tries multiple strategies:
@@ -13,6 +13,27 @@ export class TicketStorage {
13
13
  constructor(ctx) {
14
14
  this.ctx = ctx;
15
15
  }
16
+ /**
17
+ * Validate a category against the DB.
18
+ * Returns the valid category name if found, throws error if invalid.
19
+ */
20
+ async validateCategory(category) {
21
+ if (!category)
22
+ return null;
23
+ // Check if category exists in DB for ticket type
24
+ const row = this.ctx.db.prepare(`
25
+ SELECT name FROM ${T.categories} WHERE LOWER(name) = LOWER(?) AND type = 'ticket'
26
+ `).get(category);
27
+ if (!row) {
28
+ // Get valid categories for error message
29
+ const validCategories = this.ctx.db.prepare(`
30
+ SELECT name FROM ${T.categories} WHERE type = 'ticket' ORDER BY position
31
+ `).all();
32
+ const validNames = validCategories.map(c => c.name).join(', ');
33
+ throw new PMOError('INVALID', `Invalid category "${category}". Valid categories: ${validNames}`);
34
+ }
35
+ return row.name;
36
+ }
16
37
  /**
17
38
  * Resolve a project identifier to its actual ID.
18
39
  * Tries multiple strategies:
@@ -71,6 +92,8 @@ export class TicketStorage {
71
92
  const title = ticket.title || 'Untitled';
72
93
  const now = Date.now();
73
94
  const specId = ticket.specId || null;
95
+ // Validate category against DB
96
+ const validatedCategory = await this.validateCategory(ticket.category);
74
97
  // Get status_id from project's workflow
75
98
  let statusId = ticket.statusId;
76
99
  // Get the project's workflow
@@ -134,7 +157,7 @@ export class TicketStorage {
134
157
  created_at, updated_at, last_synced_from_spec, last_synced_from_board
135
158
  )
136
159
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
137
- `).run(id, projectId, title, ticket.description || null, ticket.priority || null, ticket.category || null, statusId, ticket.owner || null, ticket.assignee || null, specId, ticket.epicId || null, JSON.stringify(labels), now, now, ticket.lastSyncedFromSpec || null, ticket.lastSyncedFromBoard || null);
160
+ `).run(id, projectId, title, ticket.description || null, ticket.priority || null, validatedCategory, statusId, ticket.owner || null, ticket.assignee || null, specId, ticket.epicId || null, JSON.stringify(labels), now, now, ticket.lastSyncedFromSpec || null, ticket.lastSyncedFromBoard || null);
138
161
  }
139
162
  catch (err) {
140
163
  wrapSqliteError('Ticket', 'create', err);
@@ -196,6 +219,11 @@ export class TicketStorage {
196
219
  if (!existing) {
197
220
  throw new PMOError('NOT_FOUND', `Ticket not found: ${id}`, id);
198
221
  }
222
+ // Validate category if being updated
223
+ let validatedCategory;
224
+ if (changes.category !== undefined) {
225
+ validatedCategory = await this.validateCategory(changes.category);
226
+ }
199
227
  const updates = [];
200
228
  const params = [];
201
229
  if (changes.title !== undefined) {
@@ -210,9 +238,9 @@ export class TicketStorage {
210
238
  updates.push('priority = ?');
211
239
  params.push(changes.priority);
212
240
  }
213
- if (changes.category !== undefined) {
241
+ if (validatedCategory !== undefined) {
214
242
  updates.push('category = ?');
215
- params.push(changes.category);
243
+ params.push(validatedCategory);
216
244
  }
217
245
  if (changes.statusId !== undefined) {
218
246
  updates.push('status_id = ?');
@@ -216,3 +216,13 @@ export interface RoadmapProjectRow {
216
216
  position: number;
217
217
  created_at: string;
218
218
  }
219
+ export interface CategoryRow {
220
+ id: string;
221
+ name: string;
222
+ type: string;
223
+ description: string | null;
224
+ color: string | null;
225
+ position: number;
226
+ is_builtin: number;
227
+ created_at: string;
228
+ }
@@ -83,6 +83,31 @@ export declare const LEGACY_PRIORITY_MAP: Record<LegacyPriority, Priority>;
83
83
  */
84
84
  export declare const TICKET_CATEGORIES: readonly ["feature", "bug", "refactor", "docs", "test", "chore", "performance", "ci", "build", "security", "database", "release"];
85
85
  export type TicketCategory = typeof TICKET_CATEGORIES[number];
86
+ /**
87
+ * Category types (ticket vs status).
88
+ */
89
+ export type CategoryType = 'ticket' | 'status';
90
+ /**
91
+ * Category record for ticket or status classification.
92
+ */
93
+ export interface Category {
94
+ id: string;
95
+ name: string;
96
+ type: CategoryType;
97
+ description?: string;
98
+ color?: string;
99
+ position: number;
100
+ isBuiltin: boolean;
101
+ createdAt: Date;
102
+ }
103
+ /**
104
+ * Filter options for listing categories.
105
+ */
106
+ export interface CategoryFilter {
107
+ type?: CategoryType;
108
+ isBuiltin?: boolean;
109
+ search?: string;
110
+ }
86
111
  /**
87
112
  * Check if a string is a valid Priority value.
88
113
  */
@@ -128,20 +128,17 @@ export interface ErrorJsonOutput {
128
128
  */
129
129
  export type JsonOutput = PromptJsonOutput | SuccessJsonOutput | ErrorJsonOutput;
130
130
  /**
131
- * Flags interface for JSON mode detection (legacy)
132
- * @deprecated Use MachineOutputFlags instead
131
+ * Flags interface for JSON mode detection
133
132
  */
134
133
  export interface JsonFlags {
135
134
  json?: boolean;
136
135
  }
137
136
  /**
138
137
  * Flags interface for machine-readable output mode detection
139
- * Supports both new --machine format and legacy --json boolean
138
+ * --json is the primary flag, -m/--machine are aliases that set json=true
140
139
  */
141
140
  export interface MachineOutputFlags {
142
- /** New format: --machine (enables machine-readable JSON output for AI agents) */
143
- machine?: boolean;
144
- /** Legacy format: --json (deprecated, use --machine instead) */
141
+ /** Primary JSON output flag. -m and --machine are aliases to this flag */
145
142
  json?: boolean;
146
143
  }
147
144
  /**
@@ -154,16 +151,13 @@ export declare function isNonTTY(): boolean;
154
151
  * Determine if JSON output mode is active (for AI agents)
155
152
  *
156
153
  * Returns true if:
157
- * - The --machine flag is explicitly set
158
- * - The --json flag is explicitly set
154
+ * - The --json flag is set (or -m/--machine aliases)
159
155
  * - The environment is non-TTY (piped output)
160
156
  *
161
157
  * @param flags - Command flags object
162
158
  * @returns true if JSON mode should be used
163
159
  */
164
- export declare function shouldOutputJson(flags: JsonFlags & {
165
- machine?: boolean;
166
- }): boolean;
160
+ export declare function shouldOutputJson(flags: JsonFlags): boolean;
167
161
  /**
168
162
  * Alias for shouldOutputJson - clearer name for agent-focused code
169
163
  */
@@ -172,8 +166,7 @@ export declare const isAgentMode: typeof shouldOutputJson;
172
166
  * Determine if machine-readable output mode is active (for AI agents/scripts)
173
167
  *
174
168
  * Returns true if:
175
- * - The --machine flag is set
176
- * - The --json flag is explicitly set (legacy support)
169
+ * - The --json flag is set (or -m/--machine aliases)
177
170
  * - The environment is non-TTY (piped output)
178
171
  *
179
172
  * @param flags - Command flags object
@@ -200,15 +193,16 @@ export declare function createMetadata(command: string, flags: Record<string, un
200
193
  */
201
194
  export declare function outputPromptAsJson(config: PromptConfig, metadata: OutputMetadata): never;
202
195
  /**
203
- * Output success result as JSON
196
+ * Output success result as JSON and exit
204
197
  *
205
198
  * Use this when all required data was provided via flags
206
- * and no prompt is needed.
199
+ * and no prompt is needed. Exits with EXIT_SUCCESS (0) to signal
200
+ * successful command completion.
207
201
  *
208
202
  * @param result - Command-specific result data
209
203
  * @param metadata - Command metadata
210
204
  */
211
- export declare function outputSuccessAsJson(result: Record<string, unknown>, metadata: OutputMetadata): void;
205
+ export declare function outputSuccessAsJson(result: Record<string, unknown>, metadata: OutputMetadata): never;
212
206
  /**
213
207
  * Output error as JSON and exit
214
208
  *
@@ -41,19 +41,14 @@ export function isNonTTY() {
41
41
  * Determine if JSON output mode is active (for AI agents)
42
42
  *
43
43
  * Returns true if:
44
- * - The --machine flag is explicitly set
45
- * - The --json flag is explicitly set
44
+ * - The --json flag is set (or -m/--machine aliases)
46
45
  * - The environment is non-TTY (piped output)
47
46
  *
48
47
  * @param flags - Command flags object
49
48
  * @returns true if JSON mode should be used
50
49
  */
51
50
  export function shouldOutputJson(flags) {
52
- // New --machine flag
53
- if (flags.machine === true) {
54
- return true;
55
- }
56
- // Legacy --json flag
51
+ // --json flag (includes -m and --machine aliases)
57
52
  if (flags.json === true) {
58
53
  return true;
59
54
  }
@@ -68,19 +63,14 @@ export const isAgentMode = shouldOutputJson;
68
63
  * Determine if machine-readable output mode is active (for AI agents/scripts)
69
64
  *
70
65
  * Returns true if:
71
- * - The --machine flag is set
72
- * - The --json flag is explicitly set (legacy support)
66
+ * - The --json flag is set (or -m/--machine aliases)
73
67
  * - The environment is non-TTY (piped output)
74
68
  *
75
69
  * @param flags - Command flags object
76
70
  * @returns true if machine-readable output mode should be used
77
71
  */
78
72
  export function isMachineOutput(flags) {
79
- // New --machine flag takes precedence
80
- if (flags.machine === true) {
81
- return true;
82
- }
83
- // Legacy --json flag support
73
+ // --json flag (includes -m and --machine aliases)
84
74
  if (flags.json === true) {
85
75
  return true;
86
76
  }
@@ -128,10 +118,11 @@ export function outputPromptAsJson(config, metadata) {
128
118
  process.exit(EXIT_NEEDS_INPUT);
129
119
  }
130
120
  /**
131
- * Output success result as JSON
121
+ * Output success result as JSON and exit
132
122
  *
133
123
  * Use this when all required data was provided via flags
134
- * and no prompt is needed.
124
+ * and no prompt is needed. Exits with EXIT_SUCCESS (0) to signal
125
+ * successful command completion.
135
126
  *
136
127
  * @param result - Command-specific result data
137
128
  * @param metadata - Command metadata
@@ -144,6 +135,7 @@ export function outputSuccessAsJson(result, metadata) {
144
135
  metadata,
145
136
  };
146
137
  console.log(JSON.stringify(output, null, 2));
138
+ process.exit(EXIT_SUCCESS);
147
139
  }
148
140
  /**
149
141
  * Output error as JSON and exit