@kosuke-ai/cli 0.0.37 โ†’ 0.0.39

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 (37) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +101 -70
  3. package/dist/index.js.map +1 -1
  4. package/dist/kosuke/commands/build.d.ts +2 -1
  5. package/dist/kosuke/commands/build.d.ts.map +1 -1
  6. package/dist/kosuke/commands/build.js +158 -57
  7. package/dist/kosuke/commands/build.js.map +1 -1
  8. package/dist/kosuke/commands/test.d.ts +21 -8
  9. package/dist/kosuke/commands/test.d.ts.map +1 -1
  10. package/dist/kosuke/commands/test.js +211 -75
  11. package/dist/kosuke/commands/test.js.map +1 -1
  12. package/dist/kosuke/commands/tickets.d.ts +15 -4
  13. package/dist/kosuke/commands/tickets.d.ts.map +1 -1
  14. package/dist/kosuke/commands/tickets.js +664 -124
  15. package/dist/kosuke/commands/tickets.js.map +1 -1
  16. package/dist/kosuke/types.d.ts +32 -9
  17. package/dist/kosuke/types.d.ts.map +1 -1
  18. package/dist/kosuke/utils/prompt-generator.d.ts +4 -4
  19. package/dist/kosuke/utils/prompt-generator.d.ts.map +1 -1
  20. package/dist/kosuke/utils/prompt-generator.js +22 -11
  21. package/dist/kosuke/utils/prompt-generator.js.map +1 -1
  22. package/dist/kosuke/utils/test-runner.d.ts.map +1 -1
  23. package/dist/kosuke/utils/test-runner.js +13 -4
  24. package/dist/kosuke/utils/test-runner.js.map +1 -1
  25. package/dist/kosuke/utils/tickets-manager.d.ts +0 -4
  26. package/dist/kosuke/utils/tickets-manager.d.ts.map +1 -1
  27. package/dist/kosuke/utils/tickets-manager.js +1 -1
  28. package/dist/kosuke/utils/tickets-manager.js.map +1 -1
  29. package/dist/lib.d.ts +1 -1
  30. package/dist/lib.d.ts.map +1 -1
  31. package/dist/lib.js.map +1 -1
  32. package/dist/package.json +2 -2
  33. package/package.json +2 -2
  34. package/dist/kosuke/utils/browser-agent.d.ts +0 -33
  35. package/dist/kosuke/utils/browser-agent.d.ts.map +0 -1
  36. package/dist/kosuke/utils/browser-agent.js +0 -102
  37. package/dist/kosuke/utils/browser-agent.js.map +0 -1
@@ -2,10 +2,21 @@
2
2
  * Tickets command - Generate tickets from requirements document
3
3
  *
4
4
  * This command analyzes a requirements document (default: docs.md) and generates
5
- * structured tickets in three phases:
6
- * 1. Schema tickets (database design)
7
- * 2. Backend tickets (API, services, business logic)
8
- * 3. Frontend tickets (pages, components, UI)
5
+ * structured tickets with test coverage:
6
+ *
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)
13
+ *
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)
9
20
  *
10
21
  * Claude Code Agent explores the specified directory (default: current directory)
11
22
  * to understand the existing codebase and generate contextual tickets.
@@ -19,78 +30,442 @@
19
30
  * kosuke tickets --directory=./projects/my-app # Analyze specific directory
20
31
  * kosuke tickets --dir=./my-app --path=docs/spec.md # Custom directory and requirements path
21
32
  */
22
- import { readFileSync, writeFileSync, existsSync, statSync } from 'fs';
33
+ import { existsSync, readFileSync, statSync, writeFileSync } from 'fs';
23
34
  import { join, resolve } from 'path';
24
- import { runAgent, formatCostBreakdown } from '../utils/claude-agent.js';
35
+ import { formatCostBreakdown, runAgent } from '../utils/claude-agent.js';
25
36
  import { logger, setupCancellationHandler } from '../utils/logger.js';
26
37
  /**
27
- * Build system prompt for ticket generation
38
+ * Analyze requirements to determine which layers are needed
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.
43
+
44
+ **Your Task:**
45
+ Analyze the requirements and determine which layers (schema/backend/frontend) need changes.
46
+
47
+ **Requirements:**
48
+ ${requirementsContent}
49
+
50
+ **Context:**
51
+ 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
+
129
+ **Your Task:**
130
+ Generate ONE database test ticket to validate the schema implementation from the tickets below.
131
+
132
+ **Schema Tickets to Validate:**
133
+ ${schemaTicketsContext}
134
+
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
140
+
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
146
+
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"
158
+
159
+ **Output Format:**
160
+ Return ONLY a valid JSON array with ONE ticket. No markdown, no code blocks, just raw JSON.
161
+
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
28
262
  */
29
- function buildTicketGenerationPrompt(phase, requirementsContent, projectPath) {
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}`;
30
276
  const phaseInstructions = {
31
- schema: `
32
- **PHASE 1: DATABASE SCHEMA TICKETS**
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
33
290
 
34
- Generate ONE comprehensive schema ticket that includes:
35
- - All database tables needed
36
- - Relationships between tables
37
- - Indexes and constraints
38
- - Data types and validation rules
39
- - Any special considerations (soft deletes, timestamps, etc.)
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
40
301
 
41
- Ticket ID: SCHEMA-1
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
42
308
  `,
43
- backend: `
44
- **PHASE 2: BACKEND/API TICKETS**
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
45
318
 
46
- Generate backend tickets for:
47
- - API endpoints and routes
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
48
331
  - Service layer logic
49
- - Authentication and authorization
50
- - Business logic implementation
51
- - Data validation and processing
52
- - Third-party integrations
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
53
352
 
54
- Create ONE ticket per feature/module. Each ticket should be independently implementable.
353
+ **Granularity:**
354
+ - ONE ticket per infrastructure area
355
+ - Let Claude decide granularity based on complexity
356
+ - Each ticket should be independently implementable
55
357
 
56
- Ticket IDs: BACKEND-1, BACKEND-2, BACKEND-3, etc. (sequential)
358
+ Ticket IDs: FRONTEND-SCAFFOLD-1, FRONTEND-SCAFFOLD-2, etc. (sequential)
57
359
  `,
58
- frontend: `
59
- **PHASE 3: FRONTEND/UI TICKETS**
360
+ frontend_logic: `
361
+ **GENERATE: FRONTEND BUSINESS LOGIC TICKETS**
60
362
 
61
- Generate frontend tickets for:
62
- - Pages and routing
63
- - UI components
64
- - Forms and validation
65
- - State management
66
- - User interactions
67
- - Responsive design
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
68
369
 
69
- Create ONE ticket per PAGE. Each ticket should cover all components and functionality for that page.
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
70
374
 
71
- Ticket IDs: FRONTEND-1, FRONTEND-2, FRONTEND-3, etc. (sequential)
375
+ Ticket IDs: FRONTEND-LOGIC-1, FRONTEND-LOGIC-2, etc. (sequential)
72
376
  `,
73
377
  };
74
- return `You are an expert software architect and project planner analyzing a requirements document to generate implementation tickets.
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
435
+
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'}.
75
445
 
76
446
  **Your Task:**
77
- ${phaseInstructions[phase]}
447
+ ${phaseInstructions[phaseTypeKey]}
78
448
 
79
449
  **Requirements Document:**
80
450
  ${requirementsContent}
81
451
 
452
+ ${contextualGuidance}
453
+
82
454
  **Context:**
83
455
  You have access to the project directory at: ${projectPath}
456
+ ${isScaffoldMode ? 'The template baseline is documented in CLAUDE.md.' : ''}
84
457
  Explore the codebase to understand the tech stack, architecture patterns, and coding conventions.
85
- Use read_file, grep, and codebase_search tools to understand the existing implementation.
86
458
 
87
459
  **Ticket Structure:**
88
460
  Each ticket must be a JSON object with:
89
- - id: string (e.g., "SCHEMA-1", "BACKEND-1", "FRONTEND-1")
461
+ - id: string (e.g., "SCHEMA-SCAFFOLD-1", "BACKEND-LOGIC-2")
90
462
  - title: string (clear, concise title)
91
463
  - description: string (detailed description with acceptance criteria)
464
+ - type: "${ticketType}" (scaffold or logic)
92
465
  - estimatedEffort: number (1-10, where 1=very easy, 10=very complex)
93
466
  - status: "Todo" (all tickets start as Todo)
467
+ - category: string (see guidance below)
468
+ ${categoryGuidance}
94
469
 
95
470
  **Output Format:**
96
471
  Return ONLY a valid JSON array of tickets. No markdown, no code blocks, just raw JSON.
@@ -98,44 +473,53 @@ Return ONLY a valid JSON array of tickets. No markdown, no code blocks, just raw
98
473
  Example:
99
474
  [
100
475
  {
101
- "id": "SCHEMA-1",
102
- "title": "Design and implement complete database schema",
103
- "description": "Create database schema including:\\n- Users table with authentication fields\\n- Organizations table with multi-tenant support\\n- Posts table for social media content\\n- Automations table for campaign configuration\\n- Leads table with status tracking\\n\\nRelationships:\\n- User belongs to many Organizations\\n- Posts and Automations belong to Organization\\n- Leads belong to Automation and Post\\n\\nAcceptance Criteria:\\n- Schema validates successfully\\n- Migrations run successfully\\n- All relationships properly defined\\n- Indexes on foreign keys and frequently queried fields",
104
- "estimatedEffort": 8,
105
- "status": "Todo"
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,
481
+ "status": "Todo",
482
+ "category": "auth"
106
483
  }
107
484
  ]
108
485
 
109
486
  **Critical Instructions:**
110
- 1. Analyze the requirements thoroughly
111
- 2. Explore the project directory to understand existing patterns (use read_file, grep, codebase_search tools)
487
+ 1. Analyze requirements against template baseline
488
+ 2. Explore the project directory to understand existing patterns (use read_file, grep, codebase_search)
112
489
  3. Generate tickets that are actionable and specific
113
490
  4. Return ONLY valid JSON - no explanations, no markdown formatting
114
- 5. Ensure ticket IDs follow the naming convention (${phase.toUpperCase()}-N)
491
+ 5. Ensure ticket IDs follow the naming convention (${phase.toUpperCase()}-${ticketType.toUpperCase()}-N)
115
492
  6. Make descriptions detailed with clear acceptance criteria
116
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
117
497
 
118
- Begin by exploring the project directory, then generate the tickets.`;
498
+ Begin by exploring the project directory, analyzing requirements, then generate the tickets.`;
119
499
  }
120
500
  /**
121
501
  * Parse tickets from Claude's response
122
502
  */
123
- function parseTicketsFromResponse(response, phase) {
503
+ function parseTicketsFromResponse(response, phase, ticketType) {
124
504
  try {
125
505
  // Extract JSON from response (in case Claude includes extra text)
126
506
  const jsonMatch = response.match(/\[[\s\S]*\]/);
127
507
  if (!jsonMatch) {
128
- throw new Error(`No JSON array found in ${phase} response`);
508
+ throw new Error(`No JSON array found in ${phase} ${ticketType} response`);
129
509
  }
130
510
  const tickets = JSON.parse(jsonMatch[0]);
131
511
  // Validate tickets
132
512
  if (!Array.isArray(tickets)) {
133
513
  throw new Error(`Expected array of tickets, got ${typeof tickets}`);
134
514
  }
515
+ const validTypes = ['scaffold', 'logic', 'db-test', 'web-test'];
135
516
  for (const ticket of tickets) {
136
517
  if (!ticket.id || !ticket.title || !ticket.description) {
137
518
  throw new Error(`Invalid ticket structure: ${JSON.stringify(ticket)}`);
138
519
  }
520
+ if (!ticket.type || !validTypes.includes(ticket.type)) {
521
+ throw new Error(`Invalid or missing type for ticket ${ticket.id}: ${ticket.type}`);
522
+ }
139
523
  if (typeof ticket.estimatedEffort !== 'number' ||
140
524
  ticket.estimatedEffort < 1 ||
141
525
  ticket.estimatedEffort > 10) {
@@ -148,64 +532,72 @@ function parseTicketsFromResponse(response, phase) {
148
532
  return tickets;
149
533
  }
150
534
  catch (error) {
151
- console.error(`\nโŒ Failed to parse tickets from ${phase} phase:`);
535
+ console.error(`\nโŒ Failed to parse tickets from ${phase} ${ticketType} phase:`);
152
536
  console.error(`Raw response:\n${response.substring(0, 500)}...\n`);
153
- throw new Error(`Failed to parse ${phase} tickets: ${error instanceof Error ? error.message : String(error)}`);
537
+ throw new Error(`Failed to parse ${phase} ${ticketType} tickets: ${error instanceof Error ? error.message : String(error)}`);
154
538
  }
155
539
  }
156
540
  /**
157
541
  * Write tickets to file incrementally
158
542
  */
159
- function writeTicketsToFile(outputPath, schemaTickets, backendTickets, frontendTickets) {
160
- const allTickets = [...schemaTickets, ...backendTickets, ...frontendTickets];
543
+ function writeTicketsToFile(outputPath, tickets) {
161
544
  const outputData = {
162
545
  generatedAt: new Date().toISOString(),
163
- totalTickets: allTickets.length,
164
- tickets: allTickets,
546
+ totalTickets: tickets.length,
547
+ tickets,
165
548
  };
166
549
  writeFileSync(outputPath, JSON.stringify(outputData, null, 2), 'utf-8');
167
550
  }
168
551
  /**
169
- * Generate tickets for a specific phase
552
+ * Generate tickets for a specific phase and type
170
553
  */
171
- async function generatePhaseTickets(phase, requirementsContent, projectPath, outputPath, existingSchemaTickets, existingBackendTickets) {
554
+ async function generatePhaseTickets(phase, ticketType, requirementsContent, projectPath, outputPath, existingTickets, isScaffoldMode, previousTickets) {
172
555
  const phaseEmoji = {
173
556
  schema: '๐Ÿ—„๏ธ',
174
557
  backend: 'โš™๏ธ',
175
558
  frontend: '๐ŸŽจ',
559
+ 'db-test': '๐Ÿงช',
560
+ 'web-test': '๐ŸŒ',
561
+ };
562
+ const typeEmoji = {
563
+ scaffold: '๐Ÿ—๏ธ',
564
+ logic: '๐Ÿ’ก',
565
+ 'db-test': '๐Ÿงช',
566
+ 'web-test': '๐ŸŒ',
176
567
  };
177
568
  const phaseName = {
178
569
  schema: 'Schema',
179
570
  backend: 'Backend',
180
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',
181
580
  };
182
581
  console.log(`\n${'='.repeat(60)}`);
183
- console.log(`${phaseEmoji[phase]} Phase ${phase === 'schema' ? '1' : phase === 'backend' ? '2' : '3'}: ${phaseName[phase]} Tickets`);
582
+ console.log(`${phaseEmoji[phase]} ${typeEmoji[ticketType]} ${phaseName[phase]} ${typeName[ticketType]} Tickets`);
184
583
  console.log(`${'='.repeat(60)}\n`);
185
- const systemPrompt = buildTicketGenerationPrompt(phase, requirementsContent, projectPath);
186
- const agentResult = await runAgent(`Generate ${phaseName[phase]} tickets from the requirements document.`, {
584
+ const systemPrompt = buildTicketGenerationPrompt(phase, ticketType, requirementsContent, projectPath, isScaffoldMode, previousTickets);
585
+ const agentResult = await runAgent(`Generate ${phaseName[phase]} ${typeName[ticketType]} tickets from the requirements.`, {
187
586
  systemPrompt,
188
587
  cwd: projectPath,
189
588
  maxTurns: 25,
190
589
  verbosity: 'normal',
191
- captureConversation: true, // Capture full conversation for backend logging
590
+ captureConversation: true,
192
591
  });
193
592
  // Parse tickets from response
194
- const tickets = parseTicketsFromResponse(agentResult.response, phase);
195
- console.log(`\nโœ… Generated ${tickets.length} ${phaseName[phase]} ticket${tickets.length === 1 ? '' : 's'}`);
593
+ const tickets = parseTicketsFromResponse(agentResult.response, phase, ticketType);
594
+ console.log(`\nโœ… Generated ${tickets.length} ${phaseName[phase]} ${typeName[ticketType]} ticket${tickets.length === 1 ? '' : 's'}`);
196
595
  tickets.forEach((ticket) => {
197
- console.log(` ${phaseEmoji[phase]} ${ticket.id}: ${ticket.title} (Effort: ${ticket.estimatedEffort}/10)`);
596
+ console.log(` ${phaseEmoji[phase]} ${ticket.id}: ${ticket.title} (Effort: ${ticket.estimatedEffort}/10${ticket.category ? `, Category: ${ticket.category}` : ''})`);
198
597
  });
199
598
  // Write tickets incrementally after each phase
200
- if (phase === 'schema') {
201
- writeTicketsToFile(outputPath, tickets, [], []);
202
- }
203
- else if (phase === 'backend') {
204
- writeTicketsToFile(outputPath, existingSchemaTickets, tickets, []);
205
- }
206
- else if (phase === 'frontend') {
207
- writeTicketsToFile(outputPath, existingSchemaTickets, existingBackendTickets, tickets);
208
- }
599
+ const allTickets = [...existingTickets, ...tickets];
600
+ writeTicketsToFile(outputPath, allTickets);
209
601
  console.log(` ๐Ÿ’พ Progress saved to: ${outputPath}\n`);
210
602
  return {
211
603
  tickets,
@@ -218,7 +610,8 @@ async function generatePhaseTickets(phase, requirementsContent, projectPath, out
218
610
  * Core tickets logic
219
611
  */
220
612
  export async function ticketsCore(options) {
221
- const { path = 'docs.md', directory } = options;
613
+ const { directory, scaffold = false } = options;
614
+ const isScaffoldMode = scaffold;
222
615
  // 1. Validate and resolve project directory
223
616
  const projectPath = directory ? resolve(directory) : process.cwd();
224
617
  if (!existsSync(projectPath)) {
@@ -230,21 +623,56 @@ export async function ticketsCore(options) {
230
623
  if (!stats.isDirectory()) {
231
624
  throw new Error(`Path is not a directory: ${projectPath}\n` + `Please provide a valid directory path.`);
232
625
  }
233
- console.log(`๐Ÿ“ Using project directory: ${projectPath}\n`);
234
- // 2. Read requirements document (relative to project directory)
235
- console.log('๐Ÿ“„ Reading requirements document...');
236
- const requirementsPath = join(projectPath, path);
237
- if (!existsSync(requirementsPath)) {
238
- throw new Error(`Requirements document not found: ${path}\n` +
239
- `Please provide a valid path using --path=<file>\n` +
240
- `Example: kosuke tickets --path=requirements.md`);
626
+ console.log(`๐Ÿ“ Using project directory: ${projectPath}`);
627
+ console.log(`๐Ÿ—๏ธ Mode: ${isScaffoldMode ? 'Scaffold (infrastructure + logic)' : 'Logic-only (smart layer detection)'}\n`);
628
+ // 2. Get requirements content (from prompt or file)
629
+ let requirementsContent;
630
+ if (options.prompt && options.path) {
631
+ throw new Error('Cannot use both --prompt and --path. Please provide only one:\n' +
632
+ ' kosuke tickets --prompt="Add dark mode"\n' +
633
+ ' kosuke tickets --path=docs.md');
634
+ }
635
+ if (options.prompt) {
636
+ requirementsContent = options.prompt;
637
+ console.log(`๐Ÿ“ Using inline prompt (${requirementsContent.length} characters)\n`);
638
+ }
639
+ else if (options.path) {
640
+ const requirementsPath = join(projectPath, options.path);
641
+ if (!existsSync(requirementsPath)) {
642
+ throw new Error(`Requirements document not found: ${options.path}\n` +
643
+ `Please provide a valid path using --path=<file>\n` +
644
+ `Example: kosuke tickets --path=requirements.md`);
645
+ }
646
+ requirementsContent = readFileSync(requirementsPath, 'utf-8');
647
+ console.log(`๐Ÿ“„ Loaded ${options.path} (${requirementsContent.length} characters)\n`);
648
+ }
649
+ else {
650
+ // Default to docs.md if neither prompt nor path provided
651
+ const defaultPath = 'docs.md';
652
+ const requirementsPath = join(projectPath, defaultPath);
653
+ if (!existsSync(requirementsPath)) {
654
+ throw new Error('Requirements not provided. Use either:\n' +
655
+ ' --prompt="Your requirements here"\n' +
656
+ ' --path=requirements.md\n' +
657
+ ' Or create a docs.md file in the project directory');
658
+ }
659
+ requirementsContent = readFileSync(requirementsPath, 'utf-8');
660
+ console.log(`๐Ÿ“„ Loaded ${defaultPath} (${requirementsContent.length} characters)\n`);
241
661
  }
242
- const requirementsContent = readFileSync(requirementsPath, 'utf-8');
243
- console.log(` โœ… Loaded ${path} (${requirementsContent.length} characters)\n`);
244
662
  // 3. Determine output path for incremental writes
245
663
  const outputFilename = options.output || 'tickets.json';
246
664
  const outputPath = join(projectPath, outputFilename);
247
- // 4. Generate tickets in three phases (written incrementally)
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`);
674
+ }
675
+ // 5. Generate tickets in the new structure with tests
248
676
  let totalInputTokens = 0;
249
677
  let totalOutputTokens = 0;
250
678
  let totalCacheCreationTokens = 0;
@@ -252,37 +680,129 @@ export async function ticketsCore(options) {
252
680
  let totalCost = 0;
253
681
  // Collect all conversation messages from all phases
254
682
  const allConversationMessages = [];
255
- // Phase 1: Schema
256
- const schemaResult = await generatePhaseTickets('schema', requirementsContent, projectPath, outputPath, [], []);
257
- totalInputTokens += schemaResult.tokensUsed.input;
258
- totalOutputTokens += schemaResult.tokensUsed.output;
259
- totalCacheCreationTokens += schemaResult.tokensUsed.cacheCreation;
260
- totalCacheReadTokens += schemaResult.tokensUsed.cacheRead;
261
- totalCost += schemaResult.cost;
262
- allConversationMessages.push(...schemaResult.conversationMessages);
263
- // Phase 2: Backend
264
- const backendResult = await generatePhaseTickets('backend', requirementsContent, projectPath, outputPath, schemaResult.tickets, []);
265
- totalInputTokens += backendResult.tokensUsed.input;
266
- totalOutputTokens += backendResult.tokensUsed.output;
267
- totalCacheCreationTokens += backendResult.tokensUsed.cacheCreation;
268
- totalCacheReadTokens += backendResult.tokensUsed.cacheRead;
269
- totalCost += backendResult.cost;
270
- allConversationMessages.push(...backendResult.conversationMessages);
271
- // Phase 3: Frontend
272
- const frontendResult = await generatePhaseTickets('frontend', requirementsContent, projectPath, outputPath, schemaResult.tickets, backendResult.tickets);
273
- totalInputTokens += frontendResult.tokensUsed.input;
274
- totalOutputTokens += frontendResult.tokensUsed.output;
275
- totalCacheCreationTokens += frontendResult.tokensUsed.cacheCreation;
276
- totalCacheReadTokens += frontendResult.tokensUsed.cacheRead;
277
- totalCost += frontendResult.cost;
278
- allConversationMessages.push(...frontendResult.conversationMessages);
279
- const totalTickets = schemaResult.tickets.length + backendResult.tickets.length + frontendResult.tickets.length;
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
+ }));
753
+ }
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
+ }
793
+ }
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-'));
280
799
  return {
281
- schemaTickets: schemaResult.tickets,
282
- backendTickets: backendResult.tickets,
283
- frontendTickets: frontendResult.tickets,
284
- totalTickets,
285
- projectPath, // Include project path for output file resolution
800
+ schemaTickets,
801
+ backendTickets,
802
+ frontendTickets,
803
+ testTickets,
804
+ totalTickets: allTickets.length,
805
+ projectPath,
286
806
  tokensUsed: {
287
807
  input: totalInputTokens,
288
808
  output: totalOutputTokens,
@@ -313,13 +833,33 @@ export async function ticketsCommand(options) {
313
833
  logger.trackTokens(logContext, result.tokensUsed);
314
834
  logContext.conversationMessages = result.conversationMessages;
315
835
  // Display summary
836
+ const scaffoldTickets = [
837
+ ...result.schemaTickets,
838
+ ...result.backendTickets,
839
+ ...result.frontendTickets,
840
+ ].filter((t) => t.type === 'scaffold');
841
+ const logicTickets = [
842
+ ...result.schemaTickets,
843
+ ...result.backendTickets,
844
+ ...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');
316
848
  console.log(`\n${'='.repeat(60)}`);
317
- console.log('๐Ÿ“Š Summary');
849
+ console.log('๐Ÿ“Š Ticket Generation Summary');
318
850
  console.log(`${'='.repeat(60)}`);
319
- console.log(`๐Ÿ—„๏ธ Schema Tickets: ${result.schemaTickets.length}`);
320
- console.log(`โš™๏ธ Backend Tickets: ${result.backendTickets.length}`);
321
- console.log(`๐ŸŽจ Frontend Tickets: ${result.frontendTickets.length}`);
322
- console.log(`๐Ÿ“ Total Tickets: ${result.totalTickets}`);
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}`);
855
+ 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}`);
862
+ console.log(`\n๐Ÿ“ Total Tickets: ${result.totalTickets}`);
323
863
  console.log(`${'='.repeat(60)}\n`);
324
864
  // Display cost breakdown
325
865
  const costBreakdown = formatCostBreakdown({