From b30657a36f27356c1a00c486807afc25690f1206 Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 19 Jan 2026 02:12:32 +0100 Subject: [PATCH] feat: use custom CLAUDE.md template path when creating cases When creating new cases via /api/cases or /api/quick-start, the server now checks ~/.claudeman/settings.json for a defaultClaudeMdPath. If found and the file exists, it uses that template with placeholder substitution ([PROJECT_NAME], [PROJECT_DESCRIPTION], [DATE]). Co-Authored-By: Claude Opus 4.5 --- src/templates/claude-md.ts | 19 ++++++++++++++++++- src/web/server.ts | 23 +++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/templates/claude-md.ts b/src/templates/claude-md.ts index 532ed60a..9b442b82 100644 --- a/src/templates/claude-md.ts +++ b/src/templates/claude-md.ts @@ -1,6 +1,23 @@ -export function generateClaudeMd(caseName: string, description: string = ''): string { +import { existsSync, readFileSync } from 'node:fs'; + +export function generateClaudeMd(caseName: string, description: string = '', templatePath?: string): string { const date = new Date().toISOString().split('T')[0]; + // If a custom template path is provided and exists, use it + if (templatePath && existsSync(templatePath)) { + try { + let template = readFileSync(templatePath, 'utf-8'); + // Replace placeholders with actual values + template = template.replace(/\[PROJECT_NAME\]/g, caseName); + template = template.replace(/\[PROJECT_DESCRIPTION\]/g, description || 'A new project'); + template = template.replace(/\[DATE\]/g, date); + return template; + } catch (err) { + console.error(`Failed to read template from ${templatePath}:`, err); + // Fall through to default template + } + } + return `# CLAUDE.md - Project Configuration ## Project Overview diff --git a/src/web/server.ts b/src/web/server.ts index 58f068b0..fb727dc5 100644 --- a/src/web/server.ts +++ b/src/web/server.ts @@ -577,7 +577,9 @@ export class WebServer extends EventEmitter { mkdirSync(casePath, { recursive: true }); mkdirSync(join(casePath, 'src'), { recursive: true }); - const claudeMd = generateClaudeMd(name, description || ''); + // Read settings to get custom template path + const templatePath = this.getDefaultClaudeMdPath(); + const claudeMd = generateClaudeMd(name, description || '', templatePath); writeFileSync(join(casePath, 'CLAUDE.md'), claudeMd); this.broadcast('case:created', { name, path: casePath }); @@ -620,7 +622,9 @@ export class WebServer extends EventEmitter { mkdirSync(casePath, { recursive: true }); mkdirSync(join(casePath, 'src'), { recursive: true }); - const claudeMd = generateClaudeMd(caseName, ''); + // Read settings to get custom template path + const templatePath = this.getDefaultClaudeMdPath(); + const claudeMd = generateClaudeMd(caseName, '', templatePath); writeFileSync(join(casePath, 'CLAUDE.md'), claudeMd); this.broadcast('case:created', { name: caseName, path: casePath }); @@ -806,6 +810,21 @@ export class WebServer extends EventEmitter { this.broadcast('respawn:timerStarted', { sessionId, durationMinutes, endAt, startedAt: now }); } + // Helper to get custom CLAUDE.md template path from settings + private getDefaultClaudeMdPath(): string | undefined { + const settingsPath = join(homedir(), '.claudeman', 'settings.json'); + try { + if (existsSync(settingsPath)) { + const content = readFileSync(settingsPath, 'utf-8'); + const settings = JSON.parse(content); + return settings.defaultClaudeMdPath || undefined; + } + } catch (err) { + console.error('Failed to read settings:', err); + } + return undefined; + } + private async startScheduledRun(prompt: string, workingDir: string, durationMinutes: number): Promise { const id = uuidv4(); const now = Date.now();