@proletariat/cli 0.3.35 → 0.3.40
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.
- package/README.md +37 -2
- package/bin/dev.js +0 -0
- package/dist/commands/agent/auth.d.ts +12 -2
- package/dist/commands/agent/auth.js +128 -4
- package/dist/commands/agent/list.js +16 -7
- package/dist/commands/agent/status.js +32 -4
- package/dist/commands/board/watch.js +6 -0
- package/dist/commands/branch/list.d.ts +1 -0
- package/dist/commands/branch/list.js +43 -12
- package/dist/commands/branch/where.js +9 -19
- package/dist/commands/category/list.d.ts +2 -1
- package/dist/commands/category/list.js +38 -13
- package/dist/commands/{claude.d.ts → claude/index.d.ts} +1 -1
- package/dist/commands/{claude.js → claude/index.js} +12 -12
- package/dist/commands/claude/open.d.ts +13 -0
- package/dist/commands/claude/open.js +175 -0
- package/dist/commands/diet.js +18 -2
- package/dist/commands/docker/logs.js +7 -3
- package/dist/commands/docker/shell.js +6 -0
- package/dist/commands/docker/start.js +20 -4
- package/dist/commands/docker/sync.d.ts +4 -0
- package/dist/commands/docker/sync.js +30 -2
- package/dist/commands/epic/show.d.ts +13 -0
- package/dist/commands/epic/show.js +16 -0
- package/dist/commands/epic/ticket.js +7 -24
- package/dist/commands/epic/view.js +27 -0
- package/dist/commands/execution/config.d.ts +0 -4
- package/dist/commands/execution/config.js +14 -46
- package/dist/commands/execution/index.js +2 -1
- package/dist/commands/execution/logs.js +7 -1
- package/dist/commands/execution/stop.js +2 -1
- package/dist/commands/execution/view.js +30 -26
- package/dist/commands/init.js +2 -19
- package/dist/commands/label/create.js +2 -1
- package/dist/commands/label/delete.js +2 -1
- package/dist/commands/label/group/create.js +2 -1
- package/dist/commands/label/group/list.js +2 -1
- package/dist/commands/label/list.js +2 -1
- package/dist/commands/mcp-server.js +27 -1
- package/dist/commands/phase/template/list.js +2 -1
- package/dist/commands/pmo/init.js +12 -40
- package/dist/commands/project/create.js +3 -4
- package/dist/commands/project/update.js +5 -6
- package/dist/commands/pull.js +24 -0
- package/dist/commands/qa/index.d.ts +54 -0
- package/dist/commands/qa/index.js +762 -0
- package/dist/commands/repo/view.js +2 -8
- package/dist/commands/session/attach.js +4 -4
- package/dist/commands/session/create.d.ts +19 -0
- package/dist/commands/session/create.js +102 -0
- package/dist/commands/session/health.js +4 -23
- package/dist/commands/session/index.js +14 -1
- package/dist/commands/session/list.js +9 -8
- package/dist/commands/session/peek.d.ts +38 -0
- package/dist/commands/session/peek.js +316 -0
- package/dist/commands/session/poke.d.ts +27 -0
- package/dist/commands/session/poke.js +219 -0
- package/dist/commands/spec/view.js +29 -0
- package/dist/commands/template/list.js +2 -1
- package/dist/commands/theme/add-names.d.ts +4 -0
- package/dist/commands/theme/add-names.js +11 -1
- package/dist/commands/theme/create.d.ts +2 -0
- package/dist/commands/theme/create.js +8 -0
- package/dist/commands/ticket/bulk.js +2 -2
- package/dist/commands/ticket/complete.js +2 -2
- package/dist/commands/ticket/create.js +21 -0
- package/dist/commands/ticket/delete.js +8 -0
- package/dist/commands/ticket/edit.js +25 -0
- package/dist/commands/ticket/epic.js +17 -43
- package/dist/commands/ticket/index.js +2 -2
- package/dist/commands/ticket/move.js +25 -2
- package/dist/commands/ticket/resolve.js +3 -4
- package/dist/commands/ticket/show.d.ts +13 -0
- package/dist/commands/ticket/show.js +16 -0
- package/dist/commands/ticket/template/list.js +2 -1
- package/dist/commands/ticket/view.d.ts +0 -1
- package/dist/commands/ticket/view.js +30 -1
- package/dist/commands/work/index.js +4 -0
- package/dist/commands/work/spawn-all.js +1 -1
- package/dist/commands/work/spawn.js +15 -4
- package/dist/commands/work/start.js +186 -103
- package/dist/commands/work/status.d.ts +14 -0
- package/dist/commands/work/status.js +60 -0
- package/dist/commands/work/watch.js +1 -1
- package/dist/commands/workflow/index.js +2 -1
- package/dist/commands/workflow/show.d.ts +13 -0
- package/dist/commands/workflow/show.js +16 -0
- package/dist/commands/workspace/add.js +15 -0
- package/dist/commands/workspace/list.js +2 -1
- package/dist/commands/workspace/prune.js +7 -7
- package/dist/hooks/init.js +10 -2
- package/dist/lib/agents/commands.d.ts +5 -0
- package/dist/lib/agents/commands.js +143 -97
- package/dist/lib/branch/index.d.ts +1 -0
- package/dist/lib/database/drizzle-schema.d.ts +465 -0
- package/dist/lib/database/drizzle-schema.js +53 -0
- package/dist/lib/database/index.d.ts +47 -1
- package/dist/lib/database/index.js +138 -20
- package/dist/lib/execution/config.d.ts +15 -1
- package/dist/lib/execution/config.js +28 -0
- package/dist/lib/execution/runners.d.ts +45 -0
- package/dist/lib/execution/runners.js +187 -26
- package/dist/lib/execution/session-utils.d.ts +16 -1
- package/dist/lib/execution/session-utils.js +71 -4
- package/dist/lib/execution/spawner.js +15 -2
- package/dist/lib/execution/storage.d.ts +6 -1
- package/dist/lib/execution/storage.js +35 -5
- package/dist/lib/execution/types.d.ts +3 -0
- package/dist/lib/mcp/tools/board.js +4 -6
- package/dist/lib/mcp/tools/cli-passthrough.js +25 -6
- package/dist/lib/mcp/tools/epic.js +8 -3
- package/dist/lib/mcp/tools/index.d.ts +1 -0
- package/dist/lib/mcp/tools/index.js +1 -0
- package/dist/lib/mcp/tools/spec.js +1 -1
- package/dist/lib/mcp/tools/ticket.js +11 -9
- package/dist/lib/mcp/tools/tmux.d.ts +16 -0
- package/dist/lib/mcp/tools/tmux.js +182 -0
- package/dist/lib/mcp/tools/work.js +148 -6
- package/dist/lib/mcp/types.d.ts +10 -0
- package/dist/lib/multiline-input.js +2 -1
- package/dist/lib/pmo/base-command.js +4 -4
- package/dist/lib/pmo/schema.d.ts +1 -1
- package/dist/lib/pmo/schema.js +1 -0
- package/dist/lib/pmo/storage/actions.js +1 -1
- package/dist/lib/pmo/storage/base.js +402 -50
- package/dist/lib/pmo/storage/dependencies.d.ts +1 -0
- package/dist/lib/pmo/storage/dependencies.js +11 -3
- package/dist/lib/pmo/storage/epics.js +1 -1
- package/dist/lib/pmo/storage/helpers.d.ts +4 -4
- package/dist/lib/pmo/storage/helpers.js +36 -26
- package/dist/lib/pmo/storage/projects.d.ts +2 -0
- package/dist/lib/pmo/storage/projects.js +207 -119
- package/dist/lib/pmo/storage/specs.d.ts +2 -0
- package/dist/lib/pmo/storage/specs.js +274 -188
- package/dist/lib/pmo/storage/tickets.d.ts +2 -0
- package/dist/lib/pmo/storage/tickets.js +350 -290
- package/dist/lib/pmo/storage/types.d.ts +1 -0
- package/dist/lib/pmo/storage/views.d.ts +2 -0
- package/dist/lib/pmo/storage/views.js +183 -130
- package/dist/lib/prompt-command.d.ts +20 -0
- package/dist/lib/prompt-command.js +38 -2
- package/dist/lib/prompt-json.d.ts +41 -4
- package/dist/lib/prompt-json.js +138 -7
- package/dist/lib/styles.d.ts +37 -0
- package/dist/lib/styles.js +73 -0
- package/oclif.manifest.json +4046 -3385
- package/package.json +11 -6
- package/LICENSE +0 -190
|
@@ -178,6 +178,39 @@ export function runMigrations(db) {
|
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
+
// Migration: Migrate old ticket_dependencies schema (blocked_by_ticket_id) to new format
|
|
182
|
+
if (tableExists(T.ticket_dependencies)) {
|
|
183
|
+
const depsColumns = db.pragma(`table_info(${T.ticket_dependencies})`);
|
|
184
|
+
const depsColumnNames = new Set(depsColumns.map(c => c.name));
|
|
185
|
+
if (depsColumnNames.has('blocked_by_ticket_id') && !depsColumnNames.has('depends_on_ticket_id')) {
|
|
186
|
+
// Old schema detected - migrate to new format
|
|
187
|
+
try {
|
|
188
|
+
// Create new table with correct schema
|
|
189
|
+
db.exec(`
|
|
190
|
+
CREATE TABLE pmo_ticket_dependencies_new (
|
|
191
|
+
ticket_id TEXT NOT NULL REFERENCES ${T.tickets}(id) ON DELETE RESTRICT,
|
|
192
|
+
depends_on_ticket_id TEXT NOT NULL REFERENCES ${T.tickets}(id) ON DELETE RESTRICT,
|
|
193
|
+
dependency_type TEXT NOT NULL DEFAULT 'blocks' CHECK (dependency_type IN ('blocks', 'relates_to', 'duplicates')),
|
|
194
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
195
|
+
PRIMARY KEY (ticket_id, depends_on_ticket_id, dependency_type),
|
|
196
|
+
CHECK (ticket_id != depends_on_ticket_id)
|
|
197
|
+
)
|
|
198
|
+
`);
|
|
199
|
+
// Copy existing blocking relationships (old schema: ticket_id is blocked by blocked_by_ticket_id)
|
|
200
|
+
db.exec(`
|
|
201
|
+
INSERT OR IGNORE INTO pmo_ticket_dependencies_new (ticket_id, depends_on_ticket_id, dependency_type, created_at)
|
|
202
|
+
SELECT ticket_id, blocked_by_ticket_id, 'blocks', created_at
|
|
203
|
+
FROM ${T.ticket_dependencies}
|
|
204
|
+
`);
|
|
205
|
+
// Replace old table with new one
|
|
206
|
+
db.exec(`DROP TABLE ${T.ticket_dependencies}`);
|
|
207
|
+
db.exec(`ALTER TABLE pmo_ticket_dependencies_new RENAME TO ${T.ticket_dependencies}`);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Migration may have already been applied partially
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
181
214
|
// Migration: Convert legacy priority values (URGENT/HIGH/MEDIUM/LOW) to P0-P3
|
|
182
215
|
if (tableExists(T.tickets)) {
|
|
183
216
|
try {
|
|
@@ -252,6 +285,19 @@ export function runMigrations(db) {
|
|
|
252
285
|
// Column may already exist
|
|
253
286
|
}
|
|
254
287
|
}
|
|
288
|
+
// Migration: Add error_message column to agent_work table (TKT-1082)
|
|
289
|
+
if (tableExists(T.agent_work)) {
|
|
290
|
+
const agentWorkColumns = db.pragma(`table_info(${T.agent_work})`);
|
|
291
|
+
const agentWorkColumnNames = new Set(agentWorkColumns.map(c => c.name));
|
|
292
|
+
if (!agentWorkColumnNames.has('error_message')) {
|
|
293
|
+
try {
|
|
294
|
+
db.exec(`ALTER TABLE ${T.agent_work} ADD COLUMN error_message TEXT`);
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
// Column may already exist
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
255
301
|
// Migration: Reassign orphaned tickets (TKT-940)
|
|
256
302
|
// Tickets with project_id that doesn't match any existing project are "orphaned".
|
|
257
303
|
// This can happen when a 'default' project never existed or was deleted.
|
|
@@ -693,6 +739,361 @@ git add -A && prlt commit "your change" && git push
|
|
|
693
739
|
modifiesCode: true,
|
|
694
740
|
position: 3,
|
|
695
741
|
},
|
|
742
|
+
{
|
|
743
|
+
id: 'review',
|
|
744
|
+
name: 'Code Review',
|
|
745
|
+
description: 'Review the implementation and post feedback on the PR',
|
|
746
|
+
prompt: `${PRLT_USAGE_RULE}
|
|
747
|
+
|
|
748
|
+
---
|
|
749
|
+
|
|
750
|
+
# Action: Code Review
|
|
751
|
+
|
|
752
|
+
Review this ticket's implementation thoroughly:
|
|
753
|
+
- Check for bugs, edge cases, and potential issues
|
|
754
|
+
- Look for security vulnerabilities
|
|
755
|
+
- Verify it meets all acceptance criteria
|
|
756
|
+
- Check code quality and maintainability
|
|
757
|
+
- Suggest improvements if appropriate
|
|
758
|
+
|
|
759
|
+
After reviewing, determine your verdict:
|
|
760
|
+
- **APPROVE**: Code is ready to merge, no significant issues
|
|
761
|
+
- **REQUEST_CHANGES**: There are issues that must be fixed before merging
|
|
762
|
+
- **COMMENT**: General feedback, no blocking issues but some suggestions
|
|
763
|
+
|
|
764
|
+
Do NOT modify any code. This is a read-only review.`,
|
|
765
|
+
endPrompt: `When you have finished reviewing, post your review on the PR using \`gh pr review\`.
|
|
766
|
+
|
|
767
|
+
Choose the appropriate command based on your verdict:
|
|
768
|
+
|
|
769
|
+
**If approving:**
|
|
770
|
+
\`\`\`bash
|
|
771
|
+
gh pr review --approve --body "## Code Review
|
|
772
|
+
|
|
773
|
+
### What looks good
|
|
774
|
+
- ...
|
|
775
|
+
|
|
776
|
+
### Verdict
|
|
777
|
+
APPROVED - Code is ready to merge."
|
|
778
|
+
\`\`\`
|
|
779
|
+
|
|
780
|
+
**If requesting changes:**
|
|
781
|
+
\`\`\`bash
|
|
782
|
+
gh pr review --request-changes --body "## Code Review
|
|
783
|
+
|
|
784
|
+
### What looks good
|
|
785
|
+
- ...
|
|
786
|
+
|
|
787
|
+
### Concerns
|
|
788
|
+
- ...
|
|
789
|
+
|
|
790
|
+
### Suggested improvements
|
|
791
|
+
- ...
|
|
792
|
+
|
|
793
|
+
### Verdict
|
|
794
|
+
REQUEST CHANGES - Issues must be addressed before merging."
|
|
795
|
+
\`\`\`
|
|
796
|
+
|
|
797
|
+
**If commenting:**
|
|
798
|
+
\`\`\`bash
|
|
799
|
+
gh pr review --comment --body "## Code Review
|
|
800
|
+
|
|
801
|
+
### What looks good
|
|
802
|
+
- ...
|
|
803
|
+
|
|
804
|
+
### Suggestions
|
|
805
|
+
- ...
|
|
806
|
+
|
|
807
|
+
### Verdict
|
|
808
|
+
COMMENT - Some suggestions but no blocking issues."
|
|
809
|
+
\`\`\`
|
|
810
|
+
|
|
811
|
+
Format the body with: what looks good, concerns (if any), suggested improvements (if any), and your verdict.
|
|
812
|
+
|
|
813
|
+
No commits are needed for code review.`,
|
|
814
|
+
suggestedForCategories: ['started', 'completed'],
|
|
815
|
+
modifiesCode: false,
|
|
816
|
+
position: 4,
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
id: 'review-fix',
|
|
820
|
+
name: 'Review & Fix',
|
|
821
|
+
description: 'Review the implementation, fix issues, and post feedback on the PR',
|
|
822
|
+
prompt: `${PRLT_USAGE_RULE}
|
|
823
|
+
|
|
824
|
+
---
|
|
825
|
+
|
|
826
|
+
# Action: Review & Fix
|
|
827
|
+
|
|
828
|
+
Review this ticket's implementation thoroughly and fix any issues found:
|
|
829
|
+
- Check for bugs, edge cases, and potential issues
|
|
830
|
+
- Look for security vulnerabilities
|
|
831
|
+
- Verify it meets all acceptance criteria
|
|
832
|
+
- Check code quality and maintainability
|
|
833
|
+
- Fix any issues you find directly in the code
|
|
834
|
+
|
|
835
|
+
**IMPORTANT: Commit and push frequently!**
|
|
836
|
+
- Commit after each fix or logical group of changes
|
|
837
|
+
- Push after every 1-2 commits to save your work
|
|
838
|
+
|
|
839
|
+
\`\`\`bash
|
|
840
|
+
git add -A && prlt commit "fix: address code review findings" && git push
|
|
841
|
+
\`\`\``,
|
|
842
|
+
endPrompt: `When you have finished reviewing and fixing:
|
|
843
|
+
|
|
844
|
+
1. **If issues were found and fixed**, post a review summary and push your fixes:
|
|
845
|
+
\`\`\`bash
|
|
846
|
+
gh pr review --comment --body "## Code Review & Fix Summary
|
|
847
|
+
|
|
848
|
+
### Issues found and fixed
|
|
849
|
+
- ...
|
|
850
|
+
|
|
851
|
+
### What looks good
|
|
852
|
+
- ...
|
|
853
|
+
|
|
854
|
+
### Changes made
|
|
855
|
+
- ...
|
|
856
|
+
"
|
|
857
|
+
prlt commit "fix: address code review findings"
|
|
858
|
+
git push
|
|
859
|
+
\`\`\`
|
|
860
|
+
|
|
861
|
+
2. **If no issues were found**, approve the PR:
|
|
862
|
+
\`\`\`bash
|
|
863
|
+
gh pr review --approve --body "## Code Review
|
|
864
|
+
|
|
865
|
+
### What looks good
|
|
866
|
+
- ...
|
|
867
|
+
|
|
868
|
+
### Verdict
|
|
869
|
+
APPROVED - Code looks great, no issues found."
|
|
870
|
+
\`\`\``,
|
|
871
|
+
suggestedForCategories: ['started', 'completed'],
|
|
872
|
+
modifiesCode: true,
|
|
873
|
+
position: 5,
|
|
874
|
+
},
|
|
875
|
+
{
|
|
876
|
+
id: 'revise',
|
|
877
|
+
name: 'Revise',
|
|
878
|
+
description: 'Pull the branch, read PR review feedback, implement fixes, and push',
|
|
879
|
+
prompt: `${PRLT_USAGE_RULE}
|
|
880
|
+
|
|
881
|
+
---
|
|
882
|
+
|
|
883
|
+
# Action: Revise
|
|
884
|
+
|
|
885
|
+
Address the feedback on this ticket's pull request:
|
|
886
|
+
|
|
887
|
+
1. **Pull the latest branch** to ensure you have the most recent code:
|
|
888
|
+
\`\`\`bash
|
|
889
|
+
git pull
|
|
890
|
+
\`\`\`
|
|
891
|
+
|
|
892
|
+
2. **Read all review comments and requested changes** from the PR:
|
|
893
|
+
\`\`\`bash
|
|
894
|
+
gh pr view
|
|
895
|
+
gh api repos/{owner}/{repo}/pulls/{number}/comments
|
|
896
|
+
\`\`\`
|
|
897
|
+
|
|
898
|
+
3. **Understand each piece of feedback** before making changes — read carefully and understand the reviewer's intent
|
|
899
|
+
|
|
900
|
+
4. **Implement the requested fixes** and address each review comment:
|
|
901
|
+
- Make the necessary code changes to address each point
|
|
902
|
+
- Respond to questions with explanations
|
|
903
|
+
- Ensure all requested changes are addressed
|
|
904
|
+
|
|
905
|
+
**IMPORTANT: Commit and push frequently!**
|
|
906
|
+
- Commit after each fix or logical group of changes
|
|
907
|
+
- Push after every 1-2 commits to save your work
|
|
908
|
+
|
|
909
|
+
\`\`\`bash
|
|
910
|
+
git add -A && prlt commit "fix: address PR review feedback" && git push
|
|
911
|
+
\`\`\``,
|
|
912
|
+
endPrompt: `After addressing all feedback:
|
|
913
|
+
|
|
914
|
+
1. **Commit your changes**:
|
|
915
|
+
\`\`\`bash
|
|
916
|
+
git add -A
|
|
917
|
+
prlt commit "fix: address PR review feedback"
|
|
918
|
+
\`\`\`
|
|
919
|
+
|
|
920
|
+
2. **Push your changes**:
|
|
921
|
+
\`\`\`bash
|
|
922
|
+
git push
|
|
923
|
+
\`\`\`
|
|
924
|
+
|
|
925
|
+
3. **Optionally reply to resolved review threads** using the GitHub API:
|
|
926
|
+
\`\`\`bash
|
|
927
|
+
gh api repos/{owner}/{repo}/pulls/{number}/comments
|
|
928
|
+
\`\`\`
|
|
929
|
+
|
|
930
|
+
The PR will be updated automatically with your pushed changes.`,
|
|
931
|
+
suggestedForCategories: ['completed'],
|
|
932
|
+
defaultMoveToCategory: 'started',
|
|
933
|
+
modifiesCode: true,
|
|
934
|
+
position: 6,
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
id: 'explore-cli',
|
|
938
|
+
name: 'Explore CLI',
|
|
939
|
+
description: 'AI QA agent that autonomously explores the interactive CLI to discover bugs',
|
|
940
|
+
prompt: `${PRLT_USAGE_RULE}
|
|
941
|
+
|
|
942
|
+
---
|
|
943
|
+
|
|
944
|
+
# Action: Explore CLI (Autonomous QA)
|
|
945
|
+
|
|
946
|
+
You are an AI QA tester for the prlt CLI. You have access to a tmux session where the CLI is running.
|
|
947
|
+
Your job is to **systematically explore every menu, try every option, and find bugs**.
|
|
948
|
+
|
|
949
|
+
## Your Tools
|
|
950
|
+
|
|
951
|
+
- **tmux_send_keys** — send keystrokes to the tmux session (typing, arrows, Enter, Escape, Ctrl+C, etc.)
|
|
952
|
+
- **tmux_capture_pane** — read the current terminal screen to see what's displayed
|
|
953
|
+
- **tmux_start_session** — start a new tmux session to run the CLI in
|
|
954
|
+
- **tmux_list_sessions** — list active tmux sessions
|
|
955
|
+
- **ticket_create** — file a bug ticket when you find something broken
|
|
956
|
+
|
|
957
|
+
## Getting Started
|
|
958
|
+
|
|
959
|
+
1. Start a tmux session for testing:
|
|
960
|
+
\`\`\`
|
|
961
|
+
tmux_start_session({ session: "qa-test", command: "prlt" })
|
|
962
|
+
\`\`\`
|
|
963
|
+
2. Wait a moment, then capture the screen to see the main menu:
|
|
964
|
+
\`\`\`
|
|
965
|
+
tmux_capture_pane({ session: "qa-test" })
|
|
966
|
+
\`\`\`
|
|
967
|
+
3. Begin systematic exploration.
|
|
968
|
+
|
|
969
|
+
## Exploration Strategies
|
|
970
|
+
|
|
971
|
+
Work through these systematically. After each action, always capture the screen to see the result.
|
|
972
|
+
|
|
973
|
+
### 1. Navigate Every Top-Level Menu Item
|
|
974
|
+
- Use arrow keys (Up/Down) to highlight each option
|
|
975
|
+
- Press Enter to select each one
|
|
976
|
+
- Capture the screen to see what happens
|
|
977
|
+
- Press Escape or Ctrl+C to go back
|
|
978
|
+
|
|
979
|
+
### 2. Test Every Submenu Option
|
|
980
|
+
- For each menu item, explore all sub-options
|
|
981
|
+
- Try selecting each choice in every list/menu
|
|
982
|
+
|
|
983
|
+
### 3. Test Input Handling
|
|
984
|
+
- **Valid inputs**: Normal expected values
|
|
985
|
+
- **Empty inputs**: Just press Enter without typing anything
|
|
986
|
+
- **Invalid inputs**: Random strings, numbers where text is expected, etc.
|
|
987
|
+
- **Long strings**: Very long ticket titles, descriptions (100+ characters)
|
|
988
|
+
- **Special characters**: Quotes, backslashes, Unicode (emojis, CJK characters)
|
|
989
|
+
- **SQL injection-like**: \`'; DROP TABLE --\` style strings
|
|
990
|
+
- **Boundary values**: 0, -1, 999999, etc. for numeric inputs
|
|
991
|
+
|
|
992
|
+
### 4. Test Cancellation Flows
|
|
993
|
+
- Press Ctrl+C mid-flow (should exit gracefully, no crash)
|
|
994
|
+
- Press Escape in menus (should go back)
|
|
995
|
+
- Press q or Q in menus (common quit shortcut)
|
|
996
|
+
- Rapidly press Escape multiple times
|
|
997
|
+
|
|
998
|
+
### 5. Test Navigation Edge Cases
|
|
999
|
+
- Rapid arrow key presses (Up Up Up Down Down Down)
|
|
1000
|
+
- Arrow keys at the top/bottom of lists (should not crash)
|
|
1001
|
+
- Tab key in various contexts
|
|
1002
|
+
- Home/End keys
|
|
1003
|
+
|
|
1004
|
+
### 6. Test Error Recovery
|
|
1005
|
+
- Navigate to ticket operations without any tickets
|
|
1006
|
+
- Try to create items with duplicate names
|
|
1007
|
+
- Try operations on non-existent IDs
|
|
1008
|
+
|
|
1009
|
+
## What to Look For
|
|
1010
|
+
|
|
1011
|
+
- **Crashes**: Unhandled exceptions, stack traces visible on screen
|
|
1012
|
+
- **Rendering glitches**: Items cut off, wrong alignment, overlapping text, garbled output
|
|
1013
|
+
- **Wrong data**: Incorrect values displayed, stale data, missing information
|
|
1014
|
+
- **Missing options**: Menu items that should exist but don't
|
|
1015
|
+
- **Broken navigation**: Arrow keys don't work, can't go back, infinite loops
|
|
1016
|
+
- **Unhelpful errors**: Generic "Error" with no details, or raw error objects
|
|
1017
|
+
- **Hangs**: Screen freezes, no response to input (wait 10 seconds before declaring a hang)
|
|
1018
|
+
- **Screen overflow**: Content pushed off screen, no scrolling available
|
|
1019
|
+
- **Partial renders**: Screen shows half-drawn UI elements
|
|
1020
|
+
- **State corruption**: Operations succeed but data is wrong afterward
|
|
1021
|
+
|
|
1022
|
+
## Cross-Validation
|
|
1023
|
+
|
|
1024
|
+
Compare what you see on screen with what the MCP tools return:
|
|
1025
|
+
- After creating a ticket via the interactive menu, use \`ticket_list\` to verify it was created correctly
|
|
1026
|
+
- After moving a ticket, verify the status changed via \`ticket_show\`
|
|
1027
|
+
- Check that counts displayed in menus match actual data
|
|
1028
|
+
|
|
1029
|
+
## When You Find a Bug
|
|
1030
|
+
|
|
1031
|
+
1. **Document exact reproduction steps** — which keys you pressed, in what order
|
|
1032
|
+
2. **Capture the screen** showing the bug
|
|
1033
|
+
3. **File a ticket** using the ticket_create MCP tool:
|
|
1034
|
+
- Title: Clear description of the bug
|
|
1035
|
+
- Category: "bug"
|
|
1036
|
+
- Priority: P1 for crashes/data loss, P2 for rendering/UX issues, P3 for minor issues
|
|
1037
|
+
- Description: Include exact reproduction steps, screen capture, and expected vs actual behavior
|
|
1038
|
+
|
|
1039
|
+
## Session Management
|
|
1040
|
+
|
|
1041
|
+
- If the CLI crashes, restart it: kill the session and start a new one
|
|
1042
|
+
- If you get stuck, use Ctrl+C to escape, or kill and restart the session
|
|
1043
|
+
- Periodically capture the screen even when not expecting changes (catch intermittent issues)
|
|
1044
|
+
|
|
1045
|
+
## Test Prioritization
|
|
1046
|
+
|
|
1047
|
+
Start with the most commonly used flows, then move to edge cases:
|
|
1048
|
+
1. Main menu navigation
|
|
1049
|
+
2. Ticket operations (create, list, view, edit, move)
|
|
1050
|
+
3. Project operations
|
|
1051
|
+
4. Board view
|
|
1052
|
+
5. Work/session operations
|
|
1053
|
+
6. Epic and spec operations
|
|
1054
|
+
7. Settings and configuration
|
|
1055
|
+
8. Edge cases and stress testing`,
|
|
1056
|
+
endPrompt: `## Wrap-Up
|
|
1057
|
+
|
|
1058
|
+
When you've completed your exploration:
|
|
1059
|
+
|
|
1060
|
+
1. **Summarize your findings**: List all bugs found with their ticket IDs
|
|
1061
|
+
2. **Note areas not tested**: If you couldn't reach certain features, list them
|
|
1062
|
+
3. **Rate overall CLI quality**: Brief assessment of stability, UX, and completeness
|
|
1063
|
+
|
|
1064
|
+
Output a structured summary:
|
|
1065
|
+
\`\`\`
|
|
1066
|
+
## Exploratory QA Summary
|
|
1067
|
+
|
|
1068
|
+
### Bugs Filed
|
|
1069
|
+
- TKT-XXX: [brief description]
|
|
1070
|
+
- TKT-YYY: [brief description]
|
|
1071
|
+
|
|
1072
|
+
### Areas Tested
|
|
1073
|
+
- [ ] Main menu navigation
|
|
1074
|
+
- [ ] Ticket CRUD
|
|
1075
|
+
- [ ] Project operations
|
|
1076
|
+
- [ ] Board view
|
|
1077
|
+
- [ ] Work sessions
|
|
1078
|
+
- [ ] Epics & specs
|
|
1079
|
+
- [ ] Error handling
|
|
1080
|
+
- [ ] Input validation
|
|
1081
|
+
|
|
1082
|
+
### Areas Not Tested
|
|
1083
|
+
- [list any areas you couldn't reach]
|
|
1084
|
+
|
|
1085
|
+
### Overall Assessment
|
|
1086
|
+
[Brief quality assessment]
|
|
1087
|
+
\`\`\`
|
|
1088
|
+
|
|
1089
|
+
Clean up your tmux session:
|
|
1090
|
+
\`\`\`
|
|
1091
|
+
tmux_kill_session({ session: "qa-test" })
|
|
1092
|
+
\`\`\``,
|
|
1093
|
+
suggestedForCategories: [],
|
|
1094
|
+
modifiesCode: false,
|
|
1095
|
+
position: 8,
|
|
1096
|
+
},
|
|
696
1097
|
{
|
|
697
1098
|
id: 'test',
|
|
698
1099
|
name: 'Write Tests',
|
|
@@ -733,56 +1134,7 @@ git add -A && prlt commit "add tests for X" && git push
|
|
|
733
1134
|
**IMPORTANT:** Use the global \`prlt\` command.`,
|
|
734
1135
|
suggestedForCategories: ['started', 'completed'],
|
|
735
1136
|
modifiesCode: true,
|
|
736
|
-
position:
|
|
737
|
-
},
|
|
738
|
-
{
|
|
739
|
-
id: 'review',
|
|
740
|
-
name: 'Code Review',
|
|
741
|
-
description: 'Review the implementation for issues',
|
|
742
|
-
prompt: `Review this ticket's implementation thoroughly:
|
|
743
|
-
- Check for bugs, edge cases, and potential issues
|
|
744
|
-
- Look for security vulnerabilities
|
|
745
|
-
- Verify it meets all acceptance criteria
|
|
746
|
-
- Check code quality and maintainability
|
|
747
|
-
- Suggest improvements if appropriate
|
|
748
|
-
|
|
749
|
-
Output a review summary with your findings and any concerns.`,
|
|
750
|
-
endPrompt: `When you have finished reviewing, output a detailed review summary with:
|
|
751
|
-
- ✅ What looks good
|
|
752
|
-
- ⚠️ Concerns or potential issues
|
|
753
|
-
- 🔧 Suggested improvements
|
|
754
|
-
- 📋 Verdict: Approve, Request Changes, or Needs Discussion
|
|
755
|
-
|
|
756
|
-
No commits are needed for code review.`,
|
|
757
|
-
suggestedForCategories: ['started', 'completed'],
|
|
758
|
-
modifiesCode: false,
|
|
759
|
-
position: 5,
|
|
760
|
-
},
|
|
761
|
-
{
|
|
762
|
-
id: 'revise',
|
|
763
|
-
name: 'Revise',
|
|
764
|
-
description: 'Address PR feedback and review comments',
|
|
765
|
-
prompt: `${PRLT_USAGE_RULE}
|
|
766
|
-
|
|
767
|
-
---
|
|
768
|
-
|
|
769
|
-
# Action: Revise
|
|
770
|
-
|
|
771
|
-
Address the feedback on this ticket's pull request:
|
|
772
|
-
- Review all comments and requested changes carefully
|
|
773
|
-
- Make the necessary code changes to address each point
|
|
774
|
-
- Respond to questions with explanations
|
|
775
|
-
- Push updates to the PR branch
|
|
776
|
-
- Mark resolved conversations as resolved`,
|
|
777
|
-
endPrompt: `After addressing the feedback:
|
|
778
|
-
1. Commit your changes using \`prlt commit "your message"\`
|
|
779
|
-
2. Push your changes: \`git push\`
|
|
780
|
-
|
|
781
|
-
The PR will be updated automatically.`,
|
|
782
|
-
suggestedForCategories: ['completed'],
|
|
783
|
-
defaultMoveToCategory: 'started',
|
|
784
|
-
modifiesCode: true,
|
|
785
|
-
position: 6,
|
|
1137
|
+
position: 7,
|
|
786
1138
|
},
|
|
787
1139
|
];
|
|
788
1140
|
// Use INSERT OR REPLACE to always update builtin actions with latest prompts
|
|
@@ -16,6 +16,7 @@ export declare class DependencyStorage {
|
|
|
16
16
|
deleteTicketDependency(ticketId: string, dependsOnTicketId: string, dependencyType?: TicketDependencyType): Promise<void>;
|
|
17
17
|
/**
|
|
18
18
|
* List dependencies for a ticket.
|
|
19
|
+
* For relates_to dependencies, also returns reverse relationships (symmetric).
|
|
19
20
|
*/
|
|
20
21
|
listTicketDependencies(ticketId: string): Promise<TicketDependency[]>;
|
|
21
22
|
/**
|
|
@@ -63,14 +63,22 @@ export class DependencyStorage {
|
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
65
|
* List dependencies for a ticket.
|
|
66
|
+
* For relates_to dependencies, also returns reverse relationships (symmetric).
|
|
66
67
|
*/
|
|
67
68
|
async listTicketDependencies(ticketId) {
|
|
68
69
|
const rows = this.ctx.db.prepare(`
|
|
69
70
|
SELECT ticket_id, depends_on_ticket_id, dependency_type, created_at
|
|
70
71
|
FROM ${T.ticket_dependencies}
|
|
71
72
|
WHERE ticket_id = ?
|
|
73
|
+
|
|
74
|
+
UNION
|
|
75
|
+
|
|
76
|
+
SELECT depends_on_ticket_id AS ticket_id, ticket_id AS depends_on_ticket_id, dependency_type, created_at
|
|
77
|
+
FROM ${T.ticket_dependencies}
|
|
78
|
+
WHERE depends_on_ticket_id = ? AND dependency_type = 'relates_to'
|
|
79
|
+
|
|
72
80
|
ORDER BY created_at DESC
|
|
73
|
-
`).all(ticketId);
|
|
81
|
+
`).all(ticketId, ticketId);
|
|
74
82
|
return rows.map((row) => ({
|
|
75
83
|
ticketId: row.ticket_id,
|
|
76
84
|
dependsOnTicketId: row.depends_on_ticket_id,
|
|
@@ -92,7 +100,7 @@ export class DependencyStorage {
|
|
|
92
100
|
LEFT JOIN ${T.workflow_statuses} ws ON t.status_id = ws.id
|
|
93
101
|
WHERE d.ticket_id = ? AND d.dependency_type = 'blocks'
|
|
94
102
|
`).all(ticketId);
|
|
95
|
-
return Promise.all(rows.map((row) => rowToTicket(this.ctx.
|
|
103
|
+
return Promise.all(rows.map((row) => rowToTicket(this.ctx.drizzle, row)));
|
|
96
104
|
}
|
|
97
105
|
/**
|
|
98
106
|
* Get tickets that depend on this ticket (blocking).
|
|
@@ -108,7 +116,7 @@ export class DependencyStorage {
|
|
|
108
116
|
LEFT JOIN ${T.workflow_statuses} ws ON t.status_id = ws.id
|
|
109
117
|
WHERE d.depends_on_ticket_id = ? AND d.dependency_type = 'blocks'
|
|
110
118
|
`).all(ticketId);
|
|
111
|
-
return Promise.all(rows.map((row) => rowToTicket(this.ctx.
|
|
119
|
+
return Promise.all(rows.map((row) => rowToTicket(this.ctx.drizzle, row)));
|
|
112
120
|
}
|
|
113
121
|
/**
|
|
114
122
|
* Check if a ticket is blocked by incomplete dependencies.
|
|
@@ -186,7 +186,7 @@ export class EpicStorage {
|
|
|
186
186
|
WHERE t.project_id = ? AND t.epic_id = ?
|
|
187
187
|
ORDER BY ws.position, t.position ASC, t.created_at ASC
|
|
188
188
|
`).all(projectId, epicId);
|
|
189
|
-
return Promise.all(rows.map((row) => rowToTicket(this.ctx.
|
|
189
|
+
return Promise.all(rows.map((row) => rowToTicket(this.ctx.drizzle, row)));
|
|
190
190
|
}
|
|
191
191
|
/**
|
|
192
192
|
* Link a ticket to an epic.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Helper functions for converting database rows to domain types.
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
4
|
+
import type { DrizzleDB } from '../../database/drizzle.js';
|
|
5
5
|
import { AcceptanceCriterion, Spec, StateCategory, Ticket } from '../types.js';
|
|
6
6
|
import { SpecRow, TicketRow, WorkflowStatusRow } from './types.js';
|
|
7
7
|
/**
|
|
@@ -16,13 +16,13 @@ import { SpecRow, TicketRow, WorkflowStatusRow } from './types.js';
|
|
|
16
16
|
export declare function wrapSqliteError(entityType: string, operation: 'create' | 'update' | 'delete', err: unknown): never;
|
|
17
17
|
/**
|
|
18
18
|
* Convert a database row to a Ticket object.
|
|
19
|
-
* Fetches related data (subtasks, metadata, status info).
|
|
19
|
+
* Fetches related data (subtasks, metadata, status info) using Drizzle ORM.
|
|
20
20
|
*/
|
|
21
|
-
export declare function rowToTicket(
|
|
21
|
+
export declare function rowToTicket(drizzle: DrizzleDB, row: TicketRow): Promise<Ticket>;
|
|
22
22
|
/**
|
|
23
23
|
* Get acceptance criteria for a ticket (sync version).
|
|
24
24
|
*/
|
|
25
|
-
export declare function getAcceptanceCriteriaSync(
|
|
25
|
+
export declare function getAcceptanceCriteriaSync(drizzle: DrizzleDB, ticketId: string): AcceptanceCriterion[];
|
|
26
26
|
/**
|
|
27
27
|
* Convert a database row to a Spec object.
|
|
28
28
|
*/
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Helper functions for converting database rows to domain types.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { eq, asc } from 'drizzle-orm';
|
|
5
|
+
import { pmoSubtasks, pmoTicketMetadata, pmoWorkflowStatuses, pmoTicketAcceptanceCriteria, } from '../../database/drizzle-schema.js';
|
|
5
6
|
import { PMOError, normalizePriority, } from '../types.js';
|
|
6
7
|
/**
|
|
7
8
|
* Check if an error is a SQLite UNIQUE constraint violation.
|
|
@@ -61,31 +62,37 @@ export function wrapSqliteError(entityType, operation, err) {
|
|
|
61
62
|
// Re-throw unknown errors
|
|
62
63
|
throw err;
|
|
63
64
|
}
|
|
64
|
-
const T = PMO_TABLES;
|
|
65
65
|
/**
|
|
66
66
|
* Convert a database row to a Ticket object.
|
|
67
|
-
* Fetches related data (subtasks, metadata, status info).
|
|
67
|
+
* Fetches related data (subtasks, metadata, status info) using Drizzle ORM.
|
|
68
68
|
*/
|
|
69
|
-
export async function rowToTicket(
|
|
69
|
+
export async function rowToTicket(drizzle, row) {
|
|
70
70
|
// Get subtasks
|
|
71
|
-
const
|
|
72
|
-
.
|
|
73
|
-
.
|
|
71
|
+
const subtaskRows = drizzle
|
|
72
|
+
.select()
|
|
73
|
+
.from(pmoSubtasks)
|
|
74
|
+
.where(eq(pmoSubtasks.ticketId, row.id))
|
|
75
|
+
.orderBy(asc(pmoSubtasks.position))
|
|
76
|
+
.all();
|
|
74
77
|
// Get metadata
|
|
75
|
-
const metaRows =
|
|
76
|
-
.
|
|
77
|
-
.
|
|
78
|
+
const metaRows = drizzle
|
|
79
|
+
.select({ key: pmoTicketMetadata.key, value: pmoTicketMetadata.value })
|
|
80
|
+
.from(pmoTicketMetadata)
|
|
81
|
+
.where(eq(pmoTicketMetadata.ticketId, row.id))
|
|
82
|
+
.all();
|
|
78
83
|
const metadata = {};
|
|
79
84
|
for (const m of metaRows) {
|
|
80
|
-
metadata[m.key] = m.value;
|
|
85
|
+
metadata[m.key] = m.value || '';
|
|
81
86
|
}
|
|
82
87
|
// Get status info from workflow_statuses
|
|
83
88
|
let statusName;
|
|
84
89
|
let statusCategory;
|
|
85
90
|
if (row.status_id) {
|
|
86
|
-
const statusRow =
|
|
87
|
-
.
|
|
88
|
-
.
|
|
91
|
+
const statusRow = drizzle
|
|
92
|
+
.select({ name: pmoWorkflowStatuses.name, category: pmoWorkflowStatuses.category })
|
|
93
|
+
.from(pmoWorkflowStatuses)
|
|
94
|
+
.where(eq(pmoWorkflowStatuses.id, row.status_id))
|
|
95
|
+
.get();
|
|
89
96
|
if (statusRow) {
|
|
90
97
|
statusName = statusRow.name;
|
|
91
98
|
statusCategory = statusRow.category;
|
|
@@ -126,14 +133,14 @@ export async function rowToTicket(db, row) {
|
|
|
126
133
|
branch: row.branch || undefined,
|
|
127
134
|
specId: row.spec_id || undefined,
|
|
128
135
|
epicId: row.epic_id || undefined,
|
|
129
|
-
subtasks:
|
|
136
|
+
subtasks: subtaskRows.map((st) => ({
|
|
130
137
|
id: st.id,
|
|
131
138
|
title: st.title,
|
|
132
|
-
done: st.done
|
|
139
|
+
done: st.done ?? false,
|
|
133
140
|
})),
|
|
134
141
|
labels,
|
|
135
142
|
metadata,
|
|
136
|
-
acceptanceCriteria: getAcceptanceCriteriaSync(
|
|
143
|
+
acceptanceCriteria: getAcceptanceCriteriaSync(drizzle, row.id),
|
|
137
144
|
createdAt: new Date(row.created_at),
|
|
138
145
|
updatedAt: new Date(row.updated_at),
|
|
139
146
|
lastSyncedFromSpec: row.last_synced_from_spec
|
|
@@ -148,18 +155,21 @@ export async function rowToTicket(db, row) {
|
|
|
148
155
|
/**
|
|
149
156
|
* Get acceptance criteria for a ticket (sync version).
|
|
150
157
|
*/
|
|
151
|
-
export function getAcceptanceCriteriaSync(
|
|
152
|
-
const rows =
|
|
153
|
-
.
|
|
154
|
-
.
|
|
158
|
+
export function getAcceptanceCriteriaSync(drizzle, ticketId) {
|
|
159
|
+
const rows = drizzle
|
|
160
|
+
.select()
|
|
161
|
+
.from(pmoTicketAcceptanceCriteria)
|
|
162
|
+
.where(eq(pmoTicketAcceptanceCriteria.ticketId, ticketId))
|
|
163
|
+
.orderBy(asc(pmoTicketAcceptanceCriteria.position))
|
|
164
|
+
.all();
|
|
155
165
|
return rows.map((row) => ({
|
|
156
166
|
id: row.id,
|
|
157
|
-
ticketId: row.
|
|
167
|
+
ticketId: row.ticketId,
|
|
158
168
|
criterion: row.criterion,
|
|
159
|
-
verifiable: row.verifiable
|
|
160
|
-
verified: row.verified
|
|
161
|
-
verifiedAt: row.
|
|
162
|
-
verifiedBy: row.
|
|
169
|
+
verifiable: row.verifiable ?? true,
|
|
170
|
+
verified: row.verified ?? false,
|
|
171
|
+
verifiedAt: row.verifiedAt ? new Date(row.verifiedAt) : undefined,
|
|
172
|
+
verifiedBy: row.verifiedBy || undefined,
|
|
163
173
|
position: row.position,
|
|
164
174
|
}));
|
|
165
175
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Project operations.
|
|
3
3
|
* Board columns are now derived from workflow statuses (single source of truth).
|
|
4
|
+
*
|
|
5
|
+
* This module uses Drizzle ORM for type-safe database queries.
|
|
4
6
|
*/
|
|
5
7
|
import { Board, BoardConfig, Project, ProjectFilter } from '../types.js';
|
|
6
8
|
import { StorageContext } from './types.js';
|