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
@@ -0,0 +1,429 @@
1
+ /**
2
+ * Worktree Reconciler - State Reconciliation Engine
3
+ *
4
+ * This module detects and fixes inconsistencies between database, git, and
5
+ * filesystem state. It's the "self-healing" layer that ensures state never
6
+ * gets permanently corrupted.
7
+ *
8
+ * Four reconciliation scenarios:
9
+ * 1. Orphaned worktrees (git knows, DB doesn't)
10
+ * 2. Orphaned branches (branch exists, no worktree)
11
+ * 3. Stale database entries (DB has path, filesystem doesn't)
12
+ * 4. Git-filesystem mismatch (filesystem has dir, git doesn't know it)
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const { execSync } = require('child_process');
18
+
19
+ /**
20
+ * Reconcile all worktree state
21
+ *
22
+ * @param {Object} db - SQLite database connection
23
+ * @param {string} repoPath - Path to main git repository
24
+ * @param {Object} options - Reconciliation options
25
+ * @param {boolean} options.cleanup - Whether to attempt cleanup (default: false)
26
+ * @returns {Promise<Object>} Reconciliation results with detected issues and cleanup attempts
27
+ */
28
+ async function reconcileState(db, repoPath, options = {}) {
29
+ const cleanup = options.cleanup || false;
30
+
31
+ const results = {
32
+ orphanedWorktrees: [],
33
+ orphanedBranches: [],
34
+ staleDbEntries: [],
35
+ gitFilesystemMismatches: [],
36
+ cleanupAttempts: {
37
+ orphanedWorktrees: 0,
38
+ orphanedBranches: 0,
39
+ staleDbEntries: 0,
40
+ gitFilesystemMismatches: 0
41
+ },
42
+ errors: [],
43
+ warnings: [],
44
+ gitCommands: []
45
+ };
46
+
47
+ // Scenario 1: Orphaned worktrees (git knows, DB doesn't)
48
+ try {
49
+ const orphanedResults = await detectOrphanedWorktrees(db, repoPath);
50
+ results.orphanedWorktrees = orphanedResults;
51
+
52
+ if (cleanup && orphanedResults.length > 0) {
53
+ for (const orphaned of orphanedResults) {
54
+ try {
55
+ await cleanupOrphanedWorktree(orphaned.path, repoPath);
56
+ results.cleanupAttempts.orphanedWorktrees++;
57
+ } catch (err) {
58
+ results.errors.push(`Failed to cleanup orphaned worktree ${orphaned.path}: ${err.message}`);
59
+ }
60
+ }
61
+ }
62
+ } catch (err) {
63
+ results.errors.push(`Orphaned worktree detection failed: ${err.message}`);
64
+ }
65
+
66
+ // Scenario 2: Orphaned branches (branch exists, no worktree)
67
+ try {
68
+ const orphanedBranches = await detectOrphanedBranches(db, repoPath);
69
+ results.orphanedBranches = orphanedBranches;
70
+
71
+ if (cleanup && orphanedBranches.length > 0) {
72
+ for (const branch of orphanedBranches) {
73
+ try {
74
+ const hasCommits = await branchHasCommits(branch.name, repoPath);
75
+ if (!hasCommits) {
76
+ await cleanupOrphanedBranch(branch.name, repoPath);
77
+ results.cleanupAttempts.orphanedBranches++;
78
+ } else {
79
+ results.warnings.push(`Branch ${branch.name} has commits - preserving it`);
80
+ }
81
+ } catch (err) {
82
+ results.errors.push(`Failed to cleanup orphaned branch ${branch.name}: ${err.message}`);
83
+ }
84
+ }
85
+ }
86
+ } catch (err) {
87
+ results.errors.push(`Orphaned branch detection failed: ${err.message}`);
88
+ }
89
+
90
+ // Scenario 3: Stale database entries (DB has path, filesystem doesn't)
91
+ try {
92
+ const staleEntries = await detectStaleDbEntries(db, repoPath);
93
+ results.staleDbEntries = staleEntries;
94
+
95
+ if (cleanup && staleEntries.length > 0) {
96
+ for (const entry of staleEntries) {
97
+ try {
98
+ await markWorktreeCorrupted(db, entry.id);
99
+ results.cleanupAttempts.staleDbEntries++;
100
+ } catch (err) {
101
+ results.errors.push(`Failed to mark stale entry as corrupted: ${err.message}`);
102
+ }
103
+ }
104
+
105
+ // Run git worktree prune
106
+ try {
107
+ execSync('git worktree prune', { cwd: repoPath, stdio: 'pipe' });
108
+ results.gitCommands.push('git worktree prune');
109
+ } catch (err) {
110
+ results.errors.push(`Failed to prune git worktrees: ${err.message}`);
111
+ }
112
+ }
113
+ } catch (err) {
114
+ results.errors.push(`Stale database entry detection failed: ${err.message}`);
115
+ }
116
+
117
+ // Scenario 4: Git-filesystem mismatch (filesystem has dir, git doesn't)
118
+ try {
119
+ const mismatches = await detectGitFilesystemMismatches(db, repoPath);
120
+ results.gitFilesystemMismatches = mismatches;
121
+
122
+ if (cleanup && mismatches.length > 0) {
123
+ for (const mismatch of mismatches) {
124
+ try {
125
+ await reregisterWorktree(mismatch, repoPath);
126
+ await updateWorktreeTimestamp(db, mismatch.id);
127
+ results.cleanupAttempts.gitFilesystemMismatches++;
128
+ results.gitCommands.push(`git worktree add --force`);
129
+ } catch (err) {
130
+ await markWorktreeCorrupted(db, mismatch.id);
131
+ results.errors.push(`Failed to re-register worktree ${mismatch.path}: ${err.message}`);
132
+ }
133
+ }
134
+ }
135
+ } catch (err) {
136
+ results.errors.push(`Git-filesystem mismatch detection failed: ${err.message}`);
137
+ }
138
+
139
+ return results;
140
+ }
141
+
142
+ /**
143
+ * Detect worktrees that git knows about but database doesn't track
144
+ */
145
+ async function detectOrphanedWorktrees(db, repoPath) {
146
+ const orphaned = [];
147
+
148
+ // Get all git worktrees
149
+ const gitWorktrees = getGitWorktrees(repoPath);
150
+
151
+ // Check each against database
152
+ for (const worktree of gitWorktrees) {
153
+ const dbEntry = await new Promise((resolve, reject) => {
154
+ db.get('SELECT * FROM worktrees WHERE worktree_path = ?', [worktree.path], (err, row) => {
155
+ if (err) reject(err);
156
+ else resolve(row);
157
+ });
158
+ });
159
+
160
+ if (!dbEntry) {
161
+ orphaned.push({
162
+ path: worktree.path,
163
+ branch: worktree.branch
164
+ });
165
+ }
166
+ }
167
+
168
+ return orphaned;
169
+ }
170
+
171
+ /**
172
+ * Get all git worktrees from git worktree list
173
+ */
174
+ function getGitWorktrees(repoPath) {
175
+ try {
176
+ const output = execSync('git worktree list --porcelain', {
177
+ cwd: repoPath,
178
+ encoding: 'utf8',
179
+ stdio: 'pipe'
180
+ });
181
+
182
+ const worktrees = [];
183
+ const lines = output.split('\n');
184
+ let currentWorktree = {};
185
+
186
+ for (const line of lines) {
187
+ if (line.startsWith('worktree ')) {
188
+ if (currentWorktree.path) {
189
+ worktrees.push(currentWorktree);
190
+ }
191
+ currentWorktree = { path: line.substring(9) };
192
+ } else if (line.startsWith('branch ')) {
193
+ currentWorktree.branch = line.substring(7).replace('refs/heads/', '');
194
+ }
195
+ }
196
+
197
+ if (currentWorktree.path) {
198
+ worktrees.push(currentWorktree);
199
+ }
200
+
201
+ // Normalize paths to resolve symlinks (e.g., /var -> /private/var on macOS)
202
+ const normalizedRepoPath = fs.existsSync(repoPath) ? fs.realpathSync(repoPath) : repoPath;
203
+
204
+ // Filter out main repository (not a worktree)
205
+ return worktrees.filter(w => {
206
+ const normalizedWorktreePath = fs.existsSync(w.path) ? fs.realpathSync(w.path) : w.path;
207
+ return normalizedWorktreePath !== normalizedRepoPath;
208
+ });
209
+ } catch (err) {
210
+ // If git worktree list fails, return empty array
211
+ return [];
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Cleanup orphaned worktree
217
+ */
218
+ async function cleanupOrphanedWorktree(worktreePath, repoPath) {
219
+ try {
220
+ execSync(`git worktree remove --force "${worktreePath}"`, {
221
+ cwd: repoPath,
222
+ stdio: 'pipe'
223
+ });
224
+ } catch (err) {
225
+ throw new Error(`Failed to remove worktree: ${err.message}`);
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Detect feature branches that have no worktree
231
+ */
232
+ async function detectOrphanedBranches(db, repoPath) {
233
+ const orphaned = [];
234
+
235
+ // Get all feature branches with work-* pattern
236
+ const branches = getFeatureBranches(repoPath);
237
+
238
+ // Get all tracked worktrees from database
239
+ const trackedBranches = await new Promise((resolve, reject) => {
240
+ db.all('SELECT branch_name FROM worktrees', (err, rows) => {
241
+ if (err) reject(err);
242
+ else resolve(rows.map(r => r.branch_name));
243
+ });
244
+ });
245
+
246
+ // Check each branch
247
+ for (const branchName of branches) {
248
+ if (!trackedBranches.includes(branchName)) {
249
+ // Extract work item ID from branch name (e.g., feature/work-123-title)
250
+ const match = branchName.match(/work-(\d+)-/);
251
+ const workItemId = match ? parseInt(match[1]) : null;
252
+
253
+ orphaned.push({
254
+ name: branchName,
255
+ workItemId
256
+ });
257
+ }
258
+ }
259
+
260
+ return orphaned;
261
+ }
262
+
263
+ /**
264
+ * Get all feature branches with work-* pattern
265
+ */
266
+ function getFeatureBranches(repoPath) {
267
+ try {
268
+ const output = execSync('git branch', {
269
+ cwd: repoPath,
270
+ encoding: 'utf8',
271
+ stdio: 'pipe'
272
+ });
273
+
274
+ return output
275
+ .split('\n')
276
+ .map(line => line.trim().replace('* ', ''))
277
+ .filter(branch => branch.startsWith('feature/work-'));
278
+ } catch (err) {
279
+ return [];
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Check if branch has commits beyond main
285
+ */
286
+ async function branchHasCommits(branchName, repoPath) {
287
+ try {
288
+ const output = execSync(`git rev-list --count main..${branchName}`, {
289
+ cwd: repoPath,
290
+ encoding: 'utf8',
291
+ stdio: 'pipe'
292
+ });
293
+
294
+ const commitCount = parseInt(output.trim());
295
+ return commitCount > 0;
296
+ } catch (err) {
297
+ // If command fails, assume it has commits (safer to preserve)
298
+ return true;
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Cleanup orphaned branch
304
+ */
305
+ async function cleanupOrphanedBranch(branchName, repoPath) {
306
+ try {
307
+ execSync(`git branch -D ${branchName}`, {
308
+ cwd: repoPath,
309
+ stdio: 'pipe'
310
+ });
311
+ } catch (err) {
312
+ throw new Error(`Failed to delete branch: ${err.message}`);
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Detect database entries where filesystem path doesn't exist
318
+ */
319
+ async function detectStaleDbEntries(db, repoPath) {
320
+ const stale = [];
321
+
322
+ // Get all worktrees from database
323
+ const dbEntries = await new Promise((resolve, reject) => {
324
+ db.all('SELECT * FROM worktrees', (err, rows) => {
325
+ if (err) reject(err);
326
+ else resolve(rows);
327
+ });
328
+ });
329
+
330
+ // Check each entry
331
+ for (const entry of dbEntries) {
332
+ if (!fs.existsSync(entry.worktree_path)) {
333
+ stale.push({
334
+ id: entry.id,
335
+ path: entry.worktree_path,
336
+ workItemId: entry.work_item_id
337
+ });
338
+ }
339
+ }
340
+
341
+ return stale;
342
+ }
343
+
344
+ /**
345
+ * Mark worktree as corrupted in database
346
+ */
347
+ async function markWorktreeCorrupted(db, worktreeId) {
348
+ return new Promise((resolve, reject) => {
349
+ db.run(
350
+ 'UPDATE worktrees SET status = ?, updated_at = datetime(\'now\') WHERE id = ?',
351
+ ['corrupted', worktreeId],
352
+ (err) => {
353
+ if (err) reject(err);
354
+ else resolve();
355
+ }
356
+ );
357
+ });
358
+ }
359
+
360
+ /**
361
+ * Detect directories that exist but git doesn't track
362
+ */
363
+ async function detectGitFilesystemMismatches(db, repoPath) {
364
+ const mismatches = [];
365
+
366
+ // Get all worktrees from database
367
+ const dbEntries = await new Promise((resolve, reject) => {
368
+ db.all('SELECT * FROM worktrees', (err, rows) => {
369
+ if (err) reject(err);
370
+ else resolve(rows);
371
+ });
372
+ });
373
+
374
+ // Get all git worktrees
375
+ const gitWorktrees = getGitWorktrees(repoPath);
376
+ const gitPaths = gitWorktrees.map(w => w.path);
377
+
378
+ // Check each database entry
379
+ for (const entry of dbEntries) {
380
+ const dirExists = fs.existsSync(entry.worktree_path);
381
+ const gitKnows = gitPaths.includes(entry.worktree_path);
382
+
383
+ if (dirExists && !gitKnows) {
384
+ mismatches.push({
385
+ id: entry.id,
386
+ path: entry.worktree_path,
387
+ branchName: entry.branch_name,
388
+ workItemId: entry.work_item_id
389
+ });
390
+ }
391
+ }
392
+
393
+ return mismatches;
394
+ }
395
+
396
+ /**
397
+ * Re-register worktree directory with git
398
+ */
399
+ async function reregisterWorktree(mismatch, repoPath) {
400
+ try {
401
+ // Try to re-register the worktree with git
402
+ execSync(`git worktree add --force -b ${mismatch.branchName} "${mismatch.path}"`, {
403
+ cwd: repoPath,
404
+ stdio: 'pipe'
405
+ });
406
+ } catch (err) {
407
+ throw new Error(`Failed to re-register worktree: ${err.message}`);
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Update worktree timestamp in database
413
+ */
414
+ async function updateWorktreeTimestamp(db, worktreeId) {
415
+ return new Promise((resolve, reject) => {
416
+ db.run(
417
+ 'UPDATE worktrees SET updated_at = datetime(\'now\') WHERE id = ?',
418
+ [worktreeId],
419
+ (err) => {
420
+ if (err) reject(err);
421
+ else resolve();
422
+ }
423
+ );
424
+ });
425
+ }
426
+
427
+ module.exports = {
428
+ reconcileState
429
+ };
package/package.json CHANGED
@@ -1,17 +1,40 @@
1
1
  {
2
2
  "name": "jettypod",
3
- "version": "4.1.2",
4
- "description": "Simplified development mode manager",
3
+ "version": "4.1.4",
4
+ "description": "AI-powered development workflow manager with TDD, BDD, and automatic test generation",
5
5
  "main": "jettypod.js",
6
6
  "bin": {
7
7
  "jettypod": "./jettypod.js"
8
8
  },
9
+ "keywords": [
10
+ "workflow",
11
+ "tdd",
12
+ "bdd",
13
+ "testing",
14
+ "ai-development",
15
+ "cucumber",
16
+ "jest",
17
+ "development-tools",
18
+ "test-automation",
19
+ "project-management"
20
+ ],
21
+ "author": "Erik Spangenberg",
22
+ "license": "MIT",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/spangbaryn/jettypod-source.git"
26
+ },
27
+ "bugs": {
28
+ "url": "https://github.com/spangbaryn/jettypod-source/issues"
29
+ },
30
+ "homepage": "https://github.com/spangbaryn/jettypod-source#readme",
9
31
  "scripts": {
10
32
  "test": "npm run test:unit && npm run test:bdd && npm run test:cleanup",
11
33
  "test:bdd": "NODE_ENV=test cucumber-js",
12
34
  "test:unit": "NODE_ENV=test jest",
13
35
  "test:unit:watch": "NODE_ENV=test jest --watch",
14
- "test:cleanup": "node -e \"const fs = require('fs'); const path = require('path'); const testDb = path.join(process.cwd(), '.jettypod', 'test-work.db'); if (fs.existsSync(testDb)) { fs.unlinkSync(testDb); console.log('✅ Cleaned up test database'); }\""
36
+ "test:cleanup": "node scripts/test-cleanup.js",
37
+ "prepublishOnly": "echo '🔄 Syncing skills to templates...' && cp -r .claude/skills/* skills-templates/ && echo '✅ Skills synced'"
15
38
  },
16
39
  "devDependencies": {
17
40
  "@cucumber/cucumber": "^10.0.1",
@@ -31,6 +54,10 @@
31
54
  "coverageDirectory": "coverage"
32
55
  },
33
56
  "dependencies": {
57
+ "chalk": "^4.1.2",
34
58
  "sqlite3": "^5.1.7"
59
+ },
60
+ "engines": {
61
+ "node": "22.x"
35
62
  }
36
63
  }
@@ -200,7 +200,29 @@ After user selects preset, ask 3-5 refinement questions to tailor standards:
200
200
 
201
201
  ### Step 5: Generate and Show Recommendations
202
202
 
203
- After collecting answers, use the `lib/production-standards-engine.js` to generate tailored standards, then show recommendations:
203
+ After collecting answers, use the `lib/production-standards-engine.js` to generate tailored standards.
204
+
205
+ **Execute this code:**
206
+
207
+ ```javascript
208
+ const { generateStandards } = require('../../lib/production-standards-engine');
209
+
210
+ // Build input from collected answers
211
+ const input = {
212
+ preset: '[user-selected-preset]', // startup-mvp, production-saas, enterprise, or regulated
213
+ refinement: {
214
+ user_count: '[answer-from-question-2]', // e.g., '100-1k'
215
+ downtime_impact: '[answer-from-question-3]', // e.g., 'Users annoyed'
216
+ data_loss_impact: '[answer-from-question-4]', // e.g., 'Annoying'
217
+ sensitive_data: '[answer-from-question-5]' // e.g., 'Passwords only'
218
+ }
219
+ };
220
+
221
+ // Generate standards
222
+ const generatedStandards = generateStandards(input);
223
+ ```
224
+
225
+ Then show recommendations to the user:
204
226
 
205
227
  ```
206
228
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -279,9 +301,18 @@ Allow user to toggle/adjust standards, then regenerate the summary.
279
301
 
280
302
  ### Step 7: Save Production Standards
281
303
 
282
- After user accepts, save standards to `.jettypod/production-standards.json` using the engine's output format.
304
+ After user accepts, save standards to `.jettypod/production-standards.json`.
305
+
306
+ **Execute this code:**
307
+
308
+ ```javascript
309
+ const { saveStandards } = require('../../lib/production-standards-writer');
310
+
311
+ // Save the generated standards from Step 5
312
+ saveStandards(generatedStandards);
313
+ ```
283
314
 
284
- Confirm to user:
315
+ Then confirm to user:
285
316
 
286
317
  ```
287
318
  ✅ Production standards saved to .jettypod/production-standards.json