jettypod 4.4.43 → 4.4.45
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.
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
1
4
|
import Link from 'next/link';
|
|
2
5
|
import type { WorkItem, InFlightItem, KanbanGroup } from '@/lib/db';
|
|
3
6
|
|
|
@@ -21,34 +24,74 @@ interface KanbanCardProps {
|
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
function KanbanCard({ item, epicTitle, showEpic = false }: KanbanCardProps) {
|
|
27
|
+
const [expanded, setExpanded] = useState(false);
|
|
28
|
+
const hasChores = item.chores && item.chores.length > 0;
|
|
29
|
+
|
|
24
30
|
return (
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
<div className="flex
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
{modeLabels[item.mode].
|
|
37
|
-
|
|
31
|
+
<div className="bg-white dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 hover:border-zinc-300 dark:hover:border-zinc-600 hover:shadow-sm transition-all">
|
|
32
|
+
<Link
|
|
33
|
+
href={`/work/${item.id}`}
|
|
34
|
+
className="block p-3"
|
|
35
|
+
>
|
|
36
|
+
<div className="flex items-start gap-2">
|
|
37
|
+
<span className="text-sm flex-shrink-0">{typeIcons[item.type] || '📄'}</span>
|
|
38
|
+
<div className="flex-1 min-w-0">
|
|
39
|
+
<div className="flex items-center gap-2 mb-1 flex-wrap">
|
|
40
|
+
<span className="text-xs text-zinc-400 font-mono">#{item.id}</span>
|
|
41
|
+
{item.mode && modeLabels[item.mode] && (
|
|
42
|
+
<span className={`text-xs px-1.5 py-0.5 rounded ${modeLabels[item.mode].color}`}>
|
|
43
|
+
{modeLabels[item.mode].label}
|
|
44
|
+
</span>
|
|
45
|
+
)}
|
|
46
|
+
</div>
|
|
47
|
+
<p className="text-sm text-zinc-900 dark:text-zinc-100 leading-snug">
|
|
48
|
+
{item.title}
|
|
49
|
+
</p>
|
|
50
|
+
{showEpic && epicTitle && (
|
|
51
|
+
<p className="text-xs text-zinc-500 dark:text-zinc-400 mt-1.5 flex items-center gap-1">
|
|
52
|
+
<span>🎯</span>
|
|
53
|
+
<span>{epicTitle}</span>
|
|
54
|
+
</p>
|
|
38
55
|
)}
|
|
39
56
|
</div>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
57
|
+
</div>
|
|
58
|
+
</Link>
|
|
59
|
+
{hasChores && (
|
|
60
|
+
<div className="border-t border-zinc-200 dark:border-zinc-700">
|
|
61
|
+
<button
|
|
62
|
+
onClick={() => setExpanded(!expanded)}
|
|
63
|
+
className="w-full px-3 py-1.5 flex items-center gap-1.5 text-xs text-zinc-600 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-zinc-700/50 transition-colors"
|
|
64
|
+
>
|
|
65
|
+
<span>{expanded ? '▼' : '▶'}</span>
|
|
66
|
+
<span>🔧</span>
|
|
67
|
+
<span>{item.chores!.length} chore{item.chores!.length !== 1 ? 's' : ''}</span>
|
|
68
|
+
</button>
|
|
69
|
+
{expanded && (
|
|
70
|
+
<div className="px-3 pb-2 space-y-1">
|
|
71
|
+
{item.chores!.map((chore) => (
|
|
72
|
+
<Link
|
|
73
|
+
key={chore.id}
|
|
74
|
+
href={`/work/${chore.id}`}
|
|
75
|
+
className="block py-1 px-2 text-xs rounded hover:bg-zinc-100 dark:hover:bg-zinc-700 transition-colors"
|
|
76
|
+
>
|
|
77
|
+
<div className="flex items-center gap-2">
|
|
78
|
+
<span className="text-zinc-400 font-mono">#{chore.id}</span>
|
|
79
|
+
{chore.mode && modeLabels[chore.mode] && (
|
|
80
|
+
<span className={`px-1 py-0.5 rounded text-[10px] ${modeLabels[chore.mode].color}`}>
|
|
81
|
+
{modeLabels[chore.mode].label}
|
|
82
|
+
</span>
|
|
83
|
+
)}
|
|
84
|
+
<span className="text-zinc-700 dark:text-zinc-300 truncate">
|
|
85
|
+
{chore.title || <span className="text-zinc-400 italic">(Untitled)</span>}
|
|
86
|
+
</span>
|
|
87
|
+
</div>
|
|
88
|
+
</Link>
|
|
89
|
+
))}
|
|
90
|
+
</div>
|
|
48
91
|
)}
|
|
49
92
|
</div>
|
|
50
|
-
|
|
51
|
-
</
|
|
93
|
+
)}
|
|
94
|
+
</div>
|
|
52
95
|
);
|
|
53
96
|
}
|
|
54
97
|
|
package/apps/dashboard/lib/db.ts
CHANGED
|
@@ -20,6 +20,7 @@ export interface WorkItem {
|
|
|
20
20
|
completed_at: string | null;
|
|
21
21
|
created_at: string;
|
|
22
22
|
children?: WorkItem[];
|
|
23
|
+
chores?: WorkItem[];
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export interface Decision {
|
|
@@ -203,6 +204,26 @@ export function getKanbanData(doneLimit: number = 50): KanbanData {
|
|
|
203
204
|
`).all() as { id: number; title: string }[];
|
|
204
205
|
const epicMap = new Map(epics.map(e => [e.id, e.title]));
|
|
205
206
|
|
|
207
|
+
// Get all chores that belong to features (for chore expansion)
|
|
208
|
+
const featureChores = db.prepare(`
|
|
209
|
+
SELECT c.id, c.type, c.title, c.description, c.status, c.parent_id, c.epic_id,
|
|
210
|
+
c.branch_name, c.mode, c.phase, c.completed_at, c.created_at
|
|
211
|
+
FROM work_items c
|
|
212
|
+
INNER JOIN work_items f ON c.parent_id = f.id
|
|
213
|
+
WHERE c.type = 'chore' AND f.type = 'feature'
|
|
214
|
+
ORDER BY c.id
|
|
215
|
+
`).all() as WorkItem[];
|
|
216
|
+
|
|
217
|
+
// Group chores by parent feature ID
|
|
218
|
+
const choresByFeature = new Map<number, WorkItem[]>();
|
|
219
|
+
for (const chore of featureChores) {
|
|
220
|
+
if (chore.parent_id) {
|
|
221
|
+
const existing = choresByFeature.get(chore.parent_id) || [];
|
|
222
|
+
existing.push(chore);
|
|
223
|
+
choresByFeature.set(chore.parent_id, existing);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
206
227
|
// Get kanban-eligible items:
|
|
207
228
|
// - Features (type = 'feature')
|
|
208
229
|
// - Chores that are NOT children of features (parent is null, or parent is an epic)
|
|
@@ -241,6 +262,11 @@ export function getKanbanData(doneLimit: number = 50): KanbanData {
|
|
|
241
262
|
// Strip parent_type from the item
|
|
242
263
|
const { parent_type, ...cleanItem } = item;
|
|
243
264
|
|
|
265
|
+
// Attach chores to features
|
|
266
|
+
if (cleanItem.type === 'feature') {
|
|
267
|
+
cleanItem.chores = choresByFeature.get(cleanItem.id) || [];
|
|
268
|
+
}
|
|
269
|
+
|
|
244
270
|
if (cleanItem.status === 'in_progress') {
|
|
245
271
|
const epicId = cleanItem.parent_id || cleanItem.epic_id;
|
|
246
272
|
const epicTitle = epicId ? epicMap.get(epicId) || null : null;
|
package/package.json
CHANGED
|
@@ -703,24 +703,26 @@ Sound good? I'll create these chores once you confirm.
|
|
|
703
703
|
|
|
704
704
|
**After user confirms, execute autonomously:**
|
|
705
705
|
|
|
706
|
-
**1.
|
|
706
|
+
**1. Set feature mode to stable FIRST:**
|
|
707
707
|
|
|
708
708
|
```bash
|
|
709
|
-
jettypod work
|
|
709
|
+
jettypod work set-mode <feature-id> stable
|
|
710
710
|
```
|
|
711
711
|
|
|
712
|
-
|
|
712
|
+
**🛑 CRITICAL:** You MUST set mode to stable BEFORE creating chores. The system blocks chore creation while feature is in speed mode.
|
|
713
713
|
|
|
714
|
-
**2.
|
|
714
|
+
**2. Create stable mode chores:**
|
|
715
715
|
|
|
716
716
|
```bash
|
|
717
|
-
jettypod work
|
|
717
|
+
jettypod work create chore "[Chore title]" "[Description with scenarios addressed]" --parent=<feature-id>
|
|
718
718
|
```
|
|
719
719
|
|
|
720
|
-
|
|
720
|
+
Repeat for each confirmed chore. **Do NOT use `--mode` flag** - chores inherit mode from their parent feature.
|
|
721
|
+
|
|
722
|
+
**3. Release merge lock:**
|
|
721
723
|
|
|
722
724
|
```bash
|
|
723
|
-
jettypod work
|
|
725
|
+
jettypod work merge --release-lock
|
|
724
726
|
```
|
|
725
727
|
|
|
726
728
|
#### Step 7E: Start First Stable Chore and Invoke Stable Mode Skill
|
|
@@ -730,9 +732,11 @@ jettypod work set-mode <feature-id> stable
|
|
|
730
732
|
**1. Get the first stable chore:**
|
|
731
733
|
|
|
732
734
|
```bash
|
|
733
|
-
sqlite3 .jettypod/work.db "SELECT id, title FROM work_items WHERE parent_id = <feature-id> AND type = 'chore' AND
|
|
735
|
+
sqlite3 .jettypod/work.db "SELECT id, title FROM work_items WHERE parent_id = <feature-id> AND type = 'chore' AND status != 'done' ORDER BY created_at LIMIT 1"
|
|
734
736
|
```
|
|
735
737
|
|
|
738
|
+
Note: Chores don't have a `mode` column - they inherit context from their parent feature. Since we just set the feature to stable mode, these are the stable chores.
|
|
739
|
+
|
|
736
740
|
**2. Start the first stable chore:**
|
|
737
741
|
|
|
738
742
|
```bash
|
|
@@ -822,15 +826,16 @@ jettypod work merge --release-lock # Release held lock
|
|
|
822
826
|
jettypod work start <chore-id> # Create worktree and start chore
|
|
823
827
|
```
|
|
824
828
|
|
|
825
|
-
**
|
|
829
|
+
**Set feature mode (BEFORE creating chores):**
|
|
826
830
|
```bash
|
|
827
|
-
jettypod work
|
|
831
|
+
jettypod work set-mode <feature-id> stable
|
|
828
832
|
```
|
|
829
833
|
|
|
830
|
-
**
|
|
834
|
+
**Create chores (AFTER setting mode):**
|
|
831
835
|
```bash
|
|
832
|
-
jettypod work
|
|
836
|
+
jettypod work create chore "<title>" "<description>" --parent=<feature-id>
|
|
833
837
|
```
|
|
838
|
+
Note: Do NOT use `--mode` flag - chores inherit mode from parent feature.
|
|
834
839
|
|
|
835
840
|
**❌ DO NOT use these to complete chores:**
|
|
836
841
|
- `jettypod work status <id> done`
|