jettypod 4.4.9 → 4.4.11

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 (34) hide show
  1. package/apps/dashboard/README.md +36 -0
  2. package/apps/dashboard/app/favicon.ico +0 -0
  3. package/apps/dashboard/app/globals.css +122 -0
  4. package/apps/dashboard/app/layout.tsx +34 -0
  5. package/apps/dashboard/app/page.tsx +25 -0
  6. package/apps/dashboard/app/work/[id]/page.tsx +193 -0
  7. package/apps/dashboard/components/KanbanBoard.tsx +201 -0
  8. package/apps/dashboard/components/WorkItemTree.tsx +116 -0
  9. package/apps/dashboard/components.json +22 -0
  10. package/apps/dashboard/eslint.config.mjs +18 -0
  11. package/apps/dashboard/lib/db.ts +270 -0
  12. package/apps/dashboard/lib/utils.ts +6 -0
  13. package/apps/dashboard/next.config.ts +7 -0
  14. package/apps/dashboard/package.json +33 -0
  15. package/apps/dashboard/postcss.config.mjs +7 -0
  16. package/apps/dashboard/public/file.svg +1 -0
  17. package/apps/dashboard/public/globe.svg +1 -0
  18. package/apps/dashboard/public/next.svg +1 -0
  19. package/apps/dashboard/public/vercel.svg +1 -0
  20. package/apps/dashboard/public/window.svg +1 -0
  21. package/apps/dashboard/tsconfig.json +34 -0
  22. package/claude-hooks/enforce-skill-activation.js +225 -0
  23. package/jettypod.js +53 -0
  24. package/lib/current-work.js +10 -18
  25. package/lib/migrations/016-workflow-checkpoints-table.js +70 -0
  26. package/lib/migrations/017-backfill-epic-id.js +54 -0
  27. package/lib/planning-status.js +68 -0
  28. package/lib/workflow-checkpoint.js +204 -0
  29. package/package.json +7 -2
  30. package/skills-templates/chore-mode/SKILL.md +3 -0
  31. package/skills-templates/epic-planning/SKILL.md +225 -154
  32. package/skills-templates/feature-planning/SKILL.md +172 -87
  33. package/skills-templates/speed-mode/SKILL.md +161 -338
  34. package/skills-templates/stable-mode/SKILL.md +8 -2
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Workflow Checkpoint - Persistence for Session Resume
3
+ *
4
+ * This module provides checkpoint creation, update, and retrieval for workflow
5
+ * state persistence. Enables resuming interrupted workflows across sessions.
6
+ *
7
+ * Core principles:
8
+ * - Branch-aware: checkpoints are tied to specific git branch context
9
+ * - Atomic updates: each step transition updates the checkpoint
10
+ * - Context preservation: stores full workflow state as JSON
11
+ * - Auto-cleanup: checkpoints are deleted on successful completion
12
+ */
13
+
14
+ const { execSync } = require('child_process');
15
+
16
+ /**
17
+ * Get the current git branch name
18
+ *
19
+ * @returns {string} Current branch name
20
+ */
21
+ function getCurrentBranch() {
22
+ try {
23
+ return execSync('git branch --show-current', { encoding: 'utf8' }).trim();
24
+ } catch (err) {
25
+ return 'unknown';
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Create a new workflow checkpoint
31
+ *
32
+ * @param {Object} db - SQLite database connection
33
+ * @param {Object} options - Checkpoint options
34
+ * @param {string} options.skillName - Name of the skill (e.g., 'feature-planning')
35
+ * @param {number} options.currentStep - Current step number (1-indexed)
36
+ * @param {number} [options.totalSteps] - Total number of steps (optional)
37
+ * @param {Object} [options.context] - Workflow context to preserve (optional)
38
+ * @param {string} [options.branchName] - Git branch name (defaults to current branch)
39
+ * @param {number} [options.workItemId] - Associated work item ID (optional)
40
+ * @returns {Promise<Object>} Created checkpoint with id
41
+ */
42
+ async function createCheckpoint(db, options) {
43
+ if (!db) {
44
+ throw new Error('Database connection required');
45
+ }
46
+
47
+ const {
48
+ skillName,
49
+ currentStep,
50
+ totalSteps = null,
51
+ context = {},
52
+ branchName = getCurrentBranch(),
53
+ workItemId = null
54
+ } = options;
55
+
56
+ const contextJson = JSON.stringify(context);
57
+
58
+ return new Promise((resolve, reject) => {
59
+ db.run(
60
+ `INSERT INTO workflow_checkpoints
61
+ (skill_name, current_step, total_steps, context_json, branch_name, work_item_id)
62
+ VALUES (?, ?, ?, ?, ?, ?)`,
63
+ [skillName, currentStep, totalSteps, contextJson, branchName, workItemId],
64
+ function(err) {
65
+ if (err) {
66
+ reject(err);
67
+ } else {
68
+ resolve({
69
+ id: this.lastID,
70
+ skill_name: skillName,
71
+ current_step: currentStep,
72
+ total_steps: totalSteps,
73
+ context_json: contextJson,
74
+ branch_name: branchName,
75
+ work_item_id: workItemId
76
+ });
77
+ }
78
+ }
79
+ );
80
+ });
81
+ }
82
+
83
+ /**
84
+ * Update an existing workflow checkpoint
85
+ *
86
+ * @param {Object} db - SQLite database connection
87
+ * @param {string} branchName - Git branch name to identify checkpoint
88
+ * @param {Object} updates - Fields to update
89
+ * @param {number} [updates.currentStep] - New step number
90
+ * @param {number} [updates.totalSteps] - New total steps
91
+ * @param {Object} [updates.context] - New context (merged with existing)
92
+ * @returns {Promise<void>}
93
+ */
94
+ async function updateCheckpoint(db, branchName, updates) {
95
+ const fields = [];
96
+ const values = [];
97
+
98
+ if (updates.currentStep !== undefined) {
99
+ fields.push('current_step = ?');
100
+ values.push(updates.currentStep);
101
+ }
102
+
103
+ if (updates.totalSteps !== undefined) {
104
+ fields.push('total_steps = ?');
105
+ values.push(updates.totalSteps);
106
+ }
107
+
108
+ if (updates.context !== undefined) {
109
+ fields.push('context_json = ?');
110
+ values.push(JSON.stringify(updates.context));
111
+ }
112
+
113
+ // Always update the updated_at timestamp
114
+ fields.push('updated_at = CURRENT_TIMESTAMP');
115
+
116
+ values.push(branchName);
117
+
118
+ return new Promise((resolve, reject) => {
119
+ db.run(
120
+ `UPDATE workflow_checkpoints SET ${fields.join(', ')} WHERE branch_name = ?`,
121
+ values,
122
+ (err) => {
123
+ if (err) {
124
+ reject(err);
125
+ } else {
126
+ resolve();
127
+ }
128
+ }
129
+ );
130
+ });
131
+ }
132
+
133
+ /**
134
+ * Get checkpoint for a specific branch
135
+ *
136
+ * @param {Object} db - SQLite database connection
137
+ * @param {string} [branchName] - Git branch name (defaults to current branch)
138
+ * @returns {Promise<Object|null>} Checkpoint or null if not found
139
+ */
140
+ async function getCheckpoint(db, branchName = getCurrentBranch()) {
141
+ return new Promise((resolve, reject) => {
142
+ db.get(
143
+ `SELECT * FROM workflow_checkpoints WHERE branch_name = ?`,
144
+ [branchName],
145
+ (err, row) => {
146
+ if (err) {
147
+ reject(err);
148
+ } else if (!row) {
149
+ resolve(null);
150
+ } else {
151
+ // Parse context_json back to object
152
+ resolve({
153
+ ...row,
154
+ context: row.context_json ? JSON.parse(row.context_json) : {}
155
+ });
156
+ }
157
+ }
158
+ );
159
+ });
160
+ }
161
+
162
+ /**
163
+ * Delete checkpoint for a specific branch (on successful completion)
164
+ *
165
+ * @param {Object} db - SQLite database connection
166
+ * @param {string} [branchName] - Git branch name (defaults to current branch)
167
+ * @returns {Promise<void>}
168
+ */
169
+ async function deleteCheckpoint(db, branchName = getCurrentBranch()) {
170
+ return new Promise((resolve, reject) => {
171
+ db.run(
172
+ `DELETE FROM workflow_checkpoints WHERE branch_name = ?`,
173
+ [branchName],
174
+ (err) => {
175
+ if (err) {
176
+ reject(err);
177
+ } else {
178
+ resolve();
179
+ }
180
+ }
181
+ );
182
+ });
183
+ }
184
+
185
+ /**
186
+ * Check if a checkpoint exists for the current branch
187
+ *
188
+ * @param {Object} db - SQLite database connection
189
+ * @param {string} [branchName] - Git branch name (defaults to current branch)
190
+ * @returns {Promise<boolean>} True if checkpoint exists
191
+ */
192
+ async function hasCheckpoint(db, branchName = getCurrentBranch()) {
193
+ const checkpoint = await getCheckpoint(db, branchName);
194
+ return checkpoint !== null;
195
+ }
196
+
197
+ module.exports = {
198
+ createCheckpoint,
199
+ updateCheckpoint,
200
+ getCheckpoint,
201
+ deleteCheckpoint,
202
+ hasCheckpoint,
203
+ getCurrentBranch
204
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jettypod",
3
- "version": "4.4.9",
3
+ "version": "4.4.11",
4
4
  "description": "AI-powered development workflow manager with TDD, BDD, and automatic test generation",
5
5
  "main": "jettypod.js",
6
6
  "bin": {
@@ -34,10 +34,14 @@
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
- "prepublishOnly": "echo '🔄 Syncing skills to templates...' && cp -r .claude/skills/* skills-templates/ && echo '✅ Skills synced'"
37
+ "prepublishOnly": "echo '🔄 Syncing skills to templates...' && cp -r .claude/skills/* skills-templates/ && echo '✅ Skills synced'",
38
+ "dashboard": "npm run dev --prefix apps/dashboard",
39
+ "dashboard:build": "npm run build --prefix apps/dashboard",
40
+ "dashboard:start": "npm run start --prefix apps/dashboard"
38
41
  },
39
42
  "devDependencies": {
40
43
  "@cucumber/cucumber": "^10.0.1",
44
+ "@types/better-sqlite3": "^7.6.13",
41
45
  "chai": "^6.0.1",
42
46
  "jest": "^30.1.3"
43
47
  },
@@ -54,6 +58,7 @@
54
58
  "coverageDirectory": "coverage"
55
59
  },
56
60
  "dependencies": {
61
+ "better-sqlite3": "^12.5.0",
57
62
  "chalk": "^4.1.2",
58
63
  "sqlite3": "^5.1.7"
59
64
  },
@@ -304,6 +304,9 @@ git add .
304
304
  git commit -m "chore: [brief description]"
305
305
  git push
306
306
 
307
+ # Switch to main repo before merging (merge cannot run from inside worktree)
308
+ cd $(git rev-parse --git-common-dir)/..
309
+
307
310
  # Merge to main (auto-marks chore done)
308
311
  jettypod work merge
309
312
  ```