feat(v0.6): show CLAUDE.md on the project detail page + /init generate/update button
The detail page now reads <repo>/CLAUDE.md and shows it in a scrollable
monospace box, with one smart button: "↻ Update" when it exists / "✨ Generate"
when it doesn't. The button opens an interactive Claude session in the repo
running /init (claude "/init") so you review what it writes — reuses the project
launcher, no new backend orchestration, stays a byte-shuttle.
- ProjectDetail += hasClaudeMd / claudeMd; buildProjectDetail reads CLAUDE.md
(truncated 64KB, best-effort, in the existing Promise.all)
- renderProjectDetail: CLAUDE.md section (content box or empty state) + button
- style.css: .proj-section-row / .proj-claudemd-btn / .proj-claudemd
Tests +5 (458): backend read present/absent; frontend content+Update,
empty+Generate, button opens `claude "/init"`. Verified: tsc clean, full suite
green, build OK, curl returns the content, in-browser shows the box + button.
This commit is contained in:
@@ -361,6 +361,20 @@ export async function buildProjects(
|
||||
return sortProjects(withSessions).slice(0, MAX_PROJECTS)
|
||||
}
|
||||
|
||||
const CLAUDE_MD_MAX_BYTES = 64 * 1024 // cap CLAUDE.md content sent for display
|
||||
|
||||
/** Read <repo>/CLAUDE.md (truncated); undefined when absent/unreadable. */
|
||||
async function readClaudeMd(projectPath: string): Promise<string | undefined> {
|
||||
try {
|
||||
const text = await fs.readFile(path.join(projectPath, 'CLAUDE.md'), 'utf8')
|
||||
return text.length > CLAUDE_MD_MAX_BYTES
|
||||
? text.slice(0, CLAUDE_MD_MAX_BYTES) + '\n\n…(truncated)'
|
||||
: text
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detail for one project: branch + dirty + worktrees + its running sessions.
|
||||
* `projectPath` must be an absolute, existing directory (else null — 404). Not
|
||||
@@ -383,10 +397,11 @@ export async function buildProjectDetail(
|
||||
if (!stat.isDirectory()) return null
|
||||
|
||||
const isGit = await hasGitEntry(projectPath)
|
||||
const [branch, dirty, worktrees] = await Promise.all([
|
||||
const [branch, dirty, worktrees, claudeMd] = await Promise.all([
|
||||
isGit ? readBranch(projectPath) : Promise.resolve(undefined),
|
||||
isGit && cfg.projectDirtyCheck ? readDirty(projectPath) : Promise.resolve(undefined),
|
||||
isGit ? listWorktrees(projectPath) : Promise.resolve([]),
|
||||
readClaudeMd(projectPath),
|
||||
])
|
||||
|
||||
return {
|
||||
@@ -397,5 +412,7 @@ export async function buildProjectDetail(
|
||||
dirty,
|
||||
worktrees,
|
||||
sessions: matchSessions(projectPath, liveSessions),
|
||||
hasClaudeMd: claudeMd !== undefined,
|
||||
claudeMd,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user