jettypod 4.4.116 → 4.4.120
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/.env +7 -0
- package/apps/dashboard/app/api/claude/[workItemId]/message/route.ts +124 -48
- package/apps/dashboard/app/api/claude/[workItemId]/route.ts +171 -58
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/message/route.ts +161 -10
- package/apps/dashboard/app/api/tests/run/stream/route.ts +13 -1
- package/apps/dashboard/app/api/usage/route.ts +17 -0
- package/apps/dashboard/app/api/work/[id]/route.ts +35 -0
- package/apps/dashboard/app/api/work/[id]/status/route.ts +43 -1
- package/apps/dashboard/app/connect-claude/page.tsx +24 -0
- package/apps/dashboard/app/decision/[id]/page.tsx +14 -14
- package/apps/dashboard/app/demo/gates/page.tsx +42 -42
- package/apps/dashboard/app/design-system/page.tsx +868 -0
- package/apps/dashboard/app/globals.css +6 -2
- package/apps/dashboard/app/install-claude/page.tsx +9 -7
- package/apps/dashboard/app/layout.tsx +17 -5
- package/apps/dashboard/app/login/page.tsx +250 -0
- package/apps/dashboard/app/page.tsx +11 -9
- package/apps/dashboard/app/settings/page.tsx +4 -2
- package/apps/dashboard/app/signup/page.tsx +245 -0
- package/apps/dashboard/app/subscribe/page.tsx +11 -0
- package/apps/dashboard/app/welcome/page.tsx +24 -1
- package/apps/dashboard/app/work/[id]/page.tsx +34 -50
- package/apps/dashboard/components/AppShell.tsx +95 -55
- package/apps/dashboard/components/CardMenu.tsx +56 -13
- package/apps/dashboard/components/ClaudePanel.tsx +301 -582
- package/apps/dashboard/components/ClaudePanelInput.tsx +23 -14
- package/apps/dashboard/components/ConnectClaudeScreen.tsx +210 -0
- package/apps/dashboard/components/CopyableId.tsx +3 -3
- package/apps/dashboard/components/DetailReviewActions.tsx +109 -0
- package/apps/dashboard/components/DragContext.tsx +75 -65
- package/apps/dashboard/components/DraggableCard.tsx +6 -46
- package/apps/dashboard/components/DropZone.tsx +2 -2
- package/apps/dashboard/components/EditableDetailDescription.tsx +1 -1
- package/apps/dashboard/components/EditableTitle.tsx +26 -6
- package/apps/dashboard/components/ElapsedTimer.tsx +54 -0
- package/apps/dashboard/components/EpicGroup.tsx +329 -0
- package/apps/dashboard/components/GateCard.tsx +100 -16
- package/apps/dashboard/components/GateChoiceCard.tsx +15 -17
- package/apps/dashboard/components/InstallClaudeScreen.tsx +140 -51
- package/apps/dashboard/components/JettyLoader.tsx +38 -0
- package/apps/dashboard/components/KanbanBoard.tsx +147 -766
- package/apps/dashboard/components/KanbanCard.tsx +506 -0
- package/apps/dashboard/components/LazyMarkdown.tsx +12 -0
- package/apps/dashboard/components/MainNav.tsx +20 -54
- package/apps/dashboard/components/MessageBlock.tsx +391 -0
- package/apps/dashboard/components/ModeStartCard.tsx +15 -15
- package/apps/dashboard/components/OnboardingWelcome.tsx +214 -0
- package/apps/dashboard/components/PlaceholderCard.tsx +11 -21
- package/apps/dashboard/components/ProjectSwitcher.tsx +36 -8
- package/apps/dashboard/components/PrototypeTimeline.tsx +25 -25
- package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +265 -301
- package/apps/dashboard/components/RealTimeTestsWrapper.tsx +97 -74
- package/apps/dashboard/components/ReviewFooter.tsx +141 -0
- package/apps/dashboard/components/SessionList.tsx +19 -18
- package/apps/dashboard/components/SubscribeContent.tsx +206 -0
- package/apps/dashboard/components/TestTree.tsx +15 -14
- package/apps/dashboard/components/TipCard.tsx +177 -0
- package/apps/dashboard/components/Toast.tsx +5 -5
- package/apps/dashboard/components/TypeIcon.tsx +56 -0
- package/apps/dashboard/components/UpgradeBanner.tsx +30 -0
- package/apps/dashboard/components/WaveCompletionAnimation.tsx +61 -62
- package/apps/dashboard/components/WelcomeScreen.tsx +25 -27
- package/apps/dashboard/components/WorkItemHeader.tsx +4 -4
- package/apps/dashboard/components/WorkItemTree.tsx +9 -28
- package/apps/dashboard/components/settings/AccountSection.tsx +169 -0
- package/apps/dashboard/components/settings/EnvVarsSection.tsx +54 -79
- package/apps/dashboard/components/settings/GeneralSection.tsx +26 -31
- package/apps/dashboard/components/settings/SettingsLayout.tsx +4 -4
- package/apps/dashboard/components/ui/Button.tsx +104 -0
- package/apps/dashboard/components/ui/Input.tsx +78 -0
- package/apps/dashboard/contexts/ClaudeSessionContext.tsx +408 -105
- package/apps/dashboard/contexts/ConnectionStatusContext.tsx +25 -4
- package/apps/dashboard/contexts/UsageContext.tsx +155 -0
- package/apps/dashboard/contexts/usageHelpers.js +9 -0
- package/apps/dashboard/electron/ipc-handlers.js +281 -88
- package/apps/dashboard/electron/main.js +691 -131
- package/apps/dashboard/electron/preload.js +25 -4
- package/apps/dashboard/electron/session-manager.js +163 -0
- package/apps/dashboard/electron-builder.config.js +3 -5
- package/apps/dashboard/hooks/useKanbanAnimation.ts +29 -0
- package/apps/dashboard/hooks/useKanbanUndo.ts +83 -0
- package/apps/dashboard/lib/backlog-parser.ts +50 -0
- package/apps/dashboard/lib/claude-process-manager.ts +50 -11
- package/apps/dashboard/lib/constants.ts +43 -0
- package/apps/dashboard/lib/db-bridge.ts +33 -0
- package/apps/dashboard/lib/db.ts +136 -20
- package/apps/dashboard/lib/kanban-utils.ts +70 -0
- package/apps/dashboard/lib/run-migrations.js +27 -2
- package/apps/dashboard/lib/session-state-machine.ts +3 -0
- package/apps/dashboard/lib/session-stream-manager.ts +144 -38
- package/apps/dashboard/lib/shadows.ts +7 -0
- package/apps/dashboard/lib/tests.ts +3 -1
- package/apps/dashboard/lib/utils.ts +6 -0
- package/apps/dashboard/next.config.js +35 -14
- package/apps/dashboard/package.json +6 -3
- package/apps/dashboard/public/bug-icon.svg +9 -0
- package/apps/dashboard/public/buoy-icon.svg +9 -0
- package/apps/dashboard/public/fonts/Satoshi-Variable.woff2 +0 -0
- package/apps/dashboard/public/fonts/Satoshi-VariableItalic.woff2 +0 -0
- package/apps/dashboard/public/in-flight-seagull.svg +9 -0
- package/apps/dashboard/public/jetty-icon-loading-alt.svg +11 -0
- package/apps/dashboard/public/jetty-icon-loading.svg +11 -0
- package/apps/dashboard/public/jettypod_logo.png +0 -0
- package/apps/dashboard/public/pier-icon.svg +14 -0
- package/apps/dashboard/public/star-icon.svg +9 -0
- package/apps/dashboard/public/wrench-icon.svg +9 -0
- package/apps/dashboard/scripts/upload-to-r2.js +89 -0
- package/apps/dashboard/scripts/ws-server.js +191 -0
- package/apps/dashboard/tsconfig.tsbuildinfo +1 -0
- package/apps/update-server/package.json +16 -0
- package/apps/update-server/schema.sql +31 -0
- package/apps/update-server/src/index.ts +1085 -0
- package/apps/update-server/tsconfig.json +16 -0
- package/apps/update-server/wrangler.toml +35 -0
- package/cucumber.js +9 -3
- package/docs/COMMAND_REFERENCE.md +34 -0
- package/hooks/post-checkout +32 -75
- package/hooks/post-merge +111 -10
- package/jest.setup.js +1 -0
- package/jettypod.js +54 -116
- package/lib/chore-taxonomy.js +33 -10
- package/lib/database.js +36 -16
- package/lib/db-watcher.js +1 -1
- package/lib/git-hooks/pre-commit +1 -1
- package/lib/jettypod-backup.js +27 -4
- package/lib/migrations/027-plan-at-creation-column.js +33 -0
- package/lib/migrations/028-ready-for-review-column.js +27 -0
- package/lib/migrations/029-remove-autoincrement.js +307 -0
- package/lib/migrations/029-rename-corrupted-to-cleaned.js +149 -0
- package/lib/migrations/index.js +47 -4
- package/lib/schema.js +13 -6
- package/lib/seed-onboarding.js +101 -69
- package/lib/update-command/index.js +9 -175
- package/lib/work-commands/index.js +129 -16
- package/lib/work-tracking/index.js +86 -46
- package/lib/worktree-diagnostics.js +16 -16
- package/lib/worktree-facade.js +1 -1
- package/lib/worktree-manager.js +8 -8
- package/lib/worktree-reconciler.js +5 -5
- package/package.json +9 -2
- package/scripts/ndjson-to-cucumber-json.js +152 -0
- package/scripts/postinstall.js +25 -0
- package/skills-templates/bug-mode/SKILL.md +39 -28
- package/skills-templates/bug-planning/SKILL.md +25 -29
- package/skills-templates/chore-mode/SKILL.md +131 -68
- package/skills-templates/chore-mode/verification.js +51 -10
- package/skills-templates/chore-planning/SKILL.md +47 -18
- package/skills-templates/epic-planning/SKILL.md +68 -48
- package/skills-templates/external-transition/SKILL.md +47 -47
- package/skills-templates/feature-planning/SKILL.md +83 -73
- package/skills-templates/production-mode/SKILL.md +49 -49
- package/skills-templates/request-routing/SKILL.md +27 -14
- package/skills-templates/simple-improvement/SKILL.md +68 -44
- package/skills-templates/speed-mode/SKILL.md +209 -128
- package/skills-templates/stable-mode/SKILL.md +105 -94
- package/templates/bdd-guidance.md +139 -0
- package/templates/bdd-scaffolding/wait.js +18 -0
- package/templates/bdd-scaffolding/world.js +19 -0
- package/.jettypod-backup/work.db +0 -0
- package/apps/dashboard/app/access-code/page.tsx +0 -110
- package/lib/discovery-checkpoint.js +0 -123
- package/skills-templates/project-discovery/SKILL.md +0 -372
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: Rename worktree status 'corrupted' to 'cleaned'
|
|
3
|
+
*
|
|
4
|
+
* The 'corrupted' status was misleading — it's the terminal state for worktrees
|
|
5
|
+
* that have been successfully cleaned up, not actually corrupted data.
|
|
6
|
+
* Renaming to 'cleaned' for clarity.
|
|
7
|
+
*
|
|
8
|
+
* SQLite doesn't support ALTER CONSTRAINT, so we recreate the table.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
id: '029-rename-corrupted-to-cleaned',
|
|
13
|
+
description: 'Rename worktree status corrupted to cleaned for clarity',
|
|
14
|
+
|
|
15
|
+
async up(db) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
db.serialize(() => {
|
|
18
|
+
// 1. Update existing records
|
|
19
|
+
db.run(`UPDATE worktrees SET status = 'cleaned' WHERE status = 'corrupted'`, (err) => {
|
|
20
|
+
if (err) return reject(err);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// 2. Create new table with updated constraint
|
|
24
|
+
db.run(`
|
|
25
|
+
CREATE TABLE IF NOT EXISTS worktrees_new (
|
|
26
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
27
|
+
work_item_id INTEGER NOT NULL,
|
|
28
|
+
branch_name TEXT NOT NULL,
|
|
29
|
+
worktree_path TEXT NOT NULL,
|
|
30
|
+
status TEXT NOT NULL CHECK(status IN ('active', 'merging', 'merged', 'cleanup_pending', 'cleaned')),
|
|
31
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
32
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
33
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
34
|
+
)
|
|
35
|
+
`, (err) => {
|
|
36
|
+
if (err) return reject(err);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// 3. Copy data
|
|
40
|
+
db.run(`
|
|
41
|
+
INSERT INTO worktrees_new (id, work_item_id, branch_name, worktree_path, status, created_at, updated_at)
|
|
42
|
+
SELECT id, work_item_id, branch_name, worktree_path, status, created_at, updated_at
|
|
43
|
+
FROM worktrees
|
|
44
|
+
`, (err) => {
|
|
45
|
+
if (err) return reject(err);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// 4. Drop old indexes
|
|
49
|
+
db.run('DROP INDEX IF EXISTS idx_worktrees_work_item_id', (err) => {
|
|
50
|
+
if (err) return reject(err);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
db.run('DROP INDEX IF EXISTS idx_worktrees_status', (err) => {
|
|
54
|
+
if (err) return reject(err);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// 5. Drop old table
|
|
58
|
+
db.run('DROP TABLE worktrees', (err) => {
|
|
59
|
+
if (err) return reject(err);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 6. Rename new table
|
|
63
|
+
db.run('ALTER TABLE worktrees_new RENAME TO worktrees', (err) => {
|
|
64
|
+
if (err) return reject(err);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// 7. Recreate indexes
|
|
68
|
+
db.run(`
|
|
69
|
+
CREATE INDEX idx_worktrees_work_item_id
|
|
70
|
+
ON worktrees(work_item_id)
|
|
71
|
+
`, (err) => {
|
|
72
|
+
if (err) return reject(err);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
db.run(`
|
|
76
|
+
CREATE INDEX idx_worktrees_status
|
|
77
|
+
ON worktrees(status)
|
|
78
|
+
`, (err) => {
|
|
79
|
+
if (err) return reject(err);
|
|
80
|
+
resolve();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
async down(db) {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
db.serialize(() => {
|
|
89
|
+
db.run(`UPDATE worktrees SET status = 'corrupted' WHERE status = 'cleaned'`, (err) => {
|
|
90
|
+
if (err) return reject(err);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
db.run(`
|
|
94
|
+
CREATE TABLE IF NOT EXISTS worktrees_new (
|
|
95
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
96
|
+
work_item_id INTEGER NOT NULL,
|
|
97
|
+
branch_name TEXT NOT NULL,
|
|
98
|
+
worktree_path TEXT NOT NULL,
|
|
99
|
+
status TEXT NOT NULL CHECK(status IN ('active', 'merging', 'merged', 'cleanup_pending', 'corrupted')),
|
|
100
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
101
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
102
|
+
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
103
|
+
)
|
|
104
|
+
`, (err) => {
|
|
105
|
+
if (err) return reject(err);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
db.run(`
|
|
109
|
+
INSERT INTO worktrees_new (id, work_item_id, branch_name, worktree_path, status, created_at, updated_at)
|
|
110
|
+
SELECT id, work_item_id, branch_name, worktree_path, status, created_at, updated_at
|
|
111
|
+
FROM worktrees
|
|
112
|
+
`, (err) => {
|
|
113
|
+
if (err) return reject(err);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
db.run('DROP INDEX IF EXISTS idx_worktrees_work_item_id', (err) => {
|
|
117
|
+
if (err) return reject(err);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
db.run('DROP INDEX IF EXISTS idx_worktrees_status', (err) => {
|
|
121
|
+
if (err) return reject(err);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
db.run('DROP TABLE worktrees', (err) => {
|
|
125
|
+
if (err) return reject(err);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
db.run('ALTER TABLE worktrees_new RENAME TO worktrees', (err) => {
|
|
129
|
+
if (err) return reject(err);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
db.run(`
|
|
133
|
+
CREATE INDEX idx_worktrees_work_item_id
|
|
134
|
+
ON worktrees(work_item_id)
|
|
135
|
+
`, (err) => {
|
|
136
|
+
if (err) return reject(err);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
db.run(`
|
|
140
|
+
CREATE INDEX idx_worktrees_status
|
|
141
|
+
ON worktrees(status)
|
|
142
|
+
`, (err) => {
|
|
143
|
+
if (err) return reject(err);
|
|
144
|
+
resolve();
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
};
|
package/lib/migrations/index.js
CHANGED
|
@@ -20,11 +20,16 @@ async function runMigrations(db) {
|
|
|
20
20
|
});
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
db.
|
|
23
|
+
// Create _meta table for schema versioning
|
|
24
|
+
await new Promise((resolve, reject) => {
|
|
25
|
+
db.run(`
|
|
26
|
+
CREATE TABLE IF NOT EXISTS _meta (
|
|
27
|
+
key TEXT PRIMARY KEY,
|
|
28
|
+
value TEXT
|
|
29
|
+
)
|
|
30
|
+
`, (err) => {
|
|
26
31
|
if (err) reject(err);
|
|
27
|
-
else resolve(
|
|
32
|
+
else resolve();
|
|
28
33
|
});
|
|
29
34
|
});
|
|
30
35
|
|
|
@@ -34,6 +39,32 @@ async function runMigrations(db) {
|
|
|
34
39
|
.filter(f => f.endsWith('.js') && f !== 'index.js' && !f.endsWith('.test.js'))
|
|
35
40
|
.sort();
|
|
36
41
|
|
|
42
|
+
const knownMigrationCount = files.length;
|
|
43
|
+
|
|
44
|
+
// Check schema version for forward-compatibility
|
|
45
|
+
const currentVersion = await new Promise((resolve, reject) => {
|
|
46
|
+
db.get('SELECT value FROM _meta WHERE key = ?', ['schema_version'], (err, row) => {
|
|
47
|
+
if (err) reject(err);
|
|
48
|
+
else resolve(row ? parseInt(row.value, 10) : 0);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (currentVersion > knownMigrationCount) {
|
|
53
|
+
console.warn(
|
|
54
|
+
`⚠️ Database schema version (${currentVersion}) is newer than this version of JettyPod supports (${knownMigrationCount}).` +
|
|
55
|
+
'\n Please update JettyPod to avoid compatibility issues.'
|
|
56
|
+
);
|
|
57
|
+
return; // Don't run migrations — the DB is from a newer version
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Get list of applied migrations
|
|
61
|
+
const applied = await new Promise((resolve, reject) => {
|
|
62
|
+
db.all('SELECT id FROM migrations', [], (err, rows) => {
|
|
63
|
+
if (err) reject(err);
|
|
64
|
+
else resolve(rows.map(r => r.id));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
37
68
|
// Run pending migrations
|
|
38
69
|
for (const file of files) {
|
|
39
70
|
const migration = require(path.join(migrationsDir, file));
|
|
@@ -69,6 +100,18 @@ async function runMigrations(db) {
|
|
|
69
100
|
throw err;
|
|
70
101
|
}
|
|
71
102
|
}
|
|
103
|
+
|
|
104
|
+
// Update schema version to reflect current state
|
|
105
|
+
await new Promise((resolve, reject) => {
|
|
106
|
+
db.run(
|
|
107
|
+
'INSERT OR REPLACE INTO _meta (key, value) VALUES (?, ?)',
|
|
108
|
+
['schema_version', String(knownMigrationCount)],
|
|
109
|
+
(err) => {
|
|
110
|
+
if (err) reject(err);
|
|
111
|
+
else resolve();
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
});
|
|
72
115
|
}
|
|
73
116
|
|
|
74
117
|
module.exports = { runMigrations };
|
package/lib/schema.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
const SCHEMA_SQL = `
|
|
13
13
|
CREATE TABLE IF NOT EXISTS work_items (
|
|
14
|
-
id INTEGER PRIMARY KEY
|
|
14
|
+
id INTEGER PRIMARY KEY,
|
|
15
15
|
type TEXT NOT NULL,
|
|
16
16
|
title TEXT NOT NULL,
|
|
17
17
|
description TEXT,
|
|
@@ -30,7 +30,9 @@ const SCHEMA_SQL = `
|
|
|
30
30
|
scenario_file TEXT,
|
|
31
31
|
completed_at TEXT,
|
|
32
32
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
33
|
-
display_order INTEGER DEFAULT NULL
|
|
33
|
+
display_order INTEGER DEFAULT NULL,
|
|
34
|
+
conversational INTEGER DEFAULT 0,
|
|
35
|
+
plan_at_creation TEXT DEFAULT NULL
|
|
34
36
|
);
|
|
35
37
|
|
|
36
38
|
CREATE TABLE IF NOT EXISTS project_config (
|
|
@@ -40,7 +42,7 @@ const SCHEMA_SQL = `
|
|
|
40
42
|
);
|
|
41
43
|
|
|
42
44
|
CREATE TABLE IF NOT EXISTS external_readiness_checklist (
|
|
43
|
-
id INTEGER PRIMARY KEY
|
|
45
|
+
id INTEGER PRIMARY KEY,
|
|
44
46
|
category TEXT NOT NULL,
|
|
45
47
|
item_key TEXT NOT NULL,
|
|
46
48
|
title TEXT NOT NULL,
|
|
@@ -51,7 +53,7 @@ const SCHEMA_SQL = `
|
|
|
51
53
|
);
|
|
52
54
|
|
|
53
55
|
CREATE TABLE IF NOT EXISTS discovery_decisions (
|
|
54
|
-
id INTEGER PRIMARY KEY
|
|
56
|
+
id INTEGER PRIMARY KEY,
|
|
55
57
|
work_item_id INTEGER NOT NULL,
|
|
56
58
|
aspect TEXT NOT NULL,
|
|
57
59
|
decision TEXT NOT NULL,
|
|
@@ -61,7 +63,7 @@ const SCHEMA_SQL = `
|
|
|
61
63
|
);
|
|
62
64
|
|
|
63
65
|
CREATE TABLE IF NOT EXISTS skill_executions (
|
|
64
|
-
id INTEGER PRIMARY KEY
|
|
66
|
+
id INTEGER PRIMARY KEY,
|
|
65
67
|
work_item_id INTEGER NOT NULL,
|
|
66
68
|
skill_name TEXT NOT NULL,
|
|
67
69
|
status TEXT DEFAULT 'in_progress',
|
|
@@ -73,13 +75,18 @@ const SCHEMA_SQL = `
|
|
|
73
75
|
);
|
|
74
76
|
|
|
75
77
|
CREATE TABLE IF NOT EXISTS workflow_gates (
|
|
76
|
-
id INTEGER PRIMARY KEY
|
|
78
|
+
id INTEGER PRIMARY KEY,
|
|
77
79
|
work_item_id INTEGER NOT NULL,
|
|
78
80
|
gate_name TEXT NOT NULL,
|
|
79
81
|
passed_at DATETIME,
|
|
80
82
|
UNIQUE(work_item_id, gate_name),
|
|
81
83
|
FOREIGN KEY (work_item_id) REFERENCES work_items(id)
|
|
82
84
|
);
|
|
85
|
+
|
|
86
|
+
CREATE TABLE IF NOT EXISTS _meta (
|
|
87
|
+
key TEXT PRIMARY KEY,
|
|
88
|
+
value TEXT
|
|
89
|
+
);
|
|
83
90
|
`;
|
|
84
91
|
|
|
85
92
|
/**
|
package/lib/seed-onboarding.js
CHANGED
|
@@ -7,122 +7,154 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const ONBOARDING_EPIC = {
|
|
10
|
-
title: 'Project
|
|
10
|
+
title: 'Project Planning',
|
|
11
11
|
description: 'Get your project set up and planned. Work through these chores one at a time — each one is a short conversation.'
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
const TONE = `HOW TO RESPOND:
|
|
15
|
+
You are a collaborator, not an AI assistant. You're a knowledgeable partner having a real conversation.
|
|
16
|
+
- Do NOT open with a summary or recap of what you've been asked to do
|
|
17
|
+
- Do NOT list steps or outline a plan before starting
|
|
18
|
+
- Do NOT say "I'll help you with...", "Let me guide you through...", or "Great question!"
|
|
19
|
+
- Do NOT use headers, bullet lists, or formatted option blocks in your first message
|
|
20
|
+
- DO jump straight into conversation like a person who just sat down across the table
|
|
21
|
+
- DO ask one question at a time, then actually listen to the response
|
|
22
|
+
- DO keep responses short — a few sentences, not paragraphs
|
|
23
|
+
- DO respond naturally to what the user says, adapting your follow-ups accordingly`;
|
|
24
|
+
|
|
14
25
|
const ONBOARDING_CHORES = [
|
|
15
26
|
{
|
|
16
27
|
title: 'Align on the user journey',
|
|
17
|
-
description:
|
|
28
|
+
description: `${TONE}
|
|
29
|
+
|
|
30
|
+
YOUR ROLE: Help the user articulate what their product does and how people will use it.
|
|
18
31
|
|
|
19
|
-
|
|
20
|
-
Open with: "What do users DO in this product? Walk me through the flow from opening it to getting value from it."
|
|
32
|
+
CONVERSATION FLOW:
|
|
21
33
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- Journey 3: [Advanced/Guided approach] with flow, pros, cons
|
|
34
|
+
1. OPEN — Jump straight in. Something like:
|
|
35
|
+
"So — what are you building? Walk me through what happens when someone opens this thing up."
|
|
36
|
+
(Adapt to context. Be casual. No preamble.)
|
|
26
37
|
|
|
27
|
-
|
|
38
|
+
2. LISTEN AND DIG — Based on what they say, ask natural follow-ups. One at a time:
|
|
39
|
+
- "And then what? What happens after [thing they mentioned]?"
|
|
40
|
+
- "How do they find that the first time?"
|
|
41
|
+
- "What's the moment where it clicks — like, this is actually useful?"
|
|
42
|
+
Don't rush this. Let them think. Some people need 2-3 exchanges to articulate their vision.
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
44
|
+
3. IF THEY'RE VAGUE — Don't present a formatted options list. Talk through possibilities naturally:
|
|
45
|
+
"I could see this going a few ways. You could do [approach A] — really simple, users just [action]. Or something more like [approach B] where they [different flow]. What feels closer?"
|
|
46
|
+
If they're really stuck, describe 2-3 directions conversationally and ask what resonates.
|
|
32
47
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
48
|
+
4. CONFIRM — Once the journey is clear, reflect it back casually:
|
|
49
|
+
"Cool, so basically: user shows up, [does X], then [does Y], and the payoff is [Z]. That feel right?"
|
|
50
|
+
Wait for them to confirm or adjust.
|
|
36
51
|
|
|
37
|
-
|
|
52
|
+
5. RECORD — After they confirm, run:
|
|
53
|
+
jettypod work epic-implement <onboarding-epic-id> --aspect="User Journey" --decision="<summary of chosen journey>" --rationale="<why this flow>"
|
|
54
|
+
|
|
55
|
+
6. HAND OFF — Say something like: "Nice. Close this chat and start the next chore in your backlog to keep going."`
|
|
38
56
|
},
|
|
39
57
|
{
|
|
40
58
|
title: 'Explore UX approaches',
|
|
41
|
-
description:
|
|
59
|
+
description: `${TONE}
|
|
60
|
+
|
|
61
|
+
YOUR ROLE: Help the user decide how their product should FEEL to use. This is about the experience, not the tech.
|
|
42
62
|
|
|
43
|
-
CONTEXT
|
|
44
|
-
Read the user journey decision first:
|
|
63
|
+
CONTEXT: Read the user journey decision first:
|
|
45
64
|
jettypod decisions --epic=<onboarding-epic-id>
|
|
46
65
|
|
|
47
|
-
|
|
48
|
-
Open with: "Based on your user journey: [quote decision]. Now let's figure out how it should feel to use."
|
|
66
|
+
CONVERSATION FLOW:
|
|
49
67
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- Option 3: [Advanced approach] — pros, cons, what it feels like
|
|
68
|
+
1. OPEN — Reference what they already decided and pivot naturally:
|
|
69
|
+
"So last time you said users would [brief journey recap]. Now I'm curious — what should that actually feel like? Like, when someone [key action], are you imagining something minimal and fast, or more guided and hand-holdy?"
|
|
70
|
+
(Use their actual words from the decision. Don't be generic.)
|
|
54
71
|
|
|
55
|
-
|
|
72
|
+
2. EXPLORE THROUGH CONVERSATION — Don't dump three formatted options. Talk through directions:
|
|
73
|
+
"One direction would be something really stripped down — [describe the feel]. That works well when [context]. But you could also go more toward [different feel] where [description]. The trade-off is [honest assessment]."
|
|
74
|
+
Let them react. Follow their energy.
|
|
56
75
|
|
|
57
|
-
|
|
58
|
-
|
|
76
|
+
3. GET SPECIFIC — Once a direction emerges, push on it:
|
|
77
|
+
"OK so if we go that route, the main screen would probably be [description]. Does that feel right or is that too [much/little]?"
|
|
59
78
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
79
|
+
4. OFFER PROTOTYPES (if it would help) — If they're torn or want to see it:
|
|
80
|
+
"Want me to throw together a quick prototype of that? Nothing fancy — just enough to feel it out."
|
|
81
|
+
If yes, use: jettypod project prototype start <approach-name>
|
|
82
|
+
Build a minimal throwaway prototype, then: jettypod project prototype merge
|
|
83
|
+
After they try it: "What did you think? Does that feel like the right direction, or should we adjust?"
|
|
64
84
|
|
|
65
|
-
|
|
66
|
-
|
|
85
|
+
5. CONFIRM — Lock in the winner:
|
|
86
|
+
"Alright, so the feel is [description]. I'll record that."
|
|
67
87
|
|
|
68
|
-
|
|
88
|
+
6. RECORD — Run:
|
|
89
|
+
jettypod work epic-implement <onboarding-epic-id> --aspect="UX Approach" --decision="<chosen approach>" --rationale="<why>"
|
|
90
|
+
|
|
91
|
+
7. HAND OFF — "Close this chat and start the next chore from your backlog."`
|
|
69
92
|
},
|
|
70
93
|
{
|
|
71
94
|
title: 'Choose a tech stack',
|
|
72
|
-
description:
|
|
95
|
+
description: `${TONE}
|
|
96
|
+
|
|
97
|
+
YOUR ROLE: Help the user pick the right tech stack based on what they're building and how it should feel.
|
|
73
98
|
|
|
74
|
-
CONTEXT
|
|
75
|
-
Read previous decisions first:
|
|
99
|
+
CONTEXT: Read previous decisions first:
|
|
76
100
|
jettypod decisions --epic=<onboarding-epic-id>
|
|
77
101
|
|
|
78
|
-
|
|
79
|
-
Open with: "You're building [journey] with a [UX approach] experience. Let's pick the tech stack."
|
|
102
|
+
CONVERSATION FLOW:
|
|
80
103
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
104
|
+
1. OPEN — Connect the dots from previous decisions:
|
|
105
|
+
"You're building [journey] and you want it to feel [UX approach]. That actually narrows down the tech quite a bit."
|
|
106
|
+
Then lead with your honest take:
|
|
107
|
+
"I'd probably go with [recommendation] for this. Here's why — [reason]."
|
|
108
|
+
(Have an opinion. Don't just present options neutrally.)
|
|
85
109
|
|
|
86
|
-
|
|
110
|
+
2. HAVE A REAL DISCUSSION — After giving your take, open it up:
|
|
111
|
+
"That said — do you have a preference? Are you already comfortable with something, or is there a stack you've been wanting to try?"
|
|
112
|
+
Respond to what they say. If they push back, engage with it honestly:
|
|
113
|
+
"Yeah, [their preference] could work. The main thing you'd lose is [trade-off]. The main thing you'd gain is [benefit]. Honestly, either would be fine for this."
|
|
87
114
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
- A winner chosen with rationale
|
|
115
|
+
3. IF THEY WANT OPTIONS — Talk through 2-3 stacks conversationally, not as formatted comparison blocks:
|
|
116
|
+
"[Stack A] would be the fastest to get going — you'd have [benefit] out of the box. [Stack B] is more flexible but you'd need to wire up [thing] yourself. And then there's [Stack C] which is honestly overkill here unless you specifically want [thing]."
|
|
91
117
|
|
|
92
|
-
|
|
93
|
-
|
|
118
|
+
4. LAND ON A DECISION — Once they choose:
|
|
119
|
+
"Solid choice. [One sentence of genuine validation — why it fits their specific project, not generic praise]."
|
|
94
120
|
|
|
95
|
-
|
|
121
|
+
5. RECORD — Run:
|
|
122
|
+
jettypod work epic-implement <onboarding-epic-id> --aspect="Tech Stack" --decision="<chosen stack>" --rationale="<why>"
|
|
123
|
+
|
|
124
|
+
6. HAND OFF — "One more chore left — close this chat and start it from your backlog."`
|
|
96
125
|
},
|
|
97
126
|
{
|
|
98
127
|
title: 'Break the project into epics',
|
|
99
|
-
description:
|
|
128
|
+
description: `${TONE}
|
|
129
|
+
|
|
130
|
+
YOUR ROLE: Break the project into buildable phases (epics) based on everything they've decided.
|
|
100
131
|
|
|
101
|
-
CONTEXT
|
|
102
|
-
Read all previous decisions first:
|
|
132
|
+
CONTEXT: Read all previous decisions first:
|
|
103
133
|
jettypod decisions --epic=<onboarding-epic-id>
|
|
104
134
|
|
|
105
|
-
|
|
106
|
-
Open with: "You're building [journey] as a [UX approach] experience using [tech stack]. Let's break this into phases."
|
|
135
|
+
CONVERSATION FLOW:
|
|
107
136
|
|
|
108
|
-
|
|
137
|
+
1. OPEN — Bring it all together and jump straight to the breakdown:
|
|
138
|
+
"Alright — you're building [journey], it should feel [UX approach], and you're using [tech stack]. Let me suggest how to break this into phases."
|
|
139
|
+
Then just propose the epics conversationally:
|
|
140
|
+
"I'd start with [Epic 1] — that's the foundation, stuff like [examples]. Then [Epic 2] which covers [what]. And then [Epic 3] for [what]."
|
|
141
|
+
(Don't over-explain what an epic is. Keep it natural.)
|
|
109
142
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
- Epic 2: [Name] — what it covers
|
|
113
|
-
- Epic 3: [Name] — what it covers
|
|
143
|
+
2. QUICK CONTEXT (only if needed) — If they seem unsure about the structure:
|
|
144
|
+
"Each of these is a phase. Inside each one we'll define specific features — things users can actually do. You don't need to think about that yet though."
|
|
114
145
|
|
|
115
|
-
|
|
146
|
+
3. GET FEEDBACK — After proposing:
|
|
147
|
+
"Does that breakdown make sense? Anything you'd add, cut, or reorder?"
|
|
148
|
+
Adjust based on their input. Don't be precious about your proposal.
|
|
116
149
|
|
|
117
|
-
|
|
118
|
-
|
|
150
|
+
4. CREATE — Once they confirm, create each epic:
|
|
151
|
+
jettypod work create epic "<title>" "<description>"
|
|
152
|
+
Do this for each one.
|
|
119
153
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
- A recommendation for which epic to start first
|
|
154
|
+
5. RECOMMEND A STARTING POINT:
|
|
155
|
+
"If I were you, I'd start with [Epic name] — [brief reason, like 'everything else depends on it' or 'it's the quickest win to get something real']."
|
|
123
156
|
|
|
124
|
-
|
|
125
|
-
Tell the user: "Your project is planned! Pick an epic from the backlog and tell Claude 'Let's plan [epic name]' to start building."`
|
|
157
|
+
6. CLOSE — "Your project is planned. Pick an epic from the backlog and tell Claude 'Let's plan [epic name]' to start building."`
|
|
126
158
|
}
|
|
127
159
|
];
|
|
128
160
|
|