jettypod 4.1.2 → 4.1.4

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 (179) hide show
  1. package/.nvmrc +1 -0
  2. package/docs/COMPLETE-TESTING-STRATEGY.md +970 -0
  3. package/docs/DECISIONS.md +10 -12
  4. package/docs/NODE_VERSION.md +83 -0
  5. package/docs/TDD-INFRASTRUCTURE-STRATEGY.md +1374 -0
  6. package/docs/TESTING-FOR-NON-ENGINEERS.md +1588 -0
  7. package/docs/TESTING-STRATEGY-AUDIT.md +698 -0
  8. package/hooks/post-checkout +17 -0
  9. package/hooks/post-merge +17 -0
  10. package/hooks/pre-commit +30 -0
  11. package/jettypod.js +259 -120
  12. package/lib/coverage-tracker.js +218 -0
  13. package/lib/database.js +2 -0
  14. package/lib/db-export.js +192 -0
  15. package/lib/db-import.js +193 -0
  16. package/lib/external-transition-handler.js +32 -0
  17. package/lib/git-hook-helpers.js +174 -0
  18. package/lib/git-root.js +90 -0
  19. package/lib/infrastructure-chore-generator.js +45 -0
  20. package/lib/install-hooks.js +52 -0
  21. package/lib/jettypod-backup.js +238 -0
  22. package/lib/merge-lock.js +193 -0
  23. package/lib/migrations/012-add-worktree-path.js +38 -0
  24. package/lib/migrations/013-worktrees-table.js +86 -0
  25. package/lib/migrations/014-migrate-worktree-data.js +161 -0
  26. package/lib/migrations/015-merge-locks-table.js +67 -0
  27. package/lib/pattern-finder.js +152 -0
  28. package/lib/process-manager.js +140 -0
  29. package/lib/production-standards-reader.js +13 -2
  30. package/lib/production-standards-writer.js +85 -0
  31. package/lib/skills/feature-planning/dry-run-validator.js +135 -0
  32. package/lib/skills/feature-planning/validation-formatter.js +160 -0
  33. package/lib/smart-conflict-detection.js +168 -0
  34. package/lib/smart-fetch-rebase.js +614 -0
  35. package/lib/step-definition-parser.js +76 -0
  36. package/lib/unit-test-generator.js +232 -0
  37. package/lib/verification-command-generator.js +66 -0
  38. package/lib/worktree-diagnostics.js +413 -0
  39. package/lib/worktree-facade.js +174 -0
  40. package/lib/worktree-manager.js +636 -0
  41. package/lib/worktree-reconciler.js +429 -0
  42. package/package.json +30 -3
  43. package/skills-templates/external-transition/SKILL.md +34 -3
  44. package/skills-templates/feature-planning/SKILL.md +190 -24
  45. package/skills-templates/production-mode/SKILL.md +127 -9
  46. package/skills-templates/speed-mode/SKILL.md +454 -51
  47. package/skills-templates/stable-mode/SKILL.md +285 -76
  48. package/.claude/PROTECT_SKILLS.md +0 -28
  49. package/.claude/settings.json +0 -24
  50. package/.claude/settings.local.json +0 -16
  51. package/.claude/skills/epic-planning/SKILL.md +0 -297
  52. package/.claude/skills/external-transition/SKILL.md +0 -384
  53. package/.claude/skills/feature-planning/SKILL.md +0 -464
  54. package/.claude/skills/production-mode/SKILL.md +0 -369
  55. package/.claude/skills/speed-mode/SKILL.md +0 -481
  56. package/.claude/skills/stable-mode/SKILL.md +0 -713
  57. package/.claude/skills.backup-2025-11-10T23-33-09-368Z/epic-planning/SKILL.md +0 -297
  58. package/.claude/skills.backup-2025-11-10T23-33-09-368Z/feature-planning/SKILL.md +0 -464
  59. package/.claude/skills.backup-2025-11-10T23-33-09-368Z/speed-mode/SKILL.md +0 -467
  60. package/.claude/skills.backup-2025-11-10T23-33-09-368Z/stable-mode/SKILL.md +0 -673
  61. package/.claude/skills.backup-2025-11-11T16-15-10-070Z/epic-discover/SKILL.md +0 -297
  62. package/.claude/skills.backup-2025-11-11T16-42-43-212Z/epic-planning/SKILL.md +0 -297
  63. package/.claude/skills.backup-2025-11-11T16-42-43-212Z/feature-planning/SKILL.md +0 -464
  64. package/.claude/skills.backup-2025-11-11T16-42-43-212Z/speed-mode/SKILL.md +0 -467
  65. package/.claude/skills.backup-2025-11-11T16-42-43-212Z/stable-mode/SKILL.md +0 -673
  66. package/.claude/skills.backup-2025-11-11T17-06-09-783Z/epic-planning/SKILL.md +0 -297
  67. package/.claude/skills.backup-2025-11-11T17-06-09-783Z/feature-planning/SKILL.md +0 -464
  68. package/.claude/skills.backup-2025-11-11T17-06-09-783Z/speed-mode/SKILL.md +0 -467
  69. package/.claude/skills.backup-2025-11-11T17-06-09-783Z/stable-mode/SKILL.md +0 -673
  70. package/.devpod/current-work.json +0 -10
  71. package/.devpod/work.db +0 -0
  72. package/.github/workflows/test-safety.yml +0 -85
  73. package/.jettypod/config.json +0 -5
  74. package/.jettypod/current-work.json +0 -10
  75. package/.jettypod/hooks/README.md +0 -77
  76. package/.jettypod/hooks/protect-claude-md.js +0 -338
  77. package/.jettypod/test-work.db +0 -0
  78. package/.jettypod/work.db +0 -0
  79. package/CLAUDE.md +0 -49
  80. package/SPEED-STABLE-AUDIT.md +0 -853
  81. package/SYSTEM-BEHAVIOR.md +0 -2199
  82. package/TEST_SAFETY_AUDIT.md +0 -314
  83. package/TEST_SAFETY_IMPLEMENTATION.md +0 -97
  84. package/cucumber-report.html +0 -45
  85. package/dist/devpod-linux +0 -0
  86. package/dist/devpod-macos +0 -0
  87. package/dist/devpod-win.exe +0 -0
  88. package/docs/features/jettypod-standards-explained.md +0 -543
  89. package/docs/features/standards-inventory.md +0 -257
  90. package/features/auto-generate-production-chores.feature +0 -13
  91. package/features/backlog-command.feature +0 -26
  92. package/features/backlog-filtering-production.feature +0 -10
  93. package/features/claude-md-protection/steps.js +0 -498
  94. package/features/decisions/index.js +0 -490
  95. package/features/decisions/index.test.js +0 -208
  96. package/features/fix-text-wrapping.feature +0 -42
  97. package/features/git-hooks/git-hooks.feature +0 -30
  98. package/features/git-hooks/index.js +0 -93
  99. package/features/git-hooks/index.test.js +0 -137
  100. package/features/git-hooks/post-commit +0 -56
  101. package/features/git-hooks/post-merge +0 -47
  102. package/features/git-hooks/pre-commit +0 -28
  103. package/features/git-hooks/simple-steps.js +0 -53
  104. package/features/git-hooks/simple-test.feature +0 -10
  105. package/features/git-hooks/steps.js +0 -196
  106. package/features/jettypod-update-command.feature +0 -46
  107. package/features/mode-prompts/index.js +0 -95
  108. package/features/mode-prompts/simple-steps.js +0 -44
  109. package/features/mode-prompts/simple-test.feature +0 -9
  110. package/features/mode-prompts/validation.test.js +0 -120
  111. package/features/multiple-claude-instances.feature +0 -121
  112. package/features/production-mode-skill.feature +0 -121
  113. package/features/refactor-mode/steps.js +0 -217
  114. package/features/refactor-mode.feature +0 -49
  115. package/features/simplify-external-transition.feature +0 -166
  116. package/features/skills-update/index.test.js +0 -216
  117. package/features/step_definitions/backlog-command.steps.js +0 -37
  118. package/features/step_definitions/fix-text-wrapping.steps.js +0 -271
  119. package/features/step_definitions/multiple-claude-instances.steps.js +0 -621
  120. package/features/step_definitions/production-mode-skill.steps.js +0 -862
  121. package/features/step_definitions/simplify-external-transition.steps.js +0 -370
  122. package/features/step_definitions/terminal-logo.steps.js +0 -145
  123. package/features/step_definitions/update-command.steps.js +0 -183
  124. package/features/support/hooks.js +0 -9
  125. package/features/terminal-logo/index.js +0 -39
  126. package/features/terminal-logo/terminal-logo.feature +0 -30
  127. package/features/update-command/index.js +0 -181
  128. package/features/update-command/index.test.js +0 -225
  129. package/features/work-commands/bug-workflow-display.feature +0 -22
  130. package/features/work-commands/index.js +0 -498
  131. package/features/work-commands/simple-steps.js +0 -69
  132. package/features/work-commands/stable-tests.feature +0 -57
  133. package/features/work-commands/steps.js +0 -1174
  134. package/features/work-commands/validation.test.js +0 -88
  135. package/features/work-commands/work-commands.feature +0 -13
  136. package/features/work-tracking/discovery-validation.test.js +0 -228
  137. package/features/work-tracking/index.js +0 -1921
  138. package/features/work-tracking/mode-required.feature +0 -112
  139. package/features/work-tracking/phase-tracking.test.js +0 -482
  140. package/features/work-tracking/prototype-tracking.test.js +0 -485
  141. package/features/work-tracking/tree-view.test.js +0 -310
  142. package/features/work-tracking/work-set-mode.feature +0 -71
  143. package/features/work-tracking/work-start-mode.feature +0 -88
  144. package/full-test.txt +0 -0
  145. package/lib/bug-workflow.test.js +0 -177
  146. package/lib/claudemd.test.js +0 -195
  147. package/lib/config.test.js +0 -511
  148. package/lib/constants.test.js +0 -164
  149. package/lib/current-work.test.js +0 -146
  150. package/lib/database-project-config.test.js +0 -111
  151. package/lib/database.test.js +0 -106
  152. package/lib/decisions-generator.test.js +0 -457
  153. package/lib/decisions-helpers.test.js +0 -310
  154. package/lib/git-coordinator.js +0 -167
  155. package/lib/git.test.js +0 -145
  156. package/lib/migrations/002-default-work-item-modes.test.js +0 -351
  157. package/lib/production-chore-generator.test.js +0 -432
  158. package/lib/production-context-detector.test.js +0 -277
  159. package/lib/production-scenario-appender.test.js +0 -235
  160. package/lib/production-scenario-validator.test.js +0 -246
  161. package/lib/production-standards-reader.test.js +0 -270
  162. package/lib/project-state.test.js +0 -92
  163. package/lib/push-queue.js +0 -417
  164. package/lib/queue-processor.js +0 -74
  165. package/lib/test-helpers.js +0 -202
  166. package/lib/test-helpers.test.js +0 -255
  167. package/prototypes/2025-01-11-production-mode-autonomous.js +0 -119
  168. package/prototypes/2025-01-11-production-mode-collaborative.js +0 -166
  169. package/prototypes/2025-01-11-production-mode-guided.js +0 -217
  170. package/prototypes/2025-01-11-production-mode-smart-context.js +0 -347
  171. package/prototypes/2025-01-11-production-standards-example.md +0 -204
  172. package/prototypes/2025-11-10-backlog-filtering-tree-aware.js +0 -242
  173. package/prototypes/test/index.html +0 -1
  174. package/setup-dist-repo.sh +0 -68
  175. package/test-production-standards-engine.js +0 -130
  176. package/test-results.json +0 -2195
  177. package/test-safety-check.sh +0 -80
  178. package/work-item-tracking-plan.md +0 -199
  179. /package/{.jettypod/devpod.db → jettypod.db} +0 -0
@@ -1,498 +0,0 @@
1
- const { execSync } = require('child_process');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const { getDb, getDbPath, getJettypodDir } = require('../../lib/database');
5
- const { getCurrentWork, setCurrentWork, clearCurrentWork, getCurrentWorkPath } = require('../../lib/current-work');
6
- const { VALID_STATUSES } = require('../../lib/constants');
7
- const { updateCurrentWork } = require('../../lib/claudemd');
8
- const { createFeatureBranchName, createOrCheckoutBranch } = require('../../lib/git');
9
- const config = require('../../lib/config');
10
- const { getBugWorkflowForTerminal } = require('../../lib/bug-workflow');
11
-
12
- /**
13
- * Get paths to JettyPod files and directories
14
- * @returns {Object} Paths object with jettypodDir, currentWorkPath, dbPath, claudePath
15
- */
16
- function getPaths() {
17
- return {
18
- jettypodDir: getJettypodDir(),
19
- currentWorkPath: getCurrentWorkPath(),
20
- dbPath: getDbPath(),
21
- claudePath: path.join(process.cwd(), 'CLAUDE.md')
22
- };
23
- }
24
-
25
- /**
26
- * Start work on a work item
27
- * @param {number} id - Work item ID to start
28
- * @returns {Promise<Object>} Work item, current work, and branch name
29
- * @throws {Error} If ID is invalid, JettyPod not initialized, database missing, or work item not found
30
- */
31
- function startWork(id) {
32
- // Input validation
33
- if (!id || isNaN(id) || id < 1) {
34
- return Promise.reject(new Error('Invalid work item ID'));
35
- }
36
-
37
- const paths = getPaths();
38
-
39
- // Check jettypod directory exists
40
- if (!fs.existsSync(paths.jettypodDir)) {
41
- return Promise.reject(new Error('JettyPod not initialized. Run: jettypod init'));
42
- }
43
-
44
- // Check database exists
45
- if (!fs.existsSync(paths.dbPath)) {
46
- return Promise.reject(new Error('Work database not found. Run: jettypod init'));
47
- }
48
-
49
- const db = getDb();
50
-
51
- return new Promise((resolve, reject) => {
52
- // Get work item
53
- db.get(`
54
- SELECT w.*,
55
- p.title as parent_title, p.id as parent_id, p.scenario_file as parent_scenario_file, p.mode as parent_mode,
56
- e.title as epic_title, e.id as epic_id
57
- FROM work_items w
58
- LEFT JOIN work_items p ON w.parent_id = p.id
59
- LEFT JOIN work_items e ON w.epic_id = e.id AND w.epic_id != w.id
60
- WHERE w.id = ?
61
- `, [id], (err, workItem) => {
62
- if (err) {
63
- return reject(new Error(`Database error: ${err.message}`));
64
- }
65
-
66
- if (!workItem) {
67
- return reject(new Error(`Work item #${id} not found`));
68
- }
69
-
70
- // Update status to in_progress if currently todo
71
- const finalStatus = workItem.status === 'todo' ? 'in_progress' : workItem.status;
72
-
73
- const updateAndContinue = () => {
74
- // Create current work file
75
- const currentWork = {
76
- id: workItem.id,
77
- title: workItem.title,
78
- type: workItem.type,
79
- status: finalStatus,
80
- parent_id: workItem.parent_id,
81
- parent_title: workItem.parent_title,
82
- epic_id: workItem.epic_id,
83
- epic_title: workItem.epic_title
84
- };
85
-
86
- try {
87
- setCurrentWork(currentWork);
88
- } catch (err) {
89
- return reject(new Error(`Failed to write current work file: ${err.message}`));
90
- }
91
-
92
- // Create feature branch
93
- let branchName = null;
94
- try {
95
- branchName = createFeatureBranchName(id, workItem.title);
96
- createOrCheckoutBranch(branchName);
97
- } catch (gitError) {
98
- // Git operations failed, but continue - work tracking still works
99
- console.warn(`Warning: ${gitError.message}`);
100
- }
101
-
102
- // Update CLAUDE.md with work item's mode
103
- // For chores, inherit the parent feature's mode
104
- const modeToUse = (workItem.type === 'chore' && workItem.parent_mode)
105
- ? workItem.parent_mode
106
- : workItem.mode;
107
- updateCurrentWork(currentWork, modeToUse);
108
-
109
- // Display output
110
- let output = `Working on: [#${workItem.id}] ${workItem.title} (${workItem.type})`;
111
- if (workItem.parent_title) {
112
- output = `Working on: [#${workItem.id}] ${workItem.title} (${workItem.type} of #${workItem.parent_id} ${workItem.parent_title})`;
113
- }
114
- console.log(output);
115
-
116
- // Display bug workflow guidance for bugs
117
- // Only display if workItem has a type (defensive check)
118
- if (workItem && workItem.type === 'bug') {
119
- try {
120
- console.log(getBugWorkflowForTerminal());
121
- } catch (err) {
122
- // Silently fail if workflow display fails - don't block work start
123
- // This is a non-critical feature
124
- }
125
- }
126
-
127
- // Display epic discovery guidance for epics with needs_discovery
128
- if (workItem && workItem.type === 'epic' && workItem.needs_discovery) {
129
- // Check if decisions have been recorded
130
- db.all(
131
- `SELECT * FROM discovery_decisions WHERE work_item_id = ?`,
132
- [workItem.id],
133
- (err, decisions) => {
134
- if (err || !decisions || decisions.length === 0) {
135
- console.log('');
136
- console.log('⚠️ This epic needs architectural discovery before building features.');
137
- console.log('');
138
- console.log('💬 Recommended: Talk to Claude Code');
139
- console.log(` Say: "Let's do epic discovery for #${workItem.id}"`);
140
- console.log('');
141
- console.log(' Claude Code will guide you through:');
142
- console.log(' • Suggesting 3 architectural options');
143
- console.log(' • Building prototypes');
144
- console.log(' • Recording your decisions');
145
- console.log('');
146
- }
147
- }
148
- );
149
- }
150
-
151
- // Auto-trigger feature discovery for features in discovery phase
152
- if (workItem && workItem.type === 'feature' && workItem.phase === 'discovery') {
153
- console.log('');
154
- console.log('✨ Feature Discovery Mode');
155
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
156
- console.log('');
157
- console.log('This feature is in discovery phase. Claude Code will help you:');
158
- console.log(' 1. Explore 3 different UX approaches');
159
- console.log(' 2. (Optional) Build throwaway prototypes');
160
- console.log(' 3. Choose the winning approach');
161
- console.log(' 4. Generate BDD scenarios for the happy path');
162
- console.log(' 5. Transition to implementation (speed mode)');
163
- console.log('');
164
- console.log('💬 Claude Code is ready to guide you through feature discovery.');
165
- console.log('');
166
- console.log('📋 The feature-planning skill will automatically activate.');
167
- console.log('');
168
- }
169
-
170
- // Auto-trigger mode skills for chores in speed or stable mode
171
- if (workItem && workItem.type === 'chore' && workItem.parent_id) {
172
- // Validate parent feature exists
173
- if (!workItem.parent_title) {
174
- console.log('');
175
- console.log('⚠️ Warning: Parent feature not found');
176
- console.log('');
177
- console.log(`This chore references parent feature #${workItem.parent_id}, but that feature`);
178
- console.log('does not exist in the database.');
179
- console.log('');
180
- console.log('Suggestion: Check the parent_id or create the missing feature.');
181
- console.log('');
182
- }
183
- // Validate scenario file exists
184
- else if (!workItem.parent_scenario_file) {
185
- console.log('');
186
- console.log('⚠️ Warning: Parent feature has no scenario file');
187
- console.log('');
188
- console.log(`Parent feature #${workItem.parent_id} "${workItem.parent_title}" does not have`);
189
- console.log('a scenario file set.');
190
- console.log('');
191
- console.log('Suggestion: Create a BDD scenario file for the feature and update scenario_file.');
192
- console.log('');
193
- }
194
- else if (!fs.existsSync(path.join(process.cwd(), workItem.parent_scenario_file))) {
195
- console.log('');
196
- console.log('⚠️ Warning: Scenario file not found');
197
- console.log('');
198
- console.log(`Parent feature references scenario file: ${workItem.parent_scenario_file}`);
199
- console.log('but the file does not exist on disk.');
200
- console.log('');
201
- console.log('Suggestion: Create the scenario file or update the feature.scenario_file path.');
202
- console.log('');
203
- }
204
- else if (workItem.mode === 'speed') {
205
- console.log('');
206
- console.log('🚀 Speed Mode Skill Activated');
207
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
208
- console.log('');
209
- console.log('Claude Code will autonomously:');
210
- console.log(' 1. Analyze the BDD scenario for this feature');
211
- console.log(' 2. Analyze the codebase to understand patterns');
212
- console.log(' 3. Propose an implementation approach');
213
- console.log(' 4. Implement until the happy path scenario passes');
214
- console.log(' 5. Generate stable mode chores for comprehensive testing');
215
- console.log('');
216
- console.log('💬 The speed-mode skill is now active.');
217
- console.log(' Claude Code will guide you through implementation.');
218
- console.log('');
219
- } else if (workItem.mode === 'stable') {
220
- console.log('');
221
- console.log('🧪 Stable Mode Skill Activated');
222
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
223
- console.log('');
224
- console.log('Claude Code will autonomously:');
225
- console.log(' 1. Analyze the BDD scenario to implement');
226
- console.log(' 2. Review the existing speed mode implementation');
227
- console.log(' 3. Propose comprehensive error handling approach');
228
- console.log(' 4. Implement with proper validation and edge case coverage');
229
- console.log(' 5. Ensure all BDD scenarios pass');
230
- console.log('');
231
- console.log('💬 The stable-mode skill is now active.');
232
- console.log(' Claude Code will guide you through comprehensive testing.');
233
- console.log('');
234
- }
235
- }
236
-
237
- resolve({ workItem, currentWork, branchName });
238
- };
239
-
240
- // Update status to in_progress if currently todo
241
- if (workItem.status === 'todo') {
242
- db.run(`UPDATE work_items SET status = 'in_progress' WHERE id = ?`, [id], updateAndContinue);
243
- } else {
244
- updateAndContinue();
245
- }
246
- });
247
- });
248
- }
249
-
250
- /**
251
- * Stop work on current work item
252
- * @param {string|null} newStatus - Optional status to set (e.g., 'done', 'blocked')
253
- * @returns {Promise<Object|null>} Work item ID and status, or null if no active work
254
- * @throws {Error} If status is invalid, database missing, or file operations fail
255
- */
256
- function stopWork(newStatus = null) {
257
- const paths = getPaths();
258
-
259
- const currentWork = getCurrentWork();
260
- if (!currentWork) {
261
- console.log('No active work item');
262
- return Promise.resolve(null);
263
- }
264
-
265
- // Validate status if provided
266
- if (newStatus && !VALID_STATUSES.includes(newStatus)) {
267
- return Promise.reject(new Error(`Invalid status: ${newStatus}`));
268
- }
269
-
270
- return new Promise((resolve, reject) => {
271
- if (newStatus) {
272
- if (!fs.existsSync(paths.dbPath)) {
273
- return reject(new Error('Work database not found'));
274
- }
275
-
276
- const db = getDb();
277
- db.run(`UPDATE work_items SET status = ? WHERE id = ?`, [newStatus, currentWork.id], (err) => {
278
- if (err) {
279
- return reject(new Error(`Database error: ${err.message}`));
280
- }
281
-
282
- // Check if this completes all stable mode chores for the parent feature
283
- checkStableModeCompletion(db, currentWork, newStatus, (completionErr) => {
284
- if (completionErr) {
285
- console.warn(`Warning: ${completionErr.message}`);
286
- }
287
-
288
- try {
289
- clearCurrentWork();
290
- } catch (unlinkErr) {
291
- return reject(new Error(`Failed to remove current work file: ${unlinkErr.message}`));
292
- }
293
-
294
- console.log(`Stopped work on #${currentWork.id}, status set to ${newStatus}`);
295
- resolve({ id: currentWork.id, status: newStatus });
296
- });
297
- });
298
- } else {
299
- try {
300
- clearCurrentWork();
301
- } catch (err) {
302
- return reject(new Error(`Failed to remove current work file: ${err.message}`));
303
- }
304
-
305
- console.log(`Stopped work on #${currentWork.id}`);
306
- resolve({ id: currentWork.id });
307
- }
308
- });
309
- }
310
-
311
- /**
312
- * Check if stable mode is complete for a feature and trigger production chore generation
313
- * @param {Object} db - Database connection
314
- * @param {Object} currentWork - Current work item that was just completed
315
- * @param {string} newStatus - Status that was just set
316
- * @param {Function} callback - Callback function
317
- */
318
- async function checkStableModeCompletion(db, currentWork, newStatus, callback) {
319
- // Only check if:
320
- // 1. Current work is a chore
321
- // 2. Status was just set to 'done'
322
- // 3. Current work has a parent (feature)
323
- if (currentWork.type !== 'chore' || newStatus !== 'done' || !currentWork.parent_id) {
324
- return callback(null);
325
- }
326
-
327
- // Get parent feature details and check its mode
328
- db.get(`
329
- SELECT id, title, mode, type
330
- FROM work_items
331
- WHERE id = ?
332
- `, [currentWork.parent_id], async (err, parent) => {
333
- if (err) {
334
- return callback(new Error(`Failed to get parent feature: ${err.message}`));
335
- }
336
-
337
- if (!parent) {
338
- return callback(new Error(`Parent feature #${currentWork.parent_id} not found`));
339
- }
340
-
341
- // Only proceed if parent is a feature in stable mode
342
- if (parent.type !== 'feature' || parent.mode !== 'stable') {
343
- return callback(null);
344
- }
345
-
346
- // Check if all stable chores for this feature are done
347
- db.get(`
348
- SELECT COUNT(*) as incomplete_count
349
- FROM work_items
350
- WHERE parent_id = ?
351
- AND type = 'chore'
352
- AND mode = 'stable'
353
- AND status != 'done'
354
- `, [parent.id], async (countErr, result) => {
355
- if (countErr) {
356
- return callback(new Error(`Failed to check stable chore completion: ${countErr.message}`));
357
- }
358
-
359
- // If all stable chores are done, stable mode is complete
360
- if (result.incomplete_count === 0) {
361
- console.log('');
362
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
363
- console.log('✅ STABLE MODE COMPLETE');
364
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
365
- console.log('');
366
- console.log(`All stable chores for feature #${parent.id} "${parent.title}" are done.`);
367
- console.log('');
368
-
369
- // NOTE: Production mode transition is handled by the stable-mode skill
370
- // The skill will:
371
- // 1. Ask user if they want to add production scenarios
372
- // 2. Add production scenarios to the feature file (BDD hot context)
373
- // 3. Create production chores FROM those scenarios
374
- // 4. Elevate feature to production mode
375
- //
376
- // Production chores are NOT auto-generated from git analysis.
377
- // They come from BDD scenarios in the feature file.
378
- }
379
-
380
- callback(null);
381
- });
382
- });
383
- }
384
-
385
- /**
386
- * Generate production chores and get user confirmation
387
- * @param {Object} feature - Feature object with id and title
388
- */
389
- async function generateAndConfirmProductionChores(feature) {
390
- const { analyzeImplementation, generateProductionChores } = require('../../lib/production-chore-generator');
391
- const readline = require('readline');
392
-
393
- console.log('🔍 Analyzing implementation for production gaps...');
394
- console.log('');
395
-
396
- // Analyze implementation
397
- let analysisResult;
398
- try {
399
- analysisResult = await analyzeImplementation(feature.id);
400
- } catch (analysisErr) {
401
- console.error(`Failed to analyze implementation: ${analysisErr.message}`);
402
- return;
403
- }
404
-
405
- // Display warning if no git commits found
406
- if (analysisResult.warning) {
407
- console.log(`⚠️ ${analysisResult.warning}`);
408
- console.log('');
409
- }
410
-
411
- console.log(`✅ Analyzed ${analysisResult.filesAnalyzed.length} implementation files`);
412
- console.log('');
413
-
414
- // Generate production chore proposals
415
- const proposedChores = generateProductionChores(analysisResult, feature.title);
416
-
417
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
418
- console.log('📋 PROPOSED PRODUCTION CHORES');
419
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
420
- console.log('');
421
- console.log('These production chores will be created:');
422
- console.log('');
423
-
424
- proposedChores.forEach((chore, index) => {
425
- console.log(`${index + 1}. ${chore.title}`);
426
- console.log(` ${chore.description.split('\n')[0]}`);
427
- console.log('');
428
- });
429
-
430
- // Get user confirmation
431
- return new Promise((resolve) => {
432
- const rl = readline.createInterface({
433
- input: process.stdin,
434
- output: process.stdout
435
- });
436
-
437
- rl.question('Create these production chores? (yes/skip): ', async (answer) => {
438
- rl.close();
439
-
440
- const response = answer.trim().toLowerCase();
441
-
442
- if (response === 'yes' || response === 'y') {
443
- console.log('');
444
- console.log('Creating production chores...');
445
-
446
- try {
447
- await createProductionChoresAndElevate(feature, proposedChores);
448
- console.log('');
449
- console.log(`✅ Created ${proposedChores.length} production chores. Feature elevated to production mode.`);
450
- console.log('');
451
- } catch (createErr) {
452
- console.error(`Failed to create production chores: ${createErr.message}`);
453
- }
454
- } else {
455
- console.log('');
456
- console.log('⏭️ Skipped production chore creation');
457
- console.log(' You can generate them later by talking to Claude Code');
458
- console.log('');
459
- }
460
-
461
- resolve();
462
- });
463
- });
464
- }
465
-
466
- /**
467
- * Create production chores and elevate feature to production mode (if external)
468
- * @param {Object} feature - Feature object
469
- * @param {Array} proposedChores - Array of chore proposals
470
- */
471
- async function createProductionChoresAndElevate(feature, proposedChores) {
472
- const { create } = require('../work-tracking');
473
- const projectConfig = config.read();
474
-
475
- // Create production chores
476
- for (const chore of proposedChores) {
477
- await create('chore', chore.title, chore.description, feature.id, null, false);
478
- }
479
-
480
- // Always elevate feature to production mode
481
- // (Feature #611 controls visibility in backlog, not mode elevation)
482
- const db = getDb();
483
- await new Promise((resolve, reject) => {
484
- db.run('UPDATE work_items SET mode = ? WHERE id = ?', ['production', feature.id], (err) => {
485
- if (err) return reject(err);
486
- resolve();
487
- });
488
- });
489
- }
490
-
491
- // Re-export getCurrentWork from shared module for backwards compatibility
492
- // (used by jettypod.js)
493
-
494
- module.exports = {
495
- startWork,
496
- stopWork,
497
- getCurrentWork
498
- };
@@ -1,69 +0,0 @@
1
- const { Given, When, Then } = require('@cucumber/cucumber');
2
- const assert = require('assert');
3
- const fs = require('fs');
4
- const path = require('path');
5
- const { execSync } = require('child_process');
6
- const { resetDb } = require('../../lib/database');
7
-
8
- const testDir = path.join('/tmp', 'jettypod-test-' + Date.now());
9
- let originalDir;
10
-
11
- Given('I have initialized jettypod', function () {
12
- resetDb(); // Clear singleton db to avoid stale connections
13
-
14
- originalDir = process.cwd();
15
- // SAFETY: Only delete if testDir is in /tmp
16
- if (fs.existsSync(testDir) && testDir.startsWith('/tmp/')) {
17
- fs.rmSync(testDir, { recursive: true, force: true });
18
- }
19
- fs.mkdirSync(testDir, { recursive: true });
20
- process.chdir(testDir);
21
-
22
- execSync('git init', { stdio: 'pipe' });
23
- execSync('git config user.email "test@test.com"', { stdio: 'pipe' });
24
- execSync('git config user.name "Test"', { stdio: 'pipe' });
25
- execSync(`node ${path.join(originalDir, 'jettypod.js')} init`, { stdio: 'pipe', env: { ...process.env, NODE_ENV: 'test' } });
26
- });
27
-
28
- Given('I create an epic with id {int}', function (id) {
29
- execSync(`node ${path.join(originalDir, 'jettypod.js')} work create epic "Test Epic"`, { stdio: 'pipe', env: { ...process.env, NODE_ENV: 'test' } });
30
- });
31
-
32
- Given('I create a feature with id {int} under epic {int}', function (featureId, epicId) {
33
- execSync(`node ${path.join(originalDir, 'jettypod.js')} work create feature "Test Feature" "" --parent=${epicId}`, { stdio: 'pipe', env: { ...process.env, NODE_ENV: 'test' } });
34
- });
35
-
36
- When('I start work on item {int}', function (id) {
37
- execSync(`node ${path.join(originalDir, 'jettypod.js')} work start ${id}`, { stdio: 'pipe', env: { ...process.env, NODE_ENV: 'test' } });
38
- });
39
-
40
- Then('the current work file exists', function () {
41
- const currentWorkPath = path.join(testDir, '.jettypod', 'current-work.json');
42
- assert(fs.existsSync(currentWorkPath), 'Current work file does not exist');
43
- });
44
-
45
- Then('the current work contains item {int}', function (id) {
46
- const currentWorkPath = path.join(testDir, '.jettypod', 'current-work.json');
47
- const currentWork = JSON.parse(fs.readFileSync(currentWorkPath, 'utf-8'));
48
- assert.strictEqual(currentWork.id, id);
49
- });
50
-
51
- Then('item {int} status is in_progress or backlog', function (id) {
52
- // Use CLI to check status to avoid database connection issues
53
- try {
54
- const output = execSync(`node ${path.join(originalDir, 'jettypod.js')} work show ${id}`, { encoding: 'utf-8', env: { ...process.env, NODE_ENV: 'test' } });
55
- const statusMatch = output.match(/Status:\s+(\w+)/);
56
- if (!statusMatch) {
57
- throw new Error('Could not find status in output');
58
- }
59
- const status = statusMatch[1];
60
- assert(status === 'in_progress' || status === 'backlog', `Status is ${status}`);
61
- } finally {
62
- // SAFETY: Only delete if testDir is in /tmp
63
- if (fs.existsSync(testDir) && testDir.startsWith('/tmp/')) {
64
- process.chdir(originalDir);
65
- fs.rmSync(testDir, { recursive: true, force: true });
66
- }
67
- resetDb(); // Clear singleton after cleanup
68
- }
69
- });
@@ -1,57 +0,0 @@
1
- Feature: Work Commands - Stable Mode
2
- Edge cases and error handling for work commands
3
-
4
- Scenario: Start work with invalid ID
5
- Given jettypod is initialized
6
- When I try to start work with ID "invalid"
7
- Then I get an error "Invalid work item ID"
8
-
9
- Scenario: Start work with negative ID
10
- Given jettypod is initialized
11
- When I try to start work with ID "-1"
12
- Then I get an error "Invalid work item ID"
13
-
14
- Scenario: Start work when not initialized
15
- Given jettypod is not initialized
16
- When I try to start work with ID "1"
17
- Then I get an error "JettyPod not initialized"
18
-
19
- Scenario: Start work on non-existent item
20
- Given jettypod is initialized
21
- When I try to start work with ID "999"
22
- Then I get an error "Work item #999 not found"
23
-
24
- Scenario: Stop work with invalid status
25
- Given I have current work
26
- When I try to stop work with status "invalid"
27
- Then I get an error "Invalid status"
28
-
29
- Scenario: Stop work when no current work
30
- Given jettypod is initialized
31
- And no work is active
32
- When I try to stop work
33
- Then operation succeeds with no changes
34
-
35
- Scenario: Get current work with corrupted file
36
- Given jettypod is initialized
37
- And current work file is corrupted
38
- When I get current work
39
- Then it returns null
40
-
41
- Scenario: Start work without git repo
42
- Given jettypod is initialized without git
43
- And I have a work item
44
- When I start work on the item
45
- Then it succeeds without creating branch
46
-
47
- Scenario: Multiple start work calls
48
- Given I have a work item
49
- And I start work on it
50
- When I start work on a different item
51
- Then the first item stops being current
52
- And the second item becomes current
53
-
54
- Scenario: Start work preserves existing status
55
- Given I have a work item with status "in_progress"
56
- When I start work on it
57
- Then the status remains "in_progress"