jettypod 4.4.86 → 4.4.88
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/apps/dashboard/components/KanbanBoard.tsx +57 -10
- package/apps/dashboard/lib/db.ts +23 -1
- package/jettypod.js +55 -12
- package/package.json +1 -1
- package/skills-templates/bug-planning/SKILL.md +1 -1
- package/skills-templates/chore-planning/SKILL.md +1 -1
- package/skills-templates/epic-planning/SKILL.md +1 -1
- package/skills-templates/feature-planning/SKILL.md +1 -1
- package/skills-templates/{plan-routing → request-routing}/SKILL.md +2 -2
- package/skills-templates/simple-improvement/SKILL.md +6 -6
|
@@ -105,6 +105,11 @@ function KanbanCard({ item, epicTitle, showEpic = false, isInFlight = false, onT
|
|
|
105
105
|
const hasChores = allChores.length > 0;
|
|
106
106
|
const hasIncompleteChores = incompleteChores.length > 0;
|
|
107
107
|
|
|
108
|
+
// Calculate bugs for expandable section
|
|
109
|
+
const allBugs = item.bugs || [];
|
|
110
|
+
const incompleteBugs = allBugs.filter(b => b.status !== 'done');
|
|
111
|
+
const hasBugs = allBugs.length > 0;
|
|
112
|
+
|
|
108
113
|
const handleCardClick = () => {
|
|
109
114
|
router.push(`/work/${item.id}`);
|
|
110
115
|
};
|
|
@@ -186,24 +191,40 @@ function KanbanCard({ item, epicTitle, showEpic = false, isInFlight = false, onT
|
|
|
186
191
|
)}
|
|
187
192
|
</div>
|
|
188
193
|
</div>
|
|
189
|
-
{/* Show expandable
|
|
190
|
-
{hasChores && (
|
|
194
|
+
{/* Show expandable section for features with chores or bugs */}
|
|
195
|
+
{(hasChores || hasBugs) && (
|
|
191
196
|
<div className={`border-t ${isDone ? 'border-green-200 dark:border-green-800' : 'border-zinc-200 dark:border-zinc-700'}`}>
|
|
192
197
|
<button
|
|
193
198
|
onClick={() => setExpanded(!expanded)}
|
|
194
|
-
className={`w-full px-3 py-1.5 flex items-
|
|
199
|
+
className={`w-full px-3 py-1.5 flex items-start gap-1.5 text-xs transition-colors ${
|
|
195
200
|
isDone
|
|
196
201
|
? 'text-green-700 dark:text-green-400 hover:bg-green-100 dark:hover:bg-green-900/30'
|
|
197
202
|
: 'text-zinc-600 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-zinc-700/50'
|
|
198
203
|
}`}
|
|
199
204
|
>
|
|
200
|
-
<span>{expanded ? '▼' : '▶'}</span>
|
|
201
|
-
<
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
205
|
+
<span className="mt-0.5">{expanded ? '▼' : '▶'}</span>
|
|
206
|
+
<div className="flex flex-col gap-0.5">
|
|
207
|
+
{hasChores && (
|
|
208
|
+
<div className="flex items-center gap-1.5">
|
|
209
|
+
<span>🔧</span>
|
|
210
|
+
<span>
|
|
211
|
+
{isDone
|
|
212
|
+
? `${allChores.length === 0 ? 'no' : allChores.length} chore${allChores.length !== 1 ? 's' : ''}`
|
|
213
|
+
: `${incompleteChores.length === 0 ? 'no' : incompleteChores.length}${item.mode ? ` ${item.mode} mode` : ''} chore${incompleteChores.length !== 1 ? 's' : ''} left`}
|
|
214
|
+
</span>
|
|
215
|
+
</div>
|
|
216
|
+
)}
|
|
217
|
+
{hasBugs && (
|
|
218
|
+
<div className="flex items-center gap-1.5">
|
|
219
|
+
<span>🐛</span>
|
|
220
|
+
<span>
|
|
221
|
+
{isDone
|
|
222
|
+
? `${allBugs.length === 0 ? 'no' : allBugs.length} bug${allBugs.length !== 1 ? 's' : ''}`
|
|
223
|
+
: `${incompleteBugs.length === 0 ? 'no' : incompleteBugs.length} bug${incompleteBugs.length !== 1 ? 's' : ''} left`}
|
|
224
|
+
</span>
|
|
225
|
+
</div>
|
|
226
|
+
)}
|
|
227
|
+
</div>
|
|
207
228
|
</button>
|
|
208
229
|
{expanded && (
|
|
209
230
|
<div className="px-3 pb-2 space-y-1">
|
|
@@ -237,6 +258,32 @@ function KanbanCard({ item, epicTitle, showEpic = false, isInFlight = false, onT
|
|
|
237
258
|
</Link>
|
|
238
259
|
);
|
|
239
260
|
})}
|
|
261
|
+
{allBugs.map((bug) => {
|
|
262
|
+
const isComplete = bug.status === 'done';
|
|
263
|
+
return (
|
|
264
|
+
<Link
|
|
265
|
+
key={bug.id}
|
|
266
|
+
href={`/work/${bug.id}`}
|
|
267
|
+
className={`block py-1 px-2 text-xs rounded transition-colors ${
|
|
268
|
+
isComplete
|
|
269
|
+
? 'bg-green-100 dark:bg-green-900/30 border border-green-200 dark:border-green-800/50'
|
|
270
|
+
: 'hover:bg-zinc-100 dark:hover:bg-zinc-700'
|
|
271
|
+
}`}
|
|
272
|
+
>
|
|
273
|
+
<div className="flex items-center gap-2">
|
|
274
|
+
<span className={`font-mono ${isComplete ? 'text-zinc-500' : 'text-zinc-400'}`}>#{bug.id}</span>
|
|
275
|
+
<span>🐛</span>
|
|
276
|
+
<span className={`truncate ${
|
|
277
|
+
isComplete
|
|
278
|
+
? 'text-zinc-500'
|
|
279
|
+
: 'text-zinc-700 dark:text-zinc-300'
|
|
280
|
+
}`}>
|
|
281
|
+
{bug.title || <span className="text-zinc-400 italic">(Untitled)</span>}
|
|
282
|
+
</span>
|
|
283
|
+
</div>
|
|
284
|
+
</Link>
|
|
285
|
+
);
|
|
286
|
+
})}
|
|
240
287
|
</div>
|
|
241
288
|
)}
|
|
242
289
|
</div>
|
package/apps/dashboard/lib/db.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface WorkItem {
|
|
|
23
23
|
display_order: number | null;
|
|
24
24
|
children?: WorkItem[];
|
|
25
25
|
chores?: WorkItem[];
|
|
26
|
+
bugs?: WorkItem[];
|
|
26
27
|
current_step?: number | null;
|
|
27
28
|
total_steps?: number | null;
|
|
28
29
|
}
|
|
@@ -279,6 +280,26 @@ export function getKanbanData(doneLimit: number = 50): KanbanData {
|
|
|
279
280
|
}
|
|
280
281
|
}
|
|
281
282
|
|
|
283
|
+
// Get all bugs that belong to features (for bug expansion)
|
|
284
|
+
const featureBugs = db.prepare(`
|
|
285
|
+
SELECT b.id, b.type, b.title, b.description, b.status, b.parent_id, b.epic_id,
|
|
286
|
+
b.branch_name, b.mode, b.phase, b.completed_at, b.created_at
|
|
287
|
+
FROM work_items b
|
|
288
|
+
INNER JOIN work_items f ON b.parent_id = f.id
|
|
289
|
+
WHERE b.type = 'bug' AND f.type = 'feature'
|
|
290
|
+
ORDER BY b.id
|
|
291
|
+
`).all() as WorkItem[];
|
|
292
|
+
|
|
293
|
+
// Group bugs by parent feature ID
|
|
294
|
+
const bugsByFeature = new Map<number, WorkItem[]>();
|
|
295
|
+
for (const bug of featureBugs) {
|
|
296
|
+
if (bug.parent_id) {
|
|
297
|
+
const existing = bugsByFeature.get(bug.parent_id) || [];
|
|
298
|
+
existing.push(bug);
|
|
299
|
+
bugsByFeature.set(bug.parent_id, existing);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
282
303
|
// Get kanban-eligible items:
|
|
283
304
|
// - Features (type = 'feature')
|
|
284
305
|
// - Chores that are NOT children of features (parent is null, or parent is an epic)
|
|
@@ -319,9 +340,10 @@ export function getKanbanData(doneLimit: number = 50): KanbanData {
|
|
|
319
340
|
// Strip parent_type from the item
|
|
320
341
|
const { parent_type, ...cleanItem } = item;
|
|
321
342
|
|
|
322
|
-
// Attach chores to features
|
|
343
|
+
// Attach chores and bugs to features
|
|
323
344
|
if (cleanItem.type === 'feature') {
|
|
324
345
|
cleanItem.chores = choresByFeature.get(cleanItem.id) || [];
|
|
346
|
+
cleanItem.bugs = bugsByFeature.get(cleanItem.id) || [];
|
|
325
347
|
}
|
|
326
348
|
|
|
327
349
|
if (cleanItem.status === 'in_progress') {
|
package/jettypod.js
CHANGED
|
@@ -701,8 +701,29 @@ async function generateClaude(options = {}) {
|
|
|
701
701
|
const currentConfig = config.read();
|
|
702
702
|
|
|
703
703
|
const content = await claude.generate(currentConfig, options);
|
|
704
|
+
|
|
705
|
+
// Check if content actually changed
|
|
706
|
+
const existingContent = fs.existsSync('CLAUDE.md') ? fs.readFileSync('CLAUDE.md', 'utf-8') : '';
|
|
707
|
+
const contentChanged = content !== existingContent;
|
|
708
|
+
|
|
709
|
+
if (!contentChanged) {
|
|
710
|
+
return; // No changes needed
|
|
711
|
+
}
|
|
712
|
+
|
|
704
713
|
fs.writeFileSync('CLAUDE.md', content);
|
|
705
714
|
console.log('📝 CLAUDE.md generated');
|
|
715
|
+
|
|
716
|
+
// Auto-commit if requested (for commands that legitimately update CLAUDE.md)
|
|
717
|
+
if (options.autoCommit) {
|
|
718
|
+
try {
|
|
719
|
+
const { execSync } = require('child_process');
|
|
720
|
+
execSync('git add CLAUDE.md', { stdio: 'ignore' });
|
|
721
|
+
execSync('git commit -m "chore: update CLAUDE.md"', { stdio: 'ignore' });
|
|
722
|
+
console.log('✅ CLAUDE.md changes committed');
|
|
723
|
+
} catch (err) {
|
|
724
|
+
// Commit may fail if nothing staged or not in a git repo - that's ok
|
|
725
|
+
}
|
|
726
|
+
}
|
|
706
727
|
}
|
|
707
728
|
|
|
708
729
|
// Ensure Claude Code hooks are up to date (called on every jettypod launch)
|
|
@@ -767,9 +788,9 @@ function ensureClaudeHooks() {
|
|
|
767
788
|
}
|
|
768
789
|
}
|
|
769
790
|
|
|
770
|
-
//
|
|
771
|
-
fs.writeFileSync(claudeSettingsPath, JSON.stringify(claudeSettings, null, 2));
|
|
791
|
+
// Only write settings if hooks were actually updated (avoid untracked changes in main)
|
|
772
792
|
if (hooksUpdated) {
|
|
793
|
+
fs.writeFileSync(claudeSettingsPath, JSON.stringify(claudeSettings, null, 2));
|
|
773
794
|
console.log('⚙️ Claude Code hooks updated');
|
|
774
795
|
}
|
|
775
796
|
|
|
@@ -1263,6 +1284,21 @@ switch (command) {
|
|
|
1263
1284
|
|
|
1264
1285
|
// Ensure session.md is gitignored (fixes existing projects)
|
|
1265
1286
|
ensureJettypodGitignores();
|
|
1287
|
+
|
|
1288
|
+
// Auto-commit any changes made by update (skills, hooks, gitignore, etc.)
|
|
1289
|
+
// This prevents untracked changes from breaking worktree merges
|
|
1290
|
+
try {
|
|
1291
|
+
const { execSync } = require('child_process');
|
|
1292
|
+
// Check if there are any changes to commit
|
|
1293
|
+
const status = execSync('git status --porcelain', { encoding: 'utf-8' });
|
|
1294
|
+
if (status.trim()) {
|
|
1295
|
+
execSync('git add -A', { stdio: 'ignore' });
|
|
1296
|
+
execSync('git commit -m "chore: jettypod update - refresh skills and hooks"', { stdio: 'ignore' });
|
|
1297
|
+
console.log('✅ Update changes committed');
|
|
1298
|
+
}
|
|
1299
|
+
} catch (err) {
|
|
1300
|
+
// Commit may fail if not in a git repo or nothing to commit - that's ok
|
|
1301
|
+
}
|
|
1266
1302
|
}
|
|
1267
1303
|
|
|
1268
1304
|
process.exit(success ? 0 : 1);
|
|
@@ -1282,12 +1318,12 @@ switch (command) {
|
|
|
1282
1318
|
}
|
|
1283
1319
|
config.update({ description });
|
|
1284
1320
|
console.log('✅ Project description updated');
|
|
1285
|
-
await generateClaude();
|
|
1321
|
+
await generateClaude({ autoCommit: true });
|
|
1286
1322
|
break;
|
|
1287
1323
|
|
|
1288
1324
|
case 'generate':
|
|
1289
1325
|
// Show directive only when explicitly using generate command
|
|
1290
|
-
await generateClaude({ showDirective: true });
|
|
1326
|
+
await generateClaude({ showDirective: true, autoCommit: true });
|
|
1291
1327
|
break;
|
|
1292
1328
|
|
|
1293
1329
|
case 'work':
|
|
@@ -1914,7 +1950,7 @@ switch (command) {
|
|
|
1914
1950
|
console.log('⚠️ Remember: Customer-facing features MUST use Production mode');
|
|
1915
1951
|
console.log('');
|
|
1916
1952
|
|
|
1917
|
-
await generateClaude();
|
|
1953
|
+
await generateClaude({ autoCommit: true });
|
|
1918
1954
|
} catch (err) {
|
|
1919
1955
|
console.error(`Error: ${err.message}`);
|
|
1920
1956
|
process.exit(1);
|
|
@@ -1977,7 +2013,7 @@ switch (command) {
|
|
|
1977
2013
|
console.log(' 4. Run: jettypod project discover complete --winner=<path> --rationale="<reason>"');
|
|
1978
2014
|
console.log('');
|
|
1979
2015
|
|
|
1980
|
-
await generateClaude();
|
|
2016
|
+
await generateClaude({ autoCommit: true });
|
|
1981
2017
|
console.log('📝 CLAUDE.md updated with discovery context');
|
|
1982
2018
|
|
|
1983
2019
|
} catch (err) {
|
|
@@ -2062,7 +2098,7 @@ switch (command) {
|
|
|
2062
2098
|
const checkpoint = require('./lib/discovery-checkpoint');
|
|
2063
2099
|
checkpoint.clearCheckpoint();
|
|
2064
2100
|
|
|
2065
|
-
await generateClaude();
|
|
2101
|
+
await generateClaude({ autoCommit: true });
|
|
2066
2102
|
|
|
2067
2103
|
console.log('✅ Project discovery complete!');
|
|
2068
2104
|
console.log('');
|
|
@@ -2070,7 +2106,7 @@ switch (command) {
|
|
|
2070
2106
|
console.log(`Rationale: ${rationale}`);
|
|
2071
2107
|
console.log('');
|
|
2072
2108
|
|
|
2073
|
-
|
|
2109
|
+
// Note: duplicate call removed - generateClaude already called above
|
|
2074
2110
|
console.log('📝 CLAUDE.md updated');
|
|
2075
2111
|
console.log('');
|
|
2076
2112
|
|
|
@@ -2123,11 +2159,15 @@ switch (command) {
|
|
|
2123
2159
|
// New project - auto-initialize
|
|
2124
2160
|
await initializeProject();
|
|
2125
2161
|
} else {
|
|
2126
|
-
// Project exists - launch dashboard
|
|
2162
|
+
// Project exists - launch dashboard
|
|
2127
2163
|
const currentConfig = config.read();
|
|
2128
|
-
await generateClaude();
|
|
2129
2164
|
|
|
2130
|
-
//
|
|
2165
|
+
// Only generate CLAUDE.md if it doesn't exist (avoid untracked changes in main)
|
|
2166
|
+
if (!fs.existsSync('CLAUDE.md')) {
|
|
2167
|
+
await generateClaude();
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
// Ensure Claude Code hooks are up to date on every launch (only writes if changed)
|
|
2131
2171
|
ensureClaudeHooks();
|
|
2132
2172
|
|
|
2133
2173
|
// Launch dashboard
|
|
@@ -3170,7 +3210,10 @@ Quick commands:
|
|
|
3170
3210
|
} else {
|
|
3171
3211
|
// Already initialized - show smart guidance
|
|
3172
3212
|
const currentConfig = config.read();
|
|
3173
|
-
|
|
3213
|
+
// Only generate CLAUDE.md if it doesn't exist (avoid untracked changes)
|
|
3214
|
+
if (!fs.existsSync('CLAUDE.md')) {
|
|
3215
|
+
await generateClaude();
|
|
3216
|
+
}
|
|
3174
3217
|
|
|
3175
3218
|
const discovery = currentConfig.project_discovery;
|
|
3176
3219
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bug-planning
|
|
3
|
-
description: Guide structured bug investigation with symptom capture, hypothesis testing, and root cause identification. Invoked by
|
|
3
|
+
description: Guide structured bug investigation with symptom capture, hypothesis testing, and root cause identification. Invoked by request-routing when user reports a bug, mentions unexpected behavior, or describes something broken. (project)
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Bug Planning Skill
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: chore-planning
|
|
3
|
-
description: Guide standalone chore planning with automatic type classification and routing to chore-mode. Invoked by
|
|
3
|
+
description: Guide standalone chore planning with automatic type classification and routing to chore-mode. Invoked by request-routing for substantial technical work - refactoring, infrastructure, migrations, or enhancements where the implementation approach is obvious. (project)
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Chore Planning Skill
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: epic-planning
|
|
3
|
-
description: Guide epic planning with feature brainstorming and optional architectural decision prototyping. Invoked by
|
|
3
|
+
description: Guide epic planning with feature brainstorming and optional architectural decision prototyping. Invoked by request-routing for large multi-feature initiatives that need to be broken down into features. (project)
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Epic Planning Skill
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: feature-planning
|
|
3
|
-
description: Guide feature planning with UX approach exploration and BDD scenario generation. Invoked by
|
|
3
|
+
description: Guide feature planning with UX approach exploration and BDD scenario generation. Invoked by request-routing when implementing NEW user-facing behavior that requires UX decisions (multiple valid approaches to explore). (project)
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Feature Planning Skill
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: request-routing
|
|
3
3
|
description: "⚡ ENTRY POINT FOR ALL WORK REQUESTS. Invoke this skill FIRST when user describes ANY work - 'build X', 'fix Y', 'add Z', 'create feature', 'implement'. Do NOT create work items or invoke other planning skills directly. This skill analyzes intent and routes to the correct workflow. (project)"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Request Routing Skill
|
|
7
7
|
|
|
8
8
|
**⚡ UNIVERSAL ENTRY POINT** - This skill MUST be invoked FIRST when a user describes work they want done.
|
|
9
9
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: simple-improvement
|
|
3
|
-
description: Guide implementation of simple improvements to existing functionality. Invoked by
|
|
3
|
+
description: Guide implementation of simple improvements to existing functionality. Invoked by request-routing for straightforward changes like copy changes, styling tweaks, or minor behavior adjustments where the implementation is obvious. (project)
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Simple Improvement Skill
|
|
@@ -22,7 +22,7 @@ Lightweight workflow for basic enhancements to existing functionality. Bypasses
|
|
|
22
22
|
|
|
23
23
|
## When to Use
|
|
24
24
|
|
|
25
|
-
This skill is invoked by
|
|
25
|
+
This skill is invoked by request-routing when work is identified as a **simple improvement**:
|
|
26
26
|
- Copy/text changes
|
|
27
27
|
- Styling tweaks
|
|
28
28
|
- Minor behavior adjustments
|
|
@@ -30,7 +30,7 @@ This skill is invoked by plan-routing when work is identified as a **simple impr
|
|
|
30
30
|
|
|
31
31
|
## When NOT to Use
|
|
32
32
|
|
|
33
|
-
Route back to
|
|
33
|
+
Route back to request-routing for:
|
|
34
34
|
- New functionality (even if small)
|
|
35
35
|
- Changes requiring new data models
|
|
36
36
|
- Complex logic changes
|
|
@@ -96,7 +96,7 @@ Scan the user's description for these patterns:
|
|
|
96
96
|
| Multiple components | mentions 3+ different files/areas, "also need to" |
|
|
97
97
|
| Architectural changes | "refactor", "restructure", "new system" |
|
|
98
98
|
|
|
99
|
-
❌ **Route back to
|
|
99
|
+
❌ **Route back to request-routing if ANY complexity signal detected:**
|
|
100
100
|
- New database tables/columns needed
|
|
101
101
|
- New API endpoints required
|
|
102
102
|
- Complex conditional logic
|
|
@@ -114,11 +114,11 @@ Scan the user's description for these patterns:
|
|
|
114
114
|
```
|
|
115
115
|
⚠️ This change appears to require new data models or architectural changes. This is more than a simple improvement.
|
|
116
116
|
|
|
117
|
-
I'm routing you back to
|
|
117
|
+
I'm routing you back to request-routing for proper planning.
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
**Then IMMEDIATELY:**
|
|
121
|
-
1. Invoke
|
|
121
|
+
1. Invoke request-routing skill using the Skill tool
|
|
122
122
|
2. END this skill (do not continue)
|
|
123
123
|
|
|
124
124
|
**If confirmed simple:**
|