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
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Upload release artifacts to Cloudflare R2.
5
+ * Run after electron-builder: npm run upload:r2
6
+ *
7
+ * Requires CLOUDFLARE_API_TOKEN env var (or wrangler login).
8
+ * Uploads: latest-mac.yml, DMG, ZIP, and blockmap files.
9
+ */
10
+
11
+ const { execSync } = require('child_process');
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ const BUCKET_NAME = 'jettypod-releases';
16
+ const DIST_DIR = path.join(__dirname, '..', 'dist');
17
+
18
+ // File patterns to upload
19
+ const UPLOAD_PATTERNS = [
20
+ /^latest-mac\.yml$/,
21
+ /\.dmg$/,
22
+ /\.zip$/,
23
+ /\.blockmap$/,
24
+ ];
25
+
26
+ function findArtifacts() {
27
+ if (!fs.existsSync(DIST_DIR)) {
28
+ console.error(`❌ dist/ directory not found at ${DIST_DIR}`);
29
+ console.error('Run electron:build first.');
30
+ process.exit(1);
31
+ }
32
+
33
+ const files = fs.readdirSync(DIST_DIR);
34
+ return files.filter((file) =>
35
+ UPLOAD_PATTERNS.some((pattern) => pattern.test(file))
36
+ );
37
+ }
38
+
39
+ function uploadFile(filename) {
40
+ const filePath = path.join(DIST_DIR, filename);
41
+ const stats = fs.statSync(filePath);
42
+ const sizeMB = (stats.size / (1024 * 1024)).toFixed(1);
43
+
44
+ console.log(` Uploading ${filename} (${sizeMB} MB)...`);
45
+
46
+ try {
47
+ execSync(
48
+ `npx wrangler r2 object put "${BUCKET_NAME}/${filename}" --file="${filePath}" --remote`,
49
+ { stdio: 'pipe' }
50
+ );
51
+ console.log(` ✅ ${filename}`);
52
+ return true;
53
+ } catch (error) {
54
+ console.error(` ❌ Failed to upload ${filename}: ${error.message}`);
55
+ return false;
56
+ }
57
+ }
58
+
59
+ function main() {
60
+ console.log('🚀 Uploading release artifacts to R2...\n');
61
+
62
+ const artifacts = findArtifacts();
63
+ if (artifacts.length === 0) {
64
+ console.error('❌ No release artifacts found in dist/');
65
+ console.error('Expected: latest-mac.yml, .dmg, .zip, or .blockmap files');
66
+ process.exit(1);
67
+ }
68
+
69
+ console.log(`Found ${artifacts.length} artifact(s):\n`);
70
+
71
+ let success = 0;
72
+ let failed = 0;
73
+
74
+ for (const artifact of artifacts) {
75
+ if (uploadFile(artifact)) {
76
+ success++;
77
+ } else {
78
+ failed++;
79
+ }
80
+ }
81
+
82
+ console.log(`\n📦 Upload complete: ${success} uploaded, ${failed} failed`);
83
+
84
+ if (failed > 0) {
85
+ process.exit(1);
86
+ }
87
+ }
88
+
89
+ main();
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Standalone WebSocket server for browser dev mode.
5
+ *
6
+ * Polls the JettyPod SQLite database and cucumber-results.json for changes
7
+ * and broadcasts notifications to connected clients. This is the same
8
+ * behaviour embedded in electron/main.js, extracted so `npm run dev` can
9
+ * provide live updates without Electron running.
10
+ *
11
+ * Usage:
12
+ * node scripts/ws-server.js [project-path]
13
+ *
14
+ * project-path defaults to the repository root (two levels up from this script).
15
+ */
16
+
17
+ const { WebSocketServer } = require('ws');
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+
21
+ const WS_PORT = 47808;
22
+ const DB_POLL_MS = 500;
23
+
24
+ const projectRoot = process.argv[2] || path.resolve(__dirname, '..', '..', '..');
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // State
28
+ // ---------------------------------------------------------------------------
29
+ const clients = new Set();
30
+ let dbPollInterval = null;
31
+ let testResultsPollInterval = null;
32
+ let lastDbMtimes = { db: null, wal: null };
33
+ let lastTestResultsMtime = null;
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Helpers
37
+ // ---------------------------------------------------------------------------
38
+ function log(msg) {
39
+ console.log(`[ws-server] ${msg}`);
40
+ }
41
+
42
+ function broadcast(message) {
43
+ const payload = JSON.stringify(message);
44
+ for (const client of clients) {
45
+ if (client.readyState === 1) { // WebSocket.OPEN
46
+ client.send(payload);
47
+ }
48
+ }
49
+ }
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Database polling (WAL-aware)
53
+ // ---------------------------------------------------------------------------
54
+ function startDatabasePolling() {
55
+ const dbPath = path.join(projectRoot, '.jettypod', 'work.db');
56
+
57
+ if (!fs.existsSync(dbPath)) {
58
+ log(`Database not found at ${dbPath}, retrying in 5s...`);
59
+ setTimeout(startDatabasePolling, 5000);
60
+ return;
61
+ }
62
+
63
+ try {
64
+ const dbStats = fs.statSync(dbPath);
65
+ lastDbMtimes.db = dbStats.mtimeMs;
66
+
67
+ const walPath = dbPath + '-wal';
68
+ if (fs.existsSync(walPath)) {
69
+ lastDbMtimes.wal = fs.statSync(walPath).mtimeMs;
70
+ }
71
+ } catch (err) {
72
+ log(`Failed to get initial db stats: ${err.message}`);
73
+ return;
74
+ }
75
+
76
+ log(`Polling database for changes (WAL-aware)...`);
77
+
78
+ dbPollInterval = setInterval(() => {
79
+ try {
80
+ let changed = false;
81
+
82
+ const dbStats = fs.statSync(dbPath);
83
+ if (dbStats.mtimeMs !== lastDbMtimes.db) {
84
+ lastDbMtimes.db = dbStats.mtimeMs;
85
+ changed = true;
86
+ }
87
+
88
+ const walPath = dbPath + '-wal';
89
+ if (fs.existsSync(walPath)) {
90
+ const walStats = fs.statSync(walPath);
91
+ if (walStats.mtimeMs !== lastDbMtimes.wal) {
92
+ lastDbMtimes.wal = walStats.mtimeMs;
93
+ changed = true;
94
+ }
95
+ }
96
+
97
+ if (changed) {
98
+ broadcast({ type: 'db_change', timestamp: Date.now() });
99
+ }
100
+ } catch {
101
+ // File might be temporarily locked during writes — ignore
102
+ }
103
+ }, DB_POLL_MS);
104
+ }
105
+
106
+ // ---------------------------------------------------------------------------
107
+ // Test results polling
108
+ // ---------------------------------------------------------------------------
109
+ function startTestResultsPolling() {
110
+ const testResultsPath = path.join(projectRoot, 'cucumber-results.json');
111
+
112
+ if (fs.existsSync(testResultsPath)) {
113
+ try {
114
+ lastTestResultsMtime = fs.statSync(testResultsPath).mtimeMs;
115
+ log(`Watching test results at ${testResultsPath}`);
116
+ } catch (err) {
117
+ log(`Failed to get initial test results stats: ${err.message}`);
118
+ }
119
+ } else {
120
+ log(`Test results file not found at ${testResultsPath}, will watch for creation`);
121
+ }
122
+
123
+ testResultsPollInterval = setInterval(() => {
124
+ try {
125
+ if (!fs.existsSync(testResultsPath)) {
126
+ if (lastTestResultsMtime !== null) {
127
+ lastTestResultsMtime = null;
128
+ }
129
+ return;
130
+ }
131
+
132
+ const stats = fs.statSync(testResultsPath);
133
+ if (stats.mtimeMs !== lastTestResultsMtime) {
134
+ lastTestResultsMtime = stats.mtimeMs;
135
+ broadcast({ type: 'test_change', timestamp: Date.now() });
136
+ }
137
+ } catch {
138
+ // ignore
139
+ }
140
+ }, DB_POLL_MS);
141
+ }
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // Server
145
+ // ---------------------------------------------------------------------------
146
+ const wss = new WebSocketServer({ port: WS_PORT });
147
+
148
+ wss.on('connection', (ws) => {
149
+ clients.add(ws);
150
+ log(`Client connected. Total: ${clients.size}`);
151
+ ws.send(JSON.stringify({ type: 'connected', timestamp: Date.now() }));
152
+
153
+ ws.on('close', () => {
154
+ clients.delete(ws);
155
+ log(`Client disconnected. Total: ${clients.size}`);
156
+ });
157
+
158
+ ws.on('error', (err) => {
159
+ log(`Client error: ${err.message}`);
160
+ ws.close();
161
+ clients.delete(ws);
162
+ });
163
+ });
164
+
165
+ wss.on('error', (err) => {
166
+ if (err.code === 'EADDRINUSE') {
167
+ log(`Port ${WS_PORT} already in use (Electron running?). Exiting.`);
168
+ process.exit(0);
169
+ }
170
+ log(`Server error: ${err.message}`);
171
+ });
172
+
173
+ wss.on('listening', () => {
174
+ log(`WebSocket server running on ws://localhost:${WS_PORT}`);
175
+ log(`Watching project: ${projectRoot}`);
176
+ startDatabasePolling();
177
+ startTestResultsPolling();
178
+ });
179
+
180
+ // Graceful shutdown
181
+ function shutdown() {
182
+ log('Shutting down...');
183
+ if (dbPollInterval) clearInterval(dbPollInterval);
184
+ if (testResultsPollInterval) clearInterval(testResultsPollInterval);
185
+ for (const client of clients) client.close();
186
+ clients.clear();
187
+ wss.close(() => process.exit(0));
188
+ }
189
+
190
+ process.on('SIGINT', shutdown);
191
+ process.on('SIGTERM', shutdown);