jettypod 4.4.47 → 4.4.49
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/speed-mode/SKILL.md +3 -3
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
|
@@ -692,7 +692,7 @@ Added error handling and edge case scenarios for stable mode.
|
|
|
692
692
|
|
|
693
693
|
# Return to main repo and merge
|
|
694
694
|
cd <main-repo-path>
|
|
695
|
-
jettypod work merge <feature-id>
|
|
695
|
+
jettypod work tests merge <feature-id>
|
|
696
696
|
```
|
|
697
697
|
|
|
698
698
|
**7. Present proposal to user:**
|
|
@@ -848,8 +848,8 @@ jettypod work start <chore-id> # Create worktree and start chore
|
|
|
848
848
|
|
|
849
849
|
**Create test worktree (for writing BDD scenarios):**
|
|
850
850
|
```bash
|
|
851
|
-
jettypod work tests <feature-id>
|
|
852
|
-
jettypod work merge <feature-id> # Merge tests back to main
|
|
851
|
+
jettypod work tests <feature-id> # Create worktree for writing tests
|
|
852
|
+
jettypod work tests merge <feature-id> # Merge tests back to main
|
|
853
853
|
```
|
|
854
854
|
|
|
855
855
|
**Set feature mode (BEFORE creating chores):**
|