33 * Generate/update CLAUDE.md module documentation using CLI tools
44 */
55
6- import { readdirSync , statSync , existsSync , readFileSync } from 'fs' ;
6+ import { readdirSync , statSync , existsSync , readFileSync , writeFileSync , unlinkSync } from 'fs' ;
77import { join , resolve , basename , extname } from 'path' ;
88import { execSync } from 'child_process' ;
9+ import { tmpdir } from 'os' ;
910
1011// Directories to exclude
1112const EXCLUDE_DIRS = [
@@ -147,21 +148,43 @@ function loadTemplate() {
147148}
148149
149150/**
150- * Build CLI command
151+ * Create temporary prompt file and return cleanup function
151152 */
152- function buildCliCommand ( tool , prompt , model ) {
153- const escapedPrompt = prompt . replace ( / " / g, '\\"' ) ;
153+ function createPromptFile ( prompt ) {
154+ const timestamp = Date . now ( ) ;
155+ const randomSuffix = Math . random ( ) . toString ( 36 ) . substring ( 2 , 8 ) ;
156+ const promptFile = join ( tmpdir ( ) , `claude-prompt-${ timestamp } -${ randomSuffix } .txt` ) ;
157+ writeFileSync ( promptFile , prompt , 'utf8' ) ;
158+ return promptFile ;
159+ }
154160
161+ /**
162+ * Build CLI command using stdin piping for prompt (avoids shell escaping issues)
163+ */
164+ function buildCliCommand ( tool , promptFile , model ) {
165+ // Use stdin piping: cat file | tool or Get-Content | tool
166+ // This avoids shell escaping issues with multiline prompts
167+ const normalizedPath = promptFile . replace ( / \\ / g, '/' ) ;
168+ const isWindows = process . platform === 'win32' ;
169+
170+ // Build the cat/read command based on platform
171+ const catCmd = isWindows ? `Get-Content -Raw "${ normalizedPath } " | ` : `cat "${ normalizedPath } " | ` ;
172+
155173 switch ( tool ) {
156174 case 'qwen' :
157175 return model === 'coder-model'
158- ? `qwen -p " ${ escapedPrompt } " --yolo`
159- : `qwen -p " ${ escapedPrompt } " -m "${ model } " --yolo` ;
176+ ? `${ catCmd } qwen --yolo`
177+ : `${ catCmd } qwen -m "${ model } " --yolo` ;
160178 case 'codex' :
161- return `codex --full-auto exec "${ escapedPrompt } " -m "${ model } " --skip-git-repo-check -s danger-full-access` ;
179+ // codex uses different syntax - prompt as exec argument
180+ if ( isWindows ) {
181+ return `codex --full-auto exec (Get-Content -Raw "${ normalizedPath } ") -m "${ model } " --skip-git-repo-check -s danger-full-access` ;
182+ }
183+ return `codex --full-auto exec "$(cat "${ normalizedPath } ")" -m "${ model } " --skip-git-repo-check -s danger-full-access` ;
162184 case 'gemini' :
163185 default :
164- return `gemini -p "${ escapedPrompt } " -m "${ model } " --yolo` ;
186+ // gemini reads from stdin when no positional prompt is given
187+ return `${ catCmd } gemini -m "${ model } " --yolo` ;
165188 }
166189}
167190
@@ -232,7 +255,8 @@ Instructions:
232255- Work bottom-up: deepest directories first
233256- Parent directories reference children
234257- Each CLAUDE.md file must be in its respective directory
235- - Follow the template guidelines above for consistent structure` ;
258+ - Follow the template guidelines above for consistent structure
259+ - Use the structure analysis to understand directory hierarchy` ;
236260 } else {
237261 prompt = `Directory Structure Analysis:
238262${ structureInfo }
@@ -247,11 +271,20 @@ ${templateContent}
247271Instructions:
248272- Create exactly one CLAUDE.md file in the current directory
249273- Reference child CLAUDE.md files, do not duplicate their content
250- - Follow the template guidelines above for consistent structure` ;
274+ - Follow the template guidelines above for consistent structure
275+ - Use the structure analysis to understand the current directory context` ;
251276 }
252277
253- // Build and execute command
254- const command = buildCliCommand ( tool , prompt , actualModel ) ;
278+ // Create temporary prompt file (avoids shell escaping issues with multiline prompts)
279+ const promptFile = createPromptFile ( prompt ) ;
280+
281+ // Build command using file-based prompt
282+ const command = buildCliCommand ( tool , promptFile , actualModel ) ;
283+
284+ // Log execution info
285+ console . log ( `⚡ Updating: ${ modulePath } ` ) ;
286+ console . log ( ` Strategy: ${ strategy } | Tool: ${ tool } | Model: ${ actualModel } | Files: ${ fileCount } ` ) ;
287+ console . log ( ` Prompt file: ${ promptFile } ` ) ;
255288
256289 try {
257290 const startTime = Date . now ( ) ;
@@ -260,11 +293,21 @@ Instructions:
260293 cwd : targetPath ,
261294 encoding : 'utf8' ,
262295 stdio : 'inherit' ,
263- timeout : 300000 // 5 minutes
296+ timeout : 300000 , // 5 minutes
297+ shell : process . platform === 'win32' ? 'powershell.exe' : '/bin/bash'
264298 } ) ;
265299
266300 const duration = Math . round ( ( Date . now ( ) - startTime ) / 1000 ) ;
267301
302+ // Cleanup prompt file
303+ try {
304+ unlinkSync ( promptFile ) ;
305+ } catch ( e ) {
306+ // Ignore cleanup errors
307+ }
308+
309+ console . log ( ` ✅ Completed in ${ duration } s` ) ;
310+
268311 return {
269312 success : true ,
270313 strategy,
@@ -276,6 +319,15 @@ Instructions:
276319 message : `CLAUDE.md updated successfully in ${ duration } s`
277320 } ;
278321 } catch ( error ) {
322+ // Cleanup prompt file on error
323+ try {
324+ unlinkSync ( promptFile ) ;
325+ } catch ( e ) {
326+ // Ignore cleanup errors
327+ }
328+
329+ console . log ( ` ❌ Update failed: ${ error . message } ` ) ;
330+
279331 return {
280332 success : false ,
281333 strategy,
0 commit comments