@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.
- package/dist/index.d.ts +2 -2
- package/dist/index.js +101 -70
- package/dist/index.js.map +1 -1
- package/dist/kosuke/commands/build.d.ts +2 -1
- package/dist/kosuke/commands/build.d.ts.map +1 -1
- package/dist/kosuke/commands/build.js +158 -57
- package/dist/kosuke/commands/build.js.map +1 -1
- package/dist/kosuke/commands/test.d.ts +21 -8
- package/dist/kosuke/commands/test.d.ts.map +1 -1
- package/dist/kosuke/commands/test.js +211 -75
- package/dist/kosuke/commands/test.js.map +1 -1
- package/dist/kosuke/commands/tickets.d.ts +15 -4
- package/dist/kosuke/commands/tickets.d.ts.map +1 -1
- package/dist/kosuke/commands/tickets.js +664 -124
- package/dist/kosuke/commands/tickets.js.map +1 -1
- package/dist/kosuke/types.d.ts +32 -9
- package/dist/kosuke/types.d.ts.map +1 -1
- package/dist/kosuke/utils/prompt-generator.d.ts +4 -4
- package/dist/kosuke/utils/prompt-generator.d.ts.map +1 -1
- package/dist/kosuke/utils/prompt-generator.js +22 -11
- package/dist/kosuke/utils/prompt-generator.js.map +1 -1
- package/dist/kosuke/utils/test-runner.d.ts.map +1 -1
- package/dist/kosuke/utils/test-runner.js +13 -4
- package/dist/kosuke/utils/test-runner.js.map +1 -1
- package/dist/kosuke/utils/tickets-manager.d.ts +0 -4
- package/dist/kosuke/utils/tickets-manager.d.ts.map +1 -1
- package/dist/kosuke/utils/tickets-manager.js +1 -1
- package/dist/kosuke/utils/tickets-manager.js.map +1 -1
- package/dist/lib.d.ts +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js.map +1 -1
- package/dist/package.json +2 -2
- package/package.json +2 -2
- package/dist/kosuke/utils/browser-agent.d.ts +0 -33
- package/dist/kosuke/utils/browser-agent.d.ts.map +0 -1
- package/dist/kosuke/utils/browser-agent.js +0 -102
- 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
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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 {
|
|
33
|
+
import { existsSync, readFileSync, statSync, writeFileSync } from 'fs';
|
|
23
34
|
import { join, resolve } from 'path';
|
|
24
|
-
import {
|
|
35
|
+
import { formatCostBreakdown, runAgent } from '../utils/claude-agent.js';
|
|
25
36
|
import { logger, setupCancellationHandler } from '../utils/logger.js';
|
|
26
37
|
/**
|
|
27
|
-
*
|
|
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
|
-
|
|
32
|
-
**
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
44
|
-
**
|
|
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
|
-
|
|
47
|
-
-
|
|
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
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
|
|
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
|
-
|
|
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:
|
|
358
|
+
Ticket IDs: FRONTEND-SCAFFOLD-1, FRONTEND-SCAFFOLD-2, etc. (sequential)
|
|
57
359
|
`,
|
|
58
|
-
|
|
59
|
-
**
|
|
360
|
+
frontend_logic: `
|
|
361
|
+
**GENERATE: FRONTEND BUSINESS LOGIC TICKETS**
|
|
60
362
|
|
|
61
|
-
Generate
|
|
62
|
-
-
|
|
63
|
-
- UI components
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
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
|
-
|
|
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,
|
|
375
|
+
Ticket IDs: FRONTEND-LOGIC-1, FRONTEND-LOGIC-2, etc. (sequential)
|
|
72
376
|
`,
|
|
73
377
|
};
|
|
74
|
-
|
|
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[
|
|
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-
|
|
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": "
|
|
102
|
-
"title": "
|
|
103
|
-
"description": "
|
|
104
|
-
"
|
|
105
|
-
"
|
|
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
|
|
111
|
-
2. Explore the project directory to understand existing patterns (use read_file, grep, codebase_search
|
|
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,
|
|
160
|
-
const allTickets = [...schemaTickets, ...backendTickets, ...frontendTickets];
|
|
543
|
+
function writeTicketsToFile(outputPath, tickets) {
|
|
161
544
|
const outputData = {
|
|
162
545
|
generatedAt: new Date().toISOString(),
|
|
163
|
-
totalTickets:
|
|
164
|
-
tickets
|
|
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,
|
|
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]}
|
|
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
|
|
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,
|
|
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
|
-
|
|
201
|
-
|
|
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 {
|
|
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}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if (
|
|
238
|
-
throw new Error(
|
|
239
|
-
|
|
240
|
-
|
|
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.
|
|
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
|
-
//
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
|
282
|
-
backendTickets
|
|
283
|
-
frontendTickets
|
|
284
|
-
|
|
285
|
-
|
|
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(
|
|
320
|
-
console.log(
|
|
321
|
-
console.log(
|
|
322
|
-
console.log(
|
|
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({
|