@hustle-together/api-dev-tools 2.0.7 ā 3.1.0
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/README.md +343 -467
- package/bin/cli.js +229 -15
- package/commands/README.md +124 -251
- package/commands/api-create.md +318 -136
- package/commands/api-interview.md +252 -256
- package/commands/api-research.md +209 -234
- package/commands/api-verify.md +231 -0
- package/demo/audio/generate-all-narrations.js +581 -0
- package/demo/audio/generate-narration.js +120 -56
- package/demo/audio/generate-voice-previews.js +140 -0
- package/demo/audio/narration-adam-timing.json +4675 -0
- package/demo/audio/narration-adam.mp3 +0 -0
- package/demo/audio/narration-creature-timing.json +4675 -0
- package/demo/audio/narration-creature.mp3 +0 -0
- package/demo/audio/narration-gaming-timing.json +4675 -0
- package/demo/audio/narration-gaming.mp3 +0 -0
- package/demo/audio/narration-hope-timing.json +4675 -0
- package/demo/audio/narration-hope.mp3 +0 -0
- package/demo/audio/narration-mark-timing.json +4675 -0
- package/demo/audio/narration-mark.mp3 +0 -0
- package/demo/audio/previews/manifest.json +30 -0
- package/demo/audio/previews/preview-creature.mp3 +0 -0
- package/demo/audio/previews/preview-gaming.mp3 +0 -0
- package/demo/audio/previews/preview-hope.mp3 +0 -0
- package/demo/audio/previews/preview-mark.mp3 +0 -0
- package/demo/audio/voices-manifest.json +50 -0
- package/demo/hustle-together/blog/gemini-vs-claude-widgets.html +30 -28
- package/demo/hustle-together/blog/interview-driven-api-development.html +37 -23
- package/demo/hustle-together/index.html +142 -109
- package/demo/workflow-demo.html +2618 -1036
- package/hooks/api-workflow-check.py +2 -0
- package/hooks/enforce-deep-research.py +180 -0
- package/hooks/enforce-disambiguation.py +149 -0
- package/hooks/enforce-documentation.py +187 -0
- package/hooks/enforce-environment.py +249 -0
- package/hooks/enforce-refactor.py +187 -0
- package/hooks/enforce-research.py +93 -46
- package/hooks/enforce-schema.py +186 -0
- package/hooks/enforce-scope.py +156 -0
- package/hooks/enforce-tdd-red.py +246 -0
- package/hooks/enforce-verify.py +186 -0
- package/hooks/periodic-reground.py +154 -0
- package/hooks/session-startup.py +151 -0
- package/hooks/track-tool-use.py +109 -17
- package/hooks/verify-after-green.py +282 -0
- package/package.json +3 -2
- package/scripts/collect-test-results.ts +404 -0
- package/scripts/extract-parameters.ts +483 -0
- package/scripts/generate-test-manifest.ts +520 -0
- package/templates/CLAUDE-SECTION.md +84 -0
- package/templates/api-dev-state.json +83 -8
- package/templates/api-test/page.tsx +315 -0
- package/templates/api-test/test-structure/route.ts +269 -0
- package/templates/research-index.json +6 -0
- package/templates/settings.json +59 -0
package/bin/cli.js
CHANGED
|
@@ -65,14 +65,33 @@ function verifyInstallation(claudeDir, hooksDir) {
|
|
|
65
65
|
{ path: path.join(claudeDir, 'commands'), name: 'Commands directory' },
|
|
66
66
|
{ path: path.join(claudeDir, 'settings.json'), name: 'Settings file' },
|
|
67
67
|
{ path: path.join(claudeDir, 'api-dev-state.json'), name: 'State file' },
|
|
68
|
+
{ path: path.join(claudeDir, 'research'), name: 'Research cache directory' },
|
|
69
|
+
{ path: path.join(claudeDir, 'research', 'index.json'), name: 'Research index' },
|
|
68
70
|
];
|
|
69
71
|
|
|
70
|
-
// Add hook checks if hooks directory exists
|
|
72
|
+
// Add hook checks if hooks directory exists (v3.0 has 18 hooks for 100% phase enforcement with user checkpoints)
|
|
71
73
|
if (fs.existsSync(hooksDir)) {
|
|
72
74
|
checks.push(
|
|
73
|
-
|
|
75
|
+
// Core utility hooks (5)
|
|
76
|
+
{ path: path.join(hooksDir, 'session-startup.py'), name: 'session-startup.py' },
|
|
77
|
+
{ path: path.join(hooksDir, 'enforce-external-research.py'), name: 'enforce-external-research.py' },
|
|
74
78
|
{ path: path.join(hooksDir, 'track-tool-use.py'), name: 'track-tool-use.py' },
|
|
75
|
-
{ path: path.join(hooksDir, '
|
|
79
|
+
{ path: path.join(hooksDir, 'periodic-reground.py'), name: 'periodic-reground.py' },
|
|
80
|
+
{ path: path.join(hooksDir, 'api-workflow-check.py'), name: 'api-workflow-check.py' },
|
|
81
|
+
// Phase enforcement hooks with user checkpoints (12 - one per phase)
|
|
82
|
+
{ path: path.join(hooksDir, 'enforce-disambiguation.py'), name: 'enforce-disambiguation.py' },
|
|
83
|
+
{ path: path.join(hooksDir, 'enforce-scope.py'), name: 'enforce-scope.py' },
|
|
84
|
+
{ path: path.join(hooksDir, 'enforce-research.py'), name: 'enforce-research.py' },
|
|
85
|
+
{ path: path.join(hooksDir, 'enforce-interview.py'), name: 'enforce-interview.py' },
|
|
86
|
+
{ path: path.join(hooksDir, 'enforce-deep-research.py'), name: 'enforce-deep-research.py' },
|
|
87
|
+
{ path: path.join(hooksDir, 'enforce-schema.py'), name: 'enforce-schema.py' },
|
|
88
|
+
{ path: path.join(hooksDir, 'enforce-environment.py'), name: 'enforce-environment.py' },
|
|
89
|
+
{ path: path.join(hooksDir, 'enforce-tdd-red.py'), name: 'enforce-tdd-red.py' },
|
|
90
|
+
{ path: path.join(hooksDir, 'verify-implementation.py'), name: 'verify-implementation.py' },
|
|
91
|
+
{ path: path.join(hooksDir, 'verify-after-green.py'), name: 'verify-after-green.py' },
|
|
92
|
+
{ path: path.join(hooksDir, 'enforce-verify.py'), name: 'enforce-verify.py' },
|
|
93
|
+
{ path: path.join(hooksDir, 'enforce-refactor.py'), name: 'enforce-refactor.py' },
|
|
94
|
+
{ path: path.join(hooksDir, 'enforce-documentation.py'), name: 'enforce-documentation.py' }
|
|
76
95
|
);
|
|
77
96
|
}
|
|
78
97
|
|
|
@@ -233,6 +252,147 @@ function main() {
|
|
|
233
252
|
}
|
|
234
253
|
}
|
|
235
254
|
|
|
255
|
+
// ========================================
|
|
256
|
+
// 4b. Install Research Cache Structure (v3.0)
|
|
257
|
+
// ========================================
|
|
258
|
+
const researchDir = path.join(claudeDir, 'research');
|
|
259
|
+
const researchIndexSource = path.join(sourceTemplatesDir, 'research-index.json');
|
|
260
|
+
const researchIndexDest = path.join(researchDir, 'index.json');
|
|
261
|
+
|
|
262
|
+
log('\nš Setting up research cache:', 'cyan');
|
|
263
|
+
|
|
264
|
+
if (!fs.existsSync(researchDir)) {
|
|
265
|
+
try {
|
|
266
|
+
fs.mkdirSync(researchDir, { recursive: true });
|
|
267
|
+
log(' ā
Created .claude/research/ directory', 'green');
|
|
268
|
+
} catch (error) {
|
|
269
|
+
log(` ā Failed to create research directory: ${error.message}`, 'red');
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (fs.existsSync(researchIndexSource) && !fs.existsSync(researchIndexDest)) {
|
|
274
|
+
try {
|
|
275
|
+
fs.copyFileSync(researchIndexSource, researchIndexDest);
|
|
276
|
+
log(' ā
Created research/index.json for freshness tracking', 'green');
|
|
277
|
+
} catch (error) {
|
|
278
|
+
log(` ā Failed to create research index: ${error.message}`, 'red');
|
|
279
|
+
}
|
|
280
|
+
} else if (fs.existsSync(researchIndexDest)) {
|
|
281
|
+
log(' ā¹ļø Research index already exists (preserved)', 'blue');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ========================================
|
|
285
|
+
// 4c. Install Test UI (Parser API + Page)
|
|
286
|
+
// ========================================
|
|
287
|
+
log('\nš§Ŗ Setting up Test UI:', 'cyan');
|
|
288
|
+
|
|
289
|
+
const testUiSourceDir = path.join(sourceTemplatesDir, 'api-test');
|
|
290
|
+
const hasNextJs = fs.existsSync(path.join(targetDir, 'next.config.js')) ||
|
|
291
|
+
fs.existsSync(path.join(targetDir, 'next.config.mjs')) ||
|
|
292
|
+
fs.existsSync(path.join(targetDir, 'next.config.ts'));
|
|
293
|
+
|
|
294
|
+
if (!hasNextJs) {
|
|
295
|
+
log(' ā ļø Next.js not detected - skipping Test UI installation', 'yellow');
|
|
296
|
+
log(' š” Test UI requires Next.js App Router', 'yellow');
|
|
297
|
+
} else if (fs.existsSync(testUiSourceDir)) {
|
|
298
|
+
// Detect App Router structure
|
|
299
|
+
const appDir = fs.existsSync(path.join(targetDir, 'src', 'app'))
|
|
300
|
+
? path.join(targetDir, 'src', 'app')
|
|
301
|
+
: fs.existsSync(path.join(targetDir, 'app'))
|
|
302
|
+
? path.join(targetDir, 'app')
|
|
303
|
+
: null;
|
|
304
|
+
|
|
305
|
+
if (!appDir) {
|
|
306
|
+
log(' ā ļø App Router not detected - skipping Test UI installation', 'yellow');
|
|
307
|
+
log(' š” Test UI requires Next.js App Router (app/ or src/app/)', 'yellow');
|
|
308
|
+
} else {
|
|
309
|
+
// Install test-structure API route
|
|
310
|
+
const apiTestStructureDir = path.join(appDir, 'api', 'test-structure');
|
|
311
|
+
const apiTestStructureSource = path.join(testUiSourceDir, 'test-structure', 'route.ts');
|
|
312
|
+
const apiTestStructureDest = path.join(apiTestStructureDir, 'route.ts');
|
|
313
|
+
|
|
314
|
+
if (!fs.existsSync(apiTestStructureDir)) {
|
|
315
|
+
fs.mkdirSync(apiTestStructureDir, { recursive: true });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (!fs.existsSync(apiTestStructureDest)) {
|
|
319
|
+
try {
|
|
320
|
+
fs.copyFileSync(apiTestStructureSource, apiTestStructureDest);
|
|
321
|
+
log(' ā
Created /api/test-structure route (parses Vitest files)', 'green');
|
|
322
|
+
} catch (error) {
|
|
323
|
+
log(` ā Failed to create test-structure API: ${error.message}`, 'red');
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
log(' ā¹ļø /api/test-structure already exists (preserved)', 'blue');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Install test UI page
|
|
330
|
+
const apiTestPageDir = path.join(appDir, 'api-test');
|
|
331
|
+
const apiTestPageSource = path.join(testUiSourceDir, 'page.tsx');
|
|
332
|
+
const apiTestPageDest = path.join(apiTestPageDir, 'page.tsx');
|
|
333
|
+
|
|
334
|
+
if (!fs.existsSync(apiTestPageDir)) {
|
|
335
|
+
fs.mkdirSync(apiTestPageDir, { recursive: true });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (!fs.existsSync(apiTestPageDest)) {
|
|
339
|
+
try {
|
|
340
|
+
fs.copyFileSync(apiTestPageSource, apiTestPageDest);
|
|
341
|
+
log(' ā
Created /api-test page (displays test structure)', 'green');
|
|
342
|
+
} catch (error) {
|
|
343
|
+
log(` ā Failed to create test UI page: ${error.message}`, 'red');
|
|
344
|
+
}
|
|
345
|
+
} else {
|
|
346
|
+
log(' ā¹ļø /api-test page already exists (preserved)', 'blue');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
log(' š” Test UI available at http://localhost:3000/api-test', 'yellow');
|
|
350
|
+
}
|
|
351
|
+
} else {
|
|
352
|
+
log(' ā ļø Test UI templates not found in package', 'yellow');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ========================================
|
|
356
|
+
// 4d. Install Manifest Generation Scripts
|
|
357
|
+
// ========================================
|
|
358
|
+
log('\nš Setting up manifest generation scripts:', 'cyan');
|
|
359
|
+
|
|
360
|
+
const sourceScriptsDir = path.join(packageDir, 'scripts');
|
|
361
|
+
const targetScriptsDir = path.join(targetDir, 'scripts', 'api-dev-tools');
|
|
362
|
+
|
|
363
|
+
if (fs.existsSync(sourceScriptsDir)) {
|
|
364
|
+
if (!fs.existsSync(targetScriptsDir)) {
|
|
365
|
+
fs.mkdirSync(targetScriptsDir, { recursive: true });
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const scriptFiles = fs.readdirSync(sourceScriptsDir).filter(file =>
|
|
369
|
+
file.endsWith('.ts')
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
if (scriptFiles.length > 0) {
|
|
373
|
+
scriptFiles.forEach(file => {
|
|
374
|
+
const source = path.join(sourceScriptsDir, file);
|
|
375
|
+
const dest = path.join(targetScriptsDir, file);
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
fs.copyFileSync(source, dest);
|
|
379
|
+
log(` ā
${file}`, 'green');
|
|
380
|
+
} catch (error) {
|
|
381
|
+
log(` ā Failed to copy ${file}: ${error.message}`, 'red');
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
log('\n Script purposes:', 'blue');
|
|
386
|
+
log(' ⢠generate-test-manifest.ts - Parses tests ā manifest (NO LLM)', 'blue');
|
|
387
|
+
log(' ⢠extract-parameters.ts - Extracts Zod params ā matrix', 'blue');
|
|
388
|
+
log(' ⢠collect-test-results.ts - Runs Vitest ā results JSON', 'blue');
|
|
389
|
+
log('\n š” Scripts run automatically after tests pass (Phase 8 ā 9)', 'yellow');
|
|
390
|
+
log(' š” Manual: npx tsx scripts/api-dev-tools/generate-test-manifest.ts', 'yellow');
|
|
391
|
+
}
|
|
392
|
+
} else {
|
|
393
|
+
log(' ā ļø Scripts directory not found in package', 'yellow');
|
|
394
|
+
}
|
|
395
|
+
|
|
236
396
|
// ========================================
|
|
237
397
|
// 5. Install MCP Servers via CLI (Context7, GitHub)
|
|
238
398
|
// ========================================
|
|
@@ -268,32 +428,86 @@ function main() {
|
|
|
268
428
|
log('\n ā ļø GitHub MCP requires GITHUB_PERSONAL_ACCESS_TOKEN in env', 'yellow');
|
|
269
429
|
log(' š” Restart Claude Code for MCP tools to be available', 'yellow');
|
|
270
430
|
|
|
431
|
+
// ========================================
|
|
432
|
+
// 6. Update CLAUDE.md with workflow documentation
|
|
433
|
+
// ========================================
|
|
434
|
+
const claudeMdSection = path.join(sourceTemplatesDir, 'CLAUDE-SECTION.md');
|
|
435
|
+
const projectClaudeMd = path.join(targetDir, 'CLAUDE.md');
|
|
436
|
+
|
|
437
|
+
if (fs.existsSync(claudeMdSection)) {
|
|
438
|
+
log('\nš CLAUDE.md workflow documentation:', 'cyan');
|
|
439
|
+
|
|
440
|
+
const sectionContent = fs.readFileSync(claudeMdSection, 'utf8');
|
|
441
|
+
const sectionMarker = '## API Development Workflow (v3.0)';
|
|
442
|
+
|
|
443
|
+
if (fs.existsSync(projectClaudeMd)) {
|
|
444
|
+
const existingContent = fs.readFileSync(projectClaudeMd, 'utf8');
|
|
445
|
+
|
|
446
|
+
if (existingContent.includes(sectionMarker)) {
|
|
447
|
+
// Update existing section
|
|
448
|
+
const beforeSection = existingContent.split(sectionMarker)[0];
|
|
449
|
+
// Find the next ## heading or end of file
|
|
450
|
+
const afterMatch = existingContent.match(/## API Development Workflow[\s\S]*?((?=\n## )|$)/);
|
|
451
|
+
const afterSection = afterMatch ? existingContent.substring(existingContent.indexOf(afterMatch[0]) + afterMatch[0].length) : '';
|
|
452
|
+
|
|
453
|
+
fs.writeFileSync(projectClaudeMd, beforeSection + sectionContent + afterSection);
|
|
454
|
+
log(' ā
Updated API Development Workflow section in CLAUDE.md', 'green');
|
|
455
|
+
} else {
|
|
456
|
+
// Append section
|
|
457
|
+
fs.appendFileSync(projectClaudeMd, '\n\n' + sectionContent);
|
|
458
|
+
log(' ā
Added API Development Workflow section to CLAUDE.md', 'green');
|
|
459
|
+
}
|
|
460
|
+
} else {
|
|
461
|
+
// Create new CLAUDE.md with section
|
|
462
|
+
fs.writeFileSync(projectClaudeMd, '# Project Instructions\n\n' + sectionContent);
|
|
463
|
+
log(' ā
Created CLAUDE.md with API Development Workflow section', 'green');
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
271
467
|
// ========================================
|
|
272
468
|
// Success Summary
|
|
273
469
|
// ========================================
|
|
274
470
|
log('\n' + 'ā'.repeat(60), 'green');
|
|
275
|
-
log('š API Development Tools installed successfully!', 'green');
|
|
471
|
+
log('š API Development Tools v3.0 installed successfully!', 'green');
|
|
276
472
|
log('ā'.repeat(60) + '\n', 'green');
|
|
277
473
|
|
|
278
474
|
log('š What was installed:', 'bright');
|
|
279
475
|
log(' Commands: .claude/commands/*.md', 'blue');
|
|
280
|
-
log(' Hooks: .claude/hooks/*.py', 'blue');
|
|
476
|
+
log(' Hooks: .claude/hooks/*.py (18 hooks for 100% enforcement + user checkpoints)', 'blue');
|
|
281
477
|
log(' Settings: .claude/settings.json', 'blue');
|
|
282
478
|
log(' State: .claude/api-dev-state.json', 'blue');
|
|
479
|
+
log(' Research: .claude/research/ (with freshness tracking)', 'blue');
|
|
480
|
+
log(' Scripts: scripts/api-dev-tools/*.ts (manifest generation)', 'blue');
|
|
283
481
|
log(' MCP: context7, github (via claude mcp add)', 'blue');
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
log('
|
|
287
|
-
log(' ā¢
|
|
288
|
-
log(' ā¢
|
|
289
|
-
log(' ā¢
|
|
482
|
+
log(' Test UI: /api-test page + /api/test-structure API (if Next.js)', 'blue');
|
|
483
|
+
|
|
484
|
+
log('\nš New in v3.0:', 'bright');
|
|
485
|
+
log(' ⢠12 phases, each with mandatory user checkpoint', 'cyan');
|
|
486
|
+
log(' ⢠AskUserQuestion required at EVERY phase transition', 'cyan');
|
|
487
|
+
log(' ⢠Loop-back support when user wants changes', 'cyan');
|
|
488
|
+
log(' ⢠Adaptive research (propose-approve, not shotgun)', 'cyan');
|
|
489
|
+
log(' ⢠7-turn re-grounding (prevents context dilution)', 'cyan');
|
|
490
|
+
log(' ⢠Research freshness (7-day cache validity)', 'cyan');
|
|
491
|
+
|
|
492
|
+
log('\nš User Checkpoint Enforcement:', 'bright');
|
|
493
|
+
log(' ⢠Phase 0: "Which interpretation?" (disambiguation)', 'cyan');
|
|
494
|
+
log(' ⢠Phase 1: "Scope correct?" (scope confirmation)', 'cyan');
|
|
495
|
+
log(' ⢠Phase 2: "Proceed to interview?" (research summary)', 'cyan');
|
|
496
|
+
log(' ⢠Phase 3: "Interview complete?" (all questions answered)', 'cyan');
|
|
497
|
+
log(' ⢠Phase 4: "Approve searches?" (deep research proposal)', 'cyan');
|
|
498
|
+
log(' ⢠Phase 5: "Schema matches interview?" (schema review)', 'cyan');
|
|
499
|
+
log(' ⢠Phase 6: "Ready for testing?" (environment check)', 'cyan');
|
|
500
|
+
log(' ⢠Phase 7: "Test plan looks good?" (test matrix)', 'cyan');
|
|
501
|
+
log(' ⢠Phase 9: "Fix gaps?" (verification decision)', 'cyan');
|
|
502
|
+
log(' ⢠Phase 11: "Documentation complete?" (final checklist)', 'cyan');
|
|
290
503
|
|
|
291
504
|
log('\nš Available Commands:', 'bright');
|
|
292
|
-
log(' /api-create [endpoint] - Complete
|
|
293
|
-
log(' /api-interview [endpoint] -
|
|
294
|
-
log(' /api-research [library] -
|
|
505
|
+
log(' /api-create [endpoint] - Complete 12-phase workflow', 'blue');
|
|
506
|
+
log(' /api-interview [endpoint] - Questions FROM research', 'blue');
|
|
507
|
+
log(' /api-research [library] - Adaptive propose-approve research', 'blue');
|
|
508
|
+
log(' /api-verify [endpoint] - Manual Phase 9 verification', 'blue');
|
|
295
509
|
log(' /api-env [endpoint] - Check API keys and environment', 'blue');
|
|
296
|
-
log(' /api-status [endpoint] - Track
|
|
510
|
+
log(' /api-status [endpoint] - Track 12-phase progress', 'blue');
|
|
297
511
|
|
|
298
512
|
log('\nš Quick Start:', 'bright');
|
|
299
513
|
log(' /api-create my-endpoint', 'blue');
|