jettypod 4.4.118 → 4.4.121

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 (240) hide show
  1. package/.env +4 -3
  2. package/Cargo.lock +6450 -0
  3. package/Cargo.toml +35 -0
  4. package/README.md +5 -1
  5. package/TAURI-MIGRATION-PLAN.md +840 -0
  6. package/apps/dashboard/app/connect-claude/page.tsx +5 -6
  7. package/apps/dashboard/app/decision/[id]/page.tsx +63 -58
  8. package/apps/dashboard/app/demo/gates/page.tsx +43 -45
  9. package/apps/dashboard/app/design-system/page.tsx +868 -0
  10. package/apps/dashboard/app/globals.css +80 -4
  11. package/apps/dashboard/app/install-claude/page.tsx +4 -6
  12. package/apps/dashboard/app/login/page.tsx +72 -54
  13. package/apps/dashboard/app/page.tsx +101 -48
  14. package/apps/dashboard/app/settings/page.tsx +61 -13
  15. package/apps/dashboard/app/signup/page.tsx +242 -0
  16. package/apps/dashboard/app/subscribe/page.tsx +0 -2
  17. package/apps/dashboard/app/tests/page.tsx +37 -4
  18. package/apps/dashboard/app/welcome/page.tsx +13 -16
  19. package/apps/dashboard/app/work/[id]/page.tsx +117 -118
  20. package/apps/dashboard/app/work/[id]/proof/page.tsx +1489 -0
  21. package/apps/dashboard/components/AppShell.tsx +92 -85
  22. package/apps/dashboard/components/CardMenu.tsx +45 -12
  23. package/apps/dashboard/components/ClaudePanel.tsx +771 -850
  24. package/apps/dashboard/components/ClaudePanelInput.tsx +43 -15
  25. package/apps/dashboard/components/ConnectClaudeScreen.tsx +17 -34
  26. package/apps/dashboard/components/CopyableId.tsx +3 -4
  27. package/apps/dashboard/components/DetailReviewActions.tsx +100 -0
  28. package/apps/dashboard/components/DragContext.tsx +134 -63
  29. package/apps/dashboard/components/DraggableCard.tsx +3 -5
  30. package/apps/dashboard/components/DropZone.tsx +6 -7
  31. package/apps/dashboard/components/EditableDetailDescription.tsx +7 -13
  32. package/apps/dashboard/components/EditableDetailTitle.tsx +6 -13
  33. package/apps/dashboard/components/EditableTitle.tsx +26 -7
  34. package/apps/dashboard/components/ElapsedTimer.tsx +66 -0
  35. package/apps/dashboard/components/EpicGroup.tsx +359 -0
  36. package/apps/dashboard/components/GateCard.tsx +79 -17
  37. package/apps/dashboard/components/GateChoiceCard.tsx +15 -18
  38. package/apps/dashboard/components/InstallClaudeScreen.tsx +15 -32
  39. package/apps/dashboard/components/JettyLoader.tsx +37 -0
  40. package/apps/dashboard/components/KanbanBoard.tsx +368 -958
  41. package/apps/dashboard/components/KanbanCard.tsx +740 -0
  42. package/apps/dashboard/components/LazyCard.tsx +62 -0
  43. package/apps/dashboard/components/LazyMarkdown.tsx +11 -0
  44. package/apps/dashboard/components/MainNav.tsx +38 -73
  45. package/apps/dashboard/components/MessageBlock.tsx +468 -0
  46. package/apps/dashboard/components/ModeStartCard.tsx +15 -16
  47. package/apps/dashboard/components/OnboardingWelcome.tsx +213 -0
  48. package/apps/dashboard/components/PlaceholderCard.tsx +3 -4
  49. package/apps/dashboard/components/ProjectSwitcher.tsx +30 -30
  50. package/apps/dashboard/components/PrototypeTimeline.tsx +72 -51
  51. package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +406 -388
  52. package/apps/dashboard/components/RealTimeTestsWrapper.tsx +373 -235
  53. package/apps/dashboard/components/ReviewFooter.tsx +139 -0
  54. package/apps/dashboard/components/SessionList.tsx +19 -19
  55. package/apps/dashboard/components/SubscribeContent.tsx +91 -47
  56. package/apps/dashboard/components/TestTree.tsx +16 -16
  57. package/apps/dashboard/components/TipCard.tsx +16 -17
  58. package/apps/dashboard/components/Toast.tsx +5 -6
  59. package/apps/dashboard/components/TypeIcon.tsx +55 -0
  60. package/apps/dashboard/components/ViewModeToolbar.tsx +104 -0
  61. package/apps/dashboard/components/WaveCompletionAnimation.tsx +52 -65
  62. package/apps/dashboard/components/WelcomeScreen.tsx +19 -35
  63. package/apps/dashboard/components/WorkItemHeader.tsx +4 -5
  64. package/apps/dashboard/components/WorkItemTree.tsx +11 -32
  65. package/apps/dashboard/components/settings/AccountSection.tsx +55 -35
  66. package/apps/dashboard/components/settings/AiContextSection.tsx +89 -0
  67. package/apps/dashboard/components/settings/ContextDocumentsSection.tsx +317 -0
  68. package/apps/dashboard/components/settings/EnvVarsSection.tsx +74 -152
  69. package/apps/dashboard/components/settings/GeneralSection.tsx +162 -56
  70. package/apps/dashboard/components/settings/ProjectStackSection.tsx +948 -0
  71. package/apps/dashboard/components/settings/SettingsLayout.tsx +4 -5
  72. package/apps/dashboard/components/ui/Button.tsx +104 -0
  73. package/apps/dashboard/components/ui/Input.tsx +78 -0
  74. package/apps/dashboard/components.json +1 -1
  75. package/apps/dashboard/contexts/ClaudeSessionContext.tsx +711 -418
  76. package/apps/dashboard/contexts/ConnectionStatusContext.tsx +25 -5
  77. package/apps/dashboard/contexts/UsageContext.tsx +87 -32
  78. package/apps/dashboard/dev.sh +35 -0
  79. package/apps/dashboard/eslint.config.mjs +9 -9
  80. package/apps/dashboard/hooks/useKanbanAnimation.ts +29 -0
  81. package/apps/dashboard/hooks/useKanbanUndo.ts +83 -0
  82. package/apps/dashboard/hooks/useWebSocket.ts +138 -83
  83. package/apps/dashboard/index.html +73 -0
  84. package/apps/dashboard/lib/constants.ts +43 -0
  85. package/apps/dashboard/lib/data-bridge.ts +722 -0
  86. package/apps/dashboard/lib/db.ts +69 -1265
  87. package/apps/dashboard/lib/environment-config.ts +173 -0
  88. package/apps/dashboard/lib/environment-verification.ts +119 -0
  89. package/apps/dashboard/lib/kanban-utils.ts +270 -0
  90. package/apps/dashboard/lib/proof-run.ts +495 -0
  91. package/apps/dashboard/lib/proof-scenario-runner.ts +346 -0
  92. package/apps/dashboard/lib/run-migrations.js +27 -2
  93. package/apps/dashboard/lib/service-recovery.ts +326 -0
  94. package/apps/dashboard/lib/session-state-machine.ts +1 -0
  95. package/apps/dashboard/lib/session-state-utils.ts +0 -164
  96. package/apps/dashboard/lib/session-stream-manager.ts +308 -134
  97. package/apps/dashboard/lib/shadows.ts +7 -0
  98. package/apps/dashboard/lib/stream-manager-registry.ts +46 -6
  99. package/apps/dashboard/lib/tauri-bridge.ts +102 -0
  100. package/apps/dashboard/lib/tauri.ts +106 -0
  101. package/apps/dashboard/lib/utils.ts +6 -0
  102. package/apps/dashboard/next-env.d.ts +1 -1
  103. package/apps/dashboard/package.json +21 -32
  104. package/apps/dashboard/public/bug-icon.png +0 -0
  105. package/apps/dashboard/public/buoy-icon.png +0 -0
  106. package/apps/dashboard/public/fonts/Satoshi-Variable.woff2 +0 -0
  107. package/apps/dashboard/public/fonts/Satoshi-VariableItalic.woff2 +0 -0
  108. package/apps/dashboard/public/in-flight-seagull.png +0 -0
  109. package/apps/dashboard/public/jetty-icon-loading-alt.svg +11 -0
  110. package/apps/dashboard/public/jetty-icon-loading.svg +11 -0
  111. package/apps/dashboard/public/jettypod_logo.png +0 -0
  112. package/apps/dashboard/public/pier-icon.png +0 -0
  113. package/apps/dashboard/public/star-icon.png +0 -0
  114. package/apps/dashboard/public/wrench-icon.png +0 -0
  115. package/apps/dashboard/scripts/tauri-build.js +228 -0
  116. package/apps/dashboard/scripts/upload-tauri-to-r2.js +125 -0
  117. package/apps/dashboard/scripts/ws-server.js +191 -0
  118. package/apps/dashboard/src/main.tsx +12 -0
  119. package/apps/dashboard/src/router.tsx +107 -0
  120. package/apps/dashboard/src/vite-env.d.ts +1 -0
  121. package/apps/dashboard/tsconfig.json +7 -12
  122. package/apps/dashboard/tsconfig.tsbuildinfo +1 -1
  123. package/apps/dashboard/vite.config.ts +33 -0
  124. package/apps/update-server/src/index.ts +228 -80
  125. package/claude-hooks/global-guardrails.js +14 -13
  126. package/crates/jettypod-cli/Cargo.toml +19 -0
  127. package/crates/jettypod-cli/src/commands.rs +1249 -0
  128. package/crates/jettypod-cli/src/main.rs +595 -0
  129. package/crates/jettypod-core/Cargo.toml +26 -0
  130. package/crates/jettypod-core/build.rs +98 -0
  131. package/crates/jettypod-core/migrations/V1__baseline.sql +197 -0
  132. package/crates/jettypod-core/migrations/V2__work_items_indexes.sql +6 -0
  133. package/crates/jettypod-core/migrations/V3__qa_steps.sql +2 -0
  134. package/crates/jettypod-core/src/auth.rs +294 -0
  135. package/crates/jettypod-core/src/config.rs +397 -0
  136. package/crates/jettypod-core/src/db/mod.rs +507 -0
  137. package/crates/jettypod-core/src/db/recovery.rs +114 -0
  138. package/crates/jettypod-core/src/db/startup.rs +101 -0
  139. package/crates/jettypod-core/src/db/validate.rs +149 -0
  140. package/crates/jettypod-core/src/error.rs +76 -0
  141. package/crates/jettypod-core/src/git.rs +458 -0
  142. package/crates/jettypod-core/src/lib.rs +20 -0
  143. package/crates/jettypod-core/src/sessions.rs +625 -0
  144. package/crates/jettypod-core/src/skills.rs +556 -0
  145. package/crates/jettypod-core/src/work.rs +1086 -0
  146. package/crates/jettypod-core/src/worktree.rs +628 -0
  147. package/crates/jettypod-core/src/ws.rs +767 -0
  148. package/cucumber-test.cjs +6 -0
  149. package/cucumber.js +9 -3
  150. package/docs/COMMAND_REFERENCE.md +34 -0
  151. package/hooks/post-checkout +32 -75
  152. package/hooks/post-merge +111 -10
  153. package/jest.setup.js +1 -0
  154. package/jettypod.js +145 -116
  155. package/lib/bdd-preflight.js +96 -0
  156. package/lib/chore-taxonomy.js +33 -10
  157. package/lib/database.js +36 -16
  158. package/lib/db-watcher.js +1 -1
  159. package/lib/git-hooks/pre-commit +1 -1
  160. package/lib/jettypod-backup.js +27 -4
  161. package/lib/merge-lock.js +111 -253
  162. package/lib/migrations/027-plan-at-creation-column.js +3 -1
  163. package/lib/migrations/029-remove-autoincrement.js +307 -0
  164. package/lib/migrations/029-rename-corrupted-to-cleaned.js +149 -0
  165. package/lib/migrations/030-rejection-round-columns.js +54 -0
  166. package/lib/migrations/031-session-isolation-index.js +17 -0
  167. package/lib/migrations/index.js +47 -4
  168. package/lib/schema.js +10 -5
  169. package/lib/seed-onboarding.js +1 -1
  170. package/lib/update-command/index.js +9 -175
  171. package/lib/work-commands/index.js +144 -19
  172. package/lib/work-tracking/index.js +148 -27
  173. package/lib/worktree-diagnostics.js +16 -16
  174. package/lib/worktree-facade.js +1 -1
  175. package/lib/worktree-manager.js +8 -8
  176. package/lib/worktree-reconciler.js +5 -5
  177. package/package.json +9 -2
  178. package/scripts/ndjson-to-cucumber-json.js +152 -0
  179. package/scripts/postinstall.js +25 -0
  180. package/skills-templates/bug-mode/SKILL.md +79 -20
  181. package/skills-templates/bug-planning/SKILL.md +25 -29
  182. package/skills-templates/chore-mode/SKILL.md +171 -69
  183. package/skills-templates/chore-mode/verification.js +51 -10
  184. package/skills-templates/chore-planning/SKILL.md +47 -18
  185. package/skills-templates/design-system-selection/SKILL.md +273 -0
  186. package/skills-templates/epic-planning/SKILL.md +82 -48
  187. package/skills-templates/external-transition/SKILL.md +47 -47
  188. package/skills-templates/feature-planning/SKILL.md +173 -74
  189. package/skills-templates/production-mode/SKILL.md +69 -49
  190. package/skills-templates/request-routing/SKILL.md +4 -4
  191. package/skills-templates/simple-improvement/SKILL.md +74 -29
  192. package/skills-templates/speed-mode/SKILL.md +217 -141
  193. package/skills-templates/stable-mode/SKILL.md +148 -89
  194. package/apps/dashboard/README.md +0 -36
  195. package/apps/dashboard/app/api/claude/[workItemId]/message/route.ts +0 -386
  196. package/apps/dashboard/app/api/claude/[workItemId]/pin/route.ts +0 -24
  197. package/apps/dashboard/app/api/claude/[workItemId]/route.ts +0 -167
  198. package/apps/dashboard/app/api/claude/sessions/[sessionId]/content/route.ts +0 -52
  199. package/apps/dashboard/app/api/claude/sessions/[sessionId]/message/route.ts +0 -378
  200. package/apps/dashboard/app/api/claude/sessions/[sessionId]/pin/route.ts +0 -24
  201. package/apps/dashboard/app/api/claude/sessions/cleanup/route.ts +0 -34
  202. package/apps/dashboard/app/api/claude/sessions/route.ts +0 -184
  203. package/apps/dashboard/app/api/decisions/[id]/route.ts +0 -25
  204. package/apps/dashboard/app/api/internal/set-project/route.ts +0 -17
  205. package/apps/dashboard/app/api/kanban/route.ts +0 -15
  206. package/apps/dashboard/app/api/settings/env-vars/route.ts +0 -125
  207. package/apps/dashboard/app/api/settings/general/route.ts +0 -21
  208. package/apps/dashboard/app/api/tests/route.ts +0 -9
  209. package/apps/dashboard/app/api/tests/run/route.ts +0 -82
  210. package/apps/dashboard/app/api/tests/run/stream/route.ts +0 -71
  211. package/apps/dashboard/app/api/tests/undefined/route.ts +0 -9
  212. package/apps/dashboard/app/api/usage/route.ts +0 -17
  213. package/apps/dashboard/app/api/work/[id]/description/route.ts +0 -21
  214. package/apps/dashboard/app/api/work/[id]/epic/route.ts +0 -21
  215. package/apps/dashboard/app/api/work/[id]/order/route.ts +0 -21
  216. package/apps/dashboard/app/api/work/[id]/status/route.ts +0 -21
  217. package/apps/dashboard/app/api/work/[id]/title/route.ts +0 -21
  218. package/apps/dashboard/app/layout.tsx +0 -43
  219. package/apps/dashboard/components/UpgradeBanner.tsx +0 -29
  220. package/apps/dashboard/electron/ipc-handlers.js +0 -1028
  221. package/apps/dashboard/electron/main.js +0 -2124
  222. package/apps/dashboard/electron/preload.js +0 -123
  223. package/apps/dashboard/electron/session-manager.js +0 -141
  224. package/apps/dashboard/electron-builder.config.js +0 -357
  225. package/apps/dashboard/hooks/useClaudeSessions.ts +0 -299
  226. package/apps/dashboard/lib/claude-process-manager.ts +0 -492
  227. package/apps/dashboard/lib/db-bridge.ts +0 -282
  228. package/apps/dashboard/lib/prototypes.ts +0 -202
  229. package/apps/dashboard/lib/test-results-db.ts +0 -307
  230. package/apps/dashboard/lib/tests.ts +0 -282
  231. package/apps/dashboard/next.config.js +0 -50
  232. package/apps/dashboard/postcss.config.mjs +0 -7
  233. package/apps/dashboard/public/file.svg +0 -1
  234. package/apps/dashboard/public/globe.svg +0 -1
  235. package/apps/dashboard/public/next.svg +0 -1
  236. package/apps/dashboard/public/vercel.svg +0 -1
  237. package/apps/dashboard/public/window.svg +0 -1
  238. package/apps/dashboard/scripts/download-node.js +0 -104
  239. package/apps/dashboard/scripts/upload-to-r2.js +0 -89
  240. package/docs/bdd-guidance.md +0 -390
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ default: {
3
+ require: ['features/step_definitions/claude-cli-subprocess-env.steps.js'],
4
+ format: ['progress'],
5
+ }
6
+ };
package/cucumber.js CHANGED
@@ -1,7 +1,13 @@
1
+ const path = require('path');
2
+
1
3
  module.exports = {
2
4
  default: {
3
- require: ['features/**/steps.js', 'features/**/simple-steps.js', 'features/**/*.steps.js'],
4
- format: ['progress', 'json:cucumber-results.json'],
5
- publishQuiet: true
5
+ require: ['features/support/*.js', 'features/**/steps.js', 'features/**/simple-steps.js', 'features/**/*.steps.js'],
6
+ format: [
7
+ 'progress',
8
+ `json:${path.resolve(__dirname, 'cucumber-results.json')}`,
9
+ `message:${path.resolve(__dirname, 'cucumber-results.ndjson')}`,
10
+ `rerun:${path.resolve(__dirname, '@rerun.txt')}`,
11
+ ]
6
12
  }
7
13
  };
@@ -98,8 +98,36 @@ jettypod project discover complete \
98
98
 
99
99
  ## Work Item Management
100
100
 
101
+ ### `jettypod work create --from=<filepath>`
102
+
103
+ Create any work item type from a JSON file. **This is the preferred method** — it avoids shell quoting issues and is truncation-safe (a truncated file path errors immediately instead of hanging).
104
+
105
+ ```bash
106
+ # Write JSON file first, then create
107
+ jettypod work create --from=/tmp/jettypod-create.json
108
+ ```
109
+
110
+ **JSON file format:**
111
+ ```json
112
+ {
113
+ "type": "feature",
114
+ "title": "Login Form",
115
+ "description": "User login UI component",
116
+ "parent": 5,
117
+ "mode": "speed",
118
+ "needsDiscovery": false
119
+ }
120
+ ```
121
+
122
+ **Required fields:** `type` (epic/feature/chore/bug), `title`
123
+ **Optional fields:** `description`, `parent` (ID), `mode` (speed/stable/production), `needsDiscovery` (boolean)
124
+
125
+ ---
126
+
101
127
  ### `jettypod work create epic <title> [description] [flags]`
102
128
 
129
+ > **Prefer `--from` (above)** for Claude Code usage — positional args risk shell hangs from truncated quotes.
130
+
103
131
  Create a new epic.
104
132
 
105
133
  ```bash
@@ -126,6 +154,8 @@ jettypod work create epic "Real-time Updates" "Live data sync" --needs-discovery
126
154
 
127
155
  ### `jettypod work create feature <title> [description] [flags]`
128
156
 
157
+ > **Prefer `--from` (above)** for Claude Code usage — positional args risk shell hangs from truncated quotes.
158
+
129
159
  Create a new feature.
130
160
 
131
161
  ```bash
@@ -155,6 +185,8 @@ jettypod work create feature "Dashboard" "User dashboard" --mode=stable
155
185
 
156
186
  ### `jettypod work create chore <title> [description] [flags]`
157
187
 
188
+ > **Prefer `--from` (above)** for Claude Code usage — positional args risk shell hangs from truncated quotes.
189
+
158
190
  Create a chore (non-feature work).
159
191
 
160
192
  ```bash
@@ -170,6 +202,8 @@ jettypod work create chore "Refactor auth module" --parent=5
170
202
 
171
203
  ### `jettypod work create bug <title> [description] [flags]`
172
204
 
205
+ > **Prefer `--from` (above)** for Claude Code usage — positional args risk shell hangs from truncated quotes.
206
+
173
207
  Create a bug.
174
208
 
175
209
  ```bash
@@ -7,96 +7,53 @@
7
7
  * 2. Imports database snapshots after checkout
8
8
  */
9
9
 
10
- const { importAll } = require('jettypod/lib/db-import');
11
- const { walCheckpoint } = require('jettypod/lib/database');
12
10
  const { execSync } = require('child_process');
13
11
 
14
- (async () => {
15
- const cwd = process.cwd();
12
+ const cwd = process.cwd();
16
13
 
17
- // FIRST: Check if we're in a JettyPod worktree and prevent default branch checkout
18
- if (cwd.includes('.jettypod-work')) {
14
+ // Prevent checking out default branch in JettyPod worktrees
15
+ if (cwd.includes('.jettypod-work')) {
16
+ try {
17
+ const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {
18
+ encoding: 'utf8',
19
+ stdio: ['pipe', 'pipe', 'pipe']
20
+ }).trim();
21
+
22
+ // Detect default branch
23
+ let defaultBranch;
19
24
  try {
20
- // Get the branch we just checked out
21
- const currentBranch = execSync('git rev-parse --abbrev-ref HEAD', {
25
+ defaultBranch = execSync('git symbolic-ref refs/remotes/origin/HEAD', {
22
26
  encoding: 'utf8',
23
27
  stdio: ['pipe', 'pipe', 'pipe']
24
- }).trim();
25
-
26
- // Detect default branch
27
- let defaultBranch;
28
+ }).trim().replace('refs/remotes/origin/', '');
29
+ } catch {
28
30
  try {
29
- // Try to get the default branch from git config
30
- defaultBranch = execSync('git symbolic-ref refs/remotes/origin/HEAD', {
31
- encoding: 'utf8',
32
- stdio: ['pipe', 'pipe', 'pipe']
33
- }).trim().replace('refs/remotes/origin/', '');
31
+ execSync('git rev-parse --verify main', { stdio: ['pipe', 'pipe', 'pipe'] });
32
+ defaultBranch = 'main';
34
33
  } catch {
35
- // Fallback: check which common branch names exist
36
34
  try {
37
- execSync('git rev-parse --verify main', { stdio: ['pipe', 'pipe', 'pipe'] });
38
- defaultBranch = 'main';
35
+ execSync('git rev-parse --verify master', { stdio: ['pipe', 'pipe', 'pipe'] });
36
+ defaultBranch = 'master';
39
37
  } catch {
40
- try {
41
- execSync('git rev-parse --verify master', { stdio: ['pipe', 'pipe', 'pipe'] });
42
- defaultBranch = 'master';
43
- } catch {
44
- // Can't determine default branch - skip check
45
- defaultBranch = null;
46
- }
38
+ defaultBranch = null;
47
39
  }
48
40
  }
41
+ }
49
42
 
50
- // Check if we checked out the default branch
51
- if (defaultBranch && currentBranch === defaultBranch) {
52
- console.error('');
53
- console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
54
- console.error(' ERROR: Cannot checkout default branch in worktree');
55
- console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
56
- console.error('');
57
- console.error(`You are in a JettyPod worktree for isolated work.`);
58
- console.error(`Checking out ${defaultBranch} in a worktree bypasses the merge workflow.`);
59
- console.error('');
60
- console.error('To merge your changes:');
61
- console.error(' 1. Commit your changes: git add . && git commit -m "..."');
62
- console.error(' 2. Use JettyPod merge: jettypod work merge');
63
- console.error('');
64
- console.error('This ensures proper worktree cleanup and database updates.');
65
- console.error('');
66
- console.error('Reverting checkout...');
67
- console.error('');
68
-
69
- // Revert to previous branch
70
- try {
71
- execSync('git checkout -', { stdio: 'inherit' });
72
- } catch (revertErr) {
73
- console.error('Failed to revert checkout. You may need to manually switch branches.');
74
- }
43
+ if (defaultBranch && currentBranch === defaultBranch) {
44
+ console.error('\n❌ Cannot checkout default branch in worktree');
45
+ console.error(`Checking out ${defaultBranch} bypasses the merge workflow.\n`);
46
+ console.error('To merge: jettypod work merge\n');
47
+ console.error('Reverting checkout...\n');
75
48
 
76
- process.exit(1);
49
+ try {
50
+ execSync('git checkout -', { stdio: 'inherit' });
51
+ } catch (revertErr) {
52
+ console.error('Failed to revert. Manually switch branches.');
77
53
  }
78
- } catch (err) {
79
- // If we can't determine the branch, allow the checkout
80
- // (better to be permissive than block legitimate operations)
54
+ process.exit(1);
81
55
  }
82
- }
83
-
84
- // SECOND: Checkpoint WAL before importing to prevent corruption
85
- // This flushes any pending writes to the main database file
86
- try {
87
- await walCheckpoint();
88
- } catch (err) {
89
- // Checkpoint failure shouldn't block - just log and continue
90
- console.error('Post-checkout hook warning: WAL checkpoint failed:', err.message);
91
- }
92
-
93
- // THIRD: Import database snapshots
94
- try {
95
- await importAll();
96
- process.exit(0);
97
56
  } catch (err) {
98
- // Log error but don't block checkout
99
- console.error('Post-checkout hook warning:', err.message);
100
- process.exit(0);
57
+ // Can't determine branch - allow checkout
101
58
  }
102
- })();
59
+ }
package/hooks/post-merge CHANGED
@@ -1,17 +1,118 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { importAll } = require('jettypod/lib/db-import');
3
+ // JettyPod Git Hook: post-merge
4
+ // Backs up database to .jettypod-backup/ when merging to main
5
+ // Uses --no-verify to bypass pre-commit tests for data-only commits
4
6
 
5
- (async () => {
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const { execSync } = require('child_process');
10
+
11
+ const jettypodDir = path.join(process.cwd(), '.jettypod');
12
+ const backupDir = path.join(process.cwd(), '.jettypod-backup');
13
+
14
+ // Backup database and commit
15
+ function backupDatabase() {
16
+ if (!fs.existsSync(jettypodDir)) {
17
+ return; // No JettyPod directory, skip backup
18
+ }
19
+
20
+ // Only backup on main/master branch
21
+ let currentBranch;
6
22
  try {
7
- // Import JSON snapshots into databases after merge
8
- await importAll();
23
+ currentBranch = execSync('git branch --show-current', { encoding: 'utf-8' }).trim();
24
+ } catch {
25
+ return;
26
+ }
27
+
28
+ if (currentBranch !== 'main' && currentBranch !== 'master') {
29
+ return;
30
+ }
31
+
32
+ console.log('\n💾 Backing up database...\n');
33
+
34
+ try {
35
+ // Ensure backup directory exists
36
+ if (!fs.existsSync(backupDir)) {
37
+ fs.mkdirSync(backupDir, { recursive: true });
38
+ }
39
+
40
+ // Backup work.db using sqlite3 .backup command
41
+ // CRITICAL: fs.copyFileSync does NOT work with WAL mode - it only copies
42
+ // the base .db file, missing all data in the -wal file. sqlite3 .backup
43
+ // reads the logical database (including WAL) and writes a clean standalone file.
44
+ const sourcePath = path.join(jettypodDir, 'work.db');
45
+ const backupPath = path.join(backupDir, 'work.db');
9
46
 
10
- // Exit successfully - merge should not be blocked
11
- process.exit(0);
47
+ if (!fs.existsSync(sourcePath)) {
48
+ console.log('⚠️ No work.db found, skipping backup\n');
49
+ return;
50
+ }
51
+
52
+ execSync(`sqlite3 "${sourcePath}" ".backup '${backupPath}'"`, {
53
+ stdio: ['pipe', 'pipe', 'pipe']
54
+ });
55
+
56
+ // SAFETY: Validate backup didn't lose data
57
+ // If an existing backup has significantly more items, refuse to overwrite
58
+ const newCount = parseInt(execSync(
59
+ `sqlite3 "${backupPath}" "SELECT count(*) FROM work_items"`,
60
+ { encoding: 'utf-8', stdio: 'pipe' }
61
+ ).trim()) || 0;
62
+
63
+ if (newCount < 2) {
64
+ console.log('⚠️ Backup has fewer than 2 items - likely corrupted, skipping commit\n');
65
+ return;
66
+ }
67
+
68
+ // Check against git's previous version of the backup
69
+ try {
70
+ const oldCount = parseInt(execSync(
71
+ `git show HEAD:.jettypod-backup/work.db 2>/dev/null | sqlite3 "" "SELECT count(*) FROM work_items"`,
72
+ { encoding: 'utf-8', stdio: 'pipe' }
73
+ ).trim()) || 0;
74
+
75
+ if (oldCount > 0 && newCount < oldCount * 0.5) {
76
+ console.log(`⚠️ Backup shrank from ${oldCount} to ${newCount} items (>50% loss) - refusing to commit\n`);
77
+ return;
78
+ }
79
+ } catch {
80
+ // No previous backup in git or can't read it - proceed
81
+ }
82
+
83
+ // Stage the backup file (force-add because .jettypod-backup is gitignored
84
+ // to prevent worktree symlink corruption, but backups must be committed)
85
+ execSync(`git add -f "${backupPath}"`, {
86
+ stdio: ['pipe', 'pipe', 'pipe']
87
+ });
88
+
89
+ // Check if there are any staged changes
90
+ try {
91
+ execSync('git diff --cached --quiet', {
92
+ stdio: ['pipe', 'pipe', 'pipe']
93
+ });
94
+ // No staged changes, nothing to commit
95
+ console.log('✅ Database backup unchanged\n');
96
+ return;
97
+ } catch {
98
+ // Has staged changes, continue to commit
99
+ }
100
+
101
+ execSync('git commit --no-verify -m "chore: Update database backup"', {
102
+ stdio: ['pipe', 'pipe', 'pipe']
103
+ });
104
+
105
+ // Push the backup commit
106
+ execSync('git push', {
107
+ stdio: ['pipe', 'pipe', 'pipe']
108
+ });
109
+
110
+ console.log('✅ Database backed up and pushed\n');
12
111
  } catch (err) {
13
- // Log error but don't block merge
14
- console.error('Post-merge hook warning:', err.message);
15
- process.exit(0);
112
+ console.error('⚠️ Database backup failed:', err.message);
113
+ // Don't block - backup is important but not critical
16
114
  }
17
- })();
115
+ }
116
+
117
+ // Main
118
+ backupDatabase();
package/jest.setup.js ADDED
@@ -0,0 +1 @@
1
+ process.env.NODE_ENV = 'test';