jettypod 4.4.66 → 4.4.68
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.
- package/jettypod.js +18 -9
- package/lib/migrations/018-worktrees-merged-status.js +148 -0
- package/lib/work-commands/index.js +134 -77
- package/package.json +1 -1
- package/skills-templates/chore-mode/SKILL.md +11 -10
- package/skills-templates/feature-planning/SKILL.md +20 -10
- package/skills-templates/speed-mode/SKILL.md +33 -34
- package/skills-templates/stable-mode/SKILL.md +20 -20
package/jettypod.js
CHANGED
|
@@ -270,6 +270,7 @@ jettypod work create feature "<title>" --parent=<id>
|
|
|
270
270
|
jettypod work create chore "<title>" --parent=<id>
|
|
271
271
|
jettypod work start <id> # Creates worktree branch
|
|
272
272
|
jettypod work merge # Merges worktree back to main
|
|
273
|
+
jettypod work cleanup <id> # Cleanup worktree after merge
|
|
273
274
|
jettypod work tests <feature-id> # Create test worktree for BDD
|
|
274
275
|
jettypod work tests merge <id> # Merge tests to main
|
|
275
276
|
jettypod work status <id> cancelled
|
|
@@ -1294,18 +1295,26 @@ switch (command) {
|
|
|
1294
1295
|
}
|
|
1295
1296
|
} else if (subcommand === 'cleanup') {
|
|
1296
1297
|
const workCommands = require('./lib/work-commands/index.js');
|
|
1297
|
-
const dryRun = args
|
|
1298
|
+
const dryRun = args.includes('--dry-run');
|
|
1299
|
+
// Find numeric arg for specific work item cleanup
|
|
1300
|
+
const workItemId = args.find(a => /^\d+$/.test(a)) ? parseInt(args.find(a => /^\d+$/.test(a))) : null;
|
|
1298
1301
|
|
|
1299
1302
|
try {
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
console.log(`\n${results.message || 'No worktrees needed cleanup'}`);
|
|
1303
|
+
if (workItemId) {
|
|
1304
|
+
// Clean up specific work item's worktree
|
|
1305
|
+
await workCommands.cleanupWorkItem(workItemId);
|
|
1304
1306
|
} else {
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1307
|
+
// Batch cleanup of all orphaned worktrees
|
|
1308
|
+
const results = await workCommands.cleanupWorktrees({ dryRun });
|
|
1309
|
+
|
|
1310
|
+
if (results.cleaned === 0 && results.failed === 0) {
|
|
1311
|
+
console.log(`\n${results.message || 'No worktrees needed cleanup'}`);
|
|
1312
|
+
} else {
|
|
1313
|
+
console.log(`\n✓ Cleanup complete:`);
|
|
1314
|
+
console.log(` Cleaned: ${results.cleaned}`);
|
|
1315
|
+
if (results.failed > 0) {
|
|
1316
|
+
console.log(` Failed: ${results.failed}`);
|
|
1317
|
+
}
|
|
1309
1318
|
}
|
|
1310
1319
|
}
|
|
1311
1320
|
} catch (err) {
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: Add 'merged' status to worktrees table
|
|
3
|
+
*
|
|
4
|
+
* Purpose: Support separating merge from cleanup - worktrees stay after merge
|
|
5
|
+
* until explicitly cleaned up. This prevents shell CWD corruption when merge
|
|
6
|
+
* is run from inside the worktree.
|
|
7
|
+
*
|
|
8
|
+
* SQLite doesn't support ALTER CONSTRAINT, so we need to recreate the table.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
id: '018-worktrees-merged-status',
|
|
13
|
+
description: 'Add merged status to worktrees table for separate cleanup step',
|
|
14
|
+
|
|
15
|
+
async up(db) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
db.serialize(() => {
|
|
18
|
+
// 1. Create new table with updated constraint
|
|
19
|
+
db.run(`
|
|
20
|
+
CREATE TABLE IF NOT EXISTS worktrees_new (
|
|
21
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
22
|
+
work_item_id INTEGER NOT NULL,
|
|
23
|
+
branch_name TEXT NOT NULL,
|
|
24
|
+
worktree_path TEXT NOT NULL,
|
|
25
|
+
status TEXT NOT NULL CHECK(status IN ('active', 'merging', 'merged', 'cleanup_pending', 'corrupted')),
|
|
26
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
27
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
28
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
29
|
+
)
|
|
30
|
+
`, (err) => {
|
|
31
|
+
if (err) return reject(err);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// 2. Copy data from old table
|
|
35
|
+
db.run(`
|
|
36
|
+
INSERT INTO worktrees_new (id, work_item_id, branch_name, worktree_path, status, created_at, updated_at)
|
|
37
|
+
SELECT id, work_item_id, branch_name, worktree_path, status, created_at, updated_at
|
|
38
|
+
FROM worktrees
|
|
39
|
+
`, (err) => {
|
|
40
|
+
if (err) return reject(err);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// 3. Drop old indexes
|
|
44
|
+
db.run('DROP INDEX IF EXISTS idx_worktrees_work_item_id', (err) => {
|
|
45
|
+
if (err) return reject(err);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
db.run('DROP INDEX IF EXISTS idx_worktrees_status', (err) => {
|
|
49
|
+
if (err) return reject(err);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// 4. Drop old table
|
|
53
|
+
db.run('DROP TABLE worktrees', (err) => {
|
|
54
|
+
if (err) return reject(err);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// 5. Rename new table
|
|
58
|
+
db.run('ALTER TABLE worktrees_new RENAME TO worktrees', (err) => {
|
|
59
|
+
if (err) return reject(err);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 6. Recreate indexes
|
|
63
|
+
db.run(`
|
|
64
|
+
CREATE INDEX idx_worktrees_work_item_id
|
|
65
|
+
ON worktrees(work_item_id)
|
|
66
|
+
`, (err) => {
|
|
67
|
+
if (err) return reject(err);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
db.run(`
|
|
71
|
+
CREATE INDEX idx_worktrees_status
|
|
72
|
+
ON worktrees(status)
|
|
73
|
+
`, (err) => {
|
|
74
|
+
if (err) return reject(err);
|
|
75
|
+
resolve();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
async down(db) {
|
|
82
|
+
// Reverse migration - remove 'merged' status
|
|
83
|
+
// Any rows with 'merged' status will fail the constraint, so convert them first
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
db.serialize(() => {
|
|
86
|
+
// Convert any 'merged' status to 'cleanup_pending'
|
|
87
|
+
db.run(`UPDATE worktrees SET status = 'cleanup_pending' WHERE status = 'merged'`, (err) => {
|
|
88
|
+
if (err) return reject(err);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Create table without 'merged' in constraint
|
|
92
|
+
db.run(`
|
|
93
|
+
CREATE TABLE IF NOT EXISTS worktrees_new (
|
|
94
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
95
|
+
work_item_id INTEGER NOT NULL,
|
|
96
|
+
branch_name TEXT NOT NULL,
|
|
97
|
+
worktree_path TEXT NOT NULL,
|
|
98
|
+
status TEXT NOT NULL CHECK(status IN ('active', 'merging', 'cleanup_pending', 'corrupted')),
|
|
99
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
100
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
101
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
102
|
+
)
|
|
103
|
+
`, (err) => {
|
|
104
|
+
if (err) return reject(err);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
db.run(`
|
|
108
|
+
INSERT INTO worktrees_new (id, work_item_id, branch_name, worktree_path, status, created_at, updated_at)
|
|
109
|
+
SELECT id, work_item_id, branch_name, worktree_path, status, created_at, updated_at
|
|
110
|
+
FROM worktrees
|
|
111
|
+
`, (err) => {
|
|
112
|
+
if (err) return reject(err);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
db.run('DROP INDEX IF EXISTS idx_worktrees_work_item_id', (err) => {
|
|
116
|
+
if (err) return reject(err);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
db.run('DROP INDEX IF EXISTS idx_worktrees_status', (err) => {
|
|
120
|
+
if (err) return reject(err);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
db.run('DROP TABLE worktrees', (err) => {
|
|
124
|
+
if (err) return reject(err);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
db.run('ALTER TABLE worktrees_new RENAME TO worktrees', (err) => {
|
|
128
|
+
if (err) return reject(err);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
db.run(`
|
|
132
|
+
CREATE INDEX idx_worktrees_work_item_id
|
|
133
|
+
ON worktrees(work_item_id)
|
|
134
|
+
`, (err) => {
|
|
135
|
+
if (err) return reject(err);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
db.run(`
|
|
139
|
+
CREATE INDEX idx_worktrees_status
|
|
140
|
+
ON worktrees(status)
|
|
141
|
+
`, (err) => {
|
|
142
|
+
if (err) return reject(err);
|
|
143
|
+
resolve();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
};
|
|
@@ -1130,6 +1130,106 @@ async function cleanupWorktrees(options = {}) {
|
|
|
1130
1130
|
return results;
|
|
1131
1131
|
}
|
|
1132
1132
|
|
|
1133
|
+
/**
|
|
1134
|
+
* Clean up a specific worktree after merge
|
|
1135
|
+
* Should be run from main repo after cd'ing out of the worktree
|
|
1136
|
+
* @param {number} workItemId - The work item ID to clean up
|
|
1137
|
+
* @returns {Promise<Object>} Result with success status
|
|
1138
|
+
*/
|
|
1139
|
+
async function cleanupWorkItem(workItemId) {
|
|
1140
|
+
const db = getDb();
|
|
1141
|
+
const gitRoot = getGitRoot();
|
|
1142
|
+
|
|
1143
|
+
// Check we're not inside the worktree being deleted
|
|
1144
|
+
const cwd = process.cwd();
|
|
1145
|
+
if (cwd.includes('.jettypod-work')) {
|
|
1146
|
+
return Promise.reject(new Error(
|
|
1147
|
+
`Cannot cleanup from inside a worktree.\n\n` +
|
|
1148
|
+
`Run this first: cd ${gitRoot}`
|
|
1149
|
+
));
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// Find the worktree for this work item
|
|
1153
|
+
const worktree = await new Promise((resolve, reject) => {
|
|
1154
|
+
db.get(
|
|
1155
|
+
`SELECT w.id, w.worktree_path, w.branch_name, w.status, wi.title
|
|
1156
|
+
FROM worktrees w
|
|
1157
|
+
JOIN work_items wi ON w.work_item_id = wi.id
|
|
1158
|
+
WHERE w.work_item_id = ?`,
|
|
1159
|
+
[workItemId],
|
|
1160
|
+
(err, row) => {
|
|
1161
|
+
if (err) return reject(err);
|
|
1162
|
+
resolve(row);
|
|
1163
|
+
}
|
|
1164
|
+
);
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
if (!worktree) {
|
|
1168
|
+
console.log(`No worktree found for work item #${workItemId}`);
|
|
1169
|
+
return { success: true, message: 'No worktree to clean up' };
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
if (worktree.status === 'active') {
|
|
1173
|
+
return Promise.reject(new Error(
|
|
1174
|
+
`Worktree for #${workItemId} is still active.\n` +
|
|
1175
|
+
`Run 'jettypod work merge ${workItemId}' first.`
|
|
1176
|
+
));
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
console.log(`Cleaning up worktree for #${workItemId}: ${worktree.title}`);
|
|
1180
|
+
|
|
1181
|
+
// Remove git worktree if it exists
|
|
1182
|
+
if (worktree.worktree_path && fs.existsSync(worktree.worktree_path)) {
|
|
1183
|
+
try {
|
|
1184
|
+
execSync(`git worktree remove "${worktree.worktree_path}" --force`, {
|
|
1185
|
+
cwd: gitRoot,
|
|
1186
|
+
stdio: 'pipe'
|
|
1187
|
+
});
|
|
1188
|
+
console.log('✅ Removed worktree directory');
|
|
1189
|
+
} catch (err) {
|
|
1190
|
+
console.log(`⚠️ Failed to remove worktree directory: ${err.message}`);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
// Delete the branch if it exists
|
|
1195
|
+
if (worktree.branch_name) {
|
|
1196
|
+
try {
|
|
1197
|
+
execSync(`git branch -d "${worktree.branch_name}"`, {
|
|
1198
|
+
cwd: gitRoot,
|
|
1199
|
+
stdio: 'pipe'
|
|
1200
|
+
});
|
|
1201
|
+
console.log('✅ Deleted branch');
|
|
1202
|
+
} catch (err) {
|
|
1203
|
+
// Branch might not exist or might not be fully merged
|
|
1204
|
+
// Try force delete if regular delete failed
|
|
1205
|
+
try {
|
|
1206
|
+
execSync(`git branch -D "${worktree.branch_name}"`, {
|
|
1207
|
+
cwd: gitRoot,
|
|
1208
|
+
stdio: 'pipe'
|
|
1209
|
+
});
|
|
1210
|
+
console.log('✅ Deleted branch (force)');
|
|
1211
|
+
} catch {
|
|
1212
|
+
console.log(`⚠️ Could not delete branch: ${err.message}`);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// Delete worktree record from database
|
|
1218
|
+
await new Promise((resolve, reject) => {
|
|
1219
|
+
db.run(
|
|
1220
|
+
`DELETE FROM worktrees WHERE id = ?`,
|
|
1221
|
+
[worktree.id],
|
|
1222
|
+
(err) => {
|
|
1223
|
+
if (err) return reject(err);
|
|
1224
|
+
resolve();
|
|
1225
|
+
}
|
|
1226
|
+
);
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
console.log('✅ Worktree cleaned up');
|
|
1230
|
+
return { success: true };
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1133
1233
|
// Re-export getCurrentWork from shared module for backwards compatibility
|
|
1134
1234
|
// (used by jettypod.js)
|
|
1135
1235
|
|
|
@@ -1590,49 +1690,32 @@ async function mergeWork(options = {}) {
|
|
|
1590
1690
|
);
|
|
1591
1691
|
});
|
|
1592
1692
|
|
|
1593
|
-
//
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
// Delete worktree record from database
|
|
1609
|
-
await new Promise((resolve, reject) => {
|
|
1610
|
-
db.run(
|
|
1611
|
-
`DELETE FROM worktrees WHERE id = ?`,
|
|
1612
|
-
[worktree.id],
|
|
1613
|
-
(err) => {
|
|
1614
|
-
if (err) return reject(err);
|
|
1615
|
-
resolve();
|
|
1616
|
-
}
|
|
1617
|
-
);
|
|
1618
|
-
});
|
|
1619
|
-
|
|
1620
|
-
console.log('✅ Worktree cleaned up');
|
|
1621
|
-
|
|
1622
|
-
// Warn if shell CWD was inside deleted worktree
|
|
1623
|
-
if (cwdWillBeInvalid) {
|
|
1624
|
-
console.log('');
|
|
1625
|
-
console.log('⚠️ Your shell was inside the deleted worktree.');
|
|
1626
|
-
console.log(` Run this to fix: cd ${gitRoot}`);
|
|
1627
|
-
}
|
|
1628
|
-
} catch (worktreeErr) {
|
|
1629
|
-
console.warn(`Warning: Failed to clean up worktree: ${worktreeErr.message}`);
|
|
1630
|
-
// Non-fatal - continue with merge success
|
|
1631
|
-
}
|
|
1693
|
+
// Mark worktree as merged but DON'T delete it yet
|
|
1694
|
+
// This prevents shell CWD corruption when merge is run from inside the worktree
|
|
1695
|
+
// User must run `jettypod work cleanup` separately after cd'ing to main repo
|
|
1696
|
+
if (worktree && worktree.worktree_path) {
|
|
1697
|
+
await new Promise((resolve, reject) => {
|
|
1698
|
+
db.run(
|
|
1699
|
+
`UPDATE worktrees SET status = 'merged' WHERE id = ?`,
|
|
1700
|
+
[worktree.id],
|
|
1701
|
+
(err) => {
|
|
1702
|
+
if (err) return reject(err);
|
|
1703
|
+
resolve();
|
|
1704
|
+
}
|
|
1705
|
+
);
|
|
1706
|
+
});
|
|
1632
1707
|
}
|
|
1633
1708
|
|
|
1634
1709
|
console.log(`✅ Work item #${currentWork.id} marked as done`);
|
|
1635
1710
|
|
|
1711
|
+
// Instruct user to cleanup worktree separately
|
|
1712
|
+
if (worktree && worktree.worktree_path && fs.existsSync(worktree.worktree_path)) {
|
|
1713
|
+
console.log('');
|
|
1714
|
+
console.log('📁 Worktree preserved. To clean up:');
|
|
1715
|
+
console.log(` cd ${gitRoot}`);
|
|
1716
|
+
console.log(` jettypod work cleanup ${currentWork.id}`);
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1636
1719
|
if (withTransition) {
|
|
1637
1720
|
// Hold lock for transition phase (BDD generation)
|
|
1638
1721
|
// Work is done and worktree is cleaned up, but lock is held so no other merges
|
|
@@ -1897,46 +1980,11 @@ async function testsMerge(featureId) {
|
|
|
1897
1980
|
console.log('⚠️ Failed to push (non-fatal):', err.message);
|
|
1898
1981
|
}
|
|
1899
1982
|
|
|
1900
|
-
//
|
|
1901
|
-
//
|
|
1902
|
-
const shellCwd = process.cwd();
|
|
1903
|
-
const resolvedWorktreePath = path.resolve(worktreePath);
|
|
1904
|
-
const cwdWillBeInvalid = shellCwd.startsWith(resolvedWorktreePath);
|
|
1905
|
-
|
|
1906
|
-
try {
|
|
1907
|
-
execSync(`git worktree remove "${worktreePath}" --force`, {
|
|
1908
|
-
cwd: gitRoot,
|
|
1909
|
-
encoding: 'utf8',
|
|
1910
|
-
stdio: 'pipe'
|
|
1911
|
-
});
|
|
1912
|
-
console.log('✅ Removed worktree directory');
|
|
1913
|
-
|
|
1914
|
-
// Warn if shell CWD was inside deleted worktree
|
|
1915
|
-
if (cwdWillBeInvalid) {
|
|
1916
|
-
console.log('');
|
|
1917
|
-
console.log('⚠️ Your shell was inside the deleted worktree.');
|
|
1918
|
-
console.log(` Run this to fix: cd ${gitRoot}`);
|
|
1919
|
-
}
|
|
1920
|
-
} catch (err) {
|
|
1921
|
-
console.log('⚠️ Failed to remove worktree (non-fatal):', err.message);
|
|
1922
|
-
}
|
|
1923
|
-
|
|
1924
|
-
// Delete the test branch
|
|
1925
|
-
try {
|
|
1926
|
-
execSync(`git branch -d ${branchName}`, {
|
|
1927
|
-
cwd: gitRoot,
|
|
1928
|
-
encoding: 'utf8',
|
|
1929
|
-
stdio: 'pipe'
|
|
1930
|
-
});
|
|
1931
|
-
console.log('✅ Deleted test branch');
|
|
1932
|
-
} catch (err) {
|
|
1933
|
-
console.log('⚠️ Failed to delete branch (non-fatal):', err.message);
|
|
1934
|
-
}
|
|
1935
|
-
|
|
1936
|
-
// Delete worktree record from database (cleanup complete)
|
|
1983
|
+
// Mark worktree as merged but DON'T delete it yet
|
|
1984
|
+
// This prevents shell CWD corruption when merge is run from inside the worktree
|
|
1937
1985
|
await new Promise((resolve, reject) => {
|
|
1938
1986
|
db.run(
|
|
1939
|
-
`
|
|
1987
|
+
`UPDATE worktrees SET status = 'merged' WHERE id = ?`,
|
|
1940
1988
|
[worktree.id],
|
|
1941
1989
|
(err) => {
|
|
1942
1990
|
if (err) return reject(err);
|
|
@@ -1954,6 +2002,14 @@ async function testsMerge(featureId) {
|
|
|
1954
2002
|
console.log('');
|
|
1955
2003
|
console.log(`✅ Test worktree for feature #${featureId} merged successfully`);
|
|
1956
2004
|
|
|
2005
|
+
// Instruct user to cleanup worktree separately
|
|
2006
|
+
if (fs.existsSync(worktreePath)) {
|
|
2007
|
+
console.log('');
|
|
2008
|
+
console.log('📁 Worktree preserved. To clean up:');
|
|
2009
|
+
console.log(` cd ${gitRoot}`);
|
|
2010
|
+
console.log(` jettypod work cleanup ${featureId}`);
|
|
2011
|
+
}
|
|
2012
|
+
|
|
1957
2013
|
return Promise.resolve();
|
|
1958
2014
|
}
|
|
1959
2015
|
|
|
@@ -1962,6 +2018,7 @@ module.exports = {
|
|
|
1962
2018
|
stopWork,
|
|
1963
2019
|
getCurrentWork,
|
|
1964
2020
|
cleanupWorktrees,
|
|
2021
|
+
cleanupWorkItem,
|
|
1965
2022
|
mergeWork,
|
|
1966
2023
|
testsWork,
|
|
1967
2024
|
testsMerge
|
package/package.json
CHANGED
|
@@ -373,22 +373,23 @@ git commit -m "chore: [brief description]"
|
|
|
373
373
|
git push
|
|
374
374
|
```
|
|
375
375
|
|
|
376
|
-
|
|
376
|
+
**Merge and cleanup (3 steps):**
|
|
377
377
|
|
|
378
|
-
|
|
378
|
+
```bash
|
|
379
|
+
# Step 1: Merge (can run from worktree - it won't delete it)
|
|
380
|
+
jettypod work merge [chore-id]
|
|
381
|
+
```
|
|
379
382
|
|
|
380
383
|
```bash
|
|
381
|
-
#
|
|
384
|
+
# Step 2: cd to main repo
|
|
382
385
|
cd /path/to/main/repo
|
|
383
|
-
|
|
384
|
-
# Verify you're in the main repo
|
|
385
|
-
pwd && ls .jettypod
|
|
386
|
-
|
|
387
|
-
# Then merge (pass the work item ID explicitly)
|
|
388
|
-
jettypod work merge [chore-id]
|
|
386
|
+
pwd && ls .jettypod # verify
|
|
389
387
|
```
|
|
390
388
|
|
|
391
|
-
|
|
389
|
+
```bash
|
|
390
|
+
# Step 3: Clean up the worktree (now safe since shell is in main repo)
|
|
391
|
+
jettypod work cleanup [chore-id]
|
|
392
|
+
```
|
|
392
393
|
|
|
393
394
|
**Display:**
|
|
394
395
|
|
|
@@ -19,7 +19,7 @@ When this skill is activated, you are helping discover the best approach for a f
|
|
|
19
19
|
|---------|----------|------|-------|
|
|
20
20
|
| `work implement <feature-id>` | Transition feature to implementation phase | After chores created (Step 8C) | Feature Planning |
|
|
21
21
|
| `work tests start <feature-id>` | Create worktree for test authoring | After transition (Step 8D) | Feature Planning |
|
|
22
|
-
| `
|
|
22
|
+
| `work tests merge` → `cd` → `work cleanup` | Merge tests to main, then cleanup worktree | After tests validated (Step 8D) | Feature Planning |
|
|
23
23
|
| `work start <chore-id>` | Start implementing a specific chore | After tests merged (Step 8E) | Speed Mode |
|
|
24
24
|
|
|
25
25
|
**CRITICAL:** All commands are run by **Claude**, not the user. The distinction is:
|
|
@@ -653,8 +653,10 @@ Write your BDD files to:
|
|
|
653
653
|
<worktree>/features/email-login.feature
|
|
654
654
|
<worktree>/features/step_definitions/email-login.steps.js
|
|
655
655
|
|
|
656
|
-
When done,
|
|
657
|
-
|
|
656
|
+
When done, merge then cleanup:
|
|
657
|
+
jettypod work tests merge 42
|
|
658
|
+
cd /path/to/main/repo
|
|
659
|
+
jettypod work cleanup 42
|
|
658
660
|
```
|
|
659
661
|
|
|
660
662
|
**🛑 STOP AND CHECK:** Verify worktree was created successfully. If you see an error, investigate before continuing.
|
|
@@ -736,21 +738,29 @@ sqlite3 .jettypod/work.db "UPDATE work_items SET scenario_file = 'features/${FEA
|
|
|
736
738
|
|
|
737
739
|
**Sub-step 5: Merge tests to main**
|
|
738
740
|
|
|
739
|
-
|
|
741
|
+
**Merge and cleanup (3 steps):**
|
|
740
742
|
|
|
741
743
|
```bash
|
|
742
|
-
#
|
|
743
|
-
|
|
744
|
-
cd <main-repo-path> && jettypod work tests merge ${FEATURE_ID}
|
|
744
|
+
# Step 1: Merge tests (can run from worktree - it won't delete it)
|
|
745
|
+
jettypod work tests merge ${FEATURE_ID}
|
|
745
746
|
```
|
|
746
747
|
|
|
747
|
-
|
|
748
|
+
```bash
|
|
749
|
+
# Step 2: cd to main repo
|
|
750
|
+
cd <main-repo-path>
|
|
751
|
+
pwd && ls .jettypod # verify
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
```bash
|
|
755
|
+
# Step 3: Clean up the worktree (now safe since shell is in main repo)
|
|
756
|
+
jettypod work cleanup ${FEATURE_ID}
|
|
757
|
+
```
|
|
748
758
|
|
|
749
759
|
This will:
|
|
750
760
|
- Commit changes in the worktree
|
|
751
761
|
- Merge to main
|
|
752
762
|
- Push to remote
|
|
753
|
-
-
|
|
763
|
+
- Mark worktree as merged (cleanup is separate)
|
|
754
764
|
|
|
755
765
|
**🛑 STOP AND CHECK:** Verify merge succeeded:
|
|
756
766
|
- ✅ "Tests merged to main" → Proceed to Step 8E
|
|
@@ -920,6 +930,6 @@ Before completing feature planning, ensure:
|
|
|
920
930
|
- [ ] **Test worktree created** with `work tests start` (Step 8D)
|
|
921
931
|
- [ ] **BDD files written** in worktree (Step 8D)
|
|
922
932
|
- [ ] **BDD infrastructure validated** with dry-run (Step 8D)
|
|
923
|
-
- [ ] **Tests merged to main** with `
|
|
933
|
+
- [ ] **Tests merged to main** with `work tests merge` → `cd` → `work cleanup` (Step 8D)
|
|
924
934
|
- [ ] First chore started with `work start [chore-id]` (Step 8E)
|
|
925
935
|
- [ ] **Speed-mode skill invoked using Skill tool** (Step 8E)
|
|
@@ -613,28 +613,25 @@ More speed mode chores remain. Starting next chore:
|
|
|
613
613
|
|
|
614
614
|
**Merge and start next:**
|
|
615
615
|
|
|
616
|
-
**🚨 CRITICAL: Shell CWD Corruption Prevention**
|
|
617
|
-
|
|
618
|
-
The merge will delete the worktree. You must be in the main repo BEFORE merging.
|
|
619
|
-
|
|
620
616
|
```bash
|
|
621
617
|
# Commit changes in the worktree
|
|
622
618
|
git add . && git commit -m "feat: [brief description of what was implemented]"
|
|
623
619
|
```
|
|
624
620
|
|
|
625
621
|
```bash
|
|
626
|
-
#
|
|
627
|
-
cd /path/to/main/repo
|
|
628
|
-
|
|
629
|
-
# Verify you're in the main repo
|
|
630
|
-
pwd && ls .jettypod
|
|
631
|
-
|
|
632
|
-
# Then merge
|
|
622
|
+
# Step 1: Merge (can run from worktree - it won't delete it)
|
|
633
623
|
jettypod work merge [current-chore-id]
|
|
634
624
|
```
|
|
635
625
|
|
|
636
626
|
```bash
|
|
637
|
-
#
|
|
627
|
+
# Step 2: cd to main repo
|
|
628
|
+
cd /path/to/main/repo
|
|
629
|
+
pwd && ls .jettypod # verify
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
```bash
|
|
633
|
+
# Step 3: Clean up the worktree, then start next chore
|
|
634
|
+
jettypod work cleanup [current-chore-id]
|
|
638
635
|
jettypod work start [next-chore-id]
|
|
639
636
|
```
|
|
640
637
|
|
|
@@ -674,29 +671,28 @@ npx cucumber-js <scenario-file-path> --name "User can reach" --format progress
|
|
|
674
671
|
|
|
675
672
|
#### Step 7B: Merge Final Speed Chore
|
|
676
673
|
|
|
677
|
-
**🚨 CRITICAL: Shell CWD Corruption Prevention**
|
|
678
|
-
|
|
679
|
-
The merge will delete the worktree. You must be in the main repo BEFORE merging.
|
|
680
|
-
|
|
681
674
|
```bash
|
|
682
675
|
# Commit changes in the worktree
|
|
683
676
|
git add . && git commit -m "feat: [brief description of what was implemented]"
|
|
684
677
|
```
|
|
685
678
|
|
|
686
679
|
```bash
|
|
687
|
-
#
|
|
688
|
-
cd /path/to/main/repo
|
|
689
|
-
|
|
690
|
-
# Verify you're in the main repo
|
|
691
|
-
pwd && ls .jettypod
|
|
692
|
-
|
|
693
|
-
# Then merge with transition flag
|
|
680
|
+
# Step 1: Merge with transition flag (can run from worktree - it won't delete it)
|
|
694
681
|
jettypod work merge [current-chore-id] --with-transition
|
|
695
682
|
```
|
|
696
683
|
|
|
697
|
-
|
|
684
|
+
```bash
|
|
685
|
+
# Step 2: cd to main repo
|
|
686
|
+
cd /path/to/main/repo
|
|
687
|
+
pwd && ls .jettypod # verify
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
```bash
|
|
691
|
+
# Step 3: Clean up the worktree
|
|
692
|
+
jettypod work cleanup [current-chore-id]
|
|
693
|
+
```
|
|
698
694
|
|
|
699
|
-
After
|
|
695
|
+
After cleanup, you are on main branch. Ready to generate stable mode scenarios.
|
|
700
696
|
|
|
701
697
|
#### Step 7C: Generate and Propose Stable Mode Chores
|
|
702
698
|
|
|
@@ -770,20 +766,22 @@ Added error handling and edge case scenarios for stable mode.
|
|
|
770
766
|
- Step definitions for validation and error handling"
|
|
771
767
|
```
|
|
772
768
|
|
|
769
|
+
**Merge and cleanup (3 steps):**
|
|
770
|
+
|
|
773
771
|
```bash
|
|
774
|
-
#
|
|
775
|
-
|
|
776
|
-
cd <main-repo-path> && jettypod work tests merge <feature-id>
|
|
772
|
+
# Step 1: Merge tests (can run from worktree - it won't delete it)
|
|
773
|
+
jettypod work tests merge <feature-id>
|
|
777
774
|
```
|
|
778
775
|
|
|
779
776
|
```bash
|
|
780
|
-
#
|
|
781
|
-
|
|
777
|
+
# Step 2: cd to main repo
|
|
778
|
+
cd <main-repo-path>
|
|
779
|
+
pwd && ls .jettypod # verify
|
|
782
780
|
```
|
|
783
781
|
|
|
784
|
-
**If you see "No such file or directory" errors:** Your shell CWD was corrupted. Run:
|
|
785
782
|
```bash
|
|
786
|
-
|
|
783
|
+
# Step 3: Clean up the worktree (now safe since shell is in main repo)
|
|
784
|
+
jettypod work cleanup <feature-id>
|
|
787
785
|
```
|
|
788
786
|
|
|
789
787
|
**7. Present proposal to user:**
|
|
@@ -948,8 +946,9 @@ jettypod work start <chore-id> # Create worktree and start chore
|
|
|
948
946
|
**Create test worktree (for writing BDD scenarios):**
|
|
949
947
|
```bash
|
|
950
948
|
jettypod work tests <feature-id> # Create worktree for writing tests
|
|
951
|
-
|
|
952
|
-
cd <main-repo>
|
|
949
|
+
jettypod work tests merge <feature-id> # Merge tests (preserves worktree)
|
|
950
|
+
cd <main-repo> # cd to main repo
|
|
951
|
+
jettypod work cleanup <feature-id> # Clean up worktree
|
|
953
952
|
```
|
|
954
953
|
|
|
955
954
|
**Set feature mode (BEFORE creating chores):**
|
|
@@ -572,28 +572,25 @@ More stable mode chores remain. Starting next chore:
|
|
|
572
572
|
|
|
573
573
|
**Merge and start next:**
|
|
574
574
|
|
|
575
|
-
**🚨 CRITICAL: Shell CWD Corruption Prevention**
|
|
576
|
-
|
|
577
|
-
The merge will delete the worktree. You must be in the main repo BEFORE merging.
|
|
578
|
-
|
|
579
575
|
```bash
|
|
580
576
|
# Commit changes in the worktree
|
|
581
577
|
git add . && git commit -m "feat: [brief description of error handling added]"
|
|
582
578
|
```
|
|
583
579
|
|
|
584
580
|
```bash
|
|
585
|
-
#
|
|
586
|
-
cd /path/to/main/repo
|
|
587
|
-
|
|
588
|
-
# Verify you're in the main repo
|
|
589
|
-
pwd && ls .jettypod
|
|
590
|
-
|
|
591
|
-
# Then merge
|
|
581
|
+
# Step 1: Merge (can run from worktree - it won't delete it)
|
|
592
582
|
jettypod work merge [current-chore-id]
|
|
593
583
|
```
|
|
594
584
|
|
|
595
585
|
```bash
|
|
596
|
-
#
|
|
586
|
+
# Step 2: cd to main repo
|
|
587
|
+
cd /path/to/main/repo
|
|
588
|
+
pwd && ls .jettypod # verify
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
```bash
|
|
592
|
+
# Step 3: Clean up the worktree, then start next chore
|
|
593
|
+
jettypod work cleanup [current-chore-id]
|
|
597
594
|
jettypod work start [next-chore-id]
|
|
598
595
|
```
|
|
599
596
|
|
|
@@ -613,21 +610,24 @@ If the query returns no remaining chores, proceed to Step 7.
|
|
|
613
610
|
|
|
614
611
|
**First, merge the final stable chore:**
|
|
615
612
|
|
|
616
|
-
**🚨 CRITICAL: Shell CWD Corruption Prevention**
|
|
617
|
-
|
|
618
613
|
```bash
|
|
619
614
|
git add . && git commit -m "feat: [brief description of error handling added]"
|
|
620
615
|
```
|
|
621
616
|
|
|
622
617
|
```bash
|
|
623
|
-
#
|
|
624
|
-
|
|
618
|
+
# Step 1: Merge (can run from worktree - it won't delete it)
|
|
619
|
+
jettypod work merge [current-chore-id]
|
|
620
|
+
```
|
|
625
621
|
|
|
626
|
-
|
|
627
|
-
|
|
622
|
+
```bash
|
|
623
|
+
# Step 2: cd to main repo
|
|
624
|
+
cd /path/to/main/repo
|
|
625
|
+
pwd && ls .jettypod # verify
|
|
626
|
+
```
|
|
628
627
|
|
|
629
|
-
|
|
630
|
-
|
|
628
|
+
```bash
|
|
629
|
+
# Step 3: Clean up the worktree
|
|
630
|
+
jettypod work cleanup [current-chore-id]
|
|
631
631
|
```
|
|
632
632
|
|
|
633
633
|
**Then check project state:**
|