@proletariat/cli 0.3.34 → 0.3.36

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 (198) hide show
  1. package/dist/commands/agent/auth.d.ts +15 -3
  2. package/dist/commands/agent/auth.js +136 -15
  3. package/dist/commands/agent/index.js +11 -2
  4. package/dist/commands/agent/list.js +16 -7
  5. package/dist/commands/agent/staff/add.d.ts +1 -0
  6. package/dist/commands/agent/staff/add.js +1 -0
  7. package/dist/commands/agent/staff/index.d.ts +15 -0
  8. package/dist/commands/agent/staff/index.js +83 -0
  9. package/dist/commands/agent/staff/list.d.ts +1 -0
  10. package/dist/commands/agent/staff/list.js +1 -0
  11. package/dist/commands/agent/staff/remove.d.ts +1 -0
  12. package/dist/commands/agent/staff/remove.js +1 -0
  13. package/dist/commands/agent/status.js +32 -4
  14. package/dist/commands/agent/themes/add-names.d.ts +1 -0
  15. package/dist/commands/agent/themes/add-names.js +1 -0
  16. package/dist/commands/agent/themes/create.d.ts +1 -0
  17. package/dist/commands/agent/themes/create.js +1 -0
  18. package/dist/commands/agent/themes/index.d.ts +10 -0
  19. package/dist/commands/agent/themes/index.js +144 -0
  20. package/dist/commands/agent/themes/list.d.ts +1 -0
  21. package/dist/commands/agent/themes/list.js +1 -0
  22. package/dist/commands/agent/themes/set.d.ts +1 -0
  23. package/dist/commands/agent/themes/set.js +1 -0
  24. package/dist/commands/agents/themes/add-names.d.ts +1 -0
  25. package/dist/commands/agents/themes/add-names.js +1 -0
  26. package/dist/commands/agents/themes/create.d.ts +1 -0
  27. package/dist/commands/agents/themes/create.js +1 -0
  28. package/dist/commands/agents/themes/list.d.ts +1 -0
  29. package/dist/commands/agents/themes/list.js +1 -0
  30. package/dist/commands/board/watch.js +6 -0
  31. package/dist/commands/branch/list.d.ts +1 -0
  32. package/dist/commands/branch/list.js +43 -12
  33. package/dist/commands/branch/where.js +3 -2
  34. package/dist/commands/category/list.d.ts +2 -1
  35. package/dist/commands/category/list.js +38 -13
  36. package/dist/commands/{claude.d.ts → claude/index.d.ts} +1 -1
  37. package/dist/commands/{claude.js → claude/index.js} +12 -12
  38. package/dist/commands/claude/open.d.ts +13 -0
  39. package/dist/commands/claude/open.js +175 -0
  40. package/dist/commands/diet.js +18 -2
  41. package/dist/commands/docker/logs.js +7 -3
  42. package/dist/commands/docker/shell.js +6 -0
  43. package/dist/commands/docker/start.js +20 -4
  44. package/dist/commands/docker/sync.d.ts +4 -0
  45. package/dist/commands/docker/sync.js +30 -2
  46. package/dist/commands/epic/show.d.ts +13 -0
  47. package/dist/commands/epic/show.js +16 -0
  48. package/dist/commands/epic/view.js +27 -0
  49. package/dist/commands/execution/config.d.ts +0 -4
  50. package/dist/commands/execution/config.js +10 -32
  51. package/dist/commands/execution/index.js +2 -1
  52. package/dist/commands/execution/logs.js +1 -1
  53. package/dist/commands/execution/stop.js +2 -1
  54. package/dist/commands/execution/view.js +22 -26
  55. package/dist/commands/init.js +2 -19
  56. package/dist/commands/label/create.d.ts +20 -0
  57. package/dist/commands/label/create.js +57 -0
  58. package/dist/commands/label/delete.d.ts +17 -0
  59. package/dist/commands/label/delete.js +32 -0
  60. package/dist/commands/label/group/create.d.ts +20 -0
  61. package/dist/commands/label/group/create.js +55 -0
  62. package/dist/commands/label/group/list.d.ts +14 -0
  63. package/dist/commands/label/group/list.js +52 -0
  64. package/dist/commands/label/index.d.ts +15 -0
  65. package/dist/commands/label/index.js +58 -0
  66. package/dist/commands/label/list.d.ts +16 -0
  67. package/dist/commands/label/list.js +83 -0
  68. package/dist/commands/link/list.js +3 -2
  69. package/dist/commands/mcp-server.js +27 -1
  70. package/dist/commands/phase/template/apply.d.ts +26 -0
  71. package/dist/commands/phase/template/apply.js +14 -0
  72. package/dist/commands/phase/template/create.d.ts +23 -0
  73. package/dist/commands/phase/template/create.js +14 -0
  74. package/dist/commands/phase/template/delete.d.ts +18 -0
  75. package/dist/commands/phase/template/delete.js +61 -0
  76. package/dist/commands/phase/template/list.d.ts +17 -0
  77. package/dist/commands/phase/template/list.js +89 -0
  78. package/dist/commands/phase/template/update.d.ts +1 -0
  79. package/dist/commands/phase/template/update.js +1 -0
  80. package/dist/commands/priority/add.js +1 -1
  81. package/dist/commands/project/create.js +3 -4
  82. package/dist/commands/project/update.js +5 -8
  83. package/dist/commands/pull.js +24 -0
  84. package/dist/commands/roadmap/generate.js +1 -2
  85. package/dist/commands/session/create.d.ts +19 -0
  86. package/dist/commands/session/create.js +102 -0
  87. package/dist/commands/session/health.js +2 -21
  88. package/dist/commands/session/index.js +14 -1
  89. package/dist/commands/session/list.js +26 -7
  90. package/dist/commands/session/peek.d.ts +38 -0
  91. package/dist/commands/session/peek.js +316 -0
  92. package/dist/commands/session/poke.d.ts +27 -0
  93. package/dist/commands/session/poke.js +219 -0
  94. package/dist/commands/spec/link/depends.d.ts +18 -0
  95. package/dist/commands/spec/link/depends.js +86 -0
  96. package/dist/commands/spec/link/index.d.ts +17 -0
  97. package/dist/commands/spec/link/index.js +92 -0
  98. package/dist/commands/spec/link/remove.d.ts +18 -0
  99. package/dist/commands/spec/link/remove.js +90 -0
  100. package/dist/commands/spec/view.js +29 -0
  101. package/dist/commands/support/logs.js +2 -2
  102. package/dist/commands/template/apply.js +5 -4
  103. package/dist/commands/template/create.js +1 -1
  104. package/dist/commands/template/list.js +2 -1
  105. package/dist/commands/theme/add-names.d.ts +4 -0
  106. package/dist/commands/theme/add-names.js +11 -1
  107. package/dist/commands/theme/create.d.ts +2 -0
  108. package/dist/commands/theme/create.js +8 -0
  109. package/dist/commands/ticket/bulk.js +2 -2
  110. package/dist/commands/ticket/complete.js +2 -2
  111. package/dist/commands/ticket/create.js +21 -0
  112. package/dist/commands/ticket/delete.js +8 -0
  113. package/dist/commands/ticket/edit.js +25 -0
  114. package/dist/commands/ticket/index.js +2 -2
  115. package/dist/commands/ticket/link/block.d.ts +15 -0
  116. package/dist/commands/ticket/link/block.js +95 -0
  117. package/dist/commands/ticket/link/index.d.ts +14 -0
  118. package/dist/commands/ticket/link/index.js +96 -0
  119. package/dist/commands/ticket/list.d.ts +1 -0
  120. package/dist/commands/ticket/list.js +6 -0
  121. package/dist/commands/ticket/move.js +25 -2
  122. package/dist/commands/ticket/resolve.js +4 -5
  123. package/dist/commands/ticket/show.d.ts +13 -0
  124. package/dist/commands/ticket/show.js +16 -0
  125. package/dist/commands/ticket/template/apply.d.ts +26 -0
  126. package/dist/commands/ticket/template/apply.js +14 -0
  127. package/dist/commands/ticket/template/delete.d.ts +18 -0
  128. package/dist/commands/ticket/template/delete.js +61 -0
  129. package/dist/commands/ticket/template/list.d.ts +17 -0
  130. package/dist/commands/ticket/template/list.js +78 -0
  131. package/dist/commands/ticket/template/save.d.ts +17 -0
  132. package/dist/commands/ticket/template/save.js +97 -0
  133. package/dist/commands/ticket/view.js +30 -0
  134. package/dist/commands/work/index.js +4 -0
  135. package/dist/commands/work/ready.js +17 -0
  136. package/dist/commands/work/resolve.js +1 -1
  137. package/dist/commands/work/spawn.js +4 -4
  138. package/dist/commands/work/start.d.ts +1 -0
  139. package/dist/commands/work/start.js +203 -93
  140. package/dist/commands/work/status.d.ts +14 -0
  141. package/dist/commands/work/status.js +60 -0
  142. package/dist/commands/workflow/index.js +2 -1
  143. package/dist/commands/workflow/show.d.ts +13 -0
  144. package/dist/commands/workflow/show.js +16 -0
  145. package/dist/commands/workspace/add.js +15 -0
  146. package/dist/commands/workspace/list.js +2 -1
  147. package/dist/commands/workspace/prune.js +5 -5
  148. package/dist/lib/branch/index.d.ts +1 -0
  149. package/dist/lib/database/index.d.ts +1 -1
  150. package/dist/lib/database/index.js +20 -0
  151. package/dist/lib/execution/config.d.ts +15 -1
  152. package/dist/lib/execution/config.js +28 -0
  153. package/dist/lib/execution/devcontainer.js +3 -1
  154. package/dist/lib/execution/runners.d.ts +18 -2
  155. package/dist/lib/execution/runners.js +71 -29
  156. package/dist/lib/execution/session-utils.d.ts +11 -1
  157. package/dist/lib/execution/session-utils.js +26 -1
  158. package/dist/lib/execution/storage.d.ts +5 -0
  159. package/dist/lib/execution/storage.js +18 -3
  160. package/dist/lib/execution/types.d.ts +3 -0
  161. package/dist/lib/flags/resolver.js +1 -0
  162. package/dist/lib/mcp/helpers.d.ts +1 -2
  163. package/dist/lib/mcp/tools/board.js +4 -6
  164. package/dist/lib/mcp/tools/cli-passthrough.js +25 -6
  165. package/dist/lib/mcp/tools/diet.js +1 -0
  166. package/dist/lib/mcp/tools/epic.js +8 -3
  167. package/dist/lib/mcp/tools/index.d.ts +1 -0
  168. package/dist/lib/mcp/tools/index.js +1 -0
  169. package/dist/lib/mcp/tools/label.d.ts +6 -0
  170. package/dist/lib/mcp/tools/label.js +338 -0
  171. package/dist/lib/mcp/tools/spec.js +1 -1
  172. package/dist/lib/mcp/tools/ticket.js +57 -19
  173. package/dist/lib/mcp/tools/work.js +96 -6
  174. package/dist/lib/mcp/types.d.ts +10 -0
  175. package/dist/lib/multiline-input.js +8 -19
  176. package/dist/lib/pmo/base-command.d.ts +0 -1
  177. package/dist/lib/pmo/base-command.js +4 -5
  178. package/dist/lib/pmo/schema.d.ts +6 -0
  179. package/dist/lib/pmo/schema.js +44 -0
  180. package/dist/lib/pmo/storage/actions.js +1 -1
  181. package/dist/lib/pmo/storage/base.d.ts +6 -0
  182. package/dist/lib/pmo/storage/base.js +311 -52
  183. package/dist/lib/pmo/storage/index.d.ts +23 -1
  184. package/dist/lib/pmo/storage/index.js +59 -1
  185. package/dist/lib/pmo/storage/labels.d.ts +55 -0
  186. package/dist/lib/pmo/storage/labels.js +346 -0
  187. package/dist/lib/pmo/storage/tickets.js +17 -0
  188. package/dist/lib/pmo/storage/types.d.ts +25 -0
  189. package/dist/lib/pmo/types.d.ts +44 -0
  190. package/dist/lib/pmo/utils.js +1 -1
  191. package/dist/lib/prompt-command.d.ts +20 -0
  192. package/dist/lib/prompt-command.js +38 -2
  193. package/dist/lib/prompt-json.d.ts +36 -4
  194. package/dist/lib/prompt-json.js +129 -7
  195. package/dist/lib/styles.d.ts +37 -0
  196. package/dist/lib/styles.js +73 -0
  197. package/oclif.manifest.json +6399 -3799
  198. package/package.json +1 -1
@@ -5,7 +5,7 @@
5
5
  import { PMO_TABLES, PMO_SCHEMA_SQL, validateTicketSchema } from '../schema.js';
6
6
  import { TICKET_CATEGORIES, STATE_CATEGORY_ORDER } from '../types.js';
7
7
  import { BUILTIN_TEMPLATES } from '../templates-builtin.js';
8
- import { getWorkspacePriorities, setWorkspacePriorities, DEFAULT_PRIORITIES } from '../utils.js';
8
+ import { setWorkspacePriorities, DEFAULT_PRIORITIES } from '../utils.js';
9
9
  const T = PMO_TABLES;
10
10
  /**
11
11
  * Initialize PMO tables in the database.
@@ -20,6 +20,7 @@ export function initializePMOTables(db) {
20
20
  seedBuiltinActions(db);
21
21
  seedBuiltinTicketTemplates(db);
22
22
  seedDefaultPriorities(db); // Seed default priority scale if not set
23
+ seedBuiltinLabels(db); // Seed built-in label groups and labels
23
24
  validateTicketSchema(db);
24
25
  }
25
26
  /**
@@ -538,6 +539,8 @@ Requirements:
538
539
  - Use \`--clear-subtasks\` if replacing existing subtasks
539
540
  - Use \`--clear-ac\` if replacing existing acceptance criteria
540
541
 
542
+ **Tip:** Use \`prlt ticket view <id>\` to see full ticket details at any time.
543
+
541
544
  After updating, output a brief summary of your grooming changes.`,
542
545
  suggestedForCategories: ['backlog'],
543
546
  defaultMoveToCategory: 'unstarted',
@@ -690,6 +693,200 @@ git add -A && prlt commit "your change" && git push
690
693
  modifiesCode: true,
691
694
  position: 3,
692
695
  },
696
+ {
697
+ id: 'review',
698
+ name: 'Code Review',
699
+ description: 'Review the implementation and post feedback on the PR',
700
+ prompt: `${PRLT_USAGE_RULE}
701
+
702
+ ---
703
+
704
+ # Action: Code Review
705
+
706
+ Review this ticket's implementation thoroughly:
707
+ - Check for bugs, edge cases, and potential issues
708
+ - Look for security vulnerabilities
709
+ - Verify it meets all acceptance criteria
710
+ - Check code quality and maintainability
711
+ - Suggest improvements if appropriate
712
+
713
+ After reviewing, determine your verdict:
714
+ - **APPROVE**: Code is ready to merge, no significant issues
715
+ - **REQUEST_CHANGES**: There are issues that must be fixed before merging
716
+ - **COMMENT**: General feedback, no blocking issues but some suggestions
717
+
718
+ Do NOT modify any code. This is a read-only review.`,
719
+ endPrompt: `When you have finished reviewing, post your review on the PR using \`gh pr review\`.
720
+
721
+ Choose the appropriate command based on your verdict:
722
+
723
+ **If approving:**
724
+ \`\`\`bash
725
+ gh pr review --approve --body "## Code Review
726
+
727
+ ### What looks good
728
+ - ...
729
+
730
+ ### Verdict
731
+ APPROVED - Code is ready to merge."
732
+ \`\`\`
733
+
734
+ **If requesting changes:**
735
+ \`\`\`bash
736
+ gh pr review --request-changes --body "## Code Review
737
+
738
+ ### What looks good
739
+ - ...
740
+
741
+ ### Concerns
742
+ - ...
743
+
744
+ ### Suggested improvements
745
+ - ...
746
+
747
+ ### Verdict
748
+ REQUEST CHANGES - Issues must be addressed before merging."
749
+ \`\`\`
750
+
751
+ **If commenting:**
752
+ \`\`\`bash
753
+ gh pr review --comment --body "## Code Review
754
+
755
+ ### What looks good
756
+ - ...
757
+
758
+ ### Suggestions
759
+ - ...
760
+
761
+ ### Verdict
762
+ COMMENT - Some suggestions but no blocking issues."
763
+ \`\`\`
764
+
765
+ Format the body with: what looks good, concerns (if any), suggested improvements (if any), and your verdict.
766
+
767
+ No commits are needed for code review.`,
768
+ suggestedForCategories: ['started', 'completed'],
769
+ modifiesCode: false,
770
+ position: 4,
771
+ },
772
+ {
773
+ id: 'review-fix',
774
+ name: 'Review & Fix',
775
+ description: 'Review the implementation, fix issues, and post feedback on the PR',
776
+ prompt: `${PRLT_USAGE_RULE}
777
+
778
+ ---
779
+
780
+ # Action: Review & Fix
781
+
782
+ Review this ticket's implementation thoroughly and fix any issues found:
783
+ - Check for bugs, edge cases, and potential issues
784
+ - Look for security vulnerabilities
785
+ - Verify it meets all acceptance criteria
786
+ - Check code quality and maintainability
787
+ - Fix any issues you find directly in the code
788
+
789
+ **IMPORTANT: Commit and push frequently!**
790
+ - Commit after each fix or logical group of changes
791
+ - Push after every 1-2 commits to save your work
792
+
793
+ \`\`\`bash
794
+ git add -A && prlt commit "fix: address code review findings" && git push
795
+ \`\`\``,
796
+ endPrompt: `When you have finished reviewing and fixing:
797
+
798
+ 1. **If issues were found and fixed**, post a review summary and push your fixes:
799
+ \`\`\`bash
800
+ gh pr review --comment --body "## Code Review & Fix Summary
801
+
802
+ ### Issues found and fixed
803
+ - ...
804
+
805
+ ### What looks good
806
+ - ...
807
+
808
+ ### Changes made
809
+ - ...
810
+ "
811
+ prlt commit "fix: address code review findings"
812
+ git push
813
+ \`\`\`
814
+
815
+ 2. **If no issues were found**, approve the PR:
816
+ \`\`\`bash
817
+ gh pr review --approve --body "## Code Review
818
+
819
+ ### What looks good
820
+ - ...
821
+
822
+ ### Verdict
823
+ APPROVED - Code looks great, no issues found."
824
+ \`\`\``,
825
+ suggestedForCategories: ['started', 'completed'],
826
+ modifiesCode: true,
827
+ position: 5,
828
+ },
829
+ {
830
+ id: 'revise',
831
+ name: 'Revise',
832
+ description: 'Pull the branch, read PR review feedback, implement fixes, and push',
833
+ prompt: `${PRLT_USAGE_RULE}
834
+
835
+ ---
836
+
837
+ # Action: Revise
838
+
839
+ Address the feedback on this ticket's pull request:
840
+
841
+ 1. **Pull the latest branch** to ensure you have the most recent code:
842
+ \`\`\`bash
843
+ git pull
844
+ \`\`\`
845
+
846
+ 2. **Read all review comments and requested changes** from the PR:
847
+ \`\`\`bash
848
+ gh pr view
849
+ gh api repos/{owner}/{repo}/pulls/{number}/comments
850
+ \`\`\`
851
+
852
+ 3. **Understand each piece of feedback** before making changes — read carefully and understand the reviewer's intent
853
+
854
+ 4. **Implement the requested fixes** and address each review comment:
855
+ - Make the necessary code changes to address each point
856
+ - Respond to questions with explanations
857
+ - Ensure all requested changes are addressed
858
+
859
+ **IMPORTANT: Commit and push frequently!**
860
+ - Commit after each fix or logical group of changes
861
+ - Push after every 1-2 commits to save your work
862
+
863
+ \`\`\`bash
864
+ git add -A && prlt commit "fix: address PR review feedback" && git push
865
+ \`\`\``,
866
+ endPrompt: `After addressing all feedback:
867
+
868
+ 1. **Commit your changes**:
869
+ \`\`\`bash
870
+ git add -A
871
+ prlt commit "fix: address PR review feedback"
872
+ \`\`\`
873
+
874
+ 2. **Push your changes**:
875
+ \`\`\`bash
876
+ git push
877
+ \`\`\`
878
+
879
+ 3. **Optionally reply to resolved review threads** using the GitHub API:
880
+ \`\`\`bash
881
+ gh api repos/{owner}/{repo}/pulls/{number}/comments
882
+ \`\`\`
883
+
884
+ The PR will be updated automatically with your pushed changes.`,
885
+ suggestedForCategories: ['completed'],
886
+ defaultMoveToCategory: 'started',
887
+ modifiesCode: true,
888
+ position: 6,
889
+ },
693
890
  {
694
891
  id: 'test',
695
892
  name: 'Write Tests',
@@ -730,56 +927,7 @@ git add -A && prlt commit "add tests for X" && git push
730
927
  **IMPORTANT:** Use the global \`prlt\` command.`,
731
928
  suggestedForCategories: ['started', 'completed'],
732
929
  modifiesCode: true,
733
- position: 4,
734
- },
735
- {
736
- id: 'review',
737
- name: 'Code Review',
738
- description: 'Review the implementation for issues',
739
- prompt: `Review this ticket's implementation thoroughly:
740
- - Check for bugs, edge cases, and potential issues
741
- - Look for security vulnerabilities
742
- - Verify it meets all acceptance criteria
743
- - Check code quality and maintainability
744
- - Suggest improvements if appropriate
745
-
746
- Output a review summary with your findings and any concerns.`,
747
- endPrompt: `When you have finished reviewing, output a detailed review summary with:
748
- - ✅ What looks good
749
- - ⚠️ Concerns or potential issues
750
- - 🔧 Suggested improvements
751
- - 📋 Verdict: Approve, Request Changes, or Needs Discussion
752
-
753
- No commits are needed for code review.`,
754
- suggestedForCategories: ['started', 'completed'],
755
- modifiesCode: false,
756
- position: 5,
757
- },
758
- {
759
- id: 'revise',
760
- name: 'Revise',
761
- description: 'Address PR feedback and review comments',
762
- prompt: `${PRLT_USAGE_RULE}
763
-
764
- ---
765
-
766
- # Action: Revise
767
-
768
- Address the feedback on this ticket's pull request:
769
- - Review all comments and requested changes carefully
770
- - Make the necessary code changes to address each point
771
- - Respond to questions with explanations
772
- - Push updates to the PR branch
773
- - Mark resolved conversations as resolved`,
774
- endPrompt: `After addressing the feedback:
775
- 1. Commit your changes using \`prlt commit "your message"\`
776
- 2. Push your changes: \`git push\`
777
-
778
- The PR will be updated automatically.`,
779
- suggestedForCategories: ['completed'],
780
- defaultMoveToCategory: 'started',
781
- modifiesCode: true,
782
- position: 6,
930
+ position: 7,
783
931
  },
784
932
  ];
785
933
  // Use INSERT OR REPLACE to always update builtin actions with latest prompts
@@ -984,7 +1132,6 @@ export function seedBuiltinCategories(db) {
984
1132
  * Preserves any existing user-defined priority scale.
985
1133
  */
986
1134
  export function seedDefaultPriorities(db) {
987
- const existing = getWorkspacePriorities(db);
988
1135
  // getWorkspacePriorities returns DEFAULT_PRIORITIES if not set,
989
1136
  // but we need to check if it's actually stored in the DB
990
1137
  const row = db.prepare(`SELECT value FROM ${T.settings} WHERE key = 'priorities'`).get();
@@ -993,6 +1140,118 @@ export function seedDefaultPriorities(db) {
993
1140
  setWorkspacePriorities(db, [...DEFAULT_PRIORITIES]);
994
1141
  }
995
1142
  }
1143
+ /**
1144
+ * Seed built-in label groups and labels.
1145
+ * Creates Function, Type, and Area groups with their labels.
1146
+ * Migrates existing ticket category values to Function labels.
1147
+ */
1148
+ export function seedBuiltinLabels(db) {
1149
+ const now = new Date().toISOString();
1150
+ const insertGroup = db.prepare(`
1151
+ INSERT OR IGNORE INTO ${T.label_groups} (id, name, description, is_exclusive, is_required, position, created_at)
1152
+ VALUES (?, ?, ?, ?, ?, ?, ?)
1153
+ `);
1154
+ const insertLabel = db.prepare(`
1155
+ INSERT OR IGNORE INTO ${T.labels} (id, name, color, description, group_id, position, is_builtin, created_at)
1156
+ VALUES (?, ?, ?, ?, ?, ?, 1, ?)
1157
+ `);
1158
+ // Built-in label groups
1159
+ const groups = [
1160
+ {
1161
+ id: 'function',
1162
+ name: 'Function',
1163
+ description: 'Business function category (diet enforcement)',
1164
+ isExclusive: true,
1165
+ isRequired: true,
1166
+ position: 0,
1167
+ labels: [
1168
+ { id: 'fn-ship', name: 'ship', description: 'Core product development', color: '#2da44e' },
1169
+ { id: 'fn-grow', name: 'grow', description: 'Marketing, adoption, community', color: '#bf8700' },
1170
+ { id: 'fn-support', name: 'support', description: 'Docs, error messages, onboarding', color: '#0969da' },
1171
+ { id: 'fn-bizops', name: 'bizops', description: 'Infrastructure, CI/CD, operations', color: '#8250df' },
1172
+ { id: 'fn-strategy', name: 'strategy', description: 'Design decisions, spikes, retros, planning', color: '#cf222e' },
1173
+ ],
1174
+ },
1175
+ {
1176
+ id: 'type',
1177
+ name: 'Type',
1178
+ description: 'Work type classification',
1179
+ isExclusive: true,
1180
+ isRequired: false,
1181
+ position: 1,
1182
+ labels: [
1183
+ { id: 'type-bug', name: 'bug', description: 'Bug fix', color: '#cf222e' },
1184
+ { id: 'type-feature', name: 'feature', description: 'New feature', color: '#2da44e' },
1185
+ { id: 'type-improvement', name: 'improvement', description: 'Enhancement to existing feature', color: '#0969da' },
1186
+ { id: 'type-task', name: 'task', description: 'General task', color: '#6e7781' },
1187
+ ],
1188
+ },
1189
+ {
1190
+ id: 'area',
1191
+ name: 'Area',
1192
+ description: 'System area (non-exclusive)',
1193
+ isExclusive: false,
1194
+ isRequired: false,
1195
+ position: 2,
1196
+ labels: [
1197
+ { id: 'area-cli', name: 'cli', description: 'CLI tool', color: '#0969da' },
1198
+ { id: 'area-pmo', name: 'pmo', description: 'Project management', color: '#8250df' },
1199
+ { id: 'area-agent', name: 'agent', description: 'Agent system', color: '#2da44e' },
1200
+ { id: 'area-docker', name: 'docker', description: 'Docker integration', color: '#bf8700' },
1201
+ { id: 'area-mcp', name: 'mcp', description: 'MCP server', color: '#cf222e' },
1202
+ { id: 'area-desktop', name: 'desktop', description: 'Desktop app', color: '#6e7781' },
1203
+ ],
1204
+ },
1205
+ ];
1206
+ for (const group of groups) {
1207
+ insertGroup.run(group.id, group.name, group.description, group.isExclusive ? 1 : 0, group.isRequired ? 1 : 0, group.position, now);
1208
+ for (let i = 0; i < group.labels.length; i++) {
1209
+ const label = group.labels[i];
1210
+ insertLabel.run(label.id, label.name, label.color, label.description, group.id, i, now);
1211
+ }
1212
+ }
1213
+ // Migrate existing category column values to Function label group
1214
+ // Only run if there are tickets with category set but no Function label yet
1215
+ migrateCategoryToFunctionLabels(db);
1216
+ }
1217
+ /**
1218
+ * Migrate existing ticket category values to Function label group entries.
1219
+ * Maps known category names to function labels (e.g., 'ship' -> fn-ship).
1220
+ * This is idempotent - only creates associations that don't exist yet.
1221
+ */
1222
+ function migrateCategoryToFunctionLabels(db) {
1223
+ // Map of old category values to function label IDs
1224
+ // The ticket's category field contains values like 'feature', 'bug', etc.
1225
+ // The diet system uses ship/grow/support/bizops/strategy which are different
1226
+ // from ticket categories. The diet categories map to the Function label group.
1227
+ // Since existing categories (feature, bug, etc.) don't map to Function labels,
1228
+ // we only migrate if the category field happens to contain a Function label name.
1229
+ const functionLabelMap = {
1230
+ ship: 'fn-ship',
1231
+ grow: 'fn-grow',
1232
+ support: 'fn-support',
1233
+ bizops: 'fn-bizops',
1234
+ strategy: 'fn-strategy',
1235
+ };
1236
+ const tickets = db.prepare(`
1237
+ SELECT id, category FROM ${T.tickets}
1238
+ WHERE category IS NOT NULL AND category != ''
1239
+ `).all();
1240
+ const insertTicketLabel = db.prepare(`
1241
+ INSERT OR IGNORE INTO ${T.ticket_labels} (ticket_id, label_id)
1242
+ VALUES (?, ?)
1243
+ `);
1244
+ for (const ticket of tickets) {
1245
+ const labelId = functionLabelMap[ticket.category.toLowerCase()];
1246
+ if (labelId) {
1247
+ // Check label exists before inserting
1248
+ const labelExists = db.prepare(`SELECT id FROM ${T.labels} WHERE id = ?`).get(labelId);
1249
+ if (labelExists) {
1250
+ insertTicketLabel.run(ticket.id, labelId);
1251
+ }
1252
+ }
1253
+ }
1254
+ }
996
1255
  /**
997
1256
  * Update board timestamp for a project.
998
1257
  */
@@ -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, 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';
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, Label, LabelFilter, LabelGroup, LabelGroupFilter } from '../types.js';
13
13
  export declare class SQLiteStorage implements PMOStorage {
14
14
  readonly type: "sqlite";
15
15
  private db;
@@ -29,6 +29,7 @@ export declare class SQLiteStorage implements PMOStorage {
29
29
  private viewStorage;
30
30
  private roadmapStorage;
31
31
  private categoryStorage;
32
+ private labelStorage;
32
33
  constructor(dbPath: string);
33
34
  /**
34
35
  * Get the underlying database connection.
@@ -203,6 +204,27 @@ export declare class SQLiteStorage implements PMOStorage {
203
204
  deleteCategory(id: string): Promise<void>;
204
205
  getCategoryNames(type: CategoryType): Promise<string[]>;
205
206
  isValidCategory(name: string, type: CategoryType): Promise<boolean>;
207
+ listLabelGroups(filter?: LabelGroupFilter): Promise<LabelGroup[]>;
208
+ getLabelGroup(id: string): Promise<LabelGroup | null>;
209
+ getLabelGroupByName(name: string): Promise<LabelGroup | null>;
210
+ createLabelGroup(group: Partial<LabelGroup> & {
211
+ name: string;
212
+ }): Promise<LabelGroup>;
213
+ updateLabelGroup(id: string, changes: Partial<LabelGroup>): Promise<LabelGroup>;
214
+ deleteLabelGroup(id: string): Promise<void>;
215
+ listLabels(filter?: LabelFilter): Promise<Label[]>;
216
+ getLabel(id: string): Promise<Label | null>;
217
+ getLabelByName(name: string, groupId?: string): Promise<Label | null>;
218
+ createLabel(label: Partial<Label> & {
219
+ name: string;
220
+ }): Promise<Label>;
221
+ updateLabel(id: string, changes: Partial<Label>): Promise<Label>;
222
+ deleteLabel(id: string): Promise<void>;
223
+ addLabelToTicket(ticketId: string, labelId: string): Promise<void>;
224
+ removeLabelFromTicket(ticketId: string, labelId: string): Promise<void>;
225
+ getLabelsForTicket(ticketId: string): Promise<Label[]>;
226
+ addLabelToTicketByName(ticketId: string, labelName: string): Promise<void>;
227
+ removeLabelFromTicketByName(ticketId: string, labelName: string): Promise<void>;
206
228
  pull(): Promise<SyncResult>;
207
229
  push(): Promise<SyncResult>;
208
230
  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, seedBuiltinCategories, updateBoardTimestamp, } from './base.js';
13
+ import { runMigrations, seedBuiltinWorkflows, seedBuiltinPhases, seedBuiltinPhaseTemplates, seedBuiltinActions, seedBuiltinTicketTemplates, seedBuiltinCategories, seedBuiltinLabels, 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';
@@ -24,6 +24,7 @@ import { ActionStorage } from './actions.js';
24
24
  import { ViewStorage } from './views.js';
25
25
  import { RoadmapStorage } from './roadmaps.js';
26
26
  import { CategoryStorage } from './categories.js';
27
+ import { LabelStorage } from './labels.js';
27
28
  const T = PMO_TABLES;
28
29
  export class SQLiteStorage {
29
30
  type = 'sqlite';
@@ -45,6 +46,7 @@ export class SQLiteStorage {
45
46
  viewStorage;
46
47
  roadmapStorage;
47
48
  categoryStorage;
49
+ labelStorage;
48
50
  constructor(dbPath) {
49
51
  this.dbPath = dbPath;
50
52
  // Open database (creates if doesn't exist)
@@ -74,6 +76,7 @@ export class SQLiteStorage {
74
76
  this.viewStorage = new ViewStorage(ctx);
75
77
  this.roadmapStorage = new RoadmapStorage(ctx);
76
78
  this.categoryStorage = new CategoryStorage(ctx);
79
+ this.labelStorage = new LabelStorage(ctx);
77
80
  // Ensure PMO tables exist
78
81
  this.ensurePMOTables();
79
82
  }
@@ -104,6 +107,7 @@ export class SQLiteStorage {
104
107
  seedBuiltinActions(this.db);
105
108
  seedBuiltinTicketTemplates(this.db);
106
109
  seedBuiltinCategories(this.db);
110
+ seedBuiltinLabels(this.db);
107
111
  // Validate schema
108
112
  validateTicketSchema(this.db);
109
113
  }
@@ -616,6 +620,60 @@ export class SQLiteStorage {
616
620
  return this.categoryStorage.isValidCategory(name, type);
617
621
  }
618
622
  // ===========================================================================
623
+ // Label Operations
624
+ // ===========================================================================
625
+ async listLabelGroups(filter) {
626
+ return this.labelStorage.listLabelGroups(filter);
627
+ }
628
+ async getLabelGroup(id) {
629
+ return this.labelStorage.getLabelGroup(id);
630
+ }
631
+ async getLabelGroupByName(name) {
632
+ return this.labelStorage.getLabelGroupByName(name);
633
+ }
634
+ async createLabelGroup(group) {
635
+ return this.labelStorage.createLabelGroup(group);
636
+ }
637
+ async updateLabelGroup(id, changes) {
638
+ return this.labelStorage.updateLabelGroup(id, changes);
639
+ }
640
+ async deleteLabelGroup(id) {
641
+ return this.labelStorage.deleteLabelGroup(id);
642
+ }
643
+ async listLabels(filter) {
644
+ return this.labelStorage.listLabels(filter);
645
+ }
646
+ async getLabel(id) {
647
+ return this.labelStorage.getLabel(id);
648
+ }
649
+ async getLabelByName(name, groupId) {
650
+ return this.labelStorage.getLabelByName(name, groupId);
651
+ }
652
+ async createLabel(label) {
653
+ return this.labelStorage.createLabel(label);
654
+ }
655
+ async updateLabel(id, changes) {
656
+ return this.labelStorage.updateLabel(id, changes);
657
+ }
658
+ async deleteLabel(id) {
659
+ return this.labelStorage.deleteLabel(id);
660
+ }
661
+ async addLabelToTicket(ticketId, labelId) {
662
+ return this.labelStorage.addLabelToTicket(ticketId, labelId);
663
+ }
664
+ async removeLabelFromTicket(ticketId, labelId) {
665
+ return this.labelStorage.removeLabelFromTicket(ticketId, labelId);
666
+ }
667
+ async getLabelsForTicket(ticketId) {
668
+ return this.labelStorage.getLabelsForTicket(ticketId);
669
+ }
670
+ async addLabelToTicketByName(ticketId, labelName) {
671
+ return this.labelStorage.addLabelToTicketByName(ticketId, labelName);
672
+ }
673
+ async removeLabelFromTicketByName(ticketId, labelName) {
674
+ return this.labelStorage.removeLabelFromTicketByName(ticketId, labelName);
675
+ }
676
+ // ===========================================================================
619
677
  // Sync Operations (no-op for pure SQLite)
620
678
  // ===========================================================================
621
679
  async pull() {
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Label storage operations for PMO.
3
+ * Handles CRUD for labels, label groups, and ticket-label associations.
4
+ * Enforces group exclusivity constraints.
5
+ */
6
+ import { Label, LabelFilter, LabelGroup, LabelGroupFilter } from '../types.js';
7
+ import { StorageContext } from './types.js';
8
+ export declare class LabelStorage {
9
+ private ctx;
10
+ constructor(ctx: StorageContext);
11
+ listLabelGroups(filter?: LabelGroupFilter): Promise<LabelGroup[]>;
12
+ getLabelGroup(id: string): Promise<LabelGroup | null>;
13
+ getLabelGroupByName(name: string): Promise<LabelGroup | null>;
14
+ createLabelGroup(group: Partial<LabelGroup> & {
15
+ name: string;
16
+ }): Promise<LabelGroup>;
17
+ updateLabelGroup(id: string, changes: Partial<LabelGroup>): Promise<LabelGroup>;
18
+ deleteLabelGroup(id: string): Promise<void>;
19
+ listLabels(filter?: LabelFilter): Promise<Label[]>;
20
+ getLabel(id: string): Promise<Label | null>;
21
+ getLabelByName(name: string, groupId?: string): Promise<Label | null>;
22
+ createLabel(label: Partial<Label> & {
23
+ name: string;
24
+ }): Promise<Label>;
25
+ updateLabel(id: string, changes: Partial<Label>): Promise<Label>;
26
+ deleteLabel(id: string): Promise<void>;
27
+ /**
28
+ * Add a label to a ticket.
29
+ * Enforces group exclusivity: if the label belongs to an exclusive group,
30
+ * removes any existing label from the same group first.
31
+ */
32
+ addLabelToTicket(ticketId: string, labelId: string): Promise<void>;
33
+ /**
34
+ * Remove a label from a ticket.
35
+ */
36
+ removeLabelFromTicket(ticketId: string, labelId: string): Promise<void>;
37
+ /**
38
+ * Get all labels for a ticket.
39
+ */
40
+ getLabelsForTicket(ticketId: string): Promise<Label[]>;
41
+ /**
42
+ * Add a label to a ticket by name.
43
+ * Resolves the label name to an ID first.
44
+ * If the label name matches a group:label pattern, resolves within that group.
45
+ */
46
+ addLabelToTicketByName(ticketId: string, labelName: string): Promise<void>;
47
+ /**
48
+ * Remove a label from a ticket by name.
49
+ */
50
+ removeLabelFromTicketByName(ticketId: string, labelName: string): Promise<void>;
51
+ /**
52
+ * Resolve a label by name or group:name pattern.
53
+ */
54
+ private resolveLabelByName;
55
+ }