jettypod 4.4.116 → 4.4.120

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 (162) hide show
  1. package/.env +7 -0
  2. package/apps/dashboard/app/api/claude/[workItemId]/message/route.ts +124 -48
  3. package/apps/dashboard/app/api/claude/[workItemId]/route.ts +171 -58
  4. package/apps/dashboard/app/api/claude/sessions/[sessionId]/message/route.ts +161 -10
  5. package/apps/dashboard/app/api/tests/run/stream/route.ts +13 -1
  6. package/apps/dashboard/app/api/usage/route.ts +17 -0
  7. package/apps/dashboard/app/api/work/[id]/route.ts +35 -0
  8. package/apps/dashboard/app/api/work/[id]/status/route.ts +43 -1
  9. package/apps/dashboard/app/connect-claude/page.tsx +24 -0
  10. package/apps/dashboard/app/decision/[id]/page.tsx +14 -14
  11. package/apps/dashboard/app/demo/gates/page.tsx +42 -42
  12. package/apps/dashboard/app/design-system/page.tsx +868 -0
  13. package/apps/dashboard/app/globals.css +6 -2
  14. package/apps/dashboard/app/install-claude/page.tsx +9 -7
  15. package/apps/dashboard/app/layout.tsx +17 -5
  16. package/apps/dashboard/app/login/page.tsx +250 -0
  17. package/apps/dashboard/app/page.tsx +11 -9
  18. package/apps/dashboard/app/settings/page.tsx +4 -2
  19. package/apps/dashboard/app/signup/page.tsx +245 -0
  20. package/apps/dashboard/app/subscribe/page.tsx +11 -0
  21. package/apps/dashboard/app/welcome/page.tsx +24 -1
  22. package/apps/dashboard/app/work/[id]/page.tsx +34 -50
  23. package/apps/dashboard/components/AppShell.tsx +95 -55
  24. package/apps/dashboard/components/CardMenu.tsx +56 -13
  25. package/apps/dashboard/components/ClaudePanel.tsx +301 -582
  26. package/apps/dashboard/components/ClaudePanelInput.tsx +23 -14
  27. package/apps/dashboard/components/ConnectClaudeScreen.tsx +210 -0
  28. package/apps/dashboard/components/CopyableId.tsx +3 -3
  29. package/apps/dashboard/components/DetailReviewActions.tsx +109 -0
  30. package/apps/dashboard/components/DragContext.tsx +75 -65
  31. package/apps/dashboard/components/DraggableCard.tsx +6 -46
  32. package/apps/dashboard/components/DropZone.tsx +2 -2
  33. package/apps/dashboard/components/EditableDetailDescription.tsx +1 -1
  34. package/apps/dashboard/components/EditableTitle.tsx +26 -6
  35. package/apps/dashboard/components/ElapsedTimer.tsx +54 -0
  36. package/apps/dashboard/components/EpicGroup.tsx +329 -0
  37. package/apps/dashboard/components/GateCard.tsx +100 -16
  38. package/apps/dashboard/components/GateChoiceCard.tsx +15 -17
  39. package/apps/dashboard/components/InstallClaudeScreen.tsx +140 -51
  40. package/apps/dashboard/components/JettyLoader.tsx +38 -0
  41. package/apps/dashboard/components/KanbanBoard.tsx +147 -766
  42. package/apps/dashboard/components/KanbanCard.tsx +506 -0
  43. package/apps/dashboard/components/LazyMarkdown.tsx +12 -0
  44. package/apps/dashboard/components/MainNav.tsx +20 -54
  45. package/apps/dashboard/components/MessageBlock.tsx +391 -0
  46. package/apps/dashboard/components/ModeStartCard.tsx +15 -15
  47. package/apps/dashboard/components/OnboardingWelcome.tsx +214 -0
  48. package/apps/dashboard/components/PlaceholderCard.tsx +11 -21
  49. package/apps/dashboard/components/ProjectSwitcher.tsx +36 -8
  50. package/apps/dashboard/components/PrototypeTimeline.tsx +25 -25
  51. package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +265 -301
  52. package/apps/dashboard/components/RealTimeTestsWrapper.tsx +97 -74
  53. package/apps/dashboard/components/ReviewFooter.tsx +141 -0
  54. package/apps/dashboard/components/SessionList.tsx +19 -18
  55. package/apps/dashboard/components/SubscribeContent.tsx +206 -0
  56. package/apps/dashboard/components/TestTree.tsx +15 -14
  57. package/apps/dashboard/components/TipCard.tsx +177 -0
  58. package/apps/dashboard/components/Toast.tsx +5 -5
  59. package/apps/dashboard/components/TypeIcon.tsx +56 -0
  60. package/apps/dashboard/components/UpgradeBanner.tsx +30 -0
  61. package/apps/dashboard/components/WaveCompletionAnimation.tsx +61 -62
  62. package/apps/dashboard/components/WelcomeScreen.tsx +25 -27
  63. package/apps/dashboard/components/WorkItemHeader.tsx +4 -4
  64. package/apps/dashboard/components/WorkItemTree.tsx +9 -28
  65. package/apps/dashboard/components/settings/AccountSection.tsx +169 -0
  66. package/apps/dashboard/components/settings/EnvVarsSection.tsx +54 -79
  67. package/apps/dashboard/components/settings/GeneralSection.tsx +26 -31
  68. package/apps/dashboard/components/settings/SettingsLayout.tsx +4 -4
  69. package/apps/dashboard/components/ui/Button.tsx +104 -0
  70. package/apps/dashboard/components/ui/Input.tsx +78 -0
  71. package/apps/dashboard/contexts/ClaudeSessionContext.tsx +408 -105
  72. package/apps/dashboard/contexts/ConnectionStatusContext.tsx +25 -4
  73. package/apps/dashboard/contexts/UsageContext.tsx +155 -0
  74. package/apps/dashboard/contexts/usageHelpers.js +9 -0
  75. package/apps/dashboard/electron/ipc-handlers.js +281 -88
  76. package/apps/dashboard/electron/main.js +691 -131
  77. package/apps/dashboard/electron/preload.js +25 -4
  78. package/apps/dashboard/electron/session-manager.js +163 -0
  79. package/apps/dashboard/electron-builder.config.js +3 -5
  80. package/apps/dashboard/hooks/useKanbanAnimation.ts +29 -0
  81. package/apps/dashboard/hooks/useKanbanUndo.ts +83 -0
  82. package/apps/dashboard/lib/backlog-parser.ts +50 -0
  83. package/apps/dashboard/lib/claude-process-manager.ts +50 -11
  84. package/apps/dashboard/lib/constants.ts +43 -0
  85. package/apps/dashboard/lib/db-bridge.ts +33 -0
  86. package/apps/dashboard/lib/db.ts +136 -20
  87. package/apps/dashboard/lib/kanban-utils.ts +70 -0
  88. package/apps/dashboard/lib/run-migrations.js +27 -2
  89. package/apps/dashboard/lib/session-state-machine.ts +3 -0
  90. package/apps/dashboard/lib/session-stream-manager.ts +144 -38
  91. package/apps/dashboard/lib/shadows.ts +7 -0
  92. package/apps/dashboard/lib/tests.ts +3 -1
  93. package/apps/dashboard/lib/utils.ts +6 -0
  94. package/apps/dashboard/next.config.js +35 -14
  95. package/apps/dashboard/package.json +6 -3
  96. package/apps/dashboard/public/bug-icon.svg +9 -0
  97. package/apps/dashboard/public/buoy-icon.svg +9 -0
  98. package/apps/dashboard/public/fonts/Satoshi-Variable.woff2 +0 -0
  99. package/apps/dashboard/public/fonts/Satoshi-VariableItalic.woff2 +0 -0
  100. package/apps/dashboard/public/in-flight-seagull.svg +9 -0
  101. package/apps/dashboard/public/jetty-icon-loading-alt.svg +11 -0
  102. package/apps/dashboard/public/jetty-icon-loading.svg +11 -0
  103. package/apps/dashboard/public/jettypod_logo.png +0 -0
  104. package/apps/dashboard/public/pier-icon.svg +14 -0
  105. package/apps/dashboard/public/star-icon.svg +9 -0
  106. package/apps/dashboard/public/wrench-icon.svg +9 -0
  107. package/apps/dashboard/scripts/upload-to-r2.js +89 -0
  108. package/apps/dashboard/scripts/ws-server.js +191 -0
  109. package/apps/dashboard/tsconfig.tsbuildinfo +1 -0
  110. package/apps/update-server/package.json +16 -0
  111. package/apps/update-server/schema.sql +31 -0
  112. package/apps/update-server/src/index.ts +1085 -0
  113. package/apps/update-server/tsconfig.json +16 -0
  114. package/apps/update-server/wrangler.toml +35 -0
  115. package/cucumber.js +9 -3
  116. package/docs/COMMAND_REFERENCE.md +34 -0
  117. package/hooks/post-checkout +32 -75
  118. package/hooks/post-merge +111 -10
  119. package/jest.setup.js +1 -0
  120. package/jettypod.js +54 -116
  121. package/lib/chore-taxonomy.js +33 -10
  122. package/lib/database.js +36 -16
  123. package/lib/db-watcher.js +1 -1
  124. package/lib/git-hooks/pre-commit +1 -1
  125. package/lib/jettypod-backup.js +27 -4
  126. package/lib/migrations/027-plan-at-creation-column.js +33 -0
  127. package/lib/migrations/028-ready-for-review-column.js +27 -0
  128. package/lib/migrations/029-remove-autoincrement.js +307 -0
  129. package/lib/migrations/029-rename-corrupted-to-cleaned.js +149 -0
  130. package/lib/migrations/index.js +47 -4
  131. package/lib/schema.js +13 -6
  132. package/lib/seed-onboarding.js +101 -69
  133. package/lib/update-command/index.js +9 -175
  134. package/lib/work-commands/index.js +129 -16
  135. package/lib/work-tracking/index.js +86 -46
  136. package/lib/worktree-diagnostics.js +16 -16
  137. package/lib/worktree-facade.js +1 -1
  138. package/lib/worktree-manager.js +8 -8
  139. package/lib/worktree-reconciler.js +5 -5
  140. package/package.json +9 -2
  141. package/scripts/ndjson-to-cucumber-json.js +152 -0
  142. package/scripts/postinstall.js +25 -0
  143. package/skills-templates/bug-mode/SKILL.md +39 -28
  144. package/skills-templates/bug-planning/SKILL.md +25 -29
  145. package/skills-templates/chore-mode/SKILL.md +131 -68
  146. package/skills-templates/chore-mode/verification.js +51 -10
  147. package/skills-templates/chore-planning/SKILL.md +47 -18
  148. package/skills-templates/epic-planning/SKILL.md +68 -48
  149. package/skills-templates/external-transition/SKILL.md +47 -47
  150. package/skills-templates/feature-planning/SKILL.md +83 -73
  151. package/skills-templates/production-mode/SKILL.md +49 -49
  152. package/skills-templates/request-routing/SKILL.md +27 -14
  153. package/skills-templates/simple-improvement/SKILL.md +68 -44
  154. package/skills-templates/speed-mode/SKILL.md +209 -128
  155. package/skills-templates/stable-mode/SKILL.md +105 -94
  156. package/templates/bdd-guidance.md +139 -0
  157. package/templates/bdd-scaffolding/wait.js +18 -0
  158. package/templates/bdd-scaffolding/world.js +19 -0
  159. package/.jettypod-backup/work.db +0 -0
  160. package/apps/dashboard/app/access-code/page.tsx +0 -110
  161. package/lib/discovery-checkpoint.js +0 -123
  162. package/skills-templates/project-discovery/SKILL.md +0 -372
@@ -21,7 +21,7 @@ const config = require('./config');
21
21
  /**
22
22
  * Valid worktree statuses
23
23
  */
24
- const VALID_STATUSES = ['active', 'merging', 'cleanup_pending', 'corrupted'];
24
+ const VALID_STATUSES = ['active', 'merging', 'cleanup_pending', 'cleaned'];
25
25
 
26
26
  /**
27
27
  * Create a worktree for a work item
@@ -352,7 +352,7 @@ async function getAllActiveWorktrees(options = {}) {
352
352
  * Mark worktree status
353
353
  *
354
354
  * @param {number} worktreeId - Worktree ID
355
- * @param {string} status - New status (active|merging|cleanup_pending|corrupted)
355
+ * @param {string} status - New status (active|merging|cleanup_pending|cleaned)
356
356
  * @param {Object} options - Optional configuration
357
357
  * @param {Object} options.db - Database connection (defaults to global database)
358
358
  * @returns {Promise<void>}
@@ -671,13 +671,13 @@ async function cleanupWorktree(worktreeId, options = {}) {
671
671
  }
672
672
  }
673
673
 
674
- // Step 5: Mark as corrupted (we never delete DB entries)
675
- // Using 'corrupted' as the final state for cleaned up worktrees
674
+ // Step 5: Mark as cleaned (we never delete DB entries)
675
+ // Using 'cleaned' as the final state for cleaned up worktrees
676
676
  // This preserves the audit trail
677
677
  await new Promise((resolve, reject) => {
678
678
  db.run(
679
679
  'UPDATE worktrees SET status = ?, updated_at = datetime(\'now\') WHERE id = ?',
680
- ['corrupted', worktreeId],
680
+ ['cleaned', worktreeId],
681
681
  (err) => {
682
682
  if (err) reject(err);
683
683
  else resolve();
@@ -686,12 +686,12 @@ async function cleanupWorktree(worktreeId, options = {}) {
686
686
  });
687
687
 
688
688
  } catch (err) {
689
- // On any failure, mark as corrupted
689
+ // On any failure, mark as cleaned (terminal state)
690
690
  try {
691
691
  await new Promise((resolve, reject) => {
692
692
  db.run(
693
693
  'UPDATE worktrees SET status = ?, updated_at = datetime(\'now\') WHERE id = ?',
694
- ['corrupted', worktreeId],
694
+ ['cleaned', worktreeId],
695
695
  (err) => {
696
696
  if (err) reject(err);
697
697
  else resolve();
@@ -699,7 +699,7 @@ async function cleanupWorktree(worktreeId, options = {}) {
699
699
  );
700
700
  });
701
701
  } catch (dbErr) {
702
- console.error(`Failed to mark worktree as corrupted: ${dbErr.message}`);
702
+ console.error(`Failed to mark worktree as cleaned: ${dbErr.message}`);
703
703
  }
704
704
 
705
705
  throw new Error(`Worktree cleanup failed: ${err.message}`);
@@ -95,7 +95,7 @@ async function reconcileState(db, repoPath, options = {}) {
95
95
  if (cleanup && staleEntries.length > 0) {
96
96
  for (const entry of staleEntries) {
97
97
  try {
98
- await markWorktreeCorrupted(db, entry.id);
98
+ await markWorktreeCleaned(db, entry.id);
99
99
  results.cleanupAttempts.staleDbEntries++;
100
100
  } catch (err) {
101
101
  results.errors.push(`Failed to mark stale entry as corrupted: ${err.message}`);
@@ -127,7 +127,7 @@ async function reconcileState(db, repoPath, options = {}) {
127
127
  results.cleanupAttempts.gitFilesystemMismatches++;
128
128
  results.gitCommands.push(`git worktree add --force`);
129
129
  } catch (err) {
130
- await markWorktreeCorrupted(db, mismatch.id);
130
+ await markWorktreeCleaned(db, mismatch.id);
131
131
  results.errors.push(`Failed to re-register worktree ${mismatch.path}: ${err.message}`);
132
132
  }
133
133
  }
@@ -342,13 +342,13 @@ async function detectStaleDbEntries(db, repoPath) {
342
342
  }
343
343
 
344
344
  /**
345
- * Mark worktree as corrupted in database
345
+ * Mark worktree as cleaned in database (terminal state)
346
346
  */
347
- async function markWorktreeCorrupted(db, worktreeId) {
347
+ async function markWorktreeCleaned(db, worktreeId) {
348
348
  return new Promise((resolve, reject) => {
349
349
  db.run(
350
350
  'UPDATE worktrees SET status = ?, updated_at = datetime(\'now\') WHERE id = ?',
351
- ['corrupted', worktreeId],
351
+ ['cleaned', worktreeId],
352
352
  (err) => {
353
353
  if (err) reject(err);
354
354
  else resolve();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jettypod",
3
- "version": "4.4.116",
3
+ "version": "4.4.120",
4
4
  "description": "AI-powered development workflow manager with TDD, BDD, and automatic test generation",
5
5
  "main": "jettypod.js",
6
6
  "bin": {
@@ -34,8 +34,10 @@
34
34
  "test:unit": "NODE_ENV=test jest",
35
35
  "test:unit:watch": "NODE_ENV=test jest --watch",
36
36
  "test:cleanup": "node scripts/test-cleanup.js",
37
+ "version": "node -e \"const v=require('./package.json').version; const f='apps/dashboard/package.json'; const p=JSON.parse(require('fs').readFileSync(f)); p.version=v; require('fs').writeFileSync(f, JSON.stringify(p,null,2)+'\\n')\"",
37
38
  "prepublishOnly": "echo '🔄 Syncing skills to templates...' && cp -r .claude/skills/* skills-templates/ && (git diff --quiet skills-templates/ || (git add skills-templates/ && git commit -m 'chore: Sync skills-templates')) && echo '✅ Skills synced' && echo '🔨 Building dashboard...' && cd apps/dashboard && npm install && npm run build && echo '✅ Dashboard built'",
38
39
  "postpublish": "git push origin main --tags && echo '✅ Pushed version commit and tags'",
40
+ "postinstall": "node scripts/postinstall.js",
39
41
  "dashboard": "npm run dev --prefix apps/dashboard",
40
42
  "dashboard:build": "npm run build --prefix apps/dashboard",
41
43
  "dashboard:start": "npm run start --prefix apps/dashboard"
@@ -48,13 +50,18 @@
48
50
  "ws": "^8.18.3"
49
51
  },
50
52
  "jest": {
53
+ "setupFiles": [
54
+ "./jest.setup.js"
55
+ ],
51
56
  "testEnvironment": "node",
52
57
  "testMatch": [
53
58
  "**/__tests__/**/*.js",
54
59
  "**/*.test.js"
55
60
  ],
56
61
  "testPathIgnorePatterns": [
57
- "/node_modules/"
62
+ "/node_modules/",
63
+ "/apps/dashboard/dist/",
64
+ "/.jettypod-work/"
58
65
  ],
59
66
  "collectCoverage": false,
60
67
  "coverageDirectory": "coverage"
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+ // Convert cucumber ndjson (message format) to standard cucumber JSON format.
3
+ // Usage: node scripts/ndjson-to-cucumber-json.js [input.ndjson] [output.json]
4
+ // Defaults: cucumber-results.ndjson -> cucumber-results.json
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ function convert(ndjsonPath, jsonPath) {
10
+ const raw = fs.readFileSync(ndjsonPath, 'utf-8');
11
+ const lines = raw.split('\n').filter(l => l.trim());
12
+
13
+ // Lookup tables
14
+ const gherkinDocuments = new Map(); // uri -> gherkinDocument
15
+ const pickles = new Map(); // pickle.id -> pickle
16
+ const testCases = new Map(); // testCase.id -> testCase
17
+ const testCaseStartedMap = new Map(); // testCaseStarted.id -> testCaseStarted
18
+ const stepDefinitions = new Map(); // stepDefinition.id -> stepDefinition
19
+
20
+ // Results
21
+ const testStepResults = new Map(); // testCaseStartedId -> [{stepId, result}]
22
+
23
+ for (const line of lines) {
24
+ let msg;
25
+ try {
26
+ msg = JSON.parse(line);
27
+ } catch {
28
+ continue;
29
+ }
30
+
31
+ if (msg.gherkinDocument) {
32
+ gherkinDocuments.set(msg.gherkinDocument.uri, msg.gherkinDocument);
33
+ }
34
+ if (msg.pickle) {
35
+ pickles.set(msg.pickle.id, msg.pickle);
36
+ }
37
+ if (msg.stepDefinition) {
38
+ stepDefinitions.set(msg.stepDefinition.id, msg.stepDefinition);
39
+ }
40
+ if (msg.testCase) {
41
+ testCases.set(msg.testCase.id, msg.testCase);
42
+ }
43
+ if (msg.testCaseStarted) {
44
+ testCaseStartedMap.set(msg.testCaseStarted.id, msg.testCaseStarted);
45
+ testStepResults.set(msg.testCaseStarted.id, []);
46
+ }
47
+ if (msg.testStepFinished) {
48
+ const tsf = msg.testStepFinished;
49
+ const arr = testStepResults.get(tsf.testCaseStartedId);
50
+ if (arr) {
51
+ arr.push({
52
+ testStepId: tsf.testStepId,
53
+ result: tsf.testStepResult,
54
+ });
55
+ }
56
+ }
57
+ }
58
+
59
+ // Build cucumber JSON structure grouped by feature URI
60
+ const featureMap = new Map(); // uri -> { feature data }
61
+
62
+ for (const [tcsId, tcs] of testCaseStartedMap) {
63
+ const testCase = testCases.get(tcs.testCaseId);
64
+ if (!testCase) continue;
65
+ const pickle = pickles.get(testCase.pickleId);
66
+ if (!pickle) continue;
67
+
68
+ const uri = pickle.uri;
69
+ if (!featureMap.has(uri)) {
70
+ const doc = gherkinDocuments.get(uri);
71
+ featureMap.set(uri, {
72
+ uri,
73
+ name: doc?.feature?.name || uri,
74
+ description: doc?.feature?.description || '',
75
+ keyword: doc?.feature?.keyword || 'Feature',
76
+ elements: [],
77
+ });
78
+ }
79
+
80
+ const stepResults = testStepResults.get(tcsId) || [];
81
+ const steps = [];
82
+
83
+ for (const testStep of testCase.testSteps) {
84
+ // Skip hook steps (no pickleStepId)
85
+ if (!testStep.pickleStepId) continue;
86
+
87
+ const pickleStep = pickle.steps.find(s => s.id === testStep.pickleStepId);
88
+ if (!pickleStep) continue;
89
+
90
+ // Find the result for this step
91
+ const stepResult = stepResults.find(r => r.testStepId === testStep.id);
92
+ const result = stepResult?.result;
93
+
94
+ const dur = result?.duration || { seconds: 0, nanos: 0 };
95
+ const durationNanos = (dur.seconds || 0) * 1e9 + (dur.nanos || 0);
96
+
97
+ // Get keyword from gherkin document
98
+ let keyword = '';
99
+ const doc = gherkinDocuments.get(uri);
100
+ if (doc?.feature) {
101
+ for (const child of doc.feature.children || []) {
102
+ const scenario = child.scenario;
103
+ if (!scenario) continue;
104
+ for (const gherkinStep of scenario.steps) {
105
+ if (pickleStep.astNodeIds?.includes(gherkinStep.id)) {
106
+ keyword = gherkinStep.keyword;
107
+ break;
108
+ }
109
+ }
110
+ if (keyword) break;
111
+ }
112
+ }
113
+
114
+ steps.push({
115
+ keyword: keyword || 'Step ',
116
+ name: pickleStep.text || '',
117
+ result: {
118
+ status: (result?.status || 'UNKNOWN').toLowerCase(),
119
+ duration: durationNanos,
120
+ error_message: result?.message || undefined,
121
+ },
122
+ });
123
+ }
124
+
125
+ featureMap.get(uri).elements.push({
126
+ type: 'scenario',
127
+ name: pickle.name,
128
+ steps,
129
+ });
130
+ }
131
+
132
+ const output = Array.from(featureMap.values());
133
+ fs.writeFileSync(jsonPath, JSON.stringify(output, null, 2));
134
+ return output.reduce((sum, f) => sum + f.elements.length, 0);
135
+ }
136
+
137
+ // CLI entry point
138
+ if (require.main === module) {
139
+ const projectRoot = path.resolve(__dirname, '..');
140
+ const ndjsonPath = process.argv[2] || path.join(projectRoot, 'cucumber-results.ndjson');
141
+ const jsonPath = process.argv[3] || path.join(projectRoot, 'cucumber-results.json');
142
+
143
+ if (!fs.existsSync(ndjsonPath)) {
144
+ console.error(`No ndjson file found at ${ndjsonPath}`);
145
+ process.exit(1);
146
+ }
147
+
148
+ const count = convert(ndjsonPath, jsonPath);
149
+ console.log(`Converted ${count} scenarios to ${jsonPath}`);
150
+ }
151
+
152
+ module.exports = { convert };
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Records the source directory on global install so jettypod can auto-update.
4
+ // Writes ~/.jettypod-dev.json with the source path for staleness detection.
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const os = require('os');
9
+
10
+ // Only run for global installs
11
+ if (process.env.npm_config_global !== 'true') process.exit(0);
12
+
13
+ const sourceDir = process.env.INIT_CWD;
14
+ if (!sourceDir) process.exit(0);
15
+
16
+ // Verify source dir looks like a jettypod repo (has jettypod.js)
17
+ if (!fs.existsSync(path.join(sourceDir, 'jettypod.js'))) process.exit(0);
18
+
19
+ try {
20
+ const devInfo = { sourceDir };
21
+ const devFile = path.join(os.homedir(), '.jettypod-dev.json');
22
+ fs.writeFileSync(devFile, JSON.stringify(devInfo, null, 2) + '\n');
23
+ } catch {
24
+ // Non-fatal — skip silently
25
+ }
@@ -44,7 +44,7 @@ When this skill is activated, you are implementing a bug fix directly on the bug
44
44
 
45
45
  Your job: Implement the fix, write a regression test, and verify everything passes.
46
46
 
47
- ## 🔑 Critical Context
47
+ ## Critical Context
48
48
 
49
49
  **You are working in an isolated git worktree:**
50
50
  - `work start [bug-id]` already created a dedicated worktree
@@ -55,7 +55,7 @@ Your job: Implement the fix, write a regression test, and verify everything pass
55
55
 
56
56
  ---
57
57
 
58
- ## 🚨 SHELL CWD RECOVERY
58
+ ## SHELL CWD RECOVERY
59
59
 
60
60
  **If ALL bash commands start failing with "Error: Exit code 1" and no output:**
61
61
 
@@ -69,7 +69,7 @@ Your shell's working directory was likely inside a worktree that was deleted. Th
69
69
 
70
70
  ---
71
71
 
72
- ## 🛑 PRE-FLIGHT VALIDATION (REQUIRED)
72
+ ## PRE-FLIGHT VALIDATION (REQUIRED)
73
73
 
74
74
  **Before proceeding, validate the worktree exists.**
75
75
 
@@ -81,9 +81,9 @@ sqlite3 .jettypod/work.db "SELECT wi.id, wi.title, wi.status, wt.worktree_path,
81
81
 
82
82
  | worktree_path | What it means | Action |
83
83
  |---------------|---------------|--------|
84
- | **Has a path** | Worktree exists, ready to proceed | Continue to Step 0 |
85
- | **NULL or empty** | `work start` was not called | **STOP - run `jettypod work start [bug-id]` first** |
86
- | **No rows returned** | No bug is in progress | **STOP - verify the bug exists and run `work start`** |
84
+ | **Has a path** | Worktree exists, ready to proceed | Continue to Step 0 |
85
+ | **NULL or empty** | `work start` was not called | **STOP - run `jettypod work start [bug-id]` first** |
86
+ | **No rows returned** | No bug is in progress | **STOP - verify the bug exists and run `work start`** |
87
87
 
88
88
  **After pre-flight passes, emit gate signal:**
89
89
 
@@ -114,7 +114,7 @@ The bug-planning skill embedded implementation guidance:
114
114
 
115
115
  ```
116
116
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
117
- 🐛 BUG MODE: Fixing #[bug-id]
117
+ BUG MODE: Fixing #[bug-id]
118
118
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
119
119
 
120
120
  Bug: #[id] [title]
@@ -176,14 +176,14 @@ sqlite3 .jettypod/work.db "SELECT project_state FROM project_config WHERE id = 1
176
176
 
177
177
  ```
178
178
  ━━━ Iteration 1 ━━━
179
- 📂 Reading: [file:line from breadcrumbs]
180
- 🔧 Change: [what you're fixing]
179
+ Reading: [file:line from breadcrumbs]
180
+ Change: [what you're fixing]
181
181
  ```
182
182
 
183
183
  **After implementation:**
184
184
 
185
185
  ```
186
- 🎉 Fix Implemented
186
+ Fix Implemented
187
187
 
188
188
  Changes made:
189
189
  - [Change 1]
@@ -197,6 +197,16 @@ Now writing regression test...
197
197
 
198
198
  ---
199
199
 
200
+ ### BDD Step Definition Constraints (ENFORCED)
201
+
202
+ Before writing any step definitions, read `templates/bdd-guidance.md`. BANNED in step defs: `setTimeout`/`sleep`, module-level `let`/`var`, raw SQL, assertions in Given/When, loops/branching, self-fulfilling mocks (setting state then asserting it). Steps must be 1-5 lines calling one helper via Cucumber World (`this`). Read `features/support/helpers/` and reuse before creating.
203
+
204
+ **Scaffolding:** If `features/support/helpers/` does not exist in the project, scaffold it before writing step definitions. Copy `templates/bdd-scaffolding/wait.js` to `features/support/helpers/wait.js` and `templates/bdd-scaffolding/world.js` to `features/support/world.js`.
205
+
206
+ **Helper Reuse:** Before writing step definitions, glob `features/support/helpers/**/*.js` and read each file. Import and call existing helpers in your steps — never duplicate functionality that already exists.
207
+
208
+ ---
209
+
200
210
  ### Step 2: Write Regression Test
201
211
 
202
212
  **Goal:** Create a test that verifies the bug is fixed and prevents regression.
@@ -239,7 +249,7 @@ npx cucumber-js features/<bug-slug>.feature
239
249
  **Confirm test passes:**
240
250
 
241
251
  ```
242
- Regression Test Passing
252
+ Regression Test Passing
243
253
 
244
254
  Test: features/<bug-slug>.feature
245
255
  Status: PASSING
@@ -272,7 +282,7 @@ npx cucumber-js features/<related-feature>.feature 2>/dev/null || true
272
282
  **If regressions found:**
273
283
 
274
284
  ```
275
- ⚠️ Regression Detected
285
+ Regression Detected
276
286
 
277
287
  Failing test: [test name]
278
288
  Error: [error message]
@@ -285,16 +295,18 @@ Fix the regression and re-run tests. Iterate until all related tests pass.
285
295
  **When all related tests pass:**
286
296
 
287
297
  ```
288
- Related Tests Passing
298
+ Related Tests Passing
289
299
 
290
- - Regression test: PASS
291
- - Related area tests: [X] scenarios passing
300
+ - Regression test: PASS
301
+ - Related area tests: [X] scenarios passing
292
302
 
293
303
  Ready to commit and merge.
294
304
  ```
295
305
 
296
306
  **Proceed to Step 4.**
297
307
 
308
+ **Quality Gate:** Before committing, review every `.steps.js` file you wrote or modified. Check for banlist violations: `setTimeout`/`sleep`, module-level `let`/`var`, raw SQL, assertions in Given/When, self-fulfilling mocks, steps longer than 5 lines. Fix violations before proceeding.
309
+
298
310
  ---
299
311
 
300
312
  ### Step 4: Commit and Merge
@@ -315,7 +327,7 @@ Fix: [What was changed]
315
327
 
316
328
  Closes #<bug-id>
317
329
 
318
- 🤖 Generated with [Claude Code](https://claude.com/claude-code)
330
+ Generated with [Claude Code](https://claude.com/claude-code)
319
331
 
320
332
  Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>"
321
333
  ```
@@ -342,13 +354,10 @@ jettypod work cleanup <bug-id>
342
354
 
343
355
  ---
344
356
 
345
- ### Step 5: Mark Bug as Done
357
+ ### Step 5: Completion
346
358
 
347
- **After the fix is merged, mark the bug as done:**
348
-
349
- ```bash
350
- jettypod work status <bug-id> done
351
- ```
359
+ **The merge command automatically sets the bug as ready for review.**
360
+ It will appear with accept/reject buttons on the kanban board. Do NOT call `jettypod work status <bug-id> done` — that bypasses the review gate.
352
361
 
353
362
  **Emit gate signal:**
354
363
 
@@ -360,11 +369,11 @@ jettypod ui gate complete --data='{"summary":"Bug fixed: [brief description]","f
360
369
 
361
370
  ```
362
371
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
363
- Bug Fix Complete
372
+ Bug Fix Complete
364
373
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
365
374
 
366
- 🐛 Bug: #[bug-id] [title]
367
- 🧪 Regression test: features/<bug-slug>.feature
375
+ Bug: #[bug-id] [title]
376
+ Regression test: features/<bug-slug>.feature
368
377
 
369
378
  What was fixed:
370
379
  - Root cause: [brief description]
@@ -387,7 +396,7 @@ Before ending bug-mode skill, ensure:
387
396
  - [ ] Fix committed with descriptive message
388
397
  - [ ] Bug merged to main
389
398
  - [ ] Worktree cleaned up
390
- - [ ] Bug marked as done
399
+ - [ ] Bug merged (review gate set automatically by merge command)
391
400
 
392
401
  ---
393
402
 
@@ -403,7 +412,9 @@ cd <main-repo> # Change to main repo
403
412
  jettypod work cleanup <bug-id> # Clean up worktree
404
413
  ```
405
414
 
406
- **Mark bug complete:**
415
+ **Force-cleanup stale worktree** (when merge is blocked or fix was superseded):
407
416
  ```bash
408
- jettypod work status <bug-id> done
417
+ jettypod work cleanup --force <bug-id> # Bypasses merge requirement, prompts for confirmation
409
418
  ```
419
+
420
+ **Do NOT mark bug as done manually — the merge command sets the review gate automatically.**
@@ -30,7 +30,7 @@ When this skill is activated, you are investigating a bug to identify root cause
30
30
  **Gather from user:**
31
31
 
32
32
  ```
33
- 🐛 Bug Investigation
33
+ Bug Investigation
34
34
 
35
35
  I need to understand the bug before we investigate.
36
36
 
@@ -61,7 +61,7 @@ I need to understand the bug before we investigate.
61
61
 
62
62
  ```
63
63
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
64
- 📋 Bug Report
64
+ Bug Report
65
65
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
66
66
 
67
67
  **Symptom:** [What's happening]
@@ -105,7 +105,7 @@ git log --oneline -10 -- <relevant-files>
105
105
  **Display hypotheses:**
106
106
 
107
107
  ```
108
- 🔍 Hypotheses
108
+ Hypotheses
109
109
 
110
110
  Based on symptoms and codebase analysis:
111
111
 
@@ -148,12 +148,12 @@ Starting evidence gathering with H1...
148
148
  ```
149
149
  ━━━ Testing H1: [Title] ━━━
150
150
 
151
- 📂 Reading: [file:lines]
152
- 🔎 Looking for: [specific issue]
151
+ Reading: [file:lines]
152
+ Looking for: [specific issue]
153
153
 
154
154
  **Finding:** [What you discovered]
155
155
 
156
- **Verdict:** CONFIRMED / ELIMINATED / ⚠️ PARTIAL
156
+ **Verdict:** CONFIRMED / ELIMINATED / PARTIAL
157
157
  ```
158
158
 
159
159
  **If ELIMINATED:** Move to next hypothesis.
@@ -163,7 +163,7 @@ Starting evidence gathering with H1...
163
163
  **If all hypotheses eliminated:**
164
164
 
165
165
  ```
166
- ⚠️ Initial hypotheses eliminated
166
+ Initial hypotheses eliminated
167
167
 
168
168
  What we learned:
169
169
  - H1: Eliminated because [reason]
@@ -182,11 +182,11 @@ Loop back to Phase 2 with new information.
182
182
 
183
183
  **Goal:** Confirm root cause with user before creating work item.
184
184
 
185
- **⚡ ASYNC BOUNDARY - Wait for user confirmation**
185
+ **ASYNC BOUNDARY - Wait for user confirmation**
186
186
 
187
187
  ```
188
188
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
189
- 🎯 Root Cause Identified
189
+ Root Cause Identified
190
190
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
191
191
 
192
192
  **What's broken:** [Specific code/logic]
@@ -212,24 +212,20 @@ If user disagrees or adds context, revise understanding and confirm again.
212
212
 
213
213
  **Goal:** Create tracked bug work item with breadcrumbs for implementation.
214
214
 
215
- **Create bug work item with description:**
215
+ **Create bug work item with description (using --from for truncation safety):**
216
216
 
217
- ```bash
218
- jettypod work create bug "[Bug title - brief description]" "Root cause: [What's broken and why]
219
- Location: [file:line]
220
-
221
- Fix approach:
222
- - [Step 1]
223
- - [Step 2]
224
-
225
- Files to modify:
226
- - [file1]: [what to change]
227
- - [file2]: [what to change]
217
+ Use the **Write** tool to create `/tmp/jettypod-create.json`:
218
+ ```json
219
+ {
220
+ "type": "bug",
221
+ "title": "[Bug title - brief description]",
222
+ "description": "Root cause: [What's broken and why]\nLocation: [file:line]\n\nFix approach:\n- [Step 1]\n- [Step 2]\n\nFiles to modify:\n- [file1]: [what to change]\n- [file2]: [what to change]\n\nVerification:\n- Regression test passes\n- Original bug no longer reproduces\n- [Any other checks]"
223
+ }
224
+ ```
228
225
 
229
- Verification:
230
- - Regression test passes
231
- - Original bug no longer reproduces
232
- - [Any other checks]"
226
+ Then run via **Bash**:
227
+ ```bash
228
+ jettypod work create --from=/tmp/jettypod-create.json
233
229
  ```
234
230
 
235
231
  **Capture the bug ID from output.**
@@ -252,12 +248,12 @@ jettypod ui gate work-created --data='{"id":<bug-id>,"title":"[bug title]"}'
252
248
 
253
249
  ```
254
250
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
255
- Bug Planning Complete
251
+ Bug Planning Complete
256
252
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
257
253
 
258
- 📋 Bug: #<bug-id> [title]
259
- 📍 Root cause: [file:line]
260
- 🔧 Fix approach: [brief summary]
254
+ Bug: #<bug-id> [title]
255
+ Root cause: [file:line]
256
+ Fix approach: [brief summary]
261
257
 
262
258
  Ready to implement the fix?
263
259
  ```