@kosuke-ai/cli 0.0.39 โ†’ 0.0.40

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.
Files changed (39) hide show
  1. package/dist/index.d.ts +4 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +41 -26
  4. package/dist/index.js.map +1 -1
  5. package/dist/kosuke/commands/build.d.ts.map +1 -1
  6. package/dist/kosuke/commands/build.js +30 -13
  7. package/dist/kosuke/commands/build.js.map +1 -1
  8. package/dist/kosuke/commands/migrate.d.ts +33 -0
  9. package/dist/kosuke/commands/migrate.d.ts.map +1 -0
  10. package/dist/kosuke/commands/migrate.js +196 -0
  11. package/dist/kosuke/commands/migrate.js.map +1 -0
  12. package/dist/kosuke/commands/ship.d.ts.map +1 -1
  13. package/dist/kosuke/commands/ship.js +4 -9
  14. package/dist/kosuke/commands/ship.js.map +1 -1
  15. package/dist/kosuke/commands/test.d.ts +6 -9
  16. package/dist/kosuke/commands/test.d.ts.map +1 -1
  17. package/dist/kosuke/commands/test.js +65 -169
  18. package/dist/kosuke/commands/test.js.map +1 -1
  19. package/dist/kosuke/commands/tickets.d.ts +22 -22
  20. package/dist/kosuke/commands/tickets.d.ts.map +1 -1
  21. package/dist/kosuke/commands/tickets.js +289 -674
  22. package/dist/kosuke/commands/tickets.js.map +1 -1
  23. package/dist/kosuke/types.d.ts +23 -14
  24. package/dist/kosuke/types.d.ts.map +1 -1
  25. package/dist/kosuke/utils/logger.d.ts +1 -1
  26. package/dist/kosuke/utils/logger.d.ts.map +1 -1
  27. package/dist/kosuke/utils/prompt-generator.d.ts +0 -4
  28. package/dist/kosuke/utils/prompt-generator.d.ts.map +1 -1
  29. package/dist/kosuke/utils/prompt-generator.js +0 -25
  30. package/dist/kosuke/utils/prompt-generator.js.map +1 -1
  31. package/dist/kosuke/utils/test-runner.d.ts.map +1 -1
  32. package/dist/kosuke/utils/test-runner.js +3 -5
  33. package/dist/kosuke/utils/test-runner.js.map +1 -1
  34. package/dist/lib.d.ts +2 -1
  35. package/dist/lib.d.ts.map +1 -1
  36. package/dist/lib.js +1 -0
  37. package/dist/lib.js.map +1 -1
  38. package/dist/package.json +1 -1
  39. package/package.json +1 -1
@@ -1,518 +1,266 @@
1
1
  /**
2
2
  * Tickets command - Generate tickets from requirements document
3
3
  *
4
- * This command analyzes a requirements document (default: docs.md) and generates
5
- * structured tickets with test coverage:
4
+ * This command analyzes a requirements document and generates structured tickets:
6
5
  *
7
- * SCAFFOLD BATCH:
8
- * 1. Schema scaffold (database infrastructure)
9
- * 2. DB test (validate scaffold schema)
10
- * 3. Backend scaffold (API infrastructure)
11
- * 4. Frontend scaffold (UI infrastructure)
12
- * 5. Web tests (validate scaffold E2E)
6
+ * SCAFFOLD MODE (--scaffold flag):
7
+ * SCAFFOLD BATCH (template adaptation):
8
+ * 1. SCAFFOLD-SCHEMA-1 (database infrastructure changes, auto-validated)
9
+ * 2. SCAFFOLD-BACKEND-X (API infrastructure changes)
10
+ * 3. SCAFFOLD-FRONTEND-X (UI infrastructure changes)
11
+ * 4. SCAFFOLD-WEB-TEST-X (validate scaffold E2E)
13
12
  *
14
- * LOGIC BATCHES (1..N):
15
- * 1. Schema logic (business entities)
16
- * 2. DB test (validate logic schema)
17
- * 3. Backend logic (business API)
18
- * 4. Frontend logic (business UI)
19
- * 5. Web tests (validate logic E2E)
13
+ * LOGIC BATCH (business functionality):
14
+ * 1. LOGIC-SCHEMA-1 (business entities, auto-validated)
15
+ * 2. LOGIC-BACKEND-1 (business API)
16
+ * 3. LOGIC-FRONTEND-1 (business UI)
17
+ * 4. LOGIC-WEB-TEST-1 (validate logic E2E)
18
+ * ... (multiple logic batches per feature)
20
19
  *
21
- * Claude Code Agent explores the specified directory (default: current directory)
22
- * to understand the existing codebase and generate contextual tickets.
20
+ * LOGIC-ONLY MODE (default):
21
+ * Only generates LOGIC tickets for new features
23
22
  *
24
- * All paths (--path and --output) are relative to the project directory.
23
+ * Claude Code Agent explores the codebase to understand the tech stack and
24
+ * generates contextual tickets in a single comprehensive analysis.
25
25
  *
26
26
  * Usage:
27
- * kosuke tickets # Use docs.md in current directory
27
+ * kosuke tickets # Logic-only mode, use docs.md
28
+ * kosuke tickets --scaffold # Scaffold + logic mode
28
29
  * kosuke tickets --path=custom.md # Custom requirements file
29
- * kosuke tickets --output=my-tickets.json # Custom output file
30
- * kosuke tickets --directory=./projects/my-app # Analyze specific directory
31
- * kosuke tickets --dir=./my-app --path=docs/spec.md # Custom directory and requirements path
30
+ * kosuke tickets --prompt="Add dark mode" # Inline requirements
31
+ * kosuke tickets --directory=./my-app # Analyze specific directory
32
32
  */
33
33
  import { existsSync, readFileSync, statSync, writeFileSync } from 'fs';
34
34
  import { join, resolve } from 'path';
35
35
  import { formatCostBreakdown, runAgent } from '../utils/claude-agent.js';
36
36
  import { logger, setupCancellationHandler } from '../utils/logger.js';
37
37
  /**
38
- * Analyze requirements to determine which layers are needed
38
+ * Build unified system prompt for comprehensive ticket generation
39
39
  */
40
- async function analyzeRequiredLayers(requirementsContent, projectPath) {
41
- console.log('๐Ÿ” Analyzing requirements to determine needed layers...\n');
42
- const systemPrompt = `You are an expert software architect analyzing requirements to determine which layers need changes.
40
+ function buildTicketPrompt(requirementsContent, projectPath, isScaffoldMode) {
41
+ const scaffoldGuidance = isScaffoldMode
42
+ ? `
43
+ **SCAFFOLD TICKETS - Template Adaptation ONLY:**
44
+
45
+ These tickets focus on removing, changing, or customizing the Kosuke Template baseline.
46
+ DO NOT add new business logic or features from requirements here.
47
+
48
+ Scaffold tickets should:
49
+ - โŒ REMOVE unused template features (e.g., organizations, billing, multi-tenancy)
50
+ - ๐Ÿ”„ CHANGE existing features (e.g., swap Better Auth for Clerk, simplify billing)
51
+ - ๐ŸŽจ CUSTOMIZE infrastructure (landing page, email templates, branding, navigation)
52
+
53
+ Examples of SCAFFOLD tickets:
54
+ - "Remove organization/multi-tenancy support from auth"
55
+ - "Simplify billing to single tier (remove pro/business tiers)"
56
+ - "Customize landing page for [specific use case]"
57
+ - "Remove landing page entirely (internal tool)"
58
+ - "Update email templates for [brand name]"
59
+
60
+ **SCAFFOLD Ticket Ordering:**
61
+ 1. SCAFFOLD-SCHEMA-1 (one ticket for all database infrastructure changes, auto-validated)
62
+ 2. SCAFFOLD-BACKEND-1, SCAFFOLD-BACKEND-2, ... (backend infrastructure tickets)
63
+ 3. SCAFFOLD-FRONTEND-1, SCAFFOLD-FRONTEND-2, ... (frontend infrastructure tickets)
64
+ 4. SCAFFOLD-WEB-TEST-1, SCAFFOLD-WEB-TEST-2, ... (E2E tests for scaffold)
65
+ `
66
+ : '';
67
+ const webTestGuidance = `
68
+ **WEB TEST TICKETS - Stagehand Agent E2E Tests:**
69
+
70
+ Web test tickets are executed by Stagehand agent and must follow these guidelines:
71
+
72
+ **Test User Discovery:**
73
+ 1. **ALWAYS read seed files** to find test user credentials:
74
+ - Look for files: lib/db/seed.ts, src/lib/db/seed.ts
75
+ - Pattern: Any email ending with "+kosuke_test@example.com" uses OTP code "424242"
76
+ - Example: john+kosuke_test@example.com โ†’ OTP: 424242
77
+ - Include all discovered test users in ticket description
78
+
79
+ **Ticket Structure Requirements:**
80
+ Each web test ticket MUST include:
81
+
82
+ 1. **Test User Credentials** (at the top)
83
+ - List all test users with their emails
84
+ - Document OTP code (424242)
85
+ - Specify user roles if applicable (admin, regular user, etc.)
86
+
87
+ 2. **Test Steps** (numbered, detailed natural language)
88
+ - Navigation instructions ("Navigate to /sign-in")
89
+ - User interactions ("Click button labeled 'New Task'")
90
+ - Input actions ("Enter 'Test Task' in title field")
91
+ - Expected outcomes after each step ("Expected: Task appears in list")
92
+ - Use CLEAR element descriptions (button text, labels, placeholders)
93
+ - Use relative paths only (e.g., /sign-in, /tasks) - base URL provided as test argument
94
+
95
+ 3. **Acceptance Criteria**
96
+ - Final expected state
97
+ - Data validation points
98
+ - User feedback confirmation
99
+
100
+ **Stagehand Best Practices:**
101
+ - Use natural language, NOT code
102
+ - Be SPECIFIC about element identification (button text, input labels, exact URLs)
103
+ - Include EXPECTED OUTCOMES after each major action
104
+ - Combine related flows into ONE ticket (signup โ†’ create โ†’ invite = 1 ticket)
105
+ - Authentication steps MUST be explicit:
106
+ 1. Navigate to /sign-in
107
+ 2. Enter email: {test_user}+kosuke_test@example.com
108
+ 3. Click "Send Code" button
109
+ 4. Enter OTP: 424242
110
+ 5. Click "Verify" button
111
+ 6. Expected: Redirected to dashboard/main app
112
+
113
+ **Example Web Test Ticket:**
43
114
 
44
- **Your Task:**
45
- Analyze the requirements and determine which layers (schema/backend/frontend) need changes.
115
+ {
116
+ "id": "LOGIC-WEB-TEST-1",
117
+ "title": "E2E: User signup and create first task",
118
+ "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",
119
+ "type": "test",
120
+ "estimatedEffort": 4,
121
+ "status": "Todo",
122
+ "category": "tasks"
123
+ }`;
124
+ return `You are an expert software architect generating implementation tickets.
46
125
 
47
- **Requirements:**
126
+ **Requirements Document:**
48
127
  ${requirementsContent}
49
128
 
50
- **Context:**
129
+ **Project Context:**
51
130
  You have access to the project directory at: ${projectPath}
52
- Explore the codebase to understand the existing architecture and tech stack.
53
-
54
- **Analysis Criteria:**
55
-
56
- **Schema (Database):**
57
- - New tables, columns, or relationships
58
- - Changes to existing database structure
59
- - Data model modifications
60
- - Examples: "Add comments to posts", "Track user preferences", "Store session data"
61
-
62
- **Backend (API):**
63
- - New API endpoints or business logic
64
- - Changes to existing endpoints
65
- - Server-side processing or validation
66
- - Integration with external services
67
- - Examples: "Export data to CSV", "Send email notifications", "Process payments"
68
-
69
- **Frontend (UI):**
70
- - New pages, components, or user interactions
71
- - Changes to existing UI
72
- - User-facing features
73
- - Examples: "Add dark mode toggle", "Create dashboard", "Build user profile page"
74
-
75
- **Important:**
76
- - Simple UI changes (styling, layout) typically DON'T need backend or schema changes
77
- - Features involving data persistence ALWAYS need schema + backend + frontend
78
- - API-only features (webhooks, cron jobs) may not need frontend changes
79
- - Be precise - only include layers that are actually needed
80
-
81
- **Output Format:**
82
- Return ONLY a valid JSON object with this structure:
83
- {
84
- "needsSchema": boolean,
85
- "needsBackend": boolean,
86
- "needsFrontend": boolean,
87
- "reasoning": "Brief explanation of why each layer is or isn't needed"
88
- }
89
-
90
- No markdown, no code blocks, just raw JSON.`;
91
- const agentResult = await runAgent('Analyze requirements and determine needed layers', {
92
- systemPrompt,
93
- cwd: projectPath,
94
- maxTurns: 15,
95
- verbosity: 'minimal',
96
- });
97
- // Parse response
98
- try {
99
- const jsonMatch = agentResult.response.match(/\{[\s\S]*\}/);
100
- if (!jsonMatch) {
101
- throw new Error('No JSON found in analysis response');
102
- }
103
- const analysis = JSON.parse(jsonMatch[0]);
104
- // Validate structure
105
- if (typeof analysis.needsSchema !== 'boolean' ||
106
- typeof analysis.needsBackend !== 'boolean' ||
107
- typeof analysis.needsFrontend !== 'boolean' ||
108
- typeof analysis.reasoning !== 'string') {
109
- throw new Error('Invalid analysis structure');
110
- }
111
- return analysis;
112
- }
113
- catch (error) {
114
- console.error('โŒ Failed to parse layer analysis:', error);
115
- console.error('Raw response:', agentResult.response.substring(0, 500));
116
- throw new Error(`Failed to analyze required layers: ${error instanceof Error ? error.message : String(error)}`);
117
- }
118
- }
119
- /**
120
- * Build system prompt for DB test ticket generation
121
- */
122
- function buildDBTestPrompt(batchType, requirementsContent, projectPath, previousSchemaTickets) {
123
- const ticketId = batchType === 'scaffold' ? 'DB-TEST-1' : 'DB-TEST-2';
124
- const schemaTicketsContext = previousSchemaTickets
125
- .map((t) => `**${t.id}: ${t.title}**\n${t.description}`)
126
- .join('\n\n');
127
- return `You are an expert QA engineer generating database validation test tickets.
128
131
 
129
- **Your Task:**
130
- Generate ONE database test ticket to validate the schema implementation from the tickets below.
132
+ ${scaffoldGuidance}
133
+ ${webTestGuidance}
131
134
 
132
- **Schema Tickets to Validate:**
133
- ${schemaTicketsContext}
135
+ **LOGIC TICKETS - Business Functionality:**
134
136
 
135
- **Database Test Ticket Goal:**
136
- Based on the schema tickets above, create a test that validates those tables were correctly created:
137
- 1. Extract all table names mentioned in the schema tickets
138
- 2. List all tables that need to be validated
139
- 3. Create a test ticket that checks those tables exist
137
+ These tickets implement the actual features and requirements from the document.
140
138
 
141
- **IMPORTANT:**
142
- - Do NOT explore the codebase
143
- - Do NOT look at existing schema files
144
- - ONLY use the schema tickets above to determine what tables to validate
145
- - The test should verify that the tables described in those tickets exist
139
+ Logic tickets should:
140
+ - ๐Ÿ—„๏ธ Create schema for business entities (tasks, projects, posts, etc.)
141
+ - โš™๏ธ Build backend APIs for business features
142
+ - ๐ŸŽจ Create frontend UI for business features
146
143
 
147
- **Ticket Structure:**
148
- - id: "${ticketId}"
149
- - title: Clear description of what schema is being validated
150
- - description: Detailed test plan with:
151
- * List of tables to validate (extracted from schema tickets above)
152
- * What to check: verify all tables exist
153
- * Success criteria: all tables from schema tickets exist in database
154
- - type: "db-test"
155
- - estimatedEffort: 1-3 (these are simple validation tests)
156
- - status: "Todo"
157
- - category: "database-validation"
144
+ **LOGIC Ticket Ordering:**
145
+ Each feature can have its own batch of tickets:
158
146
 
159
- **Output Format:**
160
- Return ONLY a valid JSON array with ONE ticket. No markdown, no code blocks, just raw JSON.
147
+ Feature 1:
148
+ 1. LOGIC-SCHEMA-1 (schema for feature 1, auto-validated)
149
+ 2. LOGIC-BACKEND-1 (backend for feature 1)
150
+ 3. LOGIC-FRONTEND-1 (frontend for feature 1)
151
+ 4. LOGIC-WEB-TEST-1 (E2E test for feature 1)
161
152
 
162
- Example:
163
- [
164
- {
165
- "id": "DB-TEST-1",
166
- "title": "Validate scaffold database schema",
167
- "description": "Verify that the scaffold schema has been correctly implemented based on SCHEMA-SCAFFOLD-1:\\n\\nTables to validate:\\n- users\\n- user_subscriptions\\n- notifications\\n\\nValidation checks:\\n- Verify all tables exist\\n- Check table names are correct\\n\\nAcceptance Criteria:\\n- All tables from SCHEMA-SCAFFOLD-1 exist in database\\n- No schema errors",
168
- "type": "db-test",
169
- "estimatedEffort": 2,
170
- "status": "Todo",
171
- "category": "database-validation"
172
- }
173
- ]
174
-
175
- **Critical Instructions:**
176
- 1. Analyze the schema tickets above to extract table names
177
- 2. Generate a focused test ticket that validates those specific tables exist
178
- 3. Reference the schema ticket IDs in the description
179
- 4. Keep descriptions clear and actionable
180
- 5. Return ONLY valid JSON - no explanations, no markdown formatting`;
181
- }
182
- /**
183
- * Build system prompt for Web test ticket generation
184
- */
185
- function buildWebTestPrompt(batchType, requirementsContent, projectPath, previousImplementationTickets, startingNumber) {
186
- const backendTicketsContext = previousImplementationTickets.backend
187
- .map((t) => `**${t.id}: ${t.title}**\n${t.description}`)
188
- .join('\n\n');
189
- const frontendTicketsContext = previousImplementationTickets.frontend
190
- .map((t) => `**${t.id}: ${t.title}**\n${t.description}`)
191
- .join('\n\n');
192
- return `You are an expert QA engineer generating end-to-end web test tickets.
193
-
194
- **Your Task:**
195
- Generate web test tickets to validate the implementation from the backend and frontend tickets below.
196
-
197
- **Backend Tickets to Validate:**
198
- ${backendTicketsContext || 'No backend tickets for this batch'}
199
-
200
- **Frontend Tickets to Validate:**
201
- ${frontendTicketsContext || 'No frontend tickets for this batch'}
202
-
203
- **Web Test Ticket Goals:**
204
- Based on the implementation tickets above, create tests that validate those features work end-to-end:
205
- 1. Analyze the backend and frontend tickets to understand what features were implemented
206
- 2. Create test tickets that verify those features work correctly in the browser
207
- 3. Focus on user-facing functionality and complete user flows
208
-
209
- **IMPORTANT:**
210
- - Do NOT explore the codebase
211
- - Do NOT look at existing frontend implementation
212
- - ONLY use the implementation tickets above to determine what to test
213
- - The tests should verify that the features described in those tickets work end-to-end
214
-
215
- Let Claude decide granularity based on complexity - could be:
216
- - One test per major user flow
217
- - One test covering multiple related features
218
- - Multiple tests for complex features
219
-
220
- **Ticket Structure:**
221
- Each ticket must have:
222
- - id: "WEB-TEST-${startingNumber}", "WEB-TEST-${startingNumber + 1}", etc. (sequential)
223
- - title: Clear description of what is being tested
224
- - description: Detailed test plan with:
225
- * Reference to implementation tickets being tested
226
- * User flow to test
227
- * Steps to execute
228
- * Expected outcomes (based on implementation tickets)
229
- * Success criteria
230
- - type: "web-test"
231
- - estimatedEffort: number (1-10 based on test complexity)
232
- - status: "Todo"
233
- - category: feature name being tested
234
-
235
- **Output Format:**
236
- Return ONLY a valid JSON array of tickets. No markdown, no code blocks, just raw JSON.
237
-
238
- Example:
239
- [
240
- {
241
- "id": "WEB-TEST-1",
242
- "title": "Test authentication flow (validates BACKEND-SCAFFOLD-1, FRONTEND-SCAFFOLD-1)",
243
- "description": "Validate that the authentication implementation from BACKEND-SCAFFOLD-1 and FRONTEND-SCAFFOLD-1 works end-to-end:\\n\\nTest Flow:\\n1. Navigate to sign-in page\\n2. Enter credentials\\n3. Submit form\\n4. Verify redirect to dashboard\\n5. Check user session is active\\n\\nExpected Results (from implementation tickets):\\n- Sign-in successful\\n- User redirected to dashboard\\n- Protected content visible\\n\\nAcceptance Criteria:\\n- Authentication works as described in BACKEND-SCAFFOLD-1\\n- UI matches FRONTEND-SCAFFOLD-1 requirements\\n- No console errors\\n- Session persists correctly",
244
- "type": "web-test",
245
- "estimatedEffort": 5,
246
- "status": "Todo",
247
- "category": "authentication"
248
- }
249
- ]
250
-
251
- **Critical Instructions:**
252
- 1. Analyze the implementation tickets above to extract features to test
253
- 2. Generate test tickets that validate those specific features
254
- 3. Reference the implementation ticket IDs in test descriptions
255
- 4. Focus on end-to-end user flows that span backend + frontend
256
- 5. Make descriptions detailed with clear steps
257
- 6. Return ONLY valid JSON - no explanations, no markdown formatting
258
- 7. Ensure ticket IDs are sequential starting from WEB-TEST-${startingNumber}`;
259
- }
260
- /**
261
- * Build system prompt for ticket generation with integrated analysis
262
- */
263
- function buildTicketGenerationPrompt(phase, ticketType, requirementsContent, projectPath, isScaffoldMode, previousTickets) {
264
- // Handle test tickets differently
265
- if (phase === 'db-test') {
266
- return buildDBTestPrompt(ticketType, requirementsContent, projectPath, previousTickets?.schema || []);
267
- }
268
- if (phase === 'web-test') {
269
- const startingNumber = previousTickets?.webTestStartNumber || 1;
270
- return buildWebTestPrompt(ticketType, requirementsContent, projectPath, {
271
- backend: previousTickets?.backend || [],
272
- frontend: previousTickets?.frontend || [],
273
- }, startingNumber);
274
- }
275
- const phaseTypeKey = `${phase}_${ticketType}`;
276
- const phaseInstructions = {
277
- schema_scaffold: `
278
- **GENERATE: ONE DATABASE SCHEMA SCAFFOLD TICKET**
279
-
280
- This ticket should handle infrastructure/setup database changes based on the analysis:
281
- - Modifications to auth tables (if organizations needed or removed)
282
- - Billing/subscription tables (if changed or removed)
283
- - Email verification/notification tables
284
- - Any template baseline schema adjustments
285
-
286
- **Key Focus:**
287
- - What needs to be REMOVED from template (e.g., organization tables if not needed)
288
- - What needs to be ADDED for infrastructure (e.g., organization support if needed)
289
- - Updates to existing template tables for new requirements
290
-
291
- Ticket ID: SCHEMA-SCAFFOLD-1
292
- `,
293
- schema_logic: `
294
- **GENERATE: ONE DATABASE SCHEMA BUSINESS LOGIC TICKET**
295
-
296
- This ticket should handle core business domain tables based on the analysis:
297
- - Main application entities (e.g., tasks, projects, posts, campaigns)
298
- - Business-specific relationships
299
- - Domain-specific fields and constraints
300
- - Application data models
301
-
302
- **Key Focus:**
303
- - Core business entities unique to this application
304
- - Relationships between business entities
305
- - NOT infrastructure tables (auth, billing, etc.)
306
-
307
- Ticket ID: SCHEMA-LOGIC-1
308
- `,
309
- backend_scaffold: `
310
- **GENERATE: BACKEND SCAFFOLD TICKETS**
311
-
312
- Generate tickets for infrastructure/setup backend changes:
313
- - Auth modifications (add/remove organization support, change providers)
314
- - Billing API changes (remove Stripe, add different tiers, etc.)
315
- - Email template setup (create transactional email templates)
316
- - Landing page API routes (if needed)
317
- - Third-party integrations setup
318
-
319
- **Granularity:**
320
- - ONE ticket per infrastructure area (auth, billing, email, landing)
321
- - Let Claude decide granularity based on complexity
322
- - Each ticket should be independently implementable
323
-
324
- Ticket IDs: BACKEND-SCAFFOLD-1, BACKEND-SCAFFOLD-2, etc. (sequential)
325
- `,
326
- backend_logic: `
327
- **GENERATE: BACKEND BUSINESS LOGIC TICKETS**
328
-
329
- Generate tickets for core application backend features:
330
- - API endpoints for business entities
331
- - Service layer logic
332
- - Business rules and validation
333
- - Application-specific data processing
334
- - Feature-specific integrations
335
-
336
- **Granularity:**
337
- - ONE ticket per feature/module
338
- - Let Claude decide granularity based on complexity
339
- - Each ticket should be independently implementable
340
-
341
- Ticket IDs: BACKEND-LOGIC-1, BACKEND-LOGIC-2, etc. (sequential)
342
- `,
343
- frontend_scaffold: `
344
- **GENERATE: FRONTEND SCAFFOLD TICKETS**
345
-
346
- Generate tickets for infrastructure/setup frontend changes:
347
- - Auth UI modifications (add/remove organization switcher, change auth flow)
348
- - Billing UI changes (subscription management, pricing page updates)
349
- - Landing page customization
350
- - Email template previews
351
- - Navigation/layout updates for infrastructure changes
352
-
353
- **Granularity:**
354
- - ONE ticket per infrastructure area
355
- - Let Claude decide granularity based on complexity
356
- - Each ticket should be independently implementable
357
-
358
- Ticket IDs: FRONTEND-SCAFFOLD-1, FRONTEND-SCAFFOLD-2, etc. (sequential)
359
- `,
360
- frontend_logic: `
361
- **GENERATE: FRONTEND BUSINESS LOGIC TICKETS**
362
-
363
- Generate tickets for core application frontend features:
364
- - Application-specific pages
365
- - Feature-specific UI components
366
- - Business logic forms
367
- - User workflows
368
- - Application state management
369
-
370
- **Granularity:**
371
- - ONE ticket per PAGE or major feature
372
- - Let Claude decide granularity based on complexity
373
- - Each ticket should cover all components and functionality for that area
374
-
375
- Ticket IDs: FRONTEND-LOGIC-1, FRONTEND-LOGIC-2, etc. (sequential)
376
- `,
377
- };
378
- const categoryGuidance = ticketType === 'scaffold'
379
- ? `
380
- **Category Assignment:**
381
- Assign appropriate category to each ticket:
382
- - "auth" - Authentication and authorization changes
383
- - "billing" - Payment and subscription changes
384
- - "email" - Transactional email setup
385
- - "landing" - Public marketing pages
386
- - "infrastructure" - General setup/configuration
387
- `
388
- : `
389
- **Category Assignment:**
390
- Assign appropriate category based on business domain:
391
- - Use the main entity name (e.g., "tasks", "projects", "campaigns")
392
- - Or use feature name (e.g., "user-management", "notifications", "analytics")
393
- - Keep categories consistent across related tickets
394
- `;
395
- const contextualGuidance = isScaffoldMode
396
- ? `
397
- **Kosuke Template Baseline (what the template already includes):**
398
- - **Authentication**: Better Auth with Email OTP
399
- - **User Model**: Individual users (no organizations/multi-tenancy by default)
400
- - **Billing**: Stripe with subscription tiers (free, pro, business)
401
- - **Email**: Resend for transactional emails
402
- - **Landing Page**: Basic marketing site with pricing
403
- - **Database**: PostgreSQL with Drizzle ORM
404
- - **Stack**: Next.js 15, React 19, TypeScript, Tailwind, Shadcn UI
405
-
406
- **Analysis Instructions:**
407
- Before generating tickets, analyze the requirements to understand:
408
- 1. **Auth**: Does it need organizations/multi-tenancy? Different auth provider?
409
- 2. **Billing**: Keep Stripe? Remove billing entirely? Different tiers?
410
- 3. **Email**: What transactional emails are needed? Custom templates?
411
- 4. **Landing**: Customize marketing pages? Remove landing page?
412
- 5. **Core Domain**: What are the main business entities and workflows?
413
-
414
- For SCAFFOLD tickets:
415
- - Identify what needs to be REMOVED from template (if not needed)
416
- - Identify what needs to be ADDED to template (if needed)
417
- - Identify what needs to be CUSTOMIZED (landing page, email templates, etc.)
418
-
419
- For LOGIC tickets:
420
- - Focus on core business functionality
421
- - Implement application-specific features
422
- - Build domain models and workflows
423
- `
424
- : `
425
- **Project Context:**
426
- Explore the codebase to understand:
427
- - Tech stack and framework versions (Next.js, React, etc.)
428
- - Existing architecture patterns (App Router, API routes, etc.)
429
- - Database schema structure and ORM (Drizzle, Prisma, etc.)
430
- - API route patterns and conventions
431
- - UI component library and styling approach
432
- - Testing framework and patterns
433
- - Authentication system (if any)
434
- - State management approach
153
+ Feature 2:
154
+ 5. LOGIC-BACKEND-2 (backend for feature 2, if no schema needed)
155
+ 6. LOGIC-FRONTEND-2 (frontend for feature 2)
156
+ 7. LOGIC-WEB-TEST-2 (E2E test for feature 2)
435
157
 
436
- **Critical Instructions:**
437
- - Generate tickets that follow existing patterns in the codebase
438
- - Use the same naming conventions, file structure, and code style
439
- - Leverage existing utilities and components where possible
440
- - Match the existing tech stack (don't introduce new frameworks)
441
- - Follow the project's architectural decisions
442
- - Maintain consistency with existing code quality standards
443
- `;
444
- return `You are an expert software architect generating implementation tickets for ${isScaffoldMode ? 'a Kosuke Template project' : 'an existing project'}.
158
+ **Ticket Granularity:**
159
+ - Schema: Usually ONE ticket per batch (scaffold or logic), automatically validated during build
160
+ - Backend: Let complexity decide (could be 1-5 tickets per batch)
161
+ - Frontend: Let complexity decide (could be 1-5 tickets per batch)
162
+ - Web Tests: Let complexity decide (1 test per major user flow)
445
163
 
446
164
  **Your Task:**
447
- ${phaseInstructions[phaseTypeKey]}
448
-
449
- **Requirements Document:**
450
- ${requirementsContent}
451
-
452
- ${contextualGuidance}
453
-
454
- **Context:**
455
- You have access to the project directory at: ${projectPath}
456
- ${isScaffoldMode ? 'The template baseline is documented in CLAUDE.md.' : ''}
457
- Explore the codebase to understand the tech stack, architecture patterns, and coding conventions.
165
+ 1. **Explore the codebase** using read_file, grep, codebase_search to understand:
166
+ - Current tech stack and framework versions
167
+ - Existing architecture patterns
168
+ - Database schema structure
169
+ - API route patterns
170
+ - UI component library and styling
171
+ ${isScaffoldMode ? ' - What template features are currently present\n - What needs to be removed, changed, or customized' : ''}
172
+
173
+ 2. **Discover test users** for web testing:
174
+ - Read seed files: lib/db/seed.ts, src/lib/db/seed.ts (use read_file or grep)
175
+ - Look for test user pattern: {name}+kosuke_test@example.com
176
+ - Document all test users found (email addresses)
177
+ - Note: All test users use OTP code 424242 for Better Auth
178
+ - Include test user credentials in ALL web test tickets
179
+
180
+ 3. **Analyze requirements** to determine:
181
+ - Which layers are needed (schema/backend/frontend)
182
+ - How to break down features into logical batches
183
+ - What user flows need E2E web tests
184
+
185
+ 4. **Generate ALL tickets** in the correct order:
186
+ ${isScaffoldMode ? ' - SCAFFOLD batch first (template adaptation)\n - LOGIC batches second (business features)' : ' - LOGIC batches only (business features)'}
187
+ - Follow the ticket ordering structure above
188
+ - Schema tickets are automatically validated during build (no separate test tickets needed)
189
+ - For web tests: Include test user credentials, detailed steps, and expected outcomes
458
190
 
459
191
  **Ticket Structure:**
460
192
  Each ticket must be a JSON object with:
461
- - id: string (e.g., "SCHEMA-SCAFFOLD-1", "BACKEND-LOGIC-2")
193
+ - id: string (e.g., "SCAFFOLD-SCHEMA-1", "LOGIC-BACKEND-2", "SCAFFOLD-WEB-TEST-1")
462
194
  - title: string (clear, concise title)
463
195
  - description: string (detailed description with acceptance criteria)
464
- - type: "${ticketType}" (scaffold or logic)
196
+ - type: "schema" | "backend" | "frontend" | "test"
465
197
  - estimatedEffort: number (1-10, where 1=very easy, 10=very complex)
466
- - status: "Todo" (all tickets start as Todo)
467
- - category: string (see guidance below)
468
- ${categoryGuidance}
198
+ - status: "Todo"
199
+ - category: string (e.g., "auth", "billing", "user-management", "tasks")
469
200
 
470
201
  **Output Format:**
471
- Return ONLY a valid JSON array of tickets. No markdown, no code blocks, just raw JSON.
202
+ Return ONLY a valid JSON array of ALL tickets in the correct order. No markdown, no code blocks, just raw JSON.
472
203
 
473
204
  Example:
474
205
  [
475
206
  {
476
- "id": "BACKEND-SCAFFOLD-1",
477
- "title": "Remove organization support from authentication",
478
- "description": "Remove multi-tenancy/organization features from the template:\\n- Remove organization tables from schema\\n- Update user model to remove org references\\n- Simplify auth middleware (no org context)\\n- Remove org switcher from UI\\n\\nAcceptance Criteria:\\n- All organization references removed from codebase\\n- Auth flow works for individual users only\\n- Database migrations remove org tables\\n- No compilation errors",
479
- "type": "scaffold",
480
- "estimatedEffort": 6,
207
+ "id": "SCAFFOLD-SCHEMA-1",
208
+ "title": "Remove organizations and simplify auth schema",
209
+ "description": "Remove multi-tenancy/organization features from database:\\n- Drop organization tables\\n- Remove org foreign keys from users table\\n- Simplify schema to individual users only\\n\\nAcceptance Criteria:\\n- Organization tables removed\\n- User table simplified\\n- Migrations created and validated automatically\\n- No schema errors",
210
+ "type": "schema",
211
+ "estimatedEffort": 5,
481
212
  "status": "Todo",
482
213
  "category": "auth"
214
+ },
215
+ {
216
+ "id": "LOGIC-WEB-TEST-1",
217
+ "title": "E2E: User creates and manages a task",
218
+ "description": "**Test User Credentials:**\\n- Email: john+kosuke_test@example.com\\n- OTP Code: 424242\\n\\n**Test Steps:**\\n\\n1. **Authenticate as test user**\\n - Navigate to /sign-in\\n - Enter email: john+kosuke_test@example.com\\n - Click 'Send Code' button\\n - Enter OTP: 424242\\n - Click 'Verify' button\\n - Expected: Redirected to /tasks dashboard\\n\\n2. **Create new task**\\n - Click 'New Task' button\\n - Enter title: 'Test Task'\\n - Select priority: 'High'\\n - Click 'Create' button\\n - Expected: Task appears in task list\\n - Expected: Success toast notification shown\\n\\n3. **Edit task**\\n - Click on the created task\\n - Change title to: 'Updated Task'\\n - Expected: Task title updates immediately\\n\\n4. **Delete task**\\n - Click delete icon on task\\n - Confirm deletion in dialog\\n - Expected: Task removed from list\\n\\n**Acceptance Criteria:**\\n- User successfully authenticates\\n- Task creation, editing, and deletion work\\n- UI provides appropriate feedback\\n- Changes persist correctly",
219
+ "type": "test",
220
+ "estimatedEffort": 5,
221
+ "status": "Todo",
222
+ "category": "tasks"
483
223
  }
484
224
  ]
485
225
 
486
226
  **Critical Instructions:**
487
- 1. Analyze requirements against template baseline
488
- 2. Explore the project directory to understand existing patterns (use read_file, grep, codebase_search)
489
- 3. Generate tickets that are actionable and specific
490
- 4. Return ONLY valid JSON - no explanations, no markdown formatting
491
- 5. Ensure ticket IDs follow the naming convention (${phase.toUpperCase()}-${ticketType.toUpperCase()}-N)
492
- 6. Make descriptions detailed with clear acceptance criteria
493
- 7. Estimate effort realistically (consider complexity, dependencies, testing)
494
- 8. Assign appropriate categories for organization and filtering
495
- 9. For scaffold tickets: focus on infrastructure changes (add/remove/customize template features)
496
- 10. For logic tickets: focus on business domain implementation
497
-
498
- Begin by exploring the project directory, analyzing requirements, then generate the tickets.`;
227
+ 1. Explore the project directory thoroughly before generating tickets
228
+ 2. ${isScaffoldMode ? 'For SCAFFOLD: Focus on template adaptation ONLY (remove/change/customize)' : ''}
229
+ 3. For LOGIC: Focus on business features from requirements
230
+ 4. **IMPORTANT**: Read seed files (lib/db/seed.ts or src/lib/db/seed.ts) to discover test users
231
+ 5. **SCHEMA TICKETS**: No separate test tickets needed - validation happens automatically during build
232
+ 6. **WEB TESTS MUST INCLUDE**:
233
+ - Test user credentials at the top
234
+ - Clear numbered steps with natural language
235
+ - Expected outcomes after each step
236
+ - Specific element descriptions (button text, labels, URLs)
237
+ - Complete user flows in one ticket (signup โ†’ create โ†’ invite = 1 ticket)
238
+ 7. Follow the exact ticket ordering structure
239
+ 8. Make descriptions detailed with clear acceptance criteria
240
+ 9. Return ONLY valid JSON - no explanations, no markdown
241
+ 10. Ensure sequential ticket IDs match the ordering structure
242
+
243
+ Begin by:
244
+ 1. Reading seed files to discover test users
245
+ 2. Exploring the project directory structure
246
+ 3. Generating ALL tickets in the correct order with test user info in web tests.`;
499
247
  }
500
248
  /**
501
- * Parse tickets from Claude's response
249
+ * Parse all tickets from Claude's response
502
250
  */
503
- function parseTicketsFromResponse(response, phase, ticketType) {
251
+ function parseAllTickets(response) {
504
252
  try {
505
253
  // Extract JSON from response (in case Claude includes extra text)
506
254
  const jsonMatch = response.match(/\[[\s\S]*\]/);
507
255
  if (!jsonMatch) {
508
- throw new Error(`No JSON array found in ${phase} ${ticketType} response`);
256
+ throw new Error('No JSON array found in response');
509
257
  }
510
258
  const tickets = JSON.parse(jsonMatch[0]);
511
259
  // Validate tickets
512
260
  if (!Array.isArray(tickets)) {
513
261
  throw new Error(`Expected array of tickets, got ${typeof tickets}`);
514
262
  }
515
- const validTypes = ['scaffold', 'logic', 'db-test', 'web-test'];
263
+ const validTypes = ['schema', 'backend', 'frontend', 'test'];
516
264
  for (const ticket of tickets) {
517
265
  if (!ticket.id || !ticket.title || !ticket.description) {
518
266
  throw new Error(`Invalid ticket structure: ${JSON.stringify(ticket)}`);
@@ -532,13 +280,13 @@ function parseTicketsFromResponse(response, phase, ticketType) {
532
280
  return tickets;
533
281
  }
534
282
  catch (error) {
535
- console.error(`\nโŒ Failed to parse tickets from ${phase} ${ticketType} phase:`);
283
+ console.error('\nโŒ Failed to parse tickets from response:');
536
284
  console.error(`Raw response:\n${response.substring(0, 500)}...\n`);
537
- throw new Error(`Failed to parse ${phase} ${ticketType} tickets: ${error instanceof Error ? error.message : String(error)}`);
285
+ throw new Error(`Failed to parse tickets: ${error instanceof Error ? error.message : String(error)}`);
538
286
  }
539
287
  }
540
288
  /**
541
- * Write tickets to file incrementally
289
+ * Write tickets to output file
542
290
  */
543
291
  function writeTicketsToFile(outputPath, tickets) {
544
292
  const outputData = {
@@ -549,65 +297,7 @@ function writeTicketsToFile(outputPath, tickets) {
549
297
  writeFileSync(outputPath, JSON.stringify(outputData, null, 2), 'utf-8');
550
298
  }
551
299
  /**
552
- * Generate tickets for a specific phase and type
553
- */
554
- async function generatePhaseTickets(phase, ticketType, requirementsContent, projectPath, outputPath, existingTickets, isScaffoldMode, previousTickets) {
555
- const phaseEmoji = {
556
- schema: '๐Ÿ—„๏ธ',
557
- backend: 'โš™๏ธ',
558
- frontend: '๐ŸŽจ',
559
- 'db-test': '๐Ÿงช',
560
- 'web-test': '๐ŸŒ',
561
- };
562
- const typeEmoji = {
563
- scaffold: '๐Ÿ—๏ธ',
564
- logic: '๐Ÿ’ก',
565
- 'db-test': '๐Ÿงช',
566
- 'web-test': '๐ŸŒ',
567
- };
568
- const phaseName = {
569
- schema: 'Schema',
570
- backend: 'Backend',
571
- frontend: 'Frontend',
572
- 'db-test': 'DB Test',
573
- 'web-test': 'Web Test',
574
- };
575
- const typeName = {
576
- scaffold: 'Scaffold',
577
- logic: 'Logic',
578
- 'db-test': 'DB Test',
579
- 'web-test': 'Web Test',
580
- };
581
- console.log(`\n${'='.repeat(60)}`);
582
- console.log(`${phaseEmoji[phase]} ${typeEmoji[ticketType]} ${phaseName[phase]} ${typeName[ticketType]} Tickets`);
583
- console.log(`${'='.repeat(60)}\n`);
584
- const systemPrompt = buildTicketGenerationPrompt(phase, ticketType, requirementsContent, projectPath, isScaffoldMode, previousTickets);
585
- const agentResult = await runAgent(`Generate ${phaseName[phase]} ${typeName[ticketType]} tickets from the requirements.`, {
586
- systemPrompt,
587
- cwd: projectPath,
588
- maxTurns: 25,
589
- verbosity: 'normal',
590
- captureConversation: true,
591
- });
592
- // Parse tickets from response
593
- const tickets = parseTicketsFromResponse(agentResult.response, phase, ticketType);
594
- console.log(`\nโœ… Generated ${tickets.length} ${phaseName[phase]} ${typeName[ticketType]} ticket${tickets.length === 1 ? '' : 's'}`);
595
- tickets.forEach((ticket) => {
596
- console.log(` ${phaseEmoji[phase]} ${ticket.id}: ${ticket.title} (Effort: ${ticket.estimatedEffort}/10${ticket.category ? `, Category: ${ticket.category}` : ''})`);
597
- });
598
- // Write tickets incrementally after each phase
599
- const allTickets = [...existingTickets, ...tickets];
600
- writeTicketsToFile(outputPath, allTickets);
601
- console.log(` ๐Ÿ’พ Progress saved to: ${outputPath}\n`);
602
- return {
603
- tickets,
604
- tokensUsed: agentResult.tokensUsed,
605
- cost: agentResult.cost,
606
- conversationMessages: agentResult.conversationMessages || [],
607
- };
608
- }
609
- /**
610
- * Core tickets logic
300
+ * Core tickets logic - Simplified to single agent call
611
301
  */
612
302
  export async function ticketsCore(options) {
613
303
  const { directory, scaffold = false } = options;
@@ -624,7 +314,7 @@ export async function ticketsCore(options) {
624
314
  throw new Error(`Path is not a directory: ${projectPath}\n` + `Please provide a valid directory path.`);
625
315
  }
626
316
  console.log(`๐Ÿ“ Using project directory: ${projectPath}`);
627
- console.log(`๐Ÿ—๏ธ Mode: ${isScaffoldMode ? 'Scaffold (infrastructure + logic)' : 'Logic-only (smart layer detection)'}\n`);
317
+ console.log(`๐Ÿ—๏ธ Mode: ${isScaffoldMode ? 'Scaffold (template adaptation + business logic)' : 'Logic-only (business features)'}\n`);
628
318
  // 2. Get requirements content (from prompt or file)
629
319
  let requirementsContent;
630
320
  if (options.prompt && options.path) {
@@ -659,143 +349,71 @@ export async function ticketsCore(options) {
659
349
  requirementsContent = readFileSync(requirementsPath, 'utf-8');
660
350
  console.log(`๐Ÿ“„ Loaded ${defaultPath} (${requirementsContent.length} characters)\n`);
661
351
  }
662
- // 3. Determine output path for incremental writes
352
+ // 3. Determine output path
663
353
  const outputFilename = options.output || 'tickets.json';
664
354
  const outputPath = join(projectPath, outputFilename);
665
- // 4. Analyze required layers (only in logic-only mode)
666
- let layerAnalysis = null;
667
- if (!isScaffoldMode) {
668
- layerAnalysis = await analyzeRequiredLayers(requirementsContent, projectPath);
669
- console.log(`\n๐Ÿ“Š Layer Analysis:`);
670
- console.log(` Schema (DB): ${layerAnalysis.needsSchema ? 'โœ… Required' : 'โญ๏ธ Skip'}`);
671
- console.log(` Backend (API): ${layerAnalysis.needsBackend ? 'โœ… Required' : 'โญ๏ธ Skip'}`);
672
- console.log(` Frontend (UI): ${layerAnalysis.needsFrontend ? 'โœ… Required' : 'โญ๏ธ Skip'}`);
673
- console.log(`\n๐Ÿ’ญ Reasoning: ${layerAnalysis.reasoning}\n`);
355
+ // 4. Generate ALL tickets in a single comprehensive agent call
356
+ console.log(`\n${'='.repeat(80)}`);
357
+ console.log('๐ŸŽฏ Generating Tickets with Claude Code Agent');
358
+ console.log(`${'='.repeat(80)}\n`);
359
+ const systemPrompt = buildTicketPrompt(requirementsContent, projectPath, isScaffoldMode);
360
+ const agentResult = await runAgent('Generate all tickets from requirements', {
361
+ systemPrompt,
362
+ cwd: projectPath,
363
+ maxTurns: 40, // Give Claude enough turns to explore codebase and generate all tickets
364
+ verbosity: 'normal',
365
+ captureConversation: true,
366
+ });
367
+ // 5. Parse all tickets from single response
368
+ const allTickets = parseAllTickets(agentResult.response);
369
+ // 6. Display tickets by batch (scaffold vs logic based on ID) and by type
370
+ const scaffoldTickets = allTickets.filter((t) => t.id.toUpperCase().startsWith('SCAFFOLD-'));
371
+ const logicTickets = allTickets.filter((t) => t.id.toUpperCase().startsWith('LOGIC-'));
372
+ const testTickets = allTickets.filter((t) => t.type === 'test');
373
+ if (scaffoldTickets.length > 0) {
374
+ console.log(`\n${'='.repeat(60)}`);
375
+ console.log('๐Ÿ—๏ธ SCAFFOLD Tickets (Template Adaptation)');
376
+ console.log(`${'='.repeat(60)}`);
377
+ scaffoldTickets.forEach((ticket) => {
378
+ const emoji = ticket.id.includes('SCHEMA')
379
+ ? '๐Ÿ—„๏ธ'
380
+ : ticket.id.includes('BACKEND')
381
+ ? 'โš™๏ธ'
382
+ : ticket.id.includes('FRONTEND')
383
+ ? '๐ŸŽจ'
384
+ : '๐ŸŒ';
385
+ console.log(` ${emoji} ${ticket.id}: ${ticket.title} (Effort: ${ticket.estimatedEffort}/10${ticket.category ? `, Category: ${ticket.category}` : ''})`);
386
+ });
674
387
  }
675
- // 5. Generate tickets in the new structure with tests
676
- let totalInputTokens = 0;
677
- let totalOutputTokens = 0;
678
- let totalCacheCreationTokens = 0;
679
- let totalCacheReadTokens = 0;
680
- let totalCost = 0;
681
- // Collect all conversation messages from all phases
682
- const allConversationMessages = [];
683
- // Track all tickets
684
- let allTickets = [];
685
- // Helper to add metrics from a phase result
686
- const addMetrics = (result) => {
687
- allTickets = [...allTickets, ...result.tickets];
688
- totalInputTokens += result.tokensUsed.input;
689
- totalOutputTokens += result.tokensUsed.output;
690
- totalCacheCreationTokens += result.tokensUsed.cacheCreation;
691
- totalCacheReadTokens += result.tokensUsed.cacheRead;
692
- totalCost += result.cost;
693
- allConversationMessages.push(...result.conversationMessages);
694
- };
695
- // ==================== SCAFFOLD MODE ====================
696
- if (isScaffoldMode) {
697
- console.log('\n' + '='.repeat(80));
698
- console.log('๐Ÿ—๏ธ SCAFFOLD BATCH - Infrastructure Setup');
699
- console.log('='.repeat(80));
700
- // Track batch tickets for test generation
701
- let batchSchemaTickets = [];
702
- let batchBackendTickets = [];
703
- let batchFrontendTickets = [];
704
- // 1. Schema Scaffold
705
- const schemaScaffoldResult = await generatePhaseTickets('schema', 'scaffold', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode);
706
- addMetrics(schemaScaffoldResult);
707
- batchSchemaTickets = schemaScaffoldResult.tickets;
708
- // 2. DB Test (validate scaffold schema)
709
- addMetrics(await generatePhaseTickets('db-test', 'scaffold', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode, { schema: batchSchemaTickets }));
710
- // 3. Backend Scaffold
711
- const backendScaffoldResult = await generatePhaseTickets('backend', 'scaffold', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode);
712
- addMetrics(backendScaffoldResult);
713
- batchBackendTickets = backendScaffoldResult.tickets;
714
- // 4. Frontend Scaffold
715
- const frontendScaffoldResult = await generatePhaseTickets('frontend', 'scaffold', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode);
716
- addMetrics(frontendScaffoldResult);
717
- batchFrontendTickets = frontendScaffoldResult.tickets;
718
- // 5. Web Tests (validate scaffold E2E)
719
- addMetrics(await generatePhaseTickets('web-test', 'scaffold', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode, {
720
- backend: batchBackendTickets,
721
- frontend: batchFrontendTickets,
722
- webTestStartNumber: 1,
723
- }));
724
- // ==================== LOGIC BATCH ====================
725
- console.log('\n' + '='.repeat(80));
726
- console.log('๐Ÿ’ก LOGIC BATCH - Business Functionality');
727
- console.log('='.repeat(80));
728
- // Reset batch tracking for logic
729
- batchSchemaTickets = [];
730
- batchBackendTickets = [];
731
- batchFrontendTickets = [];
732
- // 1. Schema Logic
733
- const schemaLogicResult = await generatePhaseTickets('schema', 'logic', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode);
734
- addMetrics(schemaLogicResult);
735
- batchSchemaTickets = schemaLogicResult.tickets;
736
- // 2. DB Test (validate logic schema)
737
- addMetrics(await generatePhaseTickets('db-test', 'logic', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode, { schema: batchSchemaTickets }));
738
- // 3. Backend Logic
739
- const backendLogicResult = await generatePhaseTickets('backend', 'logic', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode);
740
- addMetrics(backendLogicResult);
741
- batchBackendTickets = backendLogicResult.tickets;
742
- // 4. Frontend Logic
743
- const frontendLogicResult = await generatePhaseTickets('frontend', 'logic', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode);
744
- addMetrics(frontendLogicResult);
745
- batchFrontendTickets = frontendLogicResult.tickets;
746
- // 5. Web Tests (validate logic E2E)
747
- const currentWebTestCount = allTickets.filter((t) => t.id.startsWith('WEB-TEST-')).length;
748
- addMetrics(await generatePhaseTickets('web-test', 'logic', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode, {
749
- backend: batchBackendTickets,
750
- frontend: batchFrontendTickets,
751
- webTestStartNumber: currentWebTestCount + 1,
752
- }));
388
+ if (logicTickets.length > 0) {
389
+ console.log(`\n${'='.repeat(60)}`);
390
+ console.log('๐Ÿ’ก LOGIC Tickets (Business Functionality)');
391
+ console.log(`${'='.repeat(60)}`);
392
+ logicTickets.forEach((ticket) => {
393
+ const emoji = ticket.id.includes('SCHEMA')
394
+ ? '๐Ÿ—„๏ธ'
395
+ : ticket.id.includes('BACKEND')
396
+ ? 'โš™๏ธ'
397
+ : '๐ŸŽจ';
398
+ console.log(` ${emoji} ${ticket.id}: ${ticket.title} (Effort: ${ticket.estimatedEffort}/10${ticket.category ? `, Category: ${ticket.category}` : ''})`);
399
+ });
753
400
  }
754
- else {
755
- // ==================== LOGIC-ONLY MODE ====================
756
- console.log('\n' + '='.repeat(80));
757
- console.log('๐Ÿ’ก LOGIC-ONLY MODE - Smart Layer Detection');
758
- console.log('='.repeat(80));
759
- // Track tickets for test generation
760
- let schemaTickets = [];
761
- let backendTickets = [];
762
- let frontendTickets = [];
763
- // 1. Schema Logic (if needed)
764
- if (layerAnalysis?.needsSchema) {
765
- const schemaLogicResult = await generatePhaseTickets('schema', 'logic', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode);
766
- addMetrics(schemaLogicResult);
767
- schemaTickets = schemaLogicResult.tickets;
768
- // DB Test (validate schema)
769
- if (schemaTickets.length > 0) {
770
- addMetrics(await generatePhaseTickets('db-test', 'logic', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode, { schema: schemaTickets }));
771
- }
772
- }
773
- // 2. Backend Logic (if needed)
774
- if (layerAnalysis?.needsBackend) {
775
- const backendLogicResult = await generatePhaseTickets('backend', 'logic', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode);
776
- addMetrics(backendLogicResult);
777
- backendTickets = backendLogicResult.tickets;
778
- }
779
- // 3. Frontend Logic (if needed)
780
- if (layerAnalysis?.needsFrontend) {
781
- const frontendLogicResult = await generatePhaseTickets('frontend', 'logic', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode);
782
- addMetrics(frontendLogicResult);
783
- frontendTickets = frontendLogicResult.tickets;
784
- }
785
- // 4. Web Tests (if backend or frontend tickets generated)
786
- if (backendTickets.length > 0 || frontendTickets.length > 0) {
787
- addMetrics(await generatePhaseTickets('web-test', 'logic', requirementsContent, projectPath, outputPath, allTickets, isScaffoldMode, {
788
- backend: backendTickets,
789
- frontend: frontendTickets,
790
- webTestStartNumber: 1,
791
- }));
792
- }
401
+ if (testTickets.length > 0) {
402
+ console.log(`\n${'='.repeat(60)}`);
403
+ console.log('๐Ÿงช TEST Tickets (E2E Validation)');
404
+ console.log(`${'='.repeat(60)}`);
405
+ testTickets.forEach((ticket) => {
406
+ console.log(` ๐ŸŒ ${ticket.id}: ${ticket.title} (Effort: ${ticket.estimatedEffort}/10)`);
407
+ });
793
408
  }
794
- // Separate tickets by phase for result
795
- const schemaTickets = allTickets.filter((t) => t.id.startsWith('SCHEMA-'));
796
- const backendTickets = allTickets.filter((t) => t.id.startsWith('BACKEND-'));
797
- const frontendTickets = allTickets.filter((t) => t.id.startsWith('FRONTEND-'));
798
- const testTickets = allTickets.filter((t) => t.id.startsWith('DB-TEST-') || t.id.startsWith('WEB-TEST-'));
409
+ // 7. Write tickets to file
410
+ writeTicketsToFile(outputPath, allTickets);
411
+ console.log(`\n๐Ÿ’พ Tickets saved to: ${outputPath}`);
412
+ // 8. Separate tickets by phase for compatibility with existing result structure
413
+ const schemaTickets = allTickets.filter((t) => t.type === 'schema');
414
+ const backendTickets = allTickets.filter((t) => t.type === 'backend');
415
+ const frontendTickets = allTickets.filter((t) => t.type === 'frontend');
416
+ // testTickets already declared above for display
799
417
  return {
800
418
  schemaTickets,
801
419
  backendTickets,
@@ -803,14 +421,9 @@ export async function ticketsCore(options) {
803
421
  testTickets,
804
422
  totalTickets: allTickets.length,
805
423
  projectPath,
806
- tokensUsed: {
807
- input: totalInputTokens,
808
- output: totalOutputTokens,
809
- cacheCreation: totalCacheCreationTokens,
810
- cacheRead: totalCacheReadTokens,
811
- },
812
- cost: totalCost,
813
- conversationMessages: allConversationMessages,
424
+ tokensUsed: agentResult.tokensUsed,
425
+ cost: agentResult.cost,
426
+ conversationMessages: agentResult.conversationMessages || [],
814
427
  };
815
428
  }
816
429
  /**
@@ -832,35 +445,37 @@ export async function ticketsCommand(options) {
832
445
  // Track metrics
833
446
  logger.trackTokens(logContext, result.tokensUsed);
834
447
  logContext.conversationMessages = result.conversationMessages;
835
- // Display summary
448
+ // Get all tickets by batch (scaffold vs logic)
836
449
  const scaffoldTickets = [
837
450
  ...result.schemaTickets,
838
451
  ...result.backendTickets,
839
452
  ...result.frontendTickets,
840
- ].filter((t) => t.type === 'scaffold');
453
+ ...result.testTickets,
454
+ ].filter((t) => t.id.toUpperCase().startsWith('SCAFFOLD-'));
841
455
  const logicTickets = [
842
456
  ...result.schemaTickets,
843
457
  ...result.backendTickets,
844
458
  ...result.frontendTickets,
845
- ].filter((t) => t.type === 'logic');
846
- const dbTestTickets = result.testTickets.filter((t) => t.type === 'db-test');
847
- const webTestTickets = result.testTickets.filter((t) => t.type === 'web-test');
848
- console.log(`\n${'='.repeat(60)}`);
459
+ ...result.testTickets,
460
+ ].filter((t) => t.id.toUpperCase().startsWith('LOGIC-'));
461
+ // Display summary
462
+ console.log(`\n${'='.repeat(80)}`);
849
463
  console.log('๐Ÿ“Š Ticket Generation Summary');
850
- console.log(`${'='.repeat(60)}`);
851
- console.log(`\n๐Ÿ—๏ธ Scaffold Tickets (Infrastructure): ${scaffoldTickets.length}`);
852
- console.log(` ๐Ÿ—„๏ธ Schema: ${result.schemaTickets.filter((t) => t.type === 'scaffold').length}`);
853
- console.log(` โš™๏ธ Backend: ${result.backendTickets.filter((t) => t.type === 'scaffold').length}`);
854
- console.log(` ๐ŸŽจ Frontend: ${result.frontendTickets.filter((t) => t.type === 'scaffold').length}`);
464
+ console.log(`${'='.repeat(80)}`);
465
+ if (scaffoldTickets.length > 0) {
466
+ console.log(`\n๐Ÿ—๏ธ Scaffold Tickets (Template Adaptation): ${scaffoldTickets.length}`);
467
+ console.log(` ๐Ÿ—„๏ธ Schema: ${result.schemaTickets.filter((t) => t.id.toUpperCase().startsWith('SCAFFOLD-')).length} (auto-validated)`);
468
+ console.log(` โš™๏ธ Backend: ${result.backendTickets.filter((t) => t.id.toUpperCase().startsWith('SCAFFOLD-')).length}`);
469
+ console.log(` ๐ŸŽจ Frontend: ${result.frontendTickets.filter((t) => t.id.toUpperCase().startsWith('SCAFFOLD-')).length}`);
470
+ console.log(` ๐Ÿงช Tests: ${result.testTickets.filter((t) => t.id.toUpperCase().startsWith('SCAFFOLD-')).length}`);
471
+ }
855
472
  console.log(`\n๐Ÿ’ก Logic Tickets (Business Functionality): ${logicTickets.length}`);
856
- console.log(` ๐Ÿ—„๏ธ Schema: ${result.schemaTickets.filter((t) => t.type === 'logic').length}`);
857
- console.log(` โš™๏ธ Backend: ${result.backendTickets.filter((t) => t.type === 'logic').length}`);
858
- console.log(` ๐ŸŽจ Frontend: ${result.frontendTickets.filter((t) => t.type === 'logic').length}`);
859
- console.log(`\n๐Ÿงช Test Tickets: ${result.testTickets.length}`);
860
- console.log(` ๐Ÿงช Database Tests: ${dbTestTickets.length}`);
861
- console.log(` ๐ŸŒ Web Tests: ${webTestTickets.length}`);
473
+ console.log(` ๐Ÿ—„๏ธ Schema: ${result.schemaTickets.filter((t) => t.id.toUpperCase().startsWith('LOGIC-')).length} (auto-validated)`);
474
+ console.log(` โš™๏ธ Backend: ${result.backendTickets.filter((t) => t.id.toUpperCase().startsWith('LOGIC-')).length}`);
475
+ console.log(` ๐ŸŽจ Frontend: ${result.frontendTickets.filter((t) => t.id.toUpperCase().startsWith('LOGIC-')).length}`);
476
+ console.log(` ๐Ÿงช Tests: ${result.testTickets.filter((t) => t.id.toUpperCase().startsWith('LOGIC-')).length}`);
862
477
  console.log(`\n๐Ÿ“ Total Tickets: ${result.totalTickets}`);
863
- console.log(`${'='.repeat(60)}\n`);
478
+ console.log(`${'='.repeat(80)}\n`);
864
479
  // Display cost breakdown
865
480
  const costBreakdown = formatCostBreakdown({
866
481
  cost: result.cost,
@@ -870,7 +485,7 @@ export async function ticketsCommand(options) {
870
485
  filesReferenced: new Set(),
871
486
  });
872
487
  console.log(`๐Ÿ’ฐ Total Cost: ${costBreakdown}\n`);
873
- // Final confirmation (file already written incrementally)
488
+ // Final confirmation
874
489
  const outputFilename = options.output || 'tickets.json';
875
490
  const outputPath = join(result.projectPath, outputFilename);
876
491
  console.log(`โœ… All tickets saved to: ${outputPath}\n`);