From 0edd7235cd861ef77d4ceb37a594ae65df52624b Mon Sep 17 00:00:00 2001 From: Clawd Date: Sat, 28 Feb 2026 15:35:00 -0800 Subject: Add session-specific artifacts, CLAUDE.md, and git worktree support - Store artifacts in .claude-flow/sessions/{sessionId}/ - Each session now has isolated research.md and plan.md - Concurrent sessions no longer conflict - Add CLAUDE.md support for shared codebase documentation - Add git worktree creation on session start - Add git commit/status IPC handlers - Update all artifact APIs to be session-specific - Remove artifact clearing on new session (no longer needed) --- src/main/claude/index.ts | 91 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 13 deletions(-) (limited to 'src/main/claude/index.ts') diff --git a/src/main/claude/index.ts b/src/main/claude/index.ts index 8bdcccd..4d8909b 100644 --- a/src/main/claude/index.ts +++ b/src/main/claude/index.ts @@ -1,6 +1,6 @@ import { query, type SDKMessage, type Query } from "@anthropic-ai/claude-agent-sdk"; import type { Session } from "../db/sessions"; -import { getPhaseConfig, getNextPhase, getArtifactFilename } from "./phases"; +import { getPhaseConfig, getNextPhase, getArtifactFilename, getSessionArtifactDir } from "./phases"; import type { Phase, UserPermissionMode } from "./phases"; import { getProject } from "../db/projects"; import { updateSession } from "../db/sessions"; @@ -10,10 +10,9 @@ import path from "node:path"; // Track active queries by session ID const activeQueries = new Map(); -function ensureArtifactDir(projectPath: string): void { - const dir = path.join(projectPath, ".claude-flow"); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); +function ensureDir(dirPath: string): void { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); } } @@ -31,7 +30,9 @@ export async function sendMessage({ const project = getProject(session.project_id); if (!project) throw new Error("Project not found"); - ensureArtifactDir(project.path); + // Ensure session artifact directory exists + const sessionDir = path.join(project.path, getSessionArtifactDir(session.id)); + ensureDir(sessionDir); const phaseConfig = getPhaseConfig( session.phase as Phase, @@ -81,8 +82,8 @@ export async function triggerReview( session: Session, onMessage: (msg: SDKMessage) => void ): Promise { - const docName = getArtifactFilename(session.phase as Phase); - const message = `I've updated .claude-flow/${docName} with annotations. Read the file, find all my inline notes (marked with // REVIEW:, // NOTE:, TODO:, or similar), address each one, and update the document accordingly. Do not implement anything yet.`; + const artifactPath = getArtifactPath(session); + const message = `I've updated ${artifactPath} with annotations. Read the file, find all my inline notes (marked with // REVIEW:, // NOTE:, TODO:, or similar), address each one, and update the document accordingly. Do not implement anything yet.`; await sendMessage({ session, message, onMessage }); } @@ -99,7 +100,63 @@ export function advancePhase(session: Session): Phase | null { } /** - * Read an artifact file from the project's .claude-flow directory + * Get the artifact path for a session and phase + */ +export function getArtifactPath(session: Session): string { + const filename = getArtifactFilename(session.phase as Phase); + return path.join(getSessionArtifactDir(session.id), filename); +} + +/** + * Read an artifact file for a session + */ +export function readSessionArtifact( + projectPath: string, + sessionId: string, + filename: string +): string | null { + const filePath = path.join(projectPath, getSessionArtifactDir(sessionId), filename); + if (fs.existsSync(filePath)) { + return fs.readFileSync(filePath, "utf-8"); + } + return null; +} + +/** + * Write an artifact file for a session + */ +export function writeSessionArtifact( + projectPath: string, + sessionId: string, + filename: string, + content: string +): void { + const dir = path.join(projectPath, getSessionArtifactDir(sessionId)); + ensureDir(dir); + fs.writeFileSync(path.join(dir, filename), content, "utf-8"); +} + +/** + * Read CLAUDE.md from project root + */ +export function readClaudeMd(projectPath: string): string | null { + const filePath = path.join(projectPath, "CLAUDE.md"); + if (fs.existsSync(filePath)) { + return fs.readFileSync(filePath, "utf-8"); + } + return null; +} + +/** + * Write CLAUDE.md to project root + */ +export function writeClaudeMd(projectPath: string, content: string): void { + const filePath = path.join(projectPath, "CLAUDE.md"); + fs.writeFileSync(filePath, content, "utf-8"); +} + +/** + * Read an artifact file from the project's .claude-flow directory (legacy path) */ export function readArtifact( projectPath: string, @@ -113,7 +170,7 @@ export function readArtifact( } /** - * Write an artifact file to the project's .claude-flow directory + * Write an artifact file to the project's .claude-flow directory (legacy path) */ export function writeArtifact( projectPath: string, @@ -121,12 +178,20 @@ export function writeArtifact( content: string ): void { const dir = path.join(projectPath, ".claude-flow"); - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }); - } + ensureDir(dir); fs.writeFileSync(path.join(dir, filename), content, "utf-8"); } +/** + * Clear session artifacts + */ +export function clearSessionArtifacts(projectPath: string, sessionId: string): void { + const dir = path.join(projectPath, getSessionArtifactDir(sessionId)); + if (fs.existsSync(dir)) { + fs.rmSync(dir, { recursive: true, force: true }); + } +} + /** * Get the initial message for a phase */ -- cgit v1.2.3