jettypod 4.4.94 → 4.4.96
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 +65 -0
- package/lib/work-commands/index.js +250 -1
- package/lib/worktree-manager.js +3 -2
- package/package.json +1 -1
- package/skills-templates/bug-mode/SKILL.md +2 -2
- package/skills-templates/chore-mode/SKILL.md +44 -0
- package/skills-templates/epic-planning/SKILL.md +38 -9
- package/skills-templates/feature-planning/SKILL.md +247 -185
- package/skills-templates/production-mode/SKILL.md +96 -0
- package/skills-templates/speed-mode/SKILL.md +63 -6
- package/skills-templates/stable-mode/SKILL.md +61 -4
package/jettypod.js
CHANGED
|
@@ -289,6 +289,8 @@ jettypod work merge # Merges worktree back to main
|
|
|
289
289
|
jettypod work cleanup <id> # Cleanup worktree after merge
|
|
290
290
|
jettypod work tests <feature-id> # Create test worktree for BDD
|
|
291
291
|
jettypod work tests merge <id> # Merge tests to main
|
|
292
|
+
jettypod work prototype start <id> <approach> # Create prototype worktree
|
|
293
|
+
jettypod work prototype merge <id> # Merge prototype to main
|
|
292
294
|
jettypod work status <id> cancelled
|
|
293
295
|
jettypod backlog
|
|
294
296
|
jettypod impact <file> # Show tests/features affected
|
|
@@ -1506,6 +1508,69 @@ switch (command) {
|
|
|
1506
1508
|
process.exit(1);
|
|
1507
1509
|
}
|
|
1508
1510
|
}
|
|
1511
|
+
} else if (subcommand === 'prototype') {
|
|
1512
|
+
// Handle: work prototype start <feature-id> <approach-name> OR work prototype merge <feature-id>
|
|
1513
|
+
const workCommands = require('./lib/work-commands/index.js');
|
|
1514
|
+
|
|
1515
|
+
if (args[1] === 'merge') {
|
|
1516
|
+
// work prototype merge <feature-id>
|
|
1517
|
+
const featureId = parseInt(args[2]);
|
|
1518
|
+
if (!featureId) {
|
|
1519
|
+
console.log('Usage: jettypod work prototype merge <feature-id>');
|
|
1520
|
+
console.log('');
|
|
1521
|
+
console.log('Merges a prototype worktree back to main and cleans up.');
|
|
1522
|
+
console.log('');
|
|
1523
|
+
console.log('This command:');
|
|
1524
|
+
console.log(' 1. Verifies all changes are committed');
|
|
1525
|
+
console.log(' 2. Merges the prototype branch to main');
|
|
1526
|
+
console.log(' 3. Pushes to remote');
|
|
1527
|
+
console.log(' 4. Removes the worktree');
|
|
1528
|
+
console.log(' 5. Deletes the prototype branch');
|
|
1529
|
+
process.exit(1);
|
|
1530
|
+
}
|
|
1531
|
+
try {
|
|
1532
|
+
await workCommands.prototypeMerge(featureId);
|
|
1533
|
+
} catch (err) {
|
|
1534
|
+
console.error(`Error: ${err.message}`);
|
|
1535
|
+
process.exit(1);
|
|
1536
|
+
}
|
|
1537
|
+
} else if (args[1] === 'start') {
|
|
1538
|
+
// work prototype start <feature-id> <approach-name>
|
|
1539
|
+
const featureId = parseInt(args[2]);
|
|
1540
|
+
const approachName = args[3];
|
|
1541
|
+
if (!featureId || !approachName) {
|
|
1542
|
+
console.log('Usage: jettypod work prototype start <feature-id> <approach-name>');
|
|
1543
|
+
console.log('');
|
|
1544
|
+
console.log('Creates a worktree for building prototypes for a feature.');
|
|
1545
|
+
console.log('');
|
|
1546
|
+
console.log('Examples:');
|
|
1547
|
+
console.log(' jettypod work prototype start 42 simple-form');
|
|
1548
|
+
console.log(' jettypod work prototype start 42 wizard-flow');
|
|
1549
|
+
console.log('');
|
|
1550
|
+
console.log('Prototypes are written in /prototypes/feature-<id>-<approach>/ directory.');
|
|
1551
|
+
console.log('When done, merge with: jettypod work prototype merge <feature-id>');
|
|
1552
|
+
process.exit(1);
|
|
1553
|
+
}
|
|
1554
|
+
try {
|
|
1555
|
+
await workCommands.prototypeWork(featureId, approachName);
|
|
1556
|
+
} catch (err) {
|
|
1557
|
+
console.error(`Error: ${err.message}`);
|
|
1558
|
+
process.exit(1);
|
|
1559
|
+
}
|
|
1560
|
+
} else {
|
|
1561
|
+
// work prototype (show usage)
|
|
1562
|
+
console.log('Usage: jettypod work prototype start <feature-id> <approach-name>');
|
|
1563
|
+
console.log(' jettypod work prototype merge <feature-id>');
|
|
1564
|
+
console.log('');
|
|
1565
|
+
console.log('Commands:');
|
|
1566
|
+
console.log(' work prototype start <id> <approach> Create a worktree for prototyping');
|
|
1567
|
+
console.log(' work prototype merge <id> Merge prototype worktree to main');
|
|
1568
|
+
console.log('');
|
|
1569
|
+
console.log('Examples:');
|
|
1570
|
+
console.log(' jettypod work prototype start 42 simple-form');
|
|
1571
|
+
console.log(' jettypod work prototype merge 42');
|
|
1572
|
+
process.exit(1);
|
|
1573
|
+
}
|
|
1509
1574
|
} else {
|
|
1510
1575
|
// CRITICAL SAFETY CHECK: Prevent work status changes from within a worktree
|
|
1511
1576
|
// This prevents cleanup failures and orphaned worktrees
|
|
@@ -2126,6 +2126,253 @@ async function testsMerge(featureId) {
|
|
|
2126
2126
|
return Promise.resolve();
|
|
2127
2127
|
}
|
|
2128
2128
|
|
|
2129
|
+
/**
|
|
2130
|
+
* Create a worktree for building prototypes for a feature or epic
|
|
2131
|
+
* @param {number} workItemId - Feature or Epic ID to create prototype worktree for
|
|
2132
|
+
* @param {string} approachName - Name of the approach being prototyped (e.g., "simple-form", "wizard")
|
|
2133
|
+
* @returns {Promise<Object>} Worktree info with path and branch
|
|
2134
|
+
* @throws {Error} If work item not found or not a feature/epic
|
|
2135
|
+
*/
|
|
2136
|
+
async function prototypeWork(workItemId, approachName) {
|
|
2137
|
+
if (!workItemId || isNaN(workItemId) || workItemId < 1) {
|
|
2138
|
+
return Promise.reject(new Error('Invalid work item ID'));
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
if (!approachName || typeof approachName !== 'string' || !approachName.trim()) {
|
|
2142
|
+
return Promise.reject(new Error('Approach name is required (e.g., "simple-form", "wizard")'));
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
// Sanitize approach name for use in branch/path
|
|
2146
|
+
const sanitizedApproach = approachName.trim().toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
2147
|
+
|
|
2148
|
+
const paths = getPaths();
|
|
2149
|
+
|
|
2150
|
+
if (!fs.existsSync(paths.jettypodDir)) {
|
|
2151
|
+
return Promise.reject(new Error('JettyPod not initialized. Run: jettypod init'));
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
if (!fs.existsSync(paths.dbPath)) {
|
|
2155
|
+
return Promise.reject(new Error('Work database not found. Run: jettypod init'));
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
const db = getDb();
|
|
2159
|
+
|
|
2160
|
+
return new Promise((resolve, reject) => {
|
|
2161
|
+
// Get feature or epic work item
|
|
2162
|
+
db.get(
|
|
2163
|
+
`SELECT * FROM work_items WHERE id = ? AND type IN ('feature', 'epic')`,
|
|
2164
|
+
[workItemId],
|
|
2165
|
+
async (err, workItem) => {
|
|
2166
|
+
if (err) {
|
|
2167
|
+
return reject(new Error(`Database error: ${err.message}`));
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
if (!workItem) {
|
|
2171
|
+
return reject(new Error(`Feature or epic #${workItemId} not found`));
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
// Create worktree with prototype/ prefix
|
|
2175
|
+
try {
|
|
2176
|
+
const gitRoot = getGitRoot();
|
|
2177
|
+
const branchPrefix = workItem.type === 'epic' ? 'prototype/epic' : 'prototype/feature';
|
|
2178
|
+
const worktreeResult = await worktreeManager.createWorktree(workItem, {
|
|
2179
|
+
repoPath: gitRoot,
|
|
2180
|
+
branchPrefix: branchPrefix,
|
|
2181
|
+
pathPrefix: `prototype-`,
|
|
2182
|
+
branchSuffix: `-${sanitizedApproach}`
|
|
2183
|
+
});
|
|
2184
|
+
|
|
2185
|
+
// Set as current work
|
|
2186
|
+
setCurrentWork({
|
|
2187
|
+
id: workItem.id,
|
|
2188
|
+
type: workItem.type,
|
|
2189
|
+
title: workItem.title,
|
|
2190
|
+
status: workItem.status || 'in_progress',
|
|
2191
|
+
worktreePath: worktreeResult.worktree_path,
|
|
2192
|
+
branchName: worktreeResult.branch_name
|
|
2193
|
+
});
|
|
2194
|
+
|
|
2195
|
+
console.log(`✅ Created prototype worktree: ${worktreeResult.worktree_path}`);
|
|
2196
|
+
console.log(`📁 IMPORTANT: Use absolute paths for file operations:`);
|
|
2197
|
+
console.log(` ${worktreeResult.worktree_path}/prototypes/${workItem.type}-${workItemId}-${sanitizedApproach}/ (correct)`);
|
|
2198
|
+
console.log(` prototypes/... (wrong - creates in main repo)`);
|
|
2199
|
+
console.log(`\nPrototyping "${approachName}" for: [#${workItem.id}] ${workItem.title}`);
|
|
2200
|
+
|
|
2201
|
+
resolve(worktreeResult);
|
|
2202
|
+
} catch (worktreeErr) {
|
|
2203
|
+
reject(new Error(`Failed to create prototype worktree: ${worktreeErr.message}`));
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
);
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
/**
|
|
2211
|
+
* Merge a prototype worktree back to main and clean up
|
|
2212
|
+
* @param {number} workItemId - Feature or Epic ID whose prototype worktree to merge
|
|
2213
|
+
* @returns {Promise<void>}
|
|
2214
|
+
*/
|
|
2215
|
+
async function prototypeMerge(workItemId) {
|
|
2216
|
+
if (!workItemId || isNaN(workItemId) || workItemId < 1) {
|
|
2217
|
+
return Promise.reject(new Error('Invalid work item ID'));
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
// Auto-chdir to main repo if running from inside a worktree
|
|
2221
|
+
const cwd = process.cwd();
|
|
2222
|
+
if (cwd.includes('.jettypod-work')) {
|
|
2223
|
+
const mainRepo = getGitRoot();
|
|
2224
|
+
console.log('📂 Changing to main repository for merge...');
|
|
2225
|
+
process.chdir(mainRepo);
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
const db = getDb();
|
|
2229
|
+
const gitRoot = getGitRoot();
|
|
2230
|
+
|
|
2231
|
+
// Find the prototype worktree for this feature or epic (branch starts with prototype/feature- or prototype/epic-)
|
|
2232
|
+
const worktree = await new Promise((resolve, reject) => {
|
|
2233
|
+
db.get(
|
|
2234
|
+
`SELECT * FROM worktrees
|
|
2235
|
+
WHERE work_item_id = ?
|
|
2236
|
+
AND (branch_name LIKE 'prototype/feature-%' OR branch_name LIKE 'prototype/epic-%')
|
|
2237
|
+
AND status = 'active'`,
|
|
2238
|
+
[workItemId],
|
|
2239
|
+
(err, row) => {
|
|
2240
|
+
if (err) return reject(err);
|
|
2241
|
+
resolve(row);
|
|
2242
|
+
}
|
|
2243
|
+
);
|
|
2244
|
+
});
|
|
2245
|
+
|
|
2246
|
+
if (!worktree) {
|
|
2247
|
+
return Promise.reject(new Error(
|
|
2248
|
+
`No active prototype worktree found for #${workItemId}.\n\n` +
|
|
2249
|
+
`Create one with: jettypod work prototype start ${workItemId} <approach-name>`
|
|
2250
|
+
));
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
const worktreePath = worktree.worktree_path;
|
|
2254
|
+
const branchName = worktree.branch_name;
|
|
2255
|
+
|
|
2256
|
+
console.log(`⏳ Merging prototype worktree for #${workItemId}...`);
|
|
2257
|
+
console.log(` Branch: ${branchName}`);
|
|
2258
|
+
console.log(` Path: ${worktreePath}`);
|
|
2259
|
+
|
|
2260
|
+
// Check for uncommitted changes in the worktree
|
|
2261
|
+
try {
|
|
2262
|
+
const status = execSync('git status --porcelain', {
|
|
2263
|
+
cwd: worktreePath,
|
|
2264
|
+
encoding: 'utf8',
|
|
2265
|
+
stdio: 'pipe'
|
|
2266
|
+
}).trim();
|
|
2267
|
+
|
|
2268
|
+
if (status) {
|
|
2269
|
+
return Promise.reject(new Error(
|
|
2270
|
+
`Uncommitted changes in prototype worktree:\n${status}\n\n` +
|
|
2271
|
+
`Commit your changes first:\n` +
|
|
2272
|
+
` cd ${worktreePath}\n` +
|
|
2273
|
+
` git add .\n` +
|
|
2274
|
+
` git commit -m "Add prototype for #${workItemId}"`
|
|
2275
|
+
));
|
|
2276
|
+
}
|
|
2277
|
+
} catch (err) {
|
|
2278
|
+
return Promise.reject(new Error(`Failed to check worktree status: ${err.message}`));
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
// Check if there are commits to merge
|
|
2282
|
+
try {
|
|
2283
|
+
const mainBranch = execSync('git symbolic-ref refs/remotes/origin/HEAD', {
|
|
2284
|
+
cwd: gitRoot,
|
|
2285
|
+
encoding: 'utf8',
|
|
2286
|
+
stdio: 'pipe'
|
|
2287
|
+
}).trim().replace('refs/remotes/origin/', '');
|
|
2288
|
+
|
|
2289
|
+
const commitCount = execSync(`git rev-list --count ${mainBranch}..${branchName}`, {
|
|
2290
|
+
cwd: gitRoot,
|
|
2291
|
+
encoding: 'utf8',
|
|
2292
|
+
stdio: 'pipe'
|
|
2293
|
+
}).trim();
|
|
2294
|
+
|
|
2295
|
+
if (commitCount === '0') {
|
|
2296
|
+
console.log('⚠️ No commits to merge - worktree has no new changes');
|
|
2297
|
+
}
|
|
2298
|
+
} catch (err) {
|
|
2299
|
+
// Non-fatal - proceed anyway
|
|
2300
|
+
console.log('⚠️ Could not check commit count, proceeding with merge');
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
// Merge the prototype branch to main
|
|
2304
|
+
try {
|
|
2305
|
+
// First, ensure we're on main
|
|
2306
|
+
execSync('git checkout main', {
|
|
2307
|
+
cwd: gitRoot,
|
|
2308
|
+
encoding: 'utf8',
|
|
2309
|
+
stdio: 'pipe'
|
|
2310
|
+
});
|
|
2311
|
+
|
|
2312
|
+
// Merge the prototype branch
|
|
2313
|
+
execSync(`git merge ${branchName} --no-ff -m "Merge prototype for #${workItemId}"`, {
|
|
2314
|
+
cwd: gitRoot,
|
|
2315
|
+
encoding: 'utf8',
|
|
2316
|
+
stdio: 'pipe'
|
|
2317
|
+
});
|
|
2318
|
+
|
|
2319
|
+
console.log('✅ Merged prototype branch to main');
|
|
2320
|
+
} catch (err) {
|
|
2321
|
+
return Promise.reject(new Error(
|
|
2322
|
+
`Merge failed: ${err.message}\n\n` +
|
|
2323
|
+
`Resolve conflicts manually, then run:\n` +
|
|
2324
|
+
` jettypod work prototype merge ${workItemId}`
|
|
2325
|
+
));
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
// Push to remote
|
|
2329
|
+
try {
|
|
2330
|
+
execSync('git push', {
|
|
2331
|
+
cwd: gitRoot,
|
|
2332
|
+
encoding: 'utf8',
|
|
2333
|
+
stdio: 'pipe'
|
|
2334
|
+
});
|
|
2335
|
+
console.log('✅ Pushed to remote');
|
|
2336
|
+
} catch (err) {
|
|
2337
|
+
console.log('⚠️ Failed to push (non-fatal):', err.message);
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
// Mark worktree as merged but DON'T delete it yet
|
|
2341
|
+
// This prevents shell CWD corruption when merge is run from inside the worktree
|
|
2342
|
+
await new Promise((resolve, reject) => {
|
|
2343
|
+
db.run(
|
|
2344
|
+
`UPDATE worktrees SET status = 'merged' WHERE id = ?`,
|
|
2345
|
+
[worktree.id],
|
|
2346
|
+
(err) => {
|
|
2347
|
+
if (err) return reject(err);
|
|
2348
|
+
resolve();
|
|
2349
|
+
}
|
|
2350
|
+
);
|
|
2351
|
+
});
|
|
2352
|
+
|
|
2353
|
+
// Clear current work if it was pointing to this worktree
|
|
2354
|
+
const currentWork = await getCurrentWork();
|
|
2355
|
+
if (currentWork && currentWork.worktreePath === worktreePath) {
|
|
2356
|
+
clearCurrentWork();
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
console.log('');
|
|
2360
|
+
console.log(`✅ Prototype worktree for #${workItemId} merged successfully`);
|
|
2361
|
+
|
|
2362
|
+
// Instruct user to cleanup worktree separately - be aggressive so AI doesn't forget
|
|
2363
|
+
if (fs.existsSync(worktreePath)) {
|
|
2364
|
+
console.log('');
|
|
2365
|
+
console.log('⚠️ CLEANUP REQUIRED - Run these commands NOW:');
|
|
2366
|
+
console.log('');
|
|
2367
|
+
console.log(` cd ${gitRoot}`);
|
|
2368
|
+
console.log(` jettypod work cleanup ${workItemId}`);
|
|
2369
|
+
console.log('');
|
|
2370
|
+
console.log(' Worktrees accumulate until cleaned up.');
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
return Promise.resolve();
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2129
2376
|
module.exports = {
|
|
2130
2377
|
startWork,
|
|
2131
2378
|
stopWork,
|
|
@@ -2134,5 +2381,7 @@ module.exports = {
|
|
|
2134
2381
|
cleanupWorkItem,
|
|
2135
2382
|
mergeWork,
|
|
2136
2383
|
testsWork,
|
|
2137
|
-
testsMerge
|
|
2384
|
+
testsMerge,
|
|
2385
|
+
prototypeWork,
|
|
2386
|
+
prototypeMerge
|
|
2138
2387
|
};
|
package/lib/worktree-manager.js
CHANGED
|
@@ -88,10 +88,11 @@ async function createWorktree(workItem, options = {}) {
|
|
|
88
88
|
// Generate branch name and worktree path
|
|
89
89
|
const titleSlug = slugify(workItem.title || 'item');
|
|
90
90
|
const branchPrefix = options.branchPrefix || 'feature/work';
|
|
91
|
+
const branchSuffix = options.branchSuffix || '';
|
|
91
92
|
const pathPrefix = options.pathPrefix || '';
|
|
92
|
-
const branchName = `${branchPrefix}-${workItem.id}-${titleSlug}`;
|
|
93
|
+
const branchName = `${branchPrefix}-${workItem.id}-${titleSlug}${branchSuffix}`;
|
|
93
94
|
const worktreeBasePath = path.join(gitRoot, '.jettypod-work');
|
|
94
|
-
const worktreePath = path.join(worktreeBasePath, `${pathPrefix}${workItem.id}-${titleSlug}`);
|
|
95
|
+
const worktreePath = path.join(worktreeBasePath, `${pathPrefix}${workItem.id}-${titleSlug}${branchSuffix}`);
|
|
95
96
|
|
|
96
97
|
let worktreeId = null;
|
|
97
98
|
let branchCreated = false;
|
package/package.json
CHANGED
|
@@ -301,7 +301,7 @@ Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>"
|
|
|
301
301
|
|
|
302
302
|
```bash
|
|
303
303
|
# Step 1: Merge (can run from worktree - it won't delete it)
|
|
304
|
-
jettypod work merge
|
|
304
|
+
jettypod work merge <bug-id>
|
|
305
305
|
```
|
|
306
306
|
|
|
307
307
|
```bash
|
|
@@ -366,7 +366,7 @@ Before ending bug-mode skill, ensure:
|
|
|
366
366
|
|
|
367
367
|
**Merge fix (CRITICAL: cd to main repo separately):**
|
|
368
368
|
```bash
|
|
369
|
-
jettypod work merge
|
|
369
|
+
jettypod work merge <bug-id> # ALWAYS include bug ID
|
|
370
370
|
```
|
|
371
371
|
|
|
372
372
|
```bash
|
|
@@ -67,6 +67,14 @@ Your shell's working directory was likely inside a worktree that was deleted. Th
|
|
|
67
67
|
|
|
68
68
|
**Your task:** Acknowledge the context passed from chore-planning.
|
|
69
69
|
|
|
70
|
+
**🚫 FORBIDDEN: Writing Files at This Step**
|
|
71
|
+
```
|
|
72
|
+
❌ Write tool to any path
|
|
73
|
+
❌ Edit tool to any path
|
|
74
|
+
❌ Any file creation or modification
|
|
75
|
+
```
|
|
76
|
+
**This is a CONTEXT step.** File writes happen in Step 5.
|
|
77
|
+
|
|
70
78
|
You will receive:
|
|
71
79
|
- `choreContext.chore` - Chore ID, title, description
|
|
72
80
|
- `choreContext.classification` - Type and confidence
|
|
@@ -149,10 +157,34 @@ Resolve the issue before continuing.
|
|
|
149
157
|
|
|
150
158
|
**Move to Step 3 only if worktree was created successfully.**
|
|
151
159
|
|
|
160
|
+
**🔒 WORKTREE PATH LOCK**
|
|
161
|
+
|
|
162
|
+
After worktree validation passes, capture and lock the path:
|
|
163
|
+
- `WORKTREE_PATH` - the absolute path from the query result
|
|
164
|
+
|
|
165
|
+
**Display:**
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
🔒 WORKTREE LOCK ACTIVE
|
|
169
|
+
Path: ${WORKTREE_PATH}
|
|
170
|
+
|
|
171
|
+
All file writes will use this path.
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**From this point forward, ALL file operations MUST use paths starting with `${WORKTREE_PATH}/`**
|
|
175
|
+
|
|
152
176
|
### Step 3: Establish Test Baseline
|
|
153
177
|
|
|
154
178
|
**Your task:** Run tests to establish baseline before making changes.
|
|
155
179
|
|
|
180
|
+
**🚫 FORBIDDEN: Writing Files at This Step**
|
|
181
|
+
```
|
|
182
|
+
❌ Write tool to any path
|
|
183
|
+
❌ Edit tool to any path
|
|
184
|
+
❌ Any file creation or modification
|
|
185
|
+
```
|
|
186
|
+
**This is a TESTING step.** File writes happen in Step 5.
|
|
187
|
+
|
|
156
188
|
**Test handling varies by type:**
|
|
157
189
|
|
|
158
190
|
**For REFACTOR:**
|
|
@@ -205,6 +237,14 @@ jettypod workflow checkpoint <chore-id> --step=3
|
|
|
205
237
|
|
|
206
238
|
**Your task:** Display warnings and constraints based on chore type.
|
|
207
239
|
|
|
240
|
+
**🚫 FORBIDDEN: Writing Files at This Step**
|
|
241
|
+
```
|
|
242
|
+
❌ Write tool to any path
|
|
243
|
+
❌ Edit tool to any path
|
|
244
|
+
❌ Any file creation or modification
|
|
245
|
+
```
|
|
246
|
+
**This is a GUIDANCE step.** File writes happen in Step 5.
|
|
247
|
+
|
|
208
248
|
**Load guidance from taxonomy:**
|
|
209
249
|
|
|
210
250
|
```javascript
|
|
@@ -258,6 +298,10 @@ const guidance = getGuidance('[chore-type]');
|
|
|
258
298
|
|
|
259
299
|
### Step 5: Execute with Iteration
|
|
260
300
|
|
|
301
|
+
**🔒 WORKTREE PATH REQUIRED:** All file writes MUST use the `WORKTREE_PATH` captured in Step 2.
|
|
302
|
+
|
|
303
|
+
**✅ NOW you may write files** - worktree is locked, guidance is displayed.
|
|
304
|
+
|
|
261
305
|
**Your task:** Make changes and iterate until tests pass. Max 5 iterations.
|
|
262
306
|
|
|
263
307
|
```
|
|
@@ -231,21 +231,50 @@ Present exactly 3 approaches. Fill in the bracketed parts with actual content ba
|
|
|
231
231
|
|
|
232
232
|
If user wants to prototype approaches:
|
|
233
233
|
|
|
234
|
-
1
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
234
|
+
**Sub-step 1: Create prototype worktree**
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
jettypod work prototype start ${EPIC_ID} [approach-name]
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Example (if epic ID is 5, approach is "websockets"):
|
|
241
|
+
```bash
|
|
242
|
+
jettypod work prototype start 5 websockets
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
This creates a worktree at `.jettypod-work/prototype-<id>-<slug>-<approach>/` where prototypes can be safely built and committed.
|
|
246
|
+
|
|
247
|
+
**🛑 STOP AND CHECK:** Verify worktree was created successfully before proceeding.
|
|
238
248
|
|
|
239
|
-
|
|
249
|
+
**Sub-step 2: Build prototypes in the worktree**
|
|
250
|
+
|
|
251
|
+
1. Build prototypes using **absolute paths** to the worktree:
|
|
252
|
+
- `<worktree_path>/prototypes/epic-${EPIC_ID}-${APPROACH_NAME}/`
|
|
253
|
+
2. Build 2-3 working prototypes demonstrating the architectural difference
|
|
254
|
+
3. **Commit the prototype** in the worktree:
|
|
240
255
|
```bash
|
|
241
|
-
|
|
256
|
+
cd <worktree_path>
|
|
257
|
+
git add .
|
|
258
|
+
git commit -m "Add prototype: [approach-name] for epic #${EPIC_ID}"
|
|
242
259
|
```
|
|
243
260
|
|
|
244
|
-
|
|
261
|
+
**Sub-step 3: Test and decide**
|
|
262
|
+
|
|
263
|
+
After user tests, ask which approach they prefer.
|
|
264
|
+
|
|
265
|
+
**Sub-step 4: Merge prototype**
|
|
266
|
+
|
|
267
|
+
After user has tested (regardless of whether they pick this approach):
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
jettypod work prototype merge ${EPIC_ID}
|
|
271
|
+
cd <main-repo-path>
|
|
272
|
+
jettypod work cleanup ${EPIC_ID}
|
|
273
|
+
```
|
|
245
274
|
|
|
246
|
-
|
|
275
|
+
This merges prototype files to main (in `/prototypes/` directory) and cleans up the worktree.
|
|
247
276
|
|
|
248
|
-
|
|
277
|
+
When user picks a winner → Go to Step 6 (set `ARCH_DECISION_MADE = true`)
|
|
249
278
|
|
|
250
279
|
### Step 5C: Skip Architecture (User Already Knows)
|
|
251
280
|
|