jettypod 4.4.106 → 4.4.107
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-backup/work.db +0 -0
- package/apps/dashboard/app/api/claude/sessions/route.ts +23 -1
- package/apps/dashboard/components/ClaudePanel.tsx +3 -0
- package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +27 -0
- package/apps/dashboard/components/SessionList.tsx +58 -21
- package/apps/dashboard/hooks/useClaudeSessions.ts +34 -0
- package/apps/dashboard/lib/db.ts +15 -0
- package/jettypod.js +5 -3
- package/lib/git-hooks/post-merge +3 -2
- package/lib/git-hooks/pre-commit +6 -1
- package/lib/work-tracking/index.js +12 -0
- package/package.json +1 -1
- package/skills-templates/bug-planning/SKILL.md +19 -0
- package/skills-templates/chore-planning/SKILL.md +17 -0
- package/skills-templates/epic-planning/SKILL.md +17 -0
- package/skills-templates/feature-planning/SKILL.md +21 -0
package/.jettypod-backup/work.db
CHANGED
|
Binary file
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
|
-
import { listSessions, createSession, linkSession, getSessionByWorkItem } from '@/lib/db';
|
|
2
|
+
import { listSessions, createSession, linkSession, getSessionByWorkItem, closeSession } from '@/lib/db';
|
|
3
3
|
|
|
4
4
|
export const dynamic = 'force-dynamic';
|
|
5
5
|
|
|
@@ -59,3 +59,25 @@ export async function PATCH(request: Request) {
|
|
|
59
59
|
return NextResponse.json({ error: 'Failed to link session' }, { status: 500 });
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
|
|
63
|
+
// DELETE /api/claude/sessions - Close a session
|
|
64
|
+
export async function DELETE(request: Request) {
|
|
65
|
+
try {
|
|
66
|
+
const { searchParams } = new URL(request.url);
|
|
67
|
+
const sessionId = searchParams.get('sessionId');
|
|
68
|
+
|
|
69
|
+
if (!sessionId) {
|
|
70
|
+
return NextResponse.json({ error: 'sessionId required' }, { status: 400 });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const success = closeSession(parseInt(sessionId, 10));
|
|
74
|
+
if (!success) {
|
|
75
|
+
return NextResponse.json({ error: 'Session not found' }, { status: 404 });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return NextResponse.json({ success: true });
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('Failed to close session:', error);
|
|
81
|
+
return NextResponse.json({ error: 'Failed to close session' }, { status: 500 });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -40,6 +40,7 @@ interface ClaudePanelProps {
|
|
|
40
40
|
standaloneSessions?: SessionItem[];
|
|
41
41
|
onSelectSession?: (sessionId: string) => void;
|
|
42
42
|
onNewSession?: () => void;
|
|
43
|
+
onCloseSession?: (sessionId: string) => void;
|
|
43
44
|
showSessionList?: boolean;
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -62,6 +63,7 @@ export function ClaudePanel({
|
|
|
62
63
|
standaloneSessions = [],
|
|
63
64
|
onSelectSession,
|
|
64
65
|
onNewSession,
|
|
66
|
+
onCloseSession,
|
|
65
67
|
showSessionList: showSessionListProp,
|
|
66
68
|
}: ClaudePanelProps) {
|
|
67
69
|
// Show session list by default when no active session, or when explicitly requested
|
|
@@ -218,6 +220,7 @@ export function ClaudePanel({
|
|
|
218
220
|
sessions={standaloneSessions}
|
|
219
221
|
onSelectSession={onSelectSession || (() => {})}
|
|
220
222
|
onNewSession={onNewSession || (() => {})}
|
|
223
|
+
onCloseSession={onCloseSession}
|
|
221
224
|
/>
|
|
222
225
|
) : (
|
|
223
226
|
<>
|
|
@@ -533,6 +533,32 @@ function RealTimeKanbanContent({ initialData, initialDecisions }: RealTimeKanban
|
|
|
533
533
|
setShowSessionList(false);
|
|
534
534
|
}, [sessions, standaloneSessions]);
|
|
535
535
|
|
|
536
|
+
const handleCloseSession = useCallback(async (sessionId: string) => {
|
|
537
|
+
// Call API to mark as completed in DB
|
|
538
|
+
try {
|
|
539
|
+
await fetch(`/api/claude/sessions?sessionId=${sessionId}`, {
|
|
540
|
+
method: 'DELETE',
|
|
541
|
+
});
|
|
542
|
+
} catch (error) {
|
|
543
|
+
console.error('Failed to close session:', error);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Remove from standalone sessions list
|
|
547
|
+
setStandaloneSessions(prev => prev.filter(s => s.id !== sessionId));
|
|
548
|
+
|
|
549
|
+
// Remove from in-memory sessions
|
|
550
|
+
setSessions(prev => {
|
|
551
|
+
const updated = new Map(prev);
|
|
552
|
+
updated.delete(sessionId);
|
|
553
|
+
return updated;
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// If this was the active session, clear it
|
|
557
|
+
if (activeSessionId === sessionId) {
|
|
558
|
+
setActiveSessionId(null);
|
|
559
|
+
}
|
|
560
|
+
}, [activeSessionId]);
|
|
561
|
+
|
|
536
562
|
const handleOpenSessionList = useCallback(() => {
|
|
537
563
|
setShowSessionList(true);
|
|
538
564
|
setClaudePanelOpen(true);
|
|
@@ -639,6 +665,7 @@ function RealTimeKanbanContent({ initialData, initialDecisions }: RealTimeKanban
|
|
|
639
665
|
standaloneSessions={standaloneSessions}
|
|
640
666
|
onSelectSession={handleSelectStandaloneSession}
|
|
641
667
|
onNewSession={handleNewSession}
|
|
668
|
+
onCloseSession={handleCloseSession}
|
|
642
669
|
showSessionList={showSessionList}
|
|
643
670
|
/>
|
|
644
671
|
</div>
|
|
@@ -14,12 +14,14 @@ interface SessionListProps {
|
|
|
14
14
|
sessions: SessionItem[];
|
|
15
15
|
onSelectSession: (sessionId: string) => void;
|
|
16
16
|
onNewSession: () => void;
|
|
17
|
+
onCloseSession?: (sessionId: string) => void;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function SessionList({
|
|
20
21
|
sessions,
|
|
21
22
|
onSelectSession,
|
|
22
23
|
onNewSession,
|
|
24
|
+
onCloseSession,
|
|
23
25
|
}: SessionListProps) {
|
|
24
26
|
return (
|
|
25
27
|
<div className="flex-1 flex flex-col" data-testid="session-list">
|
|
@@ -46,31 +48,48 @@ export function SessionList({
|
|
|
46
48
|
) : (
|
|
47
49
|
<div className="divide-y divide-zinc-800">
|
|
48
50
|
{sessions.map((session) => (
|
|
49
|
-
<
|
|
51
|
+
<div
|
|
50
52
|
key={session.id}
|
|
51
|
-
|
|
52
|
-
className="w-full px-4 py-3 text-left hover:bg-zinc-800/50 transition-colors"
|
|
53
|
-
whileHover={{ x: 4 }}
|
|
53
|
+
className="flex items-center hover:bg-zinc-800/50 transition-colors"
|
|
54
54
|
data-testid={`session-item-${session.id}`}
|
|
55
55
|
>
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
{session.featureId
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
56
|
+
<motion.button
|
|
57
|
+
onClick={() => onSelectSession(session.id)}
|
|
58
|
+
className="flex-1 px-4 py-3 text-left"
|
|
59
|
+
whileHover={{ x: 4 }}
|
|
60
|
+
>
|
|
61
|
+
<div className="flex items-center gap-2">
|
|
62
|
+
<SessionIcon hasFeature={!!session.featureId} />
|
|
63
|
+
<div className="flex-1 min-w-0">
|
|
64
|
+
<p className="text-sm font-medium text-white truncate">
|
|
65
|
+
{session.featureId ? session.featureTitle : session.title}
|
|
66
|
+
</p>
|
|
67
|
+
{session.featureId && (
|
|
68
|
+
<span className="inline-flex items-center px-1.5 py-0.5 mt-1 text-xs font-medium bg-blue-900/50 text-blue-300 rounded">
|
|
69
|
+
#{session.featureId}
|
|
70
|
+
</span>
|
|
71
|
+
)}
|
|
72
|
+
{!session.featureId && (
|
|
73
|
+
<p className="text-xs text-zinc-500 mt-0.5">Unlinked session</p>
|
|
74
|
+
)}
|
|
75
|
+
</div>
|
|
76
|
+
<ChevronIcon />
|
|
70
77
|
</div>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
</motion.button>
|
|
79
|
+
{onCloseSession && (
|
|
80
|
+
<button
|
|
81
|
+
onClick={(e) => {
|
|
82
|
+
e.stopPropagation();
|
|
83
|
+
onCloseSession(session.id);
|
|
84
|
+
}}
|
|
85
|
+
className="p-2 mr-2 rounded hover:bg-zinc-700 text-zinc-500 hover:text-zinc-300 transition-colors"
|
|
86
|
+
aria-label="Close session"
|
|
87
|
+
data-testid={`close-session-${session.id}`}
|
|
88
|
+
>
|
|
89
|
+
<CloseIcon />
|
|
90
|
+
</button>
|
|
91
|
+
)}
|
|
92
|
+
</div>
|
|
74
93
|
))}
|
|
75
94
|
</div>
|
|
76
95
|
)}
|
|
@@ -118,3 +137,21 @@ function ChevronIcon() {
|
|
|
118
137
|
</svg>
|
|
119
138
|
);
|
|
120
139
|
}
|
|
140
|
+
|
|
141
|
+
function CloseIcon() {
|
|
142
|
+
return (
|
|
143
|
+
<svg
|
|
144
|
+
className="w-4 h-4"
|
|
145
|
+
fill="none"
|
|
146
|
+
stroke="currentColor"
|
|
147
|
+
viewBox="0 0 24 24"
|
|
148
|
+
>
|
|
149
|
+
<path
|
|
150
|
+
strokeLinecap="round"
|
|
151
|
+
strokeLinejoin="round"
|
|
152
|
+
strokeWidth={2}
|
|
153
|
+
d="M6 18L18 6M6 6l12 12"
|
|
154
|
+
/>
|
|
155
|
+
</svg>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
@@ -23,6 +23,7 @@ interface UseClaudeSessionsReturn {
|
|
|
23
23
|
switchSession: (workItemId: string) => void;
|
|
24
24
|
stopSession: (workItemId: string) => void;
|
|
25
25
|
retrySession: (workItemId: string) => void;
|
|
26
|
+
closeSession: (workItemId: string, dbSessionId?: number) => Promise<void>;
|
|
26
27
|
closeAllSessions: () => void;
|
|
27
28
|
isSessionRunning: (workItemId: string) => boolean;
|
|
28
29
|
}
|
|
@@ -234,6 +235,38 @@ export function useClaudeSessions(): UseClaudeSessionsReturn {
|
|
|
234
235
|
}
|
|
235
236
|
}, [sessions, startSession]);
|
|
236
237
|
|
|
238
|
+
const closeSession = useCallback(async (workItemId: string, dbSessionId?: number) => {
|
|
239
|
+
const session = sessions.get(workItemId);
|
|
240
|
+
|
|
241
|
+
// Abort if running
|
|
242
|
+
if (session?.abortController) {
|
|
243
|
+
session.abortController.abort();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Call API to mark as completed in DB (if we have a DB session ID)
|
|
247
|
+
if (dbSessionId) {
|
|
248
|
+
try {
|
|
249
|
+
await fetch(`/api/claude/sessions?sessionId=${dbSessionId}`, {
|
|
250
|
+
method: 'DELETE',
|
|
251
|
+
});
|
|
252
|
+
} catch (error) {
|
|
253
|
+
console.error('Failed to close session in DB:', error);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Remove from local state
|
|
258
|
+
setSessions((prev) => {
|
|
259
|
+
const newMap = new Map(prev);
|
|
260
|
+
newMap.delete(workItemId);
|
|
261
|
+
return newMap;
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// If this was the active session, clear it
|
|
265
|
+
if (activeSessionId === workItemId) {
|
|
266
|
+
setActiveSessionId(null);
|
|
267
|
+
}
|
|
268
|
+
}, [sessions, activeSessionId]);
|
|
269
|
+
|
|
237
270
|
const closeAllSessions = useCallback(() => {
|
|
238
271
|
sessions.forEach((session) => {
|
|
239
272
|
if (session.abortController) {
|
|
@@ -259,6 +292,7 @@ export function useClaudeSessions(): UseClaudeSessionsReturn {
|
|
|
259
292
|
switchSession,
|
|
260
293
|
stopSession,
|
|
261
294
|
retrySession,
|
|
295
|
+
closeSession,
|
|
262
296
|
closeAllSessions,
|
|
263
297
|
isSessionRunning,
|
|
264
298
|
};
|
package/apps/dashboard/lib/db.ts
CHANGED
|
@@ -649,3 +649,18 @@ export function getSession(sessionId: number): ClaudeSession | null {
|
|
|
649
649
|
db.close();
|
|
650
650
|
}
|
|
651
651
|
}
|
|
652
|
+
|
|
653
|
+
// Close a session by ID (mark as completed)
|
|
654
|
+
export function closeSession(sessionId: number): boolean {
|
|
655
|
+
const db = getWriteDb();
|
|
656
|
+
try {
|
|
657
|
+
const result = db.prepare(`
|
|
658
|
+
UPDATE claude_sessions
|
|
659
|
+
SET status = 'completed', completed_at = datetime('now')
|
|
660
|
+
WHERE id = ?
|
|
661
|
+
`).run(sessionId);
|
|
662
|
+
return result.changes > 0;
|
|
663
|
+
} finally {
|
|
664
|
+
db.close();
|
|
665
|
+
}
|
|
666
|
+
}
|
package/jettypod.js
CHANGED
|
@@ -302,7 +302,9 @@ JettyPod: Structured workflow system with skills that guide complex workflows.
|
|
|
302
302
|
|
|
303
303
|
## ⚠️ CRITICAL: All Work Starts with request-routing
|
|
304
304
|
|
|
305
|
-
**WHY:** request-routing creates a safe workspace (worktree) where your changes can actually be committed. Without
|
|
305
|
+
**WHY:** The workflow initiated by request-routing eventually creates a safe workspace (worktree) where your changes can actually be committed. Without starting this workflow, you'll edit files on main, then discover the pre-commit hook blocks you—leaving uncommitted changes that can't be saved.
|
|
306
|
+
|
|
307
|
+
**WHEN WORKTREE IS CREATED:** The worktree is created when \`jettypod work start\` runs—this happens in mode skills (speed-mode, stable-mode, chore-mode, bug-mode) or simple-improvement, NOT immediately in request-routing. Planning skills (epic-planning, feature-planning, chore-planning, bug-planning) are READ-ONLY investigation phases—do not edit files during planning.
|
|
306
308
|
|
|
307
309
|
**FIRST RESPONSE RULE:** If user describes ANY code change, your FIRST action must be invoking request-routing. Not after reading files. Not after understanding the problem. FIRST.
|
|
308
310
|
|
|
@@ -313,10 +315,10 @@ JettyPod: Structured workflow system with skills that guide complex workflows.
|
|
|
313
315
|
- "refactor", "migrate", "upgrade" (technical work)
|
|
314
316
|
- "I noticed...", "I'm thinking..." (when followed by desired change)
|
|
315
317
|
|
|
316
|
-
**⚠️ ANTI-PATTERN: Do NOT edit files before
|
|
318
|
+
**⚠️ ANTI-PATTERN: Do NOT edit files before the worktree exists.**
|
|
317
319
|
Wrong: See problem → edit files → realize you're on main → ask about workflow
|
|
318
320
|
Wrong: Read files → understand problem → try to fix → get blocked → create work item
|
|
319
|
-
Right: User describes work → invoke request-routing →
|
|
321
|
+
Right: User describes work → invoke request-routing → complete workflow until \`work start\` creates worktree → then edit
|
|
320
322
|
|
|
321
323
|
## ⚠️ CRITICAL: Skills are MANDATORY for workflows
|
|
322
324
|
Skills auto-activate and MUST complete their full workflow:
|
package/lib/git-hooks/post-merge
CHANGED
|
@@ -48,8 +48,9 @@ function backupDatabase() {
|
|
|
48
48
|
|
|
49
49
|
fs.copyFileSync(sourcePath, backupPath);
|
|
50
50
|
|
|
51
|
-
// Stage the backup file
|
|
52
|
-
|
|
51
|
+
// Stage the backup file (force-add because .jettypod-backup is gitignored
|
|
52
|
+
// to prevent worktree symlink corruption, but backups must be committed)
|
|
53
|
+
execSync(`git add -f "${backupPath}"`, {
|
|
53
54
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
54
55
|
});
|
|
55
56
|
|
package/lib/git-hooks/pre-commit
CHANGED
|
@@ -43,7 +43,12 @@ function checkBranchRestriction() {
|
|
|
43
43
|
console.error(' 1. Undo your edits: git checkout .');
|
|
44
44
|
console.error(' 2. Start the workflow: Invoke request-routing skill');
|
|
45
45
|
console.error('');
|
|
46
|
-
console.error('
|
|
46
|
+
console.error('HOW IT WORKS:');
|
|
47
|
+
console.error(' request-routing → planning skill → mode skill → work start');
|
|
48
|
+
console.error('');
|
|
49
|
+
console.error(' The worktree is created when `jettypod work start` runs.');
|
|
50
|
+
console.error(' Planning skills are READ-ONLY investigation phases.');
|
|
51
|
+
console.error(' Only edit files AFTER the worktree exists.');
|
|
47
52
|
console.error('');
|
|
48
53
|
return false;
|
|
49
54
|
} catch (err) {
|
|
@@ -558,6 +558,17 @@ function updateStatus(id, status) {
|
|
|
558
558
|
|
|
559
559
|
// CRITICAL: Merge to main BEFORE cleanup
|
|
560
560
|
if (isGitRepo()) {
|
|
561
|
+
// Check if branch exists before attempting merge
|
|
562
|
+
const branchExists = execSync(`git branch --list ${worktree.branch_name}`, {
|
|
563
|
+
cwd: gitRoot,
|
|
564
|
+
encoding: 'utf8',
|
|
565
|
+
stdio: 'pipe'
|
|
566
|
+
}).trim();
|
|
567
|
+
|
|
568
|
+
if (!branchExists) {
|
|
569
|
+
// Branch is gone (likely already merged/deleted) - skip merge, just cleanup stale record
|
|
570
|
+
console.warn(`⚠️ Branch "${worktree.branch_name}" no longer exists - cleaning up stale record`);
|
|
571
|
+
} else {
|
|
561
572
|
try {
|
|
562
573
|
// Test merge to detect conflicts (without actually merging)
|
|
563
574
|
const mergeResult = execSync(`git merge --no-commit --no-ff ${worktree.branch_name}`, {
|
|
@@ -624,6 +635,7 @@ function updateStatus(id, status) {
|
|
|
624
635
|
console.warn(` Worktree preserved for manual resolution`);
|
|
625
636
|
throw new Error('Merge failed - worktree preserved');
|
|
626
637
|
}
|
|
638
|
+
} // end else (branch exists)
|
|
627
639
|
}
|
|
628
640
|
|
|
629
641
|
// Only cleanup after successful merge
|
package/package.json
CHANGED
|
@@ -17,6 +17,25 @@ description: Guide structured bug investigation with symptom capture, hypothesis
|
|
|
17
17
|
|
|
18
18
|
Guides Claude through systematic bug investigation. Produces a bug work item with clear breadcrumbs for implementation.
|
|
19
19
|
|
|
20
|
+
## ⚠️ READ-ONLY PHASE
|
|
21
|
+
|
|
22
|
+
**This skill is an investigation phase. No worktree exists yet.**
|
|
23
|
+
|
|
24
|
+
🚫 **FORBIDDEN during this skill:**
|
|
25
|
+
- Writing or editing any code files
|
|
26
|
+
- Creating new files
|
|
27
|
+
- Making implementation changes
|
|
28
|
+
- Adding temporary debugging code
|
|
29
|
+
|
|
30
|
+
✅ **ALLOWED during this skill:**
|
|
31
|
+
- Reading files to understand the codebase
|
|
32
|
+
- Running `jettypod` commands to create work items
|
|
33
|
+
- Running diagnostic commands (git log, grep, etc.)
|
|
34
|
+
- Asking the user questions
|
|
35
|
+
- Analyzing symptoms and forming hypotheses
|
|
36
|
+
|
|
37
|
+
**The worktree is created in Phase 6** when `jettypod work start` runs before invoking bug-mode.
|
|
38
|
+
|
|
20
39
|
## Instructions
|
|
21
40
|
|
|
22
41
|
When this skill is activated, you are investigating a bug to identify root cause and plan the fix.
|
|
@@ -7,6 +7,23 @@ description: Guide standalone chore planning with automatic type classification
|
|
|
7
7
|
|
|
8
8
|
Guides Claude through standalone chore planning including automatic type classification, loading type-specific guidance from the taxonomy, building enriched context, and routing to chore-mode for execution. For chores under **technical epics**, detects this ancestry and passes context to skip mode progression.
|
|
9
9
|
|
|
10
|
+
## ⚠️ READ-ONLY PHASE
|
|
11
|
+
|
|
12
|
+
**This skill is a planning/investigation phase. No worktree exists yet.**
|
|
13
|
+
|
|
14
|
+
🚫 **FORBIDDEN during this skill:**
|
|
15
|
+
- Writing or editing any code files
|
|
16
|
+
- Creating new files
|
|
17
|
+
- Making implementation changes
|
|
18
|
+
|
|
19
|
+
✅ **ALLOWED during this skill:**
|
|
20
|
+
- Reading files to understand the codebase
|
|
21
|
+
- Running `jettypod` commands to create work items
|
|
22
|
+
- Asking the user questions
|
|
23
|
+
- Analyzing and planning
|
|
24
|
+
|
|
25
|
+
**The worktree is created later** when chore-mode runs `jettypod work start`.
|
|
26
|
+
|
|
10
27
|
## Instructions
|
|
11
28
|
|
|
12
29
|
When this skill is activated, you are helping plan a standalone chore (one without a parent feature). Follow this structured approach:
|
|
@@ -7,6 +7,23 @@ description: Guide epic planning with feature brainstorming and optional archite
|
|
|
7
7
|
|
|
8
8
|
Guides Claude through comprehensive epic planning including feature identification and architectural decisions. For **technical epics**, skips feature brainstorming and creates chores directly.
|
|
9
9
|
|
|
10
|
+
## ⚠️ READ-ONLY PHASE
|
|
11
|
+
|
|
12
|
+
**This skill is a planning/investigation phase. No worktree exists yet.**
|
|
13
|
+
|
|
14
|
+
🚫 **FORBIDDEN during this skill:**
|
|
15
|
+
- Writing or editing any code files
|
|
16
|
+
- Creating new files
|
|
17
|
+
- Making implementation changes
|
|
18
|
+
|
|
19
|
+
✅ **ALLOWED during this skill:**
|
|
20
|
+
- Reading files to understand the codebase
|
|
21
|
+
- Running `jettypod` commands to create work items
|
|
22
|
+
- Asking the user questions
|
|
23
|
+
- Analyzing and planning
|
|
24
|
+
|
|
25
|
+
**The worktree is created later** when a mode skill (speed-mode, chore-mode, etc.) runs `jettypod work start`.
|
|
26
|
+
|
|
10
27
|
## Instructions
|
|
11
28
|
|
|
12
29
|
When this skill is activated, you are helping plan an epic. Follow this structured approach:
|
|
@@ -7,6 +7,27 @@ description: Guide feature planning with UX approach exploration and BDD scenari
|
|
|
7
7
|
|
|
8
8
|
Guides Claude through feature planning including UX approach exploration, optional prototyping, and BDD scenario generation.
|
|
9
9
|
|
|
10
|
+
## ⚠️ READ-ONLY UNTIL WORKTREE EXISTS
|
|
11
|
+
|
|
12
|
+
**This skill starts as a planning/investigation phase. No worktree exists initially.**
|
|
13
|
+
|
|
14
|
+
🚫 **FORBIDDEN until a worktree is created:**
|
|
15
|
+
- Writing or editing any code files
|
|
16
|
+
- Creating new files in the main repository
|
|
17
|
+
|
|
18
|
+
✅ **ALLOWED during planning phases:**
|
|
19
|
+
- Reading files to understand the codebase
|
|
20
|
+
- Running `jettypod` commands
|
|
21
|
+
- Asking the user questions
|
|
22
|
+
- Analyzing and planning
|
|
23
|
+
|
|
24
|
+
**Worktrees are created at specific steps:**
|
|
25
|
+
- `work prototype start` (Step 4) - for UX prototyping
|
|
26
|
+
- `work tests start` (Step 7) - for BDD test authoring
|
|
27
|
+
- `work start` (Step 13) - for chore implementation (invokes speed-mode)
|
|
28
|
+
|
|
29
|
+
**Only write files AFTER the relevant worktree command succeeds.**
|
|
30
|
+
|
|
10
31
|
## Instructions
|
|
11
32
|
|
|
12
33
|
When this skill is activated, you are helping discover the best approach for a feature. Follow this structured approach:
|