@kosuke-ai/cli 0.0.56 → 0.0.57
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/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +82 -3
- package/dist/index.js.map +1 -1
- package/dist/kosuke/commands/analyse.d.ts.map +1 -1
- package/dist/kosuke/commands/analyse.js +42 -11
- package/dist/kosuke/commands/analyse.js.map +1 -1
- package/dist/kosuke/commands/build.d.ts.map +1 -1
- package/dist/kosuke/commands/build.js +4 -3
- package/dist/kosuke/commands/build.js.map +1 -1
- package/dist/kosuke/commands/fix.d.ts +21 -0
- package/dist/kosuke/commands/fix.d.ts.map +1 -0
- package/dist/kosuke/commands/fix.js +256 -0
- package/dist/kosuke/commands/fix.js.map +1 -0
- package/dist/kosuke/commands/getcode.d.ts.map +1 -1
- package/dist/kosuke/commands/getcode.js +33 -6
- package/dist/kosuke/commands/getcode.js.map +1 -1
- package/dist/kosuke/commands/lint.d.ts.map +1 -1
- package/dist/kosuke/commands/lint.js +25 -4
- package/dist/kosuke/commands/lint.js.map +1 -1
- package/dist/kosuke/commands/plan.d.ts +2 -2
- package/dist/kosuke/commands/plan.d.ts.map +1 -1
- package/dist/kosuke/commands/plan.js +98 -44
- package/dist/kosuke/commands/plan.js.map +1 -1
- package/dist/kosuke/commands/sync-rules.d.ts.map +1 -1
- package/dist/kosuke/commands/sync-rules.js +39 -4
- package/dist/kosuke/commands/sync-rules.js.map +1 -1
- package/dist/kosuke/commands/tickets.d.ts +6 -6
- package/dist/kosuke/commands/tickets.d.ts.map +1 -1
- package/dist/kosuke/commands/tickets.js +593 -216
- package/dist/kosuke/commands/tickets.js.map +1 -1
- package/dist/kosuke/commands/validate-tickets.d.ts +23 -0
- package/dist/kosuke/commands/validate-tickets.d.ts.map +1 -0
- package/dist/kosuke/commands/validate-tickets.js +360 -0
- package/dist/kosuke/commands/validate-tickets.js.map +1 -0
- package/dist/kosuke/types.d.ts +97 -27
- package/dist/kosuke/types.d.ts.map +1 -1
- package/dist/kosuke/utils/claude-agent.d.ts +0 -5
- package/dist/kosuke/utils/claude-agent.d.ts.map +1 -1
- package/dist/kosuke/utils/claude-agent.js +1 -117
- package/dist/kosuke/utils/claude-agent.js.map +1 -1
- package/dist/kosuke/utils/json-parser.d.ts +14 -0
- package/dist/kosuke/utils/json-parser.d.ts.map +1 -0
- package/dist/kosuke/utils/json-parser.js +40 -0
- package/dist/kosuke/utils/json-parser.js.map +1 -0
- package/dist/kosuke/utils/logger.d.ts +1 -1
- package/dist/kosuke/utils/logger.d.ts.map +1 -1
- package/dist/kosuke/utils/logger.js.map +1 -1
- package/dist/kosuke/utils/tickets-manager.d.ts +3 -33
- package/dist/kosuke/utils/tickets-manager.d.ts.map +1 -1
- package/dist/kosuke/utils/tickets-manager.js +12 -237
- package/dist/kosuke/utils/tickets-manager.js.map +1 -1
- package/dist/lib.d.ts +32 -24
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +29 -20
- package/dist/lib.js.map +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
|
@@ -10,16 +10,16 @@
|
|
|
10
10
|
* SCAFFOLD MODE (--scaffold flag):
|
|
11
11
|
* SCAFFOLD BATCH (template adaptation):
|
|
12
12
|
* 1. SCAFFOLD-SCHEMA-1 (ONE ticket for ALL database infrastructure changes)
|
|
13
|
-
* 2. SCAFFOLD-BACKEND-X → SCAFFOLD-FRONTEND-X → SCAFFOLD-
|
|
13
|
+
* 2. SCAFFOLD-BACKEND-X → SCAFFOLD-FRONTEND-X → SCAFFOLD-TEST-X (feature-by-feature)
|
|
14
14
|
*
|
|
15
15
|
* LOGIC BATCH (business functionality):
|
|
16
16
|
* 1. LOGIC-SCHEMA-1 (ONE ticket for ALL business entities)
|
|
17
|
-
* 2. LOGIC-BACKEND-X → LOGIC-FRONTEND-X → LOGIC-
|
|
17
|
+
* 2. LOGIC-BACKEND-X → LOGIC-FRONTEND-X → LOGIC-TEST-X (feature-by-feature)
|
|
18
18
|
*
|
|
19
19
|
* LOGIC-ONLY MODE (default):
|
|
20
20
|
* Only generates LOGIC tickets for new features
|
|
21
21
|
* 1. LOGIC-SCHEMA-1 (ONE ticket for ALL business entities)
|
|
22
|
-
* 2. LOGIC-BACKEND-X → LOGIC-FRONTEND-X → LOGIC-
|
|
22
|
+
* 2. LOGIC-BACKEND-X → LOGIC-FRONTEND-X → LOGIC-TEST-X (feature-by-feature)
|
|
23
23
|
*
|
|
24
24
|
* Workflow:
|
|
25
25
|
* 1. Claude Code Agent explores codebase and generates tickets
|
|
@@ -31,11 +31,14 @@
|
|
|
31
31
|
* kosuke tickets --scaffold # Scaffold + logic from docs.md
|
|
32
32
|
* kosuke tickets --path=custom.md # Custom requirements file
|
|
33
33
|
*/
|
|
34
|
-
import { existsSync, readFileSync, statSync } from 'fs';
|
|
34
|
+
import { existsSync, readFileSync, statSync, writeFileSync } from 'fs';
|
|
35
35
|
import { join, resolve } from 'path';
|
|
36
|
-
import { formatCostBreakdown,
|
|
36
|
+
import { formatCostBreakdown, runAgentStream } from '../utils/claude-agent.js';
|
|
37
|
+
import { extractTicketsJSON } from '../utils/json-parser.js';
|
|
37
38
|
import { logger, setupCancellationHandler } from '../utils/logger.js';
|
|
38
|
-
import {
|
|
39
|
+
import { displayTicketsSummary, loadTicketsFile, writeTicketsFile, } from '../utils/tickets-manager.js';
|
|
40
|
+
import { fixCoreStream } from './fix.js';
|
|
41
|
+
import { validateTicketsCoreStream } from './validate-tickets.js';
|
|
39
42
|
/**
|
|
40
43
|
* Build unified system prompt for comprehensive ticket generation
|
|
41
44
|
*/
|
|
@@ -53,32 +56,57 @@ Scaffold tickets should:
|
|
|
53
56
|
- 🎨 CUSTOMIZE infrastructure (landing page, email templates, branding, navigation)
|
|
54
57
|
|
|
55
58
|
Examples of SCAFFOLD tickets:
|
|
56
|
-
- "Remove organization/multi-tenancy support from auth"
|
|
57
|
-
- "Simplify billing to single tier (
|
|
59
|
+
- "Remove organization/multi-tenancy support from auth (backend + frontend)"
|
|
60
|
+
- "Simplify billing to single tier (backend + frontend)"
|
|
58
61
|
- "Customize landing page for [specific use case]"
|
|
59
62
|
- "Remove landing page entirely (internal tool)"
|
|
60
|
-
- "Update email templates for [brand name]"
|
|
63
|
+
- "Update email templates and backend handlers for [brand name]"
|
|
61
64
|
|
|
62
65
|
**SCAFFOLD Ticket Ordering - CRITICAL:**
|
|
63
66
|
1. SCAFFOLD-SCHEMA-1 (ONE ticket for ALL database infrastructure changes, auto-validated)
|
|
64
|
-
2. SCAFFOLD-
|
|
65
|
-
3.
|
|
66
|
-
|
|
67
|
-
**SCAFFOLD
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
-
|
|
67
|
+
2. SCAFFOLD-REFACTOR-1, SCAFFOLD-REFACTOR-2, etc (each refactor touches BOTH backend + frontend)
|
|
68
|
+
3. SCAFFOLD-TEST-1 through SCAFFOLD-TEST-5 (5 separate test tickets at the end)
|
|
69
|
+
|
|
70
|
+
**SCAFFOLD Test Structure (5 MANDATORY tickets):**
|
|
71
|
+
Create exactly 5 test tickets to validate all scaffold changes:
|
|
72
|
+
|
|
73
|
+
1. **SCAFFOLD-TEST-1: Auth flow validation**
|
|
74
|
+
- Sign up without removed features (no org selection)
|
|
75
|
+
- Sign in flow works correctly
|
|
76
|
+
- Session management works
|
|
77
|
+
- No references to removed auth features
|
|
78
|
+
|
|
79
|
+
2. **SCAFFOLD-TEST-2: Navigation validation**
|
|
80
|
+
- All navigation links work
|
|
81
|
+
- No broken routes from removed pages
|
|
82
|
+
- Settings navigation updated
|
|
83
|
+
- No 404 errors from removed features
|
|
84
|
+
|
|
85
|
+
3. **SCAFFOLD-TEST-3: Landing page validation**
|
|
86
|
+
- Landing page renders with new branding
|
|
87
|
+
- Search/filter UI works
|
|
88
|
+
- CTAs function correctly
|
|
89
|
+
- No template/SaaS references
|
|
90
|
+
|
|
91
|
+
4. **SCAFFOLD-TEST-4: Removed features validation**
|
|
92
|
+
- Billing pages removed (no /billing routes)
|
|
93
|
+
- Organization pages removed (no /org routes)
|
|
94
|
+
- Tasks/orders removed (no /tasks or /orders routes)
|
|
95
|
+
- Verify removed features are truly gone
|
|
96
|
+
|
|
97
|
+
5. **SCAFFOLD-TEST-5: Settings and profile validation**
|
|
98
|
+
- Settings page loads without removed sections
|
|
99
|
+
- Profile updates work
|
|
100
|
+
- Email preferences work
|
|
101
|
+
- No billing or org settings visible
|
|
74
102
|
`
|
|
75
103
|
: '';
|
|
76
104
|
const webTestGuidance = noTest
|
|
77
105
|
? ''
|
|
78
106
|
: `
|
|
79
|
-
**
|
|
107
|
+
**TEST TICKETS - Playwright MCP E2E Tests:**
|
|
80
108
|
|
|
81
|
-
|
|
109
|
+
Test tickets are executed by Playwright MCP with Claude AI and must follow these guidelines:
|
|
82
110
|
|
|
83
111
|
**Test User Discovery:**
|
|
84
112
|
1. **ALWAYS read seed files** to find test user credentials:
|
|
@@ -121,10 +149,10 @@ Each web test ticket MUST include:
|
|
|
121
149
|
5. Click "Verify" button
|
|
122
150
|
6. Expected: Redirected to dashboard/main app
|
|
123
151
|
|
|
124
|
-
**Example
|
|
152
|
+
**Example Test Ticket:**
|
|
125
153
|
|
|
126
154
|
{
|
|
127
|
-
"id": "LOGIC-
|
|
155
|
+
"id": "LOGIC-TEST-1",
|
|
128
156
|
"title": "E2E: User signup and create first task",
|
|
129
157
|
"description": "**Test User Credentials:**\\n- Email: john+kosuke_test@example.com\\n- OTP Code: 424242\\n\\n**Test Steps:**\\n\\n1. **Sign in with test user**\\n - Navigate to /sign-in\\n - Enter email: john+kosuke_test@example.com\\n - Click button labeled 'Send Code'\\n - Enter OTP code: 424242\\n - Click button labeled 'Verify'\\n - Expected: Redirected to /tasks\\n\\n2. **Create new task**\\n - Click button with text 'New Task'\\n - Enter 'My First Task' in the Title field\\n - Select 'High' from Priority dropdown\\n - Click 'Create Task' button\\n - Expected: Task appears in task list\\n - Expected: Success message shown\\n\\n3. **Verify task persistence**\\n - Refresh the page\\n - Expected: Task 'My First Task' still visible\\n - Expected: Priority shows 'High'\\n\\n**Acceptance Criteria:**\\n- User successfully authenticates with OTP\\n- Task is created and visible\\n- Task persists after page refresh\\n- UI shows appropriate feedback",
|
|
130
158
|
"type": "test",
|
|
@@ -153,29 +181,78 @@ Logic tickets should:
|
|
|
153
181
|
- ⚙️ Build backend APIs for business features
|
|
154
182
|
- 🎨 Create frontend UI for business features
|
|
155
183
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
- ❌ WRONG: LOGIC-SCHEMA-1, LOGIC-SCHEMA-2, LOGIC-SCHEMA-3
|
|
161
|
-
- ✅ CORRECT: LOGIC-SCHEMA-1 (all entities: properties, inquiries, favorites, etc.)
|
|
162
|
-
|
|
163
|
-
**RULE 2: Feature-by-Feature Pattern (STRICT)**
|
|
164
|
-
After schema, each feature MUST follow: engine → backend → frontend → test
|
|
184
|
+
Scaffold tickets (refactor type):
|
|
185
|
+
- 🔄 Each REFACTOR ticket should handle BOTH backend AND frontend changes together
|
|
186
|
+
- Don't split refactoring work across separate backend/frontend tickets
|
|
187
|
+
- Example: SCAFFOLD-REFACTOR-1 removes billing feature (database operations + API routes + UI pages)
|
|
165
188
|
|
|
166
|
-
|
|
167
|
-
1. LOGIC-SCHEMA-1 (ALL schemas combined)
|
|
168
|
-
2. LOGIC-ENGINE-1 (feature 1 engine)
|
|
169
|
-
3. LOGIC-BACKEND-1 (feature 1 backend)
|
|
170
|
-
4. LOGIC-FRONTEND-1 (feature 1 frontend)
|
|
171
|
-
5. LOGIC-WEB-TEST-1 (feature 1 test)
|
|
172
|
-
6. LOGIC-BACKEND-2 (feature 2 backend)
|
|
173
|
-
7. LOGIC-FRONTEND-2 (feature 2 frontend)
|
|
174
|
-
8. LOGIC-WEB-TEST-2 (feature 2 test)
|
|
189
|
+
**LOGIC Ticket Ordering - CRITICAL RULES:**
|
|
175
190
|
|
|
176
|
-
**RULE
|
|
177
|
-
|
|
178
|
-
|
|
191
|
+
**RULE 1: LOGIC-SCHEMA-1 is MANDATORY and MUST be FIRST**
|
|
192
|
+
- The VERY FIRST LOGIC ticket MUST be LOGIC-SCHEMA-1
|
|
193
|
+
- This ticket combines ALL business entities into ONE schema ticket
|
|
194
|
+
- ❌ WRONG: Starting with LOGIC-BACKEND-1 (missing schema!)
|
|
195
|
+
- ❌ WRONG: LOGIC-SCHEMA-1, LOGIC-SCHEMA-2, LOGIC-SCHEMA-3 (only ONE schema ticket!)
|
|
196
|
+
- ✅ CORRECT: LOGIC-SCHEMA-1 (combines all database entities in one ticket)
|
|
197
|
+
|
|
198
|
+
**RULE 2: Feature-by-Feature Pattern (MANDATORY) - NEVER GROUP BY TYPE**
|
|
199
|
+
|
|
200
|
+
After LOGIC-SCHEMA-1, you MUST follow this STRICT pattern for EVERY feature:
|
|
201
|
+
**BACKEND → FRONTEND → TEST** (complete each feature before starting next)
|
|
202
|
+
|
|
203
|
+
⚠️ **YOU WILL GENERATE TICKETS GROUPED BY TYPE IF YOU'RE NOT CAREFUL!**
|
|
204
|
+
|
|
205
|
+
The NATURAL INSTINCT is to group by type, but this is ABSOLUTELY FORBIDDEN:
|
|
206
|
+
|
|
207
|
+
❌ **ABSOLUTELY FORBIDDEN - Grouping by Type (COMMON MISTAKE):**
|
|
208
|
+
LOGIC-SCHEMA-1 ← schema (correct)
|
|
209
|
+
LOGIC-BACKEND-1 ← properties backend
|
|
210
|
+
LOGIC-BACKEND-2 ← favorites backend ❌ STOP! This is type grouping!
|
|
211
|
+
LOGIC-BACKEND-3 ← inquiries backend ❌ All backends together is WRONG!
|
|
212
|
+
LOGIC-BACKEND-4 ← dashboard backend
|
|
213
|
+
LOGIC-FRONTEND-1 ← properties frontend ❌ All frontends grouped!
|
|
214
|
+
LOGIC-FRONTEND-2 ← favorites frontend
|
|
215
|
+
LOGIC-FRONTEND-3 ← inquiries frontend
|
|
216
|
+
LOGIC-FRONTEND-4 ← dashboard frontend
|
|
217
|
+
LOGIC-TEST-1 ← properties test ❌ All tests grouped!
|
|
218
|
+
LOGIC-TEST-2 ← favorites test
|
|
219
|
+
LOGIC-TEST-3 ← inquiries test
|
|
220
|
+
|
|
221
|
+
This is WRONG! All backends grouped together, then all frontends, then all tests.
|
|
222
|
+
THIS IS THE MOST COMMON MISTAKE - DO NOT MAKE IT!
|
|
223
|
+
|
|
224
|
+
✅ **REQUIRED - Feature-by-Feature (ONE FEATURE AT A TIME):**
|
|
225
|
+
LOGIC-SCHEMA-1 (schemas for ALL features - ALWAYS FIRST)
|
|
226
|
+
LOGIC-BACKEND-1 (properties backend API) ← Feature 1 backend
|
|
227
|
+
LOGIC-FRONTEND-1 (properties frontend UI) ← Feature 1 frontend
|
|
228
|
+
LOGIC-TEST-1 (properties test) ← Feature 1 test ✓ Complete!
|
|
229
|
+
LOGIC-BACKEND-2 (favorites backend API) ← Feature 2 backend
|
|
230
|
+
LOGIC-FRONTEND-2 (favorites frontend UI) ← Feature 2 frontend
|
|
231
|
+
LOGIC-TEST-2 (favorites test) ← Feature 2 test ✓ Complete!
|
|
232
|
+
LOGIC-BACKEND-3 (inquiries backend API) ← Feature 3 backend
|
|
233
|
+
LOGIC-FRONTEND-3 (inquiries frontend UI) ← Feature 3 frontend
|
|
234
|
+
LOGIC-TEST-3 (inquiries test) ← Feature 3 test ✓ Complete!
|
|
235
|
+
|
|
236
|
+
This is CORRECT! Each feature completed (backend → frontend → test) before next feature.
|
|
237
|
+
|
|
238
|
+
**WHY THIS MATTERS:**
|
|
239
|
+
- Each complete feature can be developed and tested independently
|
|
240
|
+
- Clear progress tracking (3 features done vs 0 features done)
|
|
241
|
+
- Natural development workflow (build API, then UI, then test)
|
|
242
|
+
- Avoids having half-built features scattered across ticket list
|
|
243
|
+
|
|
244
|
+
**WHY Feature-by-Feature?**
|
|
245
|
+
- Each feature can be developed and tested independently
|
|
246
|
+
- Clear dependency chains (backend ready → build frontend → test both)
|
|
247
|
+
- Easier to track progress per feature
|
|
248
|
+
- Natural workflow for developers
|
|
249
|
+
|
|
250
|
+
**RULE 3: Engine tickets (optional, only if needed)**
|
|
251
|
+
If Python engine endpoints are needed, insert AFTER schema, BEFORE first backend:
|
|
252
|
+
1. LOGIC-SCHEMA-1
|
|
253
|
+
2. LOGIC-ENGINE-1 (if needed for complex algorithms)
|
|
254
|
+
3. LOGIC-BACKEND-1 → LOGIC-FRONTEND-1 → LOGIC-TEST-1
|
|
255
|
+
4. LOGIC-BACKEND-2 → LOGIC-FRONTEND-2 → LOGIC-TEST-2
|
|
179
256
|
|
|
180
257
|
**Ticket Granularity:**
|
|
181
258
|
- Schema: EXACTLY ONE ticket for LOGIC batch (combines ALL entities)
|
|
@@ -215,212 +292,497 @@ Example with 2 features:
|
|
|
215
292
|
- **Use BACKEND (Next.js)** for: CRUD operations, auth logic, business rules, anything TypeScript handles well (90% of features)
|
|
216
293
|
- **Use ENGINE (Python)** for: ML/AI, data science (numpy/pandas), complex algorithms, PDF/document parsing, image processing, or when Python libraries are required
|
|
217
294
|
|
|
218
|
-
**Ticket Structure:**
|
|
295
|
+
**Ticket Structure (CRITICAL - CORRECT ID FORMAT REQUIRED):**
|
|
296
|
+
|
|
297
|
+
Each ticket MUST have an ID in the format: {BATCH}-{TYPE}-{NUMBER}
|
|
298
|
+
- Global sequential numbering within each batch
|
|
299
|
+
- ❌ WRONG: "SCAFFOLD-1", "SCAFFOLD-2", "LOGIC-1", "LOGIC-2"
|
|
300
|
+
- ✅ CORRECT: "SCAFFOLD-SCHEMA-1", "SCAFFOLD-REFACTOR-2", "LOGIC-BACKEND-1", "LOGIC-FRONTEND-2"
|
|
301
|
+
|
|
302
|
+
Valid ID patterns:
|
|
303
|
+
- SCAFFOLD batch: SCAFFOLD-SCHEMA-{n}, SCAFFOLD-REFACTOR-{n}, SCAFFOLD-TEST-{n}
|
|
304
|
+
- LOGIC batch: LOGIC-SCHEMA-{n}, LOGIC-ENGINE-{n}, LOGIC-BACKEND-{n}, LOGIC-FRONTEND-{n}, LOGIC-TEST-{n}
|
|
305
|
+
|
|
306
|
+
Examples with global numbering:
|
|
307
|
+
- [SCAFFOLD-SCHEMA-1, SCAFFOLD-REFACTOR-2, SCAFFOLD-REFACTOR-3, SCAFFOLD-TEST-4, SCAFFOLD-TEST-5]
|
|
308
|
+
- [LOGIC-SCHEMA-1, LOGIC-BACKEND-2, LOGIC-FRONTEND-3, LOGIC-TEST-4, LOGIC-BACKEND-5, LOGIC-FRONTEND-6]
|
|
309
|
+
|
|
219
310
|
Each ticket must be a JSON object with:
|
|
220
|
-
- id: string (
|
|
311
|
+
- id: string (MUST include type: "SCAFFOLD-SCHEMA-1", "LOGIC-BACKEND-2", etc.)
|
|
221
312
|
- title: string (clear, concise title)
|
|
222
313
|
- description: string (detailed description with acceptance criteria)
|
|
223
|
-
- type: "schema" | "engine" | "backend" | "frontend" | "test"
|
|
314
|
+
- type: "schema" | "refactor" | "engine" | "backend" | "frontend" | "test"
|
|
224
315
|
- estimatedEffort: number (1-10, where 1=very easy, 10=very complex)
|
|
225
316
|
- status: "Todo"
|
|
226
317
|
- category: string (e.g., "auth", "billing", "user-management", "tasks")
|
|
227
318
|
|
|
319
|
+
**Type Guidelines:**
|
|
320
|
+
- "schema": Database schema changes only
|
|
321
|
+
- "refactor": Template adaptation (SCAFFOLD only) - touches BOTH backend + frontend
|
|
322
|
+
- "engine": Python engine endpoints (LOGIC only)
|
|
323
|
+
- "backend": Backend/API implementation (LOGIC only)
|
|
324
|
+
- "frontend": Frontend UI implementation (LOGIC only)
|
|
325
|
+
- "test": E2E web tests (both SCAFFOLD and LOGIC)
|
|
326
|
+
|
|
228
327
|
**Output Format:**
|
|
229
328
|
Return ONLY a valid JSON array of ALL tickets in the correct order. No markdown, no code blocks, just raw JSON.
|
|
230
329
|
|
|
231
|
-
Example (Full Ordering Structure - Scaffold Mode):
|
|
330
|
+
Example (Full Ordering Structure - Scaffold Mode with 2 LOGIC features):
|
|
232
331
|
[
|
|
233
332
|
{
|
|
234
333
|
"id": "SCAFFOLD-SCHEMA-1",
|
|
235
|
-
"title": "Remove
|
|
236
|
-
"description": "Remove
|
|
334
|
+
"title": "Remove unused template features from schema",
|
|
335
|
+
"description": "Remove template features from database:\\n- Drop unused tables\\n- Remove foreign keys from users table\\n- Simplify schema\\n\\nAcceptance Criteria:\\n- Unused tables removed\\n- Schema simplified\\n- Migrations validated automatically\\n- No schema errors",
|
|
237
336
|
"type": "schema",
|
|
238
337
|
"estimatedEffort": 5,
|
|
239
338
|
"status": "Todo",
|
|
240
|
-
"category": "
|
|
339
|
+
"category": "cleanup"
|
|
241
340
|
},
|
|
242
341
|
{
|
|
243
|
-
"id": "SCAFFOLD-
|
|
244
|
-
"title": "Remove organization
|
|
245
|
-
"description": "Remove organization
|
|
246
|
-
"type": "
|
|
247
|
-
"estimatedEffort":
|
|
342
|
+
"id": "SCAFFOLD-REFACTOR-1",
|
|
343
|
+
"title": "Remove organization/multi-tenancy feature (backend + frontend)",
|
|
344
|
+
"description": "Remove organization feature from entire stack:\\n\\nBackend:\\n- Delete organization tRPC routers\\n- Remove org-related schemas\\n- Clean up auth middleware\\n\\nFrontend:\\n- Delete organization pages\\n- Remove org selection from signup\\n- Update navigation\\n- Simplify user settings\\n\\nAcceptance Criteria:\\n- All org-related code removed\\n- Auth flow works without orgs\\n- Navigation simplified\\n- No broken links or imports\\n- Type safety maintained",
|
|
345
|
+
"type": "refactor",
|
|
346
|
+
"estimatedEffort": 7,
|
|
248
347
|
"status": "Todo",
|
|
249
|
-
"category": "
|
|
348
|
+
"category": "cleanup"
|
|
250
349
|
},
|
|
251
350
|
{
|
|
252
|
-
"id": "SCAFFOLD-
|
|
253
|
-
"title": "
|
|
254
|
-
"description": "
|
|
255
|
-
"type": "
|
|
256
|
-
"estimatedEffort":
|
|
351
|
+
"id": "SCAFFOLD-REFACTOR-2",
|
|
352
|
+
"title": "Simplify billing to single tier (backend + frontend)",
|
|
353
|
+
"description": "Simplify billing system:\\n\\nBackend:\\n- Remove tier logic from routers\\n- Simplify subscription schemas\\n- Update billing webhooks\\n\\nFrontend:\\n- Remove pricing page\\n- Simplify settings billing section\\n- Update subscription UI\\n\\nAcceptance Criteria:\\n- Single tier billing works\\n- No pro/business tier references\\n- Settings page simplified\\n- Webhooks handle new structure",
|
|
354
|
+
"type": "refactor",
|
|
355
|
+
"estimatedEffort": 6,
|
|
257
356
|
"status": "Todo",
|
|
258
|
-
"category": "
|
|
357
|
+
"category": "billing"
|
|
259
358
|
},
|
|
260
359
|
{
|
|
261
|
-
"id": "SCAFFOLD-
|
|
262
|
-
"title": "E2E:
|
|
263
|
-
"description": "**Test User Credentials:**\\n- Email: john+kosuke_test@example.com\\n- OTP Code: 424242\\n\\n**Test Steps:**\\n\\n1. **Sign up
|
|
360
|
+
"id": "SCAFFOLD-TEST-1",
|
|
361
|
+
"title": "E2E: Auth flow validation",
|
|
362
|
+
"description": "**Test User Credentials:**\\n- Email: john+kosuke_test@example.com\\n- OTP Code: 424242\\n\\n**Test Steps:**\\n\\n1. **Sign up flow**\\n - Navigate to /sign-up\\n - Enter test email\\n - Enter OTP code\\n - Expected: Successful signup\\n\\n2. **Sign in flow**\\n - Navigate to /sign-in\\n - Enter test credentials\\n - Expected: Successful login\\n\\n**Acceptance Criteria:**\\n- Auth flows work correctly\\n- No errors in removed features",
|
|
264
363
|
"type": "test",
|
|
265
|
-
"estimatedEffort":
|
|
364
|
+
"estimatedEffort": 3,
|
|
266
365
|
"status": "Todo",
|
|
267
366
|
"category": "auth"
|
|
268
367
|
},
|
|
368
|
+
{
|
|
369
|
+
"id": "SCAFFOLD-TEST-2",
|
|
370
|
+
"title": "E2E: Navigation validation",
|
|
371
|
+
"description": "**Test Steps:**\\n\\n1. **Verify navigation**\\n - Check navigation structure\\n - Click each navigation link\\n - Expected: No 404 errors\\n\\n**Acceptance Criteria:**\\n- Navigation works correctly\\n- No broken links from removed features",
|
|
372
|
+
"type": "test",
|
|
373
|
+
"estimatedEffort": 2,
|
|
374
|
+
"status": "Todo",
|
|
375
|
+
"category": "navigation"
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
"id": "SCAFFOLD-TEST-3",
|
|
379
|
+
"title": "E2E: Landing page validation",
|
|
380
|
+
"description": "**Test Steps:**\\n\\n1. **Verify landing page**\\n - Navigate to /\\n - Check page content\\n - Test CTAs\\n\\n**Acceptance Criteria:**\\n- Landing page works\\n- All CTAs functional",
|
|
381
|
+
"type": "test",
|
|
382
|
+
"estimatedEffort": 2,
|
|
383
|
+
"status": "Todo",
|
|
384
|
+
"category": "landing"
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"id": "SCAFFOLD-TEST-4",
|
|
388
|
+
"title": "E2E: Removed features validation",
|
|
389
|
+
"description": "**Test Steps:**\\n\\n1. **Verify removed routes**\\n - Try accessing removed routes\\n - Expected: 404 errors\\n\\n**Acceptance Criteria:**\\n- Removed features truly gone\\n- No accessible old routes",
|
|
390
|
+
"type": "test",
|
|
391
|
+
"estimatedEffort": 2,
|
|
392
|
+
"status": "Todo",
|
|
393
|
+
"category": "cleanup"
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
"id": "SCAFFOLD-TEST-5",
|
|
397
|
+
"title": "E2E: Settings validation",
|
|
398
|
+
"description": "**Test User Credentials:**\\n- Email: john+kosuke_test@example.com\\n- OTP Code: 424242\\n\\n**Test Steps:**\\n\\n1. **Access settings**\\n - Sign in as test user\\n - Navigate to /settings\\n - Test profile updates\\n\\n**Acceptance Criteria:**\\n- Settings work correctly\\n- Profile management functional",
|
|
399
|
+
"type": "test",
|
|
400
|
+
"estimatedEffort": 3,
|
|
401
|
+
"status": "Todo",
|
|
402
|
+
"category": "settings"
|
|
403
|
+
},
|
|
269
404
|
{
|
|
270
405
|
"id": "LOGIC-SCHEMA-1",
|
|
271
|
-
"title": "Create
|
|
272
|
-
"description": "Create database schema for
|
|
406
|
+
"title": "Create database schema for business entities",
|
|
407
|
+
"description": "Create database schema for ALL business entities:\\n- Create enums for entity types\\n- Create main entity tables\\n- Create relationship tables\\n- Export inferred types\\n\\nAcceptance Criteria:\\n- All tables created\\n- Enums defined at database level\\n- Migrations validated automatically\\n- No schema errors",
|
|
273
408
|
"type": "schema",
|
|
274
|
-
"estimatedEffort":
|
|
409
|
+
"estimatedEffort": 8,
|
|
275
410
|
"status": "Todo",
|
|
276
|
-
"category": "
|
|
411
|
+
"category": "schema"
|
|
277
412
|
},
|
|
278
413
|
{
|
|
279
414
|
"id": "LOGIC-BACKEND-1",
|
|
280
|
-
"title": "Create
|
|
281
|
-
"description": "Create backend API for
|
|
415
|
+
"title": "Create feature A tRPC router",
|
|
416
|
+
"description": "Create backend API for feature A:\\n- Create schemas\\n- Create router\\n- Implement CRUD operations\\n- Server-side filtering and pagination\\n\\nAcceptance Criteria:\\n- All CRUD operations work\\n- Authorization enforced\\n- Type-safe implementation",
|
|
282
417
|
"type": "backend",
|
|
283
|
-
"estimatedEffort":
|
|
418
|
+
"estimatedEffort": 7,
|
|
284
419
|
"status": "Todo",
|
|
285
|
-
"category": "
|
|
420
|
+
"category": "feature-a"
|
|
286
421
|
},
|
|
287
422
|
{
|
|
288
423
|
"id": "LOGIC-FRONTEND-1",
|
|
289
|
-
"title": "Create
|
|
290
|
-
"description": "Create
|
|
424
|
+
"title": "Create feature A UI",
|
|
425
|
+
"description": "Create UI for feature A:\\n- Create page components\\n- Add list view with filters\\n- Add search functionality\\n- Add pagination\\n\\nAcceptance Criteria:\\n- UI displays correctly\\n- Filters work server-side\\n- Search functional\\n- Responsive design",
|
|
291
426
|
"type": "frontend",
|
|
292
|
-
"estimatedEffort":
|
|
427
|
+
"estimatedEffort": 8,
|
|
293
428
|
"status": "Todo",
|
|
294
|
-
"category": "
|
|
429
|
+
"category": "feature-a"
|
|
295
430
|
},
|
|
296
431
|
{
|
|
297
|
-
"id": "LOGIC-
|
|
298
|
-
"title": "E2E:
|
|
299
|
-
"description": "**Test User Credentials:**\\n- Email: john+kosuke_test@example.com\\n- OTP Code: 424242\\n\\n**Test Steps:**\\n\\n1. **
|
|
432
|
+
"id": "LOGIC-TEST-1",
|
|
433
|
+
"title": "E2E: Feature A workflow",
|
|
434
|
+
"description": "**Test User Credentials:**\\n- Email: john+kosuke_test@example.com\\n- OTP Code: 424242\\n\\n**Test Steps:**\\n\\n1. **Test main workflow**\\n - Navigate to feature page\\n - Test CRUD operations\\n - Test filters and search\\n\\n**Acceptance Criteria:**\\n- All operations work\\n- UI behaves correctly",
|
|
300
435
|
"type": "test",
|
|
436
|
+
"estimatedEffort": 4,
|
|
437
|
+
"status": "Todo",
|
|
438
|
+
"category": "feature-a"
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
"id": "LOGIC-BACKEND-2",
|
|
442
|
+
"title": "Create feature B tRPC router",
|
|
443
|
+
"description": "Create backend API for feature B:\\n- Create schemas\\n- Create router\\n- Implement operations\\n\\nAcceptance Criteria:\\n- Operations work\\n- Authorization enforced",
|
|
444
|
+
"type": "backend",
|
|
445
|
+
"estimatedEffort": 4,
|
|
446
|
+
"status": "Todo",
|
|
447
|
+
"category": "feature-b"
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
"id": "LOGIC-FRONTEND-2",
|
|
451
|
+
"title": "Create feature B UI",
|
|
452
|
+
"description": "Create UI for feature B:\\n- Create page components\\n- Add main functionality\\n\\nAcceptance Criteria:\\n- UI works correctly\\n- Responsive design",
|
|
453
|
+
"type": "frontend",
|
|
301
454
|
"estimatedEffort": 5,
|
|
302
455
|
"status": "Todo",
|
|
303
|
-
"category": "
|
|
456
|
+
"category": "feature-b"
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
"id": "LOGIC-TEST-2",
|
|
460
|
+
"title": "E2E: Feature B workflow",
|
|
461
|
+
"description": "**Test User Credentials:**\\n- Email: john+kosuke_test@example.com\\n- OTP Code: 424242\\n\\n**Test Steps:**\\n\\n1. **Test feature B**\\n - Navigate to feature page\\n - Test main operations\\n\\n**Acceptance Criteria:**\\n- Operations work correctly",
|
|
462
|
+
"type": "test",
|
|
463
|
+
"estimatedEffort": 5,
|
|
464
|
+
"status": "Todo",
|
|
465
|
+
"category": "feature-b"
|
|
304
466
|
}
|
|
305
467
|
]
|
|
306
468
|
|
|
307
469
|
**Critical Instructions:**
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
-
|
|
470
|
+
|
|
471
|
+
1. **Explore the project directory thoroughly** before generating tickets
|
|
472
|
+
- Read seed files (lib/db/seed.ts or src/lib/db/seed.ts) to discover test users
|
|
473
|
+
- Understand current tech stack, architecture, database schema
|
|
474
|
+
${isScaffoldMode ? '- Identify what template features need to be removed/changed/customized' : ''}
|
|
475
|
+
|
|
476
|
+
2. **SCAFFOLD Tickets (if in scaffold mode):**
|
|
477
|
+
${isScaffoldMode ? '- Focus on template adaptation ONLY (remove/change/customize)\n - Use REFACTOR type (not backend/frontend) - each ticket touches BOTH layers\n - MUST create exactly 5 SCAFFOLD-TEST tickets (numbered 1-5)\n - Tests validate: auth flow, navigation, landing page, removed features, settings' : '- Not applicable in logic-only mode'}
|
|
478
|
+
|
|
479
|
+
3. **LOGIC Tickets (MANDATORY ordering - FEATURE-BY-FEATURE ONLY):**
|
|
480
|
+
- **FIRST TICKET MUST BE LOGIC-SCHEMA-1** (combines ALL business entities)
|
|
481
|
+
- After schema, follow STRICT feature-by-feature pattern for EVERY feature:
|
|
482
|
+
- LOGIC-BACKEND-1 → LOGIC-FRONTEND-1 → LOGIC-TEST-1 (complete feature 1)
|
|
483
|
+
- LOGIC-BACKEND-2 → LOGIC-FRONTEND-2 → LOGIC-TEST-2 (complete feature 2)
|
|
484
|
+
- LOGIC-BACKEND-3 → LOGIC-FRONTEND-3 → LOGIC-TEST-3 (complete feature 3)
|
|
485
|
+
- ❌ ABSOLUTELY FORBIDDEN: Grouping by type
|
|
486
|
+
- BACKEND-1, BACKEND-2, BACKEND-3, FRONTEND-1, FRONTEND-2 ← WRONG!
|
|
487
|
+
- ✅ REQUIRED: Complete each feature before next
|
|
488
|
+
- BACKEND-1, FRONTEND-1, TEST-1, BACKEND-2, FRONTEND-2, TEST-2 ← CORRECT!
|
|
489
|
+
|
|
490
|
+
4. **Schema and Refactor Tickets:**
|
|
491
|
+
- SCAFFOLD-SCHEMA-1: ONE ticket for ALL infrastructure changes
|
|
492
|
+
- SCAFFOLD-REFACTOR-X: Each refactor ticket touches BOTH backend + frontend (don't split)
|
|
493
|
+
- LOGIC-SCHEMA-1: ONE ticket for ALL business entities (MANDATORY!)
|
|
494
|
+
- No separate test tickets needed for schema - validation is automatic
|
|
495
|
+
|
|
496
|
+
5. **Test Tickets:**
|
|
497
|
+
- ${isScaffoldMode ? 'Exactly 5 SCAFFOLD-TEST tickets (mandatory)\n - ' : ''}One LOGIC-TEST per feature (matches frontend tickets)
|
|
498
|
+
- MUST include test user credentials at the top
|
|
316
499
|
- Clear numbered steps with natural language
|
|
317
500
|
- Expected outcomes after each step
|
|
318
501
|
- Specific element descriptions (button text, labels, URLs)
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
502
|
+
|
|
503
|
+
6. **Ticket Quality:**
|
|
504
|
+
- Detailed descriptions with clear acceptance criteria
|
|
505
|
+
- Realistic effort estimates (1-10 scale)
|
|
506
|
+
- Proper categorization
|
|
507
|
+
- Sequential IDs matching ordering structure
|
|
508
|
+
|
|
509
|
+
7. **Output Format:**
|
|
510
|
+
- Return ONLY valid JSON array
|
|
511
|
+
- No markdown, no code blocks, no explanations
|
|
512
|
+
- All tickets in correct order
|
|
513
|
+
|
|
514
|
+
**VALIDATION CHECKLIST before returning tickets:**
|
|
515
|
+
|
|
516
|
+
🔴 **CRITICAL - VERIFY LOGIC TICKET ORDERING:**
|
|
517
|
+
Read through your LOGIC tickets. Are they in this pattern?
|
|
518
|
+
BACKEND-1, BACKEND-2, BACKEND-3, FRONTEND-1, FRONTEND-2, FRONTEND-3, TEST-1, TEST-2
|
|
519
|
+
If YES → YOU MADE THE GROUPING MISTAKE! Fix it now!
|
|
520
|
+
|
|
521
|
+
✅ Correct pattern should be:
|
|
522
|
+
SCHEMA-1, BACKEND-1, FRONTEND-1, TEST-1, BACKEND-2, FRONTEND-2, TEST-2
|
|
523
|
+
|
|
524
|
+
Other checks:
|
|
525
|
+
- ✅ ALL ticket IDs follow {BATCH}-{TYPE}-{NUMBER} format (e.g., SCAFFOLD-SCHEMA-1, LOGIC-BACKEND-2)
|
|
526
|
+
- ✅ Global sequential numbering within each batch (no gaps, sequential: 1, 2, 3, 4...)
|
|
527
|
+
- ✅ ${isScaffoldMode ? 'SCAFFOLD-SCHEMA-1 is first scaffold ticket' : 'N/A for logic-only mode'}
|
|
528
|
+
- ✅ ${isScaffoldMode ? 'SCAFFOLD tickets use REFACTOR type (not backend/frontend split)' : 'N/A for logic-only mode'}
|
|
529
|
+
- ✅ ${isScaffoldMode ? 'Exactly 5 SCAFFOLD-TEST tickets (with proper IDs like SCAFFOLD-TEST-4, SCAFFOLD-TEST-5)' : 'N/A for logic-only mode'}
|
|
530
|
+
- ✅ LOGIC-SCHEMA-1 is first LOGIC ticket (MANDATORY!)
|
|
531
|
+
- ✅ LOGIC tickets are feature-by-feature (BACKEND-1 → FRONTEND-1 → TEST-1 → BACKEND-2 → FRONTEND-2 → TEST-2)
|
|
532
|
+
- ✅ NO grouping by type for LOGIC tickets (verify tickets are NOT: all backends, then frontends, then tests)
|
|
533
|
+
- ✅ Each web test includes test user credentials
|
|
534
|
+
- ✅ Ticket IDs match their type field (BACKEND ticket has BACKEND in ID, FRONTEND has FRONTEND in ID)
|
|
535
|
+
|
|
536
|
+
**CRITICAL REMINDERS:**
|
|
537
|
+
|
|
538
|
+
1. **ID FORMAT (MOST IMPORTANT):**
|
|
539
|
+
- EVERY ticket ID MUST include the type: {BATCH}-{TYPE}-{NUMBER}
|
|
540
|
+
- ❌ WRONG: SCAFFOLD-1, SCAFFOLD-2, LOGIC-1, LOGIC-2
|
|
541
|
+
- ✅ CORRECT: SCAFFOLD-SCHEMA-1, SCAFFOLD-REFACTOR-2, LOGIC-BACKEND-1, LOGIC-FRONTEND-2
|
|
542
|
+
|
|
543
|
+
2. **ORDERING:**
|
|
544
|
+
- LOGIC tickets MUST be feature-by-feature, NOT grouped by type!
|
|
545
|
+
- ❌ WRONG: BACKEND-1, BACKEND-2, BACKEND-3, FRONTEND-1, FRONTEND-2, FRONTEND-3
|
|
546
|
+
- ✅ CORRECT: BACKEND-1, FRONTEND-1, TEST-1, BACKEND-2, FRONTEND-2, TEST-2
|
|
547
|
+
|
|
548
|
+
Begin now:
|
|
549
|
+
1. Read seed files to discover test users
|
|
550
|
+
2. Explore project directory structure
|
|
551
|
+
3. Generate ALL tickets with CORRECT ID format ({BATCH}-{TYPE}-{NUMBER})
|
|
552
|
+
4. Follow STRICT feature-by-feature ordering (NEVER group by type!)`;
|
|
330
553
|
}
|
|
331
554
|
/**
|
|
332
|
-
* Core tickets logic -
|
|
555
|
+
* Core tickets logic - Streaming version (exported for programmatic use)
|
|
333
556
|
*/
|
|
334
|
-
export async function
|
|
557
|
+
export async function* ticketsCoreStream(options) {
|
|
335
558
|
const { directory, scaffold = false } = options;
|
|
336
559
|
const isScaffoldMode = scaffold;
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
const requirementsPath = join(projectPath, options.path);
|
|
354
|
-
if (!existsSync(requirementsPath)) {
|
|
355
|
-
throw new Error(`Requirements document not found: ${options.path}\n` +
|
|
356
|
-
`Please provide a valid path using --path=<file>\n` +
|
|
357
|
-
`Example: kosuke tickets --path=requirements.md`);
|
|
560
|
+
try {
|
|
561
|
+
// 1. Validate and resolve project directory
|
|
562
|
+
const projectPath = directory ? resolve(directory) : process.cwd();
|
|
563
|
+
if (!existsSync(projectPath)) {
|
|
564
|
+
yield {
|
|
565
|
+
type: 'done',
|
|
566
|
+
data: {
|
|
567
|
+
status: 'error',
|
|
568
|
+
ticketsFile: null,
|
|
569
|
+
totalTickets: 0,
|
|
570
|
+
tokensUsed: { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 },
|
|
571
|
+
cost: 0,
|
|
572
|
+
error: `Directory not found: ${projectPath}\nPlease provide a valid directory using --directory=<path>\nExample: kosuke tickets --directory=./my-project`,
|
|
573
|
+
},
|
|
574
|
+
};
|
|
575
|
+
return;
|
|
358
576
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
577
|
+
const stats = statSync(projectPath);
|
|
578
|
+
if (!stats.isDirectory()) {
|
|
579
|
+
yield {
|
|
580
|
+
type: 'done',
|
|
581
|
+
data: {
|
|
582
|
+
status: 'error',
|
|
583
|
+
ticketsFile: null,
|
|
584
|
+
totalTickets: 0,
|
|
585
|
+
tokensUsed: { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 },
|
|
586
|
+
cost: 0,
|
|
587
|
+
error: `Path is not a directory: ${projectPath}\nPlease provide a valid directory path.`,
|
|
588
|
+
},
|
|
589
|
+
};
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
// 2. Get requirements content from file
|
|
593
|
+
let requirementsContent;
|
|
594
|
+
if (options.path) {
|
|
595
|
+
const requirementsPath = join(projectPath, options.path);
|
|
596
|
+
if (!existsSync(requirementsPath)) {
|
|
597
|
+
yield {
|
|
598
|
+
type: 'done',
|
|
599
|
+
data: {
|
|
600
|
+
status: 'error',
|
|
601
|
+
ticketsFile: null,
|
|
602
|
+
totalTickets: 0,
|
|
603
|
+
tokensUsed: { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 },
|
|
604
|
+
cost: 0,
|
|
605
|
+
error: `Requirements document not found: ${options.path}\nPlease provide a valid path using --path=<file>\nExample: kosuke tickets --path=requirements.md`,
|
|
606
|
+
},
|
|
607
|
+
};
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
requirementsContent = readFileSync(requirementsPath, 'utf-8');
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
// Default to docs.md if path not provided
|
|
614
|
+
const defaultPath = 'docs.md';
|
|
615
|
+
const requirementsPath = join(projectPath, defaultPath);
|
|
616
|
+
if (!existsSync(requirementsPath)) {
|
|
617
|
+
yield {
|
|
618
|
+
type: 'done',
|
|
619
|
+
data: {
|
|
620
|
+
status: 'error',
|
|
621
|
+
ticketsFile: null,
|
|
622
|
+
totalTickets: 0,
|
|
623
|
+
tokensUsed: { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 },
|
|
624
|
+
cost: 0,
|
|
625
|
+
error: 'Requirements document not found. Use either:\n --path=requirements.md (custom file)\n Or create a docs.md file in the project directory\n\nFor interactive ticket creation with questions, use:\n kosuke plan --prompt="Your feature description"',
|
|
626
|
+
},
|
|
627
|
+
};
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
requirementsContent = readFileSync(requirementsPath, 'utf-8');
|
|
631
|
+
}
|
|
632
|
+
// 3. Determine output path
|
|
633
|
+
const outputFilename = options.output || 'tickets.json';
|
|
634
|
+
const outputPath = join(projectPath, outputFilename);
|
|
635
|
+
// 4. Generate ALL tickets in a single comprehensive agent call
|
|
636
|
+
const systemPrompt = buildTicketPrompt(requirementsContent, projectPath, isScaffoldMode, options.noTest ?? false);
|
|
637
|
+
const stream = runAgentStream('Generate all tickets from requirements', {
|
|
638
|
+
systemPrompt,
|
|
639
|
+
cwd: projectPath,
|
|
640
|
+
maxTurns: 40,
|
|
641
|
+
verbosity: 'normal',
|
|
642
|
+
captureConversation: true,
|
|
643
|
+
});
|
|
644
|
+
let fullResponse = '';
|
|
645
|
+
let tokensUsed = { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 };
|
|
646
|
+
let cost = 0;
|
|
647
|
+
// Yield events from stream
|
|
648
|
+
for await (const event of stream) {
|
|
649
|
+
if (event.type === 'message') {
|
|
650
|
+
yield { type: 'message', data: event.data };
|
|
651
|
+
// Accumulate response text
|
|
652
|
+
const claudeMessage = event.data;
|
|
653
|
+
if (claudeMessage.type === 'assistant' && claudeMessage.message) {
|
|
654
|
+
const msg = claudeMessage.message;
|
|
655
|
+
if (msg.content && Array.isArray(msg.content)) {
|
|
656
|
+
for (const block of msg.content) {
|
|
657
|
+
if (block.type === 'text' && block.text) {
|
|
658
|
+
fullResponse += block.text;
|
|
659
|
+
}
|
|
660
|
+
else if (block.type === 'tool_use' && block.name) {
|
|
661
|
+
yield {
|
|
662
|
+
type: 'tool_call',
|
|
663
|
+
data: { name: block.name, input: block.input || {} },
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
else if (event.type === 'agent_result') {
|
|
671
|
+
tokensUsed = event.data.tokensUsed;
|
|
672
|
+
cost = event.data.cost;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
// 5. Extract JSON from response (simple extraction)
|
|
676
|
+
let tickets = extractTicketsJSON(fullResponse);
|
|
677
|
+
// 6. If extraction failed, try to fix via Claude using fix stream
|
|
678
|
+
if (!tickets) {
|
|
679
|
+
// Write raw response to temporary file for fixing
|
|
680
|
+
const tempRawPath = outputPath.replace('.json', '.raw.txt');
|
|
681
|
+
writeFileSync(tempRawPath, fullResponse, 'utf-8');
|
|
682
|
+
try {
|
|
683
|
+
const fixStream = fixCoreStream({
|
|
684
|
+
format: 'json',
|
|
685
|
+
file: tempRawPath,
|
|
686
|
+
output: outputPath,
|
|
687
|
+
directory: projectPath,
|
|
688
|
+
noLogs: true,
|
|
689
|
+
});
|
|
690
|
+
let fixResult = null;
|
|
691
|
+
for await (const event of fixStream) {
|
|
692
|
+
if (event.type === 'done') {
|
|
693
|
+
fixResult = event.data;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (!fixResult || fixResult.status === 'error') {
|
|
697
|
+
yield {
|
|
698
|
+
type: 'done',
|
|
699
|
+
data: {
|
|
700
|
+
status: 'error',
|
|
701
|
+
ticketsFile: null,
|
|
702
|
+
totalTickets: 0,
|
|
703
|
+
tokensUsed,
|
|
704
|
+
cost,
|
|
705
|
+
error: fixResult?.error || 'Failed to fix JSON extraction',
|
|
706
|
+
},
|
|
707
|
+
};
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
// Load tickets from file (fix wrote them)
|
|
711
|
+
const fixedData = loadTicketsFile(outputPath);
|
|
712
|
+
tickets = fixedData.tickets;
|
|
713
|
+
}
|
|
714
|
+
finally {
|
|
715
|
+
// Clean up temporary file
|
|
716
|
+
const { unlinkSync } = await import('fs');
|
|
717
|
+
if (existsSync(tempRawPath)) {
|
|
718
|
+
unlinkSync(tempRawPath);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
372
721
|
}
|
|
373
|
-
|
|
374
|
-
|
|
722
|
+
else {
|
|
723
|
+
// 7. Write raw tickets to file (before validation)
|
|
724
|
+
writeTicketsFile(outputPath, tickets);
|
|
725
|
+
}
|
|
726
|
+
// 8. Validate tickets file using validate-tickets stream
|
|
727
|
+
const validateStream = validateTicketsCoreStream({
|
|
728
|
+
tickets: outputPath,
|
|
729
|
+
directory: projectPath,
|
|
730
|
+
strict: false,
|
|
731
|
+
noLogs: false,
|
|
732
|
+
});
|
|
733
|
+
let validateResult = null;
|
|
734
|
+
for await (const event of validateStream) {
|
|
735
|
+
if (event.type === 'done') {
|
|
736
|
+
validateResult = event.data;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
if (!validateResult || validateResult.status === 'error') {
|
|
740
|
+
yield {
|
|
741
|
+
type: 'done',
|
|
742
|
+
data: {
|
|
743
|
+
status: 'error',
|
|
744
|
+
ticketsFile: outputPath,
|
|
745
|
+
totalTickets: tickets?.length || 0,
|
|
746
|
+
tokensUsed,
|
|
747
|
+
cost,
|
|
748
|
+
error: validateResult?.error || 'Validation failed',
|
|
749
|
+
},
|
|
750
|
+
};
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
// 9. Load validated tickets
|
|
754
|
+
const ticketsData = loadTicketsFile(outputPath);
|
|
755
|
+
const allTickets = ticketsData.tickets;
|
|
756
|
+
yield {
|
|
757
|
+
type: 'done',
|
|
758
|
+
data: {
|
|
759
|
+
status: 'success',
|
|
760
|
+
ticketsFile: outputPath,
|
|
761
|
+
totalTickets: allTickets.length,
|
|
762
|
+
tokensUsed,
|
|
763
|
+
cost,
|
|
764
|
+
},
|
|
765
|
+
};
|
|
375
766
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
verbosity: 'normal',
|
|
389
|
-
captureConversation: true,
|
|
390
|
-
});
|
|
391
|
-
// 5. Parse initial tickets using shared utility
|
|
392
|
-
const initialTickets = parseTickets(agentResult.response);
|
|
393
|
-
console.log(`\n✅ Initial generation: ${initialTickets.length} tickets created`);
|
|
394
|
-
// 6. Validate, fix, and write tickets using shared utility
|
|
395
|
-
const { tickets: allTickets, validationIssues } = await processAndWriteTickets(initialTickets, outputPath, projectPath, { displaySummary: true });
|
|
396
|
-
// Display additional info about validation
|
|
397
|
-
if (validationIssues.length > 0) {
|
|
398
|
-
console.log(`📝 ${validationIssues.length} issue(s) were automatically fixed`);
|
|
767
|
+
catch (error) {
|
|
768
|
+
yield {
|
|
769
|
+
type: 'done',
|
|
770
|
+
data: {
|
|
771
|
+
status: 'error',
|
|
772
|
+
ticketsFile: null,
|
|
773
|
+
totalTickets: 0,
|
|
774
|
+
tokensUsed: { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 },
|
|
775
|
+
cost: 0,
|
|
776
|
+
error: error instanceof Error ? error.message : String(error),
|
|
777
|
+
},
|
|
778
|
+
};
|
|
399
779
|
}
|
|
400
|
-
// 10. Separate tickets by phase for compatibility with existing result structure
|
|
401
|
-
const schemaTickets = allTickets.filter((t) => t.type === 'schema');
|
|
402
|
-
const engineTickets = allTickets.filter((t) => t.type === 'engine');
|
|
403
|
-
const backendTickets = allTickets.filter((t) => t.type === 'backend');
|
|
404
|
-
const frontendTickets = allTickets.filter((t) => t.type === 'frontend');
|
|
405
|
-
const testTickets = allTickets.filter((t) => t.type === 'test');
|
|
406
|
-
return {
|
|
407
|
-
schemaTickets,
|
|
408
|
-
engineTickets,
|
|
409
|
-
backendTickets,
|
|
410
|
-
frontendTickets,
|
|
411
|
-
testTickets,
|
|
412
|
-
totalTickets: allTickets.length,
|
|
413
|
-
projectPath,
|
|
414
|
-
tokensUsed: agentResult.tokensUsed,
|
|
415
|
-
cost: agentResult.cost,
|
|
416
|
-
conversationMessages: agentResult.conversationMessages || [],
|
|
417
|
-
};
|
|
418
780
|
}
|
|
419
781
|
/**
|
|
420
782
|
* Main tickets command
|
|
421
783
|
*/
|
|
422
784
|
export async function ticketsCommand(options) {
|
|
423
|
-
const { noLogs = false } = options;
|
|
785
|
+
const { noLogs = false, directory, scaffold = false } = options;
|
|
424
786
|
console.log('🎫 Starting Ticket Generation...\n');
|
|
425
787
|
// Initialize logging context
|
|
426
788
|
const logContext = logger.createContext('tickets', { noLogs });
|
|
@@ -430,59 +792,74 @@ export async function ticketsCommand(options) {
|
|
|
430
792
|
if (!process.env.ANTHROPIC_API_KEY) {
|
|
431
793
|
throw new Error('ANTHROPIC_API_KEY environment variable is required');
|
|
432
794
|
}
|
|
433
|
-
//
|
|
434
|
-
const
|
|
795
|
+
// Resolve paths for display
|
|
796
|
+
const projectPath = directory ? resolve(directory) : process.cwd();
|
|
797
|
+
const isScaffoldMode = scaffold;
|
|
798
|
+
console.log(`📁 Using project directory: ${projectPath}`);
|
|
799
|
+
console.log(`🏗️ Mode: ${isScaffoldMode ? 'Scaffold (template adaptation + business logic)' : 'Logic-only (business features)'}\n`);
|
|
800
|
+
// Get requirements path for display
|
|
801
|
+
const requirementsPath = options.path
|
|
802
|
+
? join(projectPath, options.path)
|
|
803
|
+
: join(projectPath, 'docs.md');
|
|
804
|
+
if (existsSync(requirementsPath)) {
|
|
805
|
+
const requirementsContent = readFileSync(requirementsPath, 'utf-8');
|
|
806
|
+
console.log(`📄 Loaded ${options.path || 'docs.md'} (${requirementsContent.length} characters)\n`);
|
|
807
|
+
}
|
|
808
|
+
console.log(`\n${'='.repeat(80)}`);
|
|
809
|
+
console.log('🎯 Generating Tickets with Claude Code Agent');
|
|
810
|
+
console.log(`${'='.repeat(80)}\n`);
|
|
811
|
+
// Consume stream
|
|
812
|
+
const stream = ticketsCoreStream(options);
|
|
813
|
+
let finalResult = null;
|
|
814
|
+
for await (const event of stream) {
|
|
815
|
+
if (event.type === 'done') {
|
|
816
|
+
finalResult = event.data;
|
|
817
|
+
}
|
|
818
|
+
// Messages are already logged by runAgentStream with verbosity
|
|
819
|
+
}
|
|
820
|
+
if (!finalResult || finalResult.status === 'error') {
|
|
821
|
+
throw new Error(finalResult?.error || 'Unknown error during ticket generation');
|
|
822
|
+
}
|
|
823
|
+
// Now TypeScript knows finalResult.status === 'success'
|
|
824
|
+
const { ticketsFile, totalTickets, tokensUsed, cost } = finalResult;
|
|
435
825
|
// Track metrics
|
|
436
|
-
logger.trackTokens(logContext,
|
|
437
|
-
|
|
826
|
+
logger.trackTokens(logContext, tokensUsed);
|
|
827
|
+
// Load and display tickets summary
|
|
828
|
+
const ticketsData = loadTicketsFile(ticketsFile);
|
|
829
|
+
const allTickets = ticketsData.tickets;
|
|
830
|
+
displayTicketsSummary(allTickets);
|
|
438
831
|
// Get all tickets by batch (scaffold vs logic)
|
|
439
|
-
const scaffoldTickets =
|
|
440
|
-
|
|
441
|
-
...result.engineTickets,
|
|
442
|
-
...result.backendTickets,
|
|
443
|
-
...result.frontendTickets,
|
|
444
|
-
...result.testTickets,
|
|
445
|
-
].filter((t) => t.id.toUpperCase().startsWith('SCAFFOLD-'));
|
|
446
|
-
const logicTickets = [
|
|
447
|
-
...result.schemaTickets,
|
|
448
|
-
...result.engineTickets,
|
|
449
|
-
...result.backendTickets,
|
|
450
|
-
...result.frontendTickets,
|
|
451
|
-
...result.testTickets,
|
|
452
|
-
].filter((t) => t.id.toUpperCase().startsWith('LOGIC-'));
|
|
832
|
+
const scaffoldTickets = allTickets.filter((t) => t.id.toUpperCase().startsWith('SCAFFOLD-'));
|
|
833
|
+
const logicTickets = allTickets.filter((t) => t.id.toUpperCase().startsWith('LOGIC-'));
|
|
453
834
|
// Display summary
|
|
454
835
|
console.log(`\n${'='.repeat(80)}`);
|
|
455
836
|
console.log('📊 Ticket Generation Summary');
|
|
456
837
|
console.log(`${'='.repeat(80)}`);
|
|
457
838
|
if (scaffoldTickets.length > 0) {
|
|
458
839
|
console.log(`\n🏗️ Scaffold Tickets (Template Adaptation): ${scaffoldTickets.length}`);
|
|
459
|
-
console.log(` 🗄️ Schema: ${
|
|
460
|
-
console.log(`
|
|
461
|
-
console.log(`
|
|
462
|
-
console.log(` 🎨 Frontend: ${result.frontendTickets.filter((t) => t.id.toUpperCase().startsWith('SCAFFOLD-')).length}`);
|
|
463
|
-
console.log(` 🧪 Tests: ${result.testTickets.filter((t) => t.id.toUpperCase().startsWith('SCAFFOLD-')).length}`);
|
|
840
|
+
console.log(` 🗄️ Schema: ${scaffoldTickets.filter((t) => t.type === 'schema').length} (auto-validated)`);
|
|
841
|
+
console.log(` 🔄 Refactor: ${scaffoldTickets.filter((t) => t.type === 'refactor').length} (backend + frontend)`);
|
|
842
|
+
console.log(` 🧪 Tests: ${scaffoldTickets.filter((t) => t.type === 'test').length}`);
|
|
464
843
|
}
|
|
465
844
|
console.log(`\n💡 Logic Tickets (Business Functionality): ${logicTickets.length}`);
|
|
466
|
-
console.log(` 🗄️ Schema: ${
|
|
467
|
-
console.log(` 🐍 Engine: ${
|
|
468
|
-
console.log(` ⚙️ Backend: ${
|
|
469
|
-
console.log(` 🎨 Frontend: ${
|
|
470
|
-
console.log(` 🧪 Tests: ${
|
|
471
|
-
console.log(`\n📝 Total Tickets: ${
|
|
845
|
+
console.log(` 🗄️ Schema: ${logicTickets.filter((t) => t.type === 'schema').length} (auto-validated)`);
|
|
846
|
+
console.log(` 🐍 Engine: ${logicTickets.filter((t) => t.type === 'engine').length}`);
|
|
847
|
+
console.log(` ⚙️ Backend: ${logicTickets.filter((t) => t.type === 'backend').length}`);
|
|
848
|
+
console.log(` 🎨 Frontend: ${logicTickets.filter((t) => t.type === 'frontend').length}`);
|
|
849
|
+
console.log(` 🧪 Tests: ${logicTickets.filter((t) => t.type === 'test').length}`);
|
|
850
|
+
console.log(`\n📝 Total Tickets: ${totalTickets}`);
|
|
472
851
|
console.log(`${'='.repeat(80)}\n`);
|
|
473
852
|
// Display cost breakdown
|
|
474
853
|
const costBreakdown = formatCostBreakdown({
|
|
475
|
-
cost
|
|
476
|
-
tokensUsed
|
|
854
|
+
cost,
|
|
855
|
+
tokensUsed,
|
|
477
856
|
fixCount: 0,
|
|
478
857
|
response: '',
|
|
479
858
|
filesReferenced: new Set(),
|
|
480
859
|
});
|
|
481
860
|
console.log(`💰 Total Cost: ${costBreakdown}\n`);
|
|
482
861
|
// Final confirmation
|
|
483
|
-
|
|
484
|
-
const outputPath = join(result.projectPath, outputFilename);
|
|
485
|
-
console.log(`✅ All tickets saved to: ${outputPath}\n`);
|
|
862
|
+
console.log(`✅ All tickets saved to: ${ticketsFile}\n`);
|
|
486
863
|
console.log('✅ Ticket generation completed successfully!');
|
|
487
864
|
// Log successful execution
|
|
488
865
|
await logger.complete(logContext, 'success');
|