11import { join } from 'node:path' ;
2+ import { execa } from 'execa' ;
23import type { AnalysisResult , InterviewResult , RobocoConfig } from '../types/index.js' ;
3- import { ensureDir , fileExists , writeText } from '../utils/fs.js' ;
4+ import { ensureDir , fileExists , writeText , readText } from '../utils/fs.js' ;
45import { logger } from '../utils/logger.js' ;
56
67interface FileOperation {
@@ -17,7 +18,12 @@ export async function generate(
1718 const files : FileOperation [ ] = [ ] ;
1819
1920 // Claude Code Environment (required)
20- files . push ( ...generateClaudeEnv ( targetPath , analysis , interviewResult ) ) ;
21+ // Step 1: Run claude /init to generate base CLAUDE.md
22+ await runClaudeInit ( targetPath ) ;
23+ // Step 2: Append ROBOCO context + generate settings/hooks
24+ await appendRobocoContext ( targetPath , analysis , interviewResult ) ;
25+ // Step 3: Generate .claude/settings.json and hooks
26+ files . push ( ...generateClaudeSettings ( targetPath , analysis ) ) ;
2127
2228 // Process Documents (optional)
2329 if ( interviewResult . setupDomains . processDocs ) {
@@ -51,50 +57,84 @@ function relative(base: string, full: string): string {
5157 return full . startsWith ( base ) ? full . slice ( base . length + 1 ) : full ;
5258}
5359
54- function generateClaudeEnv (
55- targetPath : string ,
56- analysis : AnalysisResult ,
57- _interviewResult : InterviewResult ,
58- ) : FileOperation [ ] {
59- const files : FileOperation [ ] = [ ] ;
60- const projectName = analysis . git . repoName ?? 'My Project' ;
61- const stackList =
62- [ ...analysis . stack . languages , ...analysis . stack . frameworks ] . join ( ', ' ) || 'Not detected' ;
63-
64- files . push ( {
65- path : join ( targetPath , 'CLAUDE.md' ) ,
66- content : `# ${ projectName }
60+ async function runClaudeInit ( targetPath : string ) : Promise < void > {
61+ const claudeMdPath = join ( targetPath , 'CLAUDE.md' ) ;
62+ if ( await fileExists ( claudeMdPath ) ) {
63+ logger . info ( 'CLAUDE.md already exists — skipping claude /init' ) ;
64+ return ;
65+ }
6766
68- ## Project Overview
69- <!-- Describe your project here -->
67+ try {
68+ await execa ( 'claude' , [ '/init' ] , {
69+ cwd : targetPath ,
70+ timeout : 30000 ,
71+ env : { ...process . env , CLAUDE_CODE_HEADLESS : '1' } ,
72+ stdin : 'ignore' ,
73+ } ) ;
74+ // Verify claude /init actually created the file
75+ if ( await fileExists ( claudeMdPath ) ) {
76+ logger . success ( 'CLAUDE.md generated via claude /init' ) ;
77+ return ;
78+ }
79+ } catch {
80+ // Fall through to template generation
81+ }
7082
71- ## Tech Stack
72- ${ stackList }
83+ logger . warn ( 'claude /init did not produce CLAUDE.md — generating template' ) ;
84+ const { basename } = await import ( 'node:path' ) ;
85+ await writeText (
86+ claudeMdPath ,
87+ `# ${ basename ( targetPath ) } \n\n## Project Overview\n\nDescribe your project here.\n` ,
88+ ) ;
89+ }
7390
74- ## Development Commands
75- \`\`\`bash
76- # Build
77- # Test
78- # Lint
79- \`\`\`
91+ async function appendRobocoContext (
92+ targetPath : string ,
93+ analysis : AnalysisResult ,
94+ interviewResult : InterviewResult ,
95+ ) : Promise < void > {
96+ const claudeMdPath = join ( targetPath , 'CLAUDE.md' ) ;
97+ if ( ! ( await fileExists ( claudeMdPath ) ) ) return ;
98+
99+ const existing = await readText ( claudeMdPath ) ;
100+ if ( existing . includes ( '<roboco>' ) ) {
101+ logger . info ( 'ROBOCO context already present in CLAUDE.md — skipping' ) ;
102+ return ;
103+ }
80104
81- ## Coding Conventions
82- <!-- Add your coding conventions here -->
105+ const tools = Object . entries ( interviewResult . tools )
106+ . filter ( ( [ , v ] ) => v )
107+ . map ( ( [ k ] ) => k )
108+ . join ( ', ' ) ;
83109
110+ const robocoBlock = `
111+ <roboco>
84112## Vibe Coding Process
85- This project follows the 5-stage vibe coding process:
113+
114+ This project uses ROBOCO for AI-native development with the 5-stage vibe coding process:
861151. **Intent** — Communicate what you want to build
871162. **Requirements** — Define requirements through deep interview
881173. **Research** — Investigate approaches and tools
891184. **Plan** — Create an implementation plan
901195. **Implement** — Build with AI assistance
91120
92- ## Non-Goals
93- <!-- List things explicitly out of scope -->
94- ` ,
95- description : 'CLAUDE.md' ,
96- } ) ;
121+ Each stage produces documents in \`docs/vibe-coding/\`. You can restart from any stage.
122+
123+ ## ROBOCO Configuration
124+
125+ - **Stack**: ${ [ ...analysis . stack . languages , ...analysis . stack . frameworks ] . join ( ', ' ) || 'Not detected' }
126+ - **Tools**: ${ tools }
127+ - **Config**: \`.roboco/config.json\`
128+
129+ Run \`roboco status\` to check setup, \`roboco audit\` for maturity scoring.
130+ </roboco>
131+ ` ;
132+
133+ await writeText ( claudeMdPath , existing . trimEnd ( ) + '\n' + robocoBlock ) ;
134+ logger . success ( 'ROBOCO context appended to CLAUDE.md' ) ;
135+ }
97136
137+ function generateClaudeSettings ( targetPath : string , analysis : AnalysisResult ) : FileOperation [ ] {
98138 const hooks = generateHooksForStack ( analysis . stack . languages ) ;
99139 const settings = {
100140 permissions : {
@@ -114,13 +154,13 @@ This project follows the 5-stage vibe coding process:
114154 ...( Object . keys ( hooks ) . length > 0 ? { hooks } : { } ) ,
115155 } ;
116156
117- files . push ( {
118- path : join ( targetPath , '.claude' , 'settings.json' ) ,
119- content : JSON . stringify ( settings , null , 2 ) + '\n' ,
120- description : '.claude/ settings.json ',
121- } ) ;
122-
123- return files ;
157+ return [
158+ {
159+ path : join ( targetPath , '.claude' , 'settings.json' ) ,
160+ content : JSON . stringify ( settings , null , 2 ) + '\n ',
161+ description : '.claude/settings.json' ,
162+ } ,
163+ ] ;
124164}
125165
126166function generateHooksForStack ( languages : string [ ] ) : Record < string , unknown > {
0 commit comments