jettypod 4.4.46 → 4.4.48
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 +60 -14
- package/lib/work-commands/index.js +186 -1
- package/package.json +1 -1
- package/skills-templates/chore-mode/SKILL.md +8 -0
- package/skills-templates/feature-planning/SKILL.md +16 -0
- package/skills-templates/speed-mode/SKILL.md +8 -0
- package/skills-templates/stable-mode/SKILL.md +8 -0
package/jettypod.js
CHANGED
|
@@ -1304,21 +1304,67 @@ switch (command) {
|
|
|
1304
1304
|
process.exit(1);
|
|
1305
1305
|
}
|
|
1306
1306
|
} else if (subcommand === 'tests') {
|
|
1307
|
-
//
|
|
1308
|
-
const featureId = parseInt(args[1]);
|
|
1309
|
-
if (!featureId) {
|
|
1310
|
-
console.log('Usage: jettypod work tests <feature-id>');
|
|
1311
|
-
console.log('');
|
|
1312
|
-
console.log('Creates a worktree for writing BDD tests for a feature.');
|
|
1313
|
-
console.log('Tests are written in isolation, then merged with: jettypod work merge');
|
|
1314
|
-
process.exit(1);
|
|
1315
|
-
}
|
|
1307
|
+
// Handle: work tests <feature-id> OR work tests merge <feature-id>
|
|
1316
1308
|
const workCommands = require('./lib/work-commands/index.js');
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1309
|
+
|
|
1310
|
+
if (args[1] === 'merge') {
|
|
1311
|
+
// work tests merge <feature-id>
|
|
1312
|
+
const featureId = parseInt(args[2]);
|
|
1313
|
+
if (!featureId) {
|
|
1314
|
+
console.log('Usage: jettypod work tests merge <feature-id>');
|
|
1315
|
+
console.log('');
|
|
1316
|
+
console.log('Merges a test worktree back to main and cleans up.');
|
|
1317
|
+
console.log('');
|
|
1318
|
+
console.log('This command:');
|
|
1319
|
+
console.log(' 1. Verifies all changes are committed');
|
|
1320
|
+
console.log(' 2. Merges the test branch to main');
|
|
1321
|
+
console.log(' 3. Pushes to remote');
|
|
1322
|
+
console.log(' 4. Removes the worktree');
|
|
1323
|
+
console.log(' 5. Deletes the test branch');
|
|
1324
|
+
process.exit(1);
|
|
1325
|
+
}
|
|
1326
|
+
try {
|
|
1327
|
+
await workCommands.testsMerge(featureId);
|
|
1328
|
+
} catch (err) {
|
|
1329
|
+
console.error(`Error: ${err.message}`);
|
|
1330
|
+
process.exit(1);
|
|
1331
|
+
}
|
|
1332
|
+
} else if (args[1] === 'start') {
|
|
1333
|
+
// work tests start <feature-id> (alias for work tests <feature-id>)
|
|
1334
|
+
const featureId = parseInt(args[2]);
|
|
1335
|
+
if (!featureId) {
|
|
1336
|
+
console.log('Usage: jettypod work tests start <feature-id>');
|
|
1337
|
+
console.log('');
|
|
1338
|
+
console.log('Creates a worktree for writing BDD tests for a feature.');
|
|
1339
|
+
console.log('Tests are written in isolation, then merged with: jettypod work tests merge <feature-id>');
|
|
1340
|
+
process.exit(1);
|
|
1341
|
+
}
|
|
1342
|
+
try {
|
|
1343
|
+
await workCommands.testsWork(featureId);
|
|
1344
|
+
} catch (err) {
|
|
1345
|
+
console.error(`Error: ${err.message}`);
|
|
1346
|
+
process.exit(1);
|
|
1347
|
+
}
|
|
1348
|
+
} else {
|
|
1349
|
+
// work tests <feature-id> (create worktree)
|
|
1350
|
+
const featureId = parseInt(args[1]);
|
|
1351
|
+
if (!featureId) {
|
|
1352
|
+
console.log('Usage: jettypod work tests <feature-id>');
|
|
1353
|
+
console.log(' jettypod work tests start <feature-id>');
|
|
1354
|
+
console.log(' jettypod work tests merge <feature-id>');
|
|
1355
|
+
console.log('');
|
|
1356
|
+
console.log('Commands:');
|
|
1357
|
+
console.log(' work tests <id> Create a worktree for writing BDD tests');
|
|
1358
|
+
console.log(' work tests start <id> Same as above (explicit alias)');
|
|
1359
|
+
console.log(' work tests merge <id> Merge test worktree to main and clean up');
|
|
1360
|
+
process.exit(1);
|
|
1361
|
+
}
|
|
1362
|
+
try {
|
|
1363
|
+
await workCommands.testsWork(featureId);
|
|
1364
|
+
} catch (err) {
|
|
1365
|
+
console.error(`Error: ${err.message}`);
|
|
1366
|
+
process.exit(1);
|
|
1367
|
+
}
|
|
1322
1368
|
}
|
|
1323
1369
|
} else {
|
|
1324
1370
|
// CRITICAL SAFETY CHECK: Prevent work status changes from within a worktree
|
|
@@ -1700,11 +1700,196 @@ async function testsWork(featureId) {
|
|
|
1700
1700
|
});
|
|
1701
1701
|
}
|
|
1702
1702
|
|
|
1703
|
+
/**
|
|
1704
|
+
* Merge a test worktree back to main and clean up
|
|
1705
|
+
* @param {number} featureId - Feature ID whose test worktree to merge
|
|
1706
|
+
* @returns {Promise<void>}
|
|
1707
|
+
*/
|
|
1708
|
+
async function testsMerge(featureId) {
|
|
1709
|
+
if (!featureId || isNaN(featureId) || featureId < 1) {
|
|
1710
|
+
return Promise.reject(new Error('Invalid feature ID'));
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
// CRITICAL: Refuse to run from inside a worktree
|
|
1714
|
+
const cwd = process.cwd();
|
|
1715
|
+
if (cwd.includes('.jettypod-work')) {
|
|
1716
|
+
const mainRepo = getGitRoot();
|
|
1717
|
+
console.error('❌ Cannot merge from inside a worktree.');
|
|
1718
|
+
console.error('');
|
|
1719
|
+
console.error(' The merge will delete this worktree, breaking your shell session.');
|
|
1720
|
+
console.error('');
|
|
1721
|
+
console.error(' Run from the main repository instead:');
|
|
1722
|
+
console.error(` cd ${mainRepo}`);
|
|
1723
|
+
console.error(` jettypod work tests merge ${featureId}`);
|
|
1724
|
+
return Promise.reject(new Error('Cannot merge from inside a worktree'));
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
const db = getDb();
|
|
1728
|
+
const gitRoot = getGitRoot();
|
|
1729
|
+
|
|
1730
|
+
// Find the test worktree for this feature (branch starts with tests/feature-)
|
|
1731
|
+
const worktree = await new Promise((resolve, reject) => {
|
|
1732
|
+
db.get(
|
|
1733
|
+
`SELECT * FROM worktrees
|
|
1734
|
+
WHERE work_item_id = ?
|
|
1735
|
+
AND branch_name LIKE 'tests/feature-%'
|
|
1736
|
+
AND status = 'active'`,
|
|
1737
|
+
[featureId],
|
|
1738
|
+
(err, row) => {
|
|
1739
|
+
if (err) return reject(err);
|
|
1740
|
+
resolve(row);
|
|
1741
|
+
}
|
|
1742
|
+
);
|
|
1743
|
+
});
|
|
1744
|
+
|
|
1745
|
+
if (!worktree) {
|
|
1746
|
+
return Promise.reject(new Error(
|
|
1747
|
+
`No active test worktree found for feature #${featureId}.\n\n` +
|
|
1748
|
+
`Create one with: jettypod work tests ${featureId}`
|
|
1749
|
+
));
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
const worktreePath = worktree.worktree_path;
|
|
1753
|
+
const branchName = worktree.branch_name;
|
|
1754
|
+
|
|
1755
|
+
console.log(`⏳ Merging test worktree for feature #${featureId}...`);
|
|
1756
|
+
console.log(` Branch: ${branchName}`);
|
|
1757
|
+
console.log(` Path: ${worktreePath}`);
|
|
1758
|
+
|
|
1759
|
+
// Check for uncommitted changes in the worktree
|
|
1760
|
+
try {
|
|
1761
|
+
const status = execSync('git status --porcelain', {
|
|
1762
|
+
cwd: worktreePath,
|
|
1763
|
+
encoding: 'utf8',
|
|
1764
|
+
stdio: 'pipe'
|
|
1765
|
+
}).trim();
|
|
1766
|
+
|
|
1767
|
+
if (status) {
|
|
1768
|
+
return Promise.reject(new Error(
|
|
1769
|
+
`Uncommitted changes in test worktree:\n${status}\n\n` +
|
|
1770
|
+
`Commit your changes first:\n` +
|
|
1771
|
+
` cd ${worktreePath}\n` +
|
|
1772
|
+
` git add .\n` +
|
|
1773
|
+
` git commit -m "Add BDD tests for feature #${featureId}"`
|
|
1774
|
+
));
|
|
1775
|
+
}
|
|
1776
|
+
} catch (err) {
|
|
1777
|
+
return Promise.reject(new Error(`Failed to check worktree status: ${err.message}`));
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
// Check if there are commits to merge
|
|
1781
|
+
try {
|
|
1782
|
+
const mainBranch = execSync('git symbolic-ref refs/remotes/origin/HEAD', {
|
|
1783
|
+
cwd: gitRoot,
|
|
1784
|
+
encoding: 'utf8',
|
|
1785
|
+
stdio: 'pipe'
|
|
1786
|
+
}).trim().replace('refs/remotes/origin/', '');
|
|
1787
|
+
|
|
1788
|
+
const commitCount = execSync(`git rev-list --count ${mainBranch}..${branchName}`, {
|
|
1789
|
+
cwd: gitRoot,
|
|
1790
|
+
encoding: 'utf8',
|
|
1791
|
+
stdio: 'pipe'
|
|
1792
|
+
}).trim();
|
|
1793
|
+
|
|
1794
|
+
if (commitCount === '0') {
|
|
1795
|
+
console.log('⚠️ No commits to merge - worktree has no new changes');
|
|
1796
|
+
}
|
|
1797
|
+
} catch (err) {
|
|
1798
|
+
// Non-fatal - proceed anyway
|
|
1799
|
+
console.log('⚠️ Could not check commit count, proceeding with merge');
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
// Merge the test branch to main
|
|
1803
|
+
try {
|
|
1804
|
+
// First, ensure we're on main
|
|
1805
|
+
execSync('git checkout main', {
|
|
1806
|
+
cwd: gitRoot,
|
|
1807
|
+
encoding: 'utf8',
|
|
1808
|
+
stdio: 'pipe'
|
|
1809
|
+
});
|
|
1810
|
+
|
|
1811
|
+
// Merge the test branch
|
|
1812
|
+
execSync(`git merge ${branchName} --no-ff -m "Merge BDD tests for feature #${featureId}"`, {
|
|
1813
|
+
cwd: gitRoot,
|
|
1814
|
+
encoding: 'utf8',
|
|
1815
|
+
stdio: 'pipe'
|
|
1816
|
+
});
|
|
1817
|
+
|
|
1818
|
+
console.log('✅ Merged test branch to main');
|
|
1819
|
+
} catch (err) {
|
|
1820
|
+
return Promise.reject(new Error(
|
|
1821
|
+
`Merge failed: ${err.message}\n\n` +
|
|
1822
|
+
`Resolve conflicts manually, then run:\n` +
|
|
1823
|
+
` jettypod work tests merge ${featureId}`
|
|
1824
|
+
));
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
// Push to remote
|
|
1828
|
+
try {
|
|
1829
|
+
execSync('git push', {
|
|
1830
|
+
cwd: gitRoot,
|
|
1831
|
+
encoding: 'utf8',
|
|
1832
|
+
stdio: 'pipe'
|
|
1833
|
+
});
|
|
1834
|
+
console.log('✅ Pushed to remote');
|
|
1835
|
+
} catch (err) {
|
|
1836
|
+
console.log('⚠️ Failed to push (non-fatal):', err.message);
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
// Clean up the worktree
|
|
1840
|
+
try {
|
|
1841
|
+
execSync(`git worktree remove "${worktreePath}" --force`, {
|
|
1842
|
+
cwd: gitRoot,
|
|
1843
|
+
encoding: 'utf8',
|
|
1844
|
+
stdio: 'pipe'
|
|
1845
|
+
});
|
|
1846
|
+
console.log('✅ Removed worktree directory');
|
|
1847
|
+
} catch (err) {
|
|
1848
|
+
console.log('⚠️ Failed to remove worktree (non-fatal):', err.message);
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
// Delete the test branch
|
|
1852
|
+
try {
|
|
1853
|
+
execSync(`git branch -d ${branchName}`, {
|
|
1854
|
+
cwd: gitRoot,
|
|
1855
|
+
encoding: 'utf8',
|
|
1856
|
+
stdio: 'pipe'
|
|
1857
|
+
});
|
|
1858
|
+
console.log('✅ Deleted test branch');
|
|
1859
|
+
} catch (err) {
|
|
1860
|
+
console.log('⚠️ Failed to delete branch (non-fatal):', err.message);
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// Update worktree status in database
|
|
1864
|
+
await new Promise((resolve, reject) => {
|
|
1865
|
+
db.run(
|
|
1866
|
+
`UPDATE worktrees SET status = 'merged' WHERE id = ?`,
|
|
1867
|
+
[worktree.id],
|
|
1868
|
+
(err) => {
|
|
1869
|
+
if (err) return reject(err);
|
|
1870
|
+
resolve();
|
|
1871
|
+
}
|
|
1872
|
+
);
|
|
1873
|
+
});
|
|
1874
|
+
|
|
1875
|
+
// Clear current work if it was pointing to this worktree
|
|
1876
|
+
const currentWork = getCurrentWorkSync();
|
|
1877
|
+
if (currentWork && currentWork.worktreePath === worktreePath) {
|
|
1878
|
+
clearCurrentWork();
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
console.log('');
|
|
1882
|
+
console.log(`✅ Test worktree for feature #${featureId} merged successfully`);
|
|
1883
|
+
|
|
1884
|
+
return Promise.resolve();
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1703
1887
|
module.exports = {
|
|
1704
1888
|
startWork,
|
|
1705
1889
|
stopWork,
|
|
1706
1890
|
getCurrentWork,
|
|
1707
1891
|
cleanupWorktrees,
|
|
1708
1892
|
mergeWork,
|
|
1709
|
-
testsWork
|
|
1893
|
+
testsWork,
|
|
1894
|
+
testsMerge
|
|
1710
1895
|
};
|
package/package.json
CHANGED
|
@@ -78,6 +78,14 @@ Implementation Plan:
|
|
|
78
78
|
|
|
79
79
|
**Your task:** Create an isolated worktree for this chore.
|
|
80
80
|
|
|
81
|
+
**🚫 FORBIDDEN: Manual Git Worktree Commands**
|
|
82
|
+
```
|
|
83
|
+
❌ git worktree add ...
|
|
84
|
+
❌ git checkout -b feature/...
|
|
85
|
+
❌ git branch feature/...
|
|
86
|
+
```
|
|
87
|
+
**ALWAYS use `jettypod work start`** - it handles branch naming, path conventions, database tracking, and cleanup. Manual git commands will create orphaned worktrees that break the merge workflow.
|
|
88
|
+
|
|
81
89
|
```bash
|
|
82
90
|
jettypod work start [chore-id]
|
|
83
91
|
```
|
|
@@ -588,6 +588,14 @@ Now I'll write the BDD tests in an isolated worktree...
|
|
|
588
588
|
|
|
589
589
|
**CRITICAL:** Tests are written in an isolated worktree, not directly on main. This ensures agent failures don't leave uncommitted garbage on main.
|
|
590
590
|
|
|
591
|
+
**🚫 FORBIDDEN: Manual Git Worktree Commands**
|
|
592
|
+
```
|
|
593
|
+
❌ git worktree add ...
|
|
594
|
+
❌ git checkout -b tests/...
|
|
595
|
+
❌ git branch tests/...
|
|
596
|
+
```
|
|
597
|
+
**ALWAYS use jettypod commands** - they handle branch naming, path conventions, database tracking, and cleanup. Manual git commands will create orphaned worktrees that break the merge workflow.
|
|
598
|
+
|
|
591
599
|
**Sub-step 1: Create test worktree**
|
|
592
600
|
|
|
593
601
|
```bash
|
|
@@ -756,6 +764,14 @@ End feature-planning skill without starting a chore. Do NOT invoke speed-mode.
|
|
|
756
764
|
|
|
757
765
|
**After user picks a chore, execute these steps IN ORDER:**
|
|
758
766
|
|
|
767
|
+
**🚫 FORBIDDEN: Manual Git Worktree Commands**
|
|
768
|
+
```
|
|
769
|
+
❌ git worktree add ...
|
|
770
|
+
❌ git checkout -b feature/...
|
|
771
|
+
❌ git branch feature/...
|
|
772
|
+
```
|
|
773
|
+
**ALWAYS use `jettypod work start`** - it handles branch naming, path conventions, database tracking, and cleanup. Manual git commands will create orphaned worktrees that break the merge workflow.
|
|
774
|
+
|
|
759
775
|
**Step 1: Start the chore (creates worktree and branch):**
|
|
760
776
|
```bash
|
|
761
777
|
jettypod work start [chore-id]
|
|
@@ -629,6 +629,14 @@ After merge, you are on main branch. Ready to generate stable mode scenarios.
|
|
|
629
629
|
|
|
630
630
|
**1. Create a test worktree for writing stable mode scenarios:**
|
|
631
631
|
|
|
632
|
+
**🚫 FORBIDDEN: Manual Git Worktree Commands**
|
|
633
|
+
```
|
|
634
|
+
❌ git worktree add ...
|
|
635
|
+
❌ git checkout -b tests/...
|
|
636
|
+
❌ git branch tests/...
|
|
637
|
+
```
|
|
638
|
+
**ALWAYS use jettypod commands** - they handle branch naming, path conventions, database tracking, and cleanup. Manual git commands will create orphaned worktrees that break the merge workflow.
|
|
639
|
+
|
|
632
640
|
```bash
|
|
633
641
|
jettypod work tests <feature-id>
|
|
634
642
|
```
|
|
@@ -72,6 +72,14 @@ sqlite3 .jettypod/work.db "SELECT wi.id, wi.title, wi.status, wt.worktree_path,
|
|
|
72
72
|
|
|
73
73
|
**🛑 STOP GATE:** If `worktree_path` is NULL or no rows returned, you MUST run `jettypod work start [chore-id]` before continuing. DO NOT proceed without a valid worktree.
|
|
74
74
|
|
|
75
|
+
**🚫 FORBIDDEN: Manual Git Worktree Commands**
|
|
76
|
+
```
|
|
77
|
+
❌ git worktree add ...
|
|
78
|
+
❌ git checkout -b feature/...
|
|
79
|
+
❌ git branch feature/...
|
|
80
|
+
```
|
|
81
|
+
**ALWAYS use `jettypod work start`** - it handles branch naming, path conventions, database tracking, and cleanup. Manual git commands will create orphaned worktrees that break the merge workflow.
|
|
82
|
+
|
|
75
83
|
**Example of valid output:**
|
|
76
84
|
```
|
|
77
85
|
4|Add error handling|in_progress|/Users/erik/project/.jettypod-work/4-add-error-handling|feature/work-4-add-error-handling
|