aboutsummaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
authorClawd <ai@clawd.bot>2026-02-28 18:46:11 -0800
committerClawd <ai@clawd.bot>2026-02-28 18:46:11 -0800
commit3ac34530578b9a6f59bcea6b5aeefd97eb03d588 (patch)
tree099fe4a4788c5dd5997d1f16f5d2db917eda86d0 /src/main
parentde242df9cbe7dfe483f59f9b25e980727baa4c11 (diff)
Move artifacts to ~/.claude-flow/ (outside repo)
- Store session artifacts in ~/.claude-flow/projects/{projectId}/sessions/{sessionId}/ - Artifacts no longer live in project directory - can't be accidentally committed - Remove .claude-flow/ from .gitignore (not needed anymore) - Update all IPC handlers and renderer to use projectId instead of projectPath - Update prompts to remove worktree references - Update README with new storage location
Diffstat (limited to 'src/main')
-rw-r--r--src/main/claude/index.ts65
-rw-r--r--src/main/claude/phases.ts55
-rw-r--r--src/main/ipc/handlers.ts34
-rw-r--r--src/main/preload.ts37
4 files changed, 61 insertions, 130 deletions
diff --git a/src/main/claude/index.ts b/src/main/claude/index.ts
index 4d8909b..b8c9c07 100644
--- a/src/main/claude/index.ts
+++ b/src/main/claude/index.ts
@@ -6,16 +6,28 @@ import { getProject } from "../db/projects";
6import { updateSession } from "../db/sessions"; 6import { updateSession } from "../db/sessions";
7import fs from "node:fs"; 7import fs from "node:fs";
8import path from "node:path"; 8import path from "node:path";
9import os from "node:os";
9 10
10// Track active queries by session ID 11// Track active queries by session ID
11const activeQueries = new Map<string, Query>(); 12const activeQueries = new Map<string, Query>();
12 13
14// Global storage in home directory
15const GLOBAL_CLAUDE_FLOW_DIR = path.join(os.homedir(), ".claude-flow");
16
13function ensureDir(dirPath: string): void { 17function ensureDir(dirPath: string): void {
14 if (!fs.existsSync(dirPath)) { 18 if (!fs.existsSync(dirPath)) {
15 fs.mkdirSync(dirPath, { recursive: true }); 19 fs.mkdirSync(dirPath, { recursive: true });
16 } 20 }
17} 21}
18 22
23function getProjectDir(projectId: string): string {
24 return path.join(GLOBAL_CLAUDE_FLOW_DIR, "projects", projectId);
25}
26
27function getSessionDir(projectId: string, sessionId: string): string {
28 return path.join(getProjectDir(projectId), "sessions", sessionId);
29}
30
19export interface SendMessageOptions { 31export interface SendMessageOptions {
20 session: Session; 32 session: Session;
21 message: string; 33 message: string;
@@ -30,8 +42,8 @@ export async function sendMessage({
30 const project = getProject(session.project_id); 42 const project = getProject(session.project_id);
31 if (!project) throw new Error("Project not found"); 43 if (!project) throw new Error("Project not found");
32 44
33 // Ensure session artifact directory exists 45 // Ensure session artifact directory exists in global storage
34 const sessionDir = path.join(project.path, getSessionArtifactDir(session.id)); 46 const sessionDir = getSessionDir(session.project_id, session.id);
35 ensureDir(sessionDir); 47 ensureDir(sessionDir);
36 48
37 const phaseConfig = getPhaseConfig( 49 const phaseConfig = getPhaseConfig(
@@ -100,22 +112,22 @@ export function advancePhase(session: Session): Phase | null {
100} 112}
101 113
102/** 114/**
103 * Get the artifact path for a session and phase 115 * Get the artifact path for a session and phase (in global storage)
104 */ 116 */
105export function getArtifactPath(session: Session): string { 117export function getArtifactPath(session: Session): string {
106 const filename = getArtifactFilename(session.phase as Phase); 118 const filename = getArtifactFilename(session.phase as Phase);
107 return path.join(getSessionArtifactDir(session.id), filename); 119 return path.join(getSessionDir(session.project_id, session.id), filename);
108} 120}
109 121
110/** 122/**
111 * Read an artifact file for a session 123 * Read an artifact file for a session (from global storage)
112 */ 124 */
113export function readSessionArtifact( 125export function readSessionArtifact(
114 projectPath: string, 126 projectId: string,
115 sessionId: string, 127 sessionId: string,
116 filename: string 128 filename: string
117): string | null { 129): string | null {
118 const filePath = path.join(projectPath, getSessionArtifactDir(sessionId), filename); 130 const filePath = path.join(getSessionDir(projectId, sessionId), filename);
119 if (fs.existsSync(filePath)) { 131 if (fs.existsSync(filePath)) {
120 return fs.readFileSync(filePath, "utf-8"); 132 return fs.readFileSync(filePath, "utf-8");
121 } 133 }
@@ -123,15 +135,15 @@ export function readSessionArtifact(
123} 135}
124 136
125/** 137/**
126 * Write an artifact file for a session 138 * Write an artifact file for a session (to global storage)
127 */ 139 */
128export function writeSessionArtifact( 140export function writeSessionArtifact(
129 projectPath: string, 141 projectId: string,
130 sessionId: string, 142 sessionId: string,
131 filename: string, 143 filename: string,
132 content: string 144 content: string
133): void { 145): void {
134 const dir = path.join(projectPath, getSessionArtifactDir(sessionId)); 146 const dir = getSessionDir(projectId, sessionId);
135 ensureDir(dir); 147 ensureDir(dir);
136 fs.writeFileSync(path.join(dir, filename), content, "utf-8"); 148 fs.writeFileSync(path.join(dir, filename), content, "utf-8");
137} 149}
@@ -156,37 +168,10 @@ export function writeClaudeMd(projectPath: string, content: string): void {
156} 168}
157 169
158/** 170/**
159 * Read an artifact file from the project's .claude-flow directory (legacy path) 171 * Clear session artifacts from global storage
160 */
161export function readArtifact(
162 projectPath: string,
163 filename: string
164): string | null {
165 const filePath = path.join(projectPath, ".claude-flow", filename);
166 if (fs.existsSync(filePath)) {
167 return fs.readFileSync(filePath, "utf-8");
168 }
169 return null;
170}
171
172/**
173 * Write an artifact file to the project's .claude-flow directory (legacy path)
174 */
175export function writeArtifact(
176 projectPath: string,
177 filename: string,
178 content: string
179): void {
180 const dir = path.join(projectPath, ".claude-flow");
181 ensureDir(dir);
182 fs.writeFileSync(path.join(dir, filename), content, "utf-8");
183}
184
185/**
186 * Clear session artifacts
187 */ 172 */
188export function clearSessionArtifacts(projectPath: string, sessionId: string): void { 173export function clearSessionArtifacts(projectId: string, sessionId: string): void {
189 const dir = path.join(projectPath, getSessionArtifactDir(sessionId)); 174 const dir = getSessionDir(projectId, sessionId);
190 if (fs.existsSync(dir)) { 175 if (fs.existsSync(dir)) {
191 fs.rmSync(dir, { recursive: true, force: true }); 176 fs.rmSync(dir, { recursive: true, force: true });
192 } 177 }
diff --git a/src/main/claude/phases.ts b/src/main/claude/phases.ts
index 6678c08..f1df719 100644
--- a/src/main/claude/phases.ts
+++ b/src/main/claude/phases.ts
@@ -10,9 +10,9 @@ export interface PhaseConfig {
10 initialMessage: string; 10 initialMessage: string;
11} 11}
12 12
13// Get session-specific artifact path 13// Get session-specific artifact path (relative to ~/.claude-flow/)
14export function getSessionArtifactDir(sessionId: string): string { 14export function getSessionArtifactDir(sessionId: string): string {
15 return `.claude-flow/sessions/${sessionId}`; 15 return `sessions/${sessionId}`;
16} 16}
17 17
18export function getArtifactPath(phase: Phase, sessionId: string): string { 18export function getArtifactPath(phase: Phase, sessionId: string): string {
@@ -30,7 +30,7 @@ export const phaseConfigs: Record<Phase, PhaseConfig> = {
30 systemPrompt: `You are in RESEARCH mode. Your ONLY job is to understand the codebase. 30 systemPrompt: `You are in RESEARCH mode. Your ONLY job is to understand the codebase.
31 31
32CRITICAL RULES: 32CRITICAL RULES:
331. You MUST write ALL findings to \`.claude-flow/sessions/{sessionId}/research.md\` — this is your PRIMARY output 331. You MUST write ALL findings to the session research.md — this is your PRIMARY output
342. DO NOT just respond in chat. The document viewer shows research.md, so write there. 342. DO NOT just respond in chat. The document viewer shows research.md, so write there.
353. DO NOT suggest moving to planning or implementation 353. DO NOT suggest moving to planning or implementation
364. DO NOT ask "are you ready to implement?" or similar 364. DO NOT ask "are you ready to implement?" or similar
@@ -38,16 +38,15 @@ CRITICAL RULES:
386. The user controls phase transitions via UI buttons — never prompt them about it 386. The user controls phase transitions via UI buttons — never prompt them about it
39 39
40CONTEXT: 40CONTEXT:
41- You are in a git worktree at \`.claude-flow/worktrees/{sessionId}/\` 41- Read CLAUDE.md in the project root (if it exists) for codebase overview
42- Read CLAUDE.md in the project root for shared codebase overview
43- If CLAUDE.md doesn't exist, create it with your initial findings
44- This file contains general architecture info shared across all sessions 42- This file contains general architecture info shared across all sessions
43- If CLAUDE.md doesn't exist, create it with your initial findings
45 44
46WORKFLOW: 45WORKFLOW:
471. Read CLAUDE.md (create at project root if missing) 461. Read CLAUDE.md (create at project root if missing)
482. Ask what to research (if unclear) 472. Ask what to research (if unclear)
493. Read files thoroughly using Read, Glob, Grep 483. Read files thoroughly using Read, Glob, Grep
504. Write findings to \`.claude-flow/sessions/{sessionId}/research.md\` 494. Write findings to session research.md
515. Update CLAUDE.md with any new general insights worth sharing 505. Update CLAUDE.md with any new general insights worth sharing
52 51
53FORMAT for research.md: 52FORMAT for research.md:
@@ -70,7 +69,7 @@ FORMAT for research.md:
70[Things that need clarification] 69[Things that need clarification]
71\`\`\` 70\`\`\`
72 71
73Remember: Your output goes in \`.claude-flow/sessions/{sessionId}/research.md\`, not chat. Chat is for clarifying questions only.`, 72Remember: Your output goes in research.md, not chat. Chat is for clarifying questions only.`,
74 }, 73 },
75 74
76 plan: { 75 plan: {
@@ -81,7 +80,7 @@ Remember: Your output goes in \`.claude-flow/sessions/{sessionId}/research.md\`,
81 systemPrompt: `You are in PLANNING mode. Your ONLY job is to create an implementation plan. 80 systemPrompt: `You are in PLANNING mode. Your ONLY job is to create an implementation plan.
82 81
83CRITICAL RULES: 82CRITICAL RULES:
841. You MUST write the plan to \`.claude-flow/sessions/{sessionId}/plan.md\` — this is your PRIMARY output 831. You MUST write the plan to session plan.md — this is your PRIMARY output
852. DO NOT just respond in chat. The document viewer shows plan.md, so write there. 842. DO NOT just respond in chat. The document viewer shows plan.md, so write there.
863. DO NOT implement anything — no code changes to source files 853. DO NOT implement anything — no code changes to source files
874. DO NOT ask "should I start implementing?" or similar 864. DO NOT ask "should I start implementing?" or similar
@@ -89,14 +88,13 @@ CRITICAL RULES:
896. Base your plan on the session research.md and CLAUDE.md 886. Base your plan on the session research.md and CLAUDE.md
90 89
91CONTEXT: 90CONTEXT:
92- You are in a git worktree at \`.claude-flow/worktrees/{sessionId}/\`
93- Read CLAUDE.md at project root for codebase overview 91- Read CLAUDE.md at project root for codebase overview
94- Read \`.claude-flow/sessions/{sessionId}/research.md\` for this specific task 92- Read the session research.md to understand the specific task
95 93
96WORKFLOW: 94WORKFLOW:
971. Read CLAUDE.md for codebase overview 951. Read CLAUDE.md for codebase overview
982. Read the session research.md to understand the specific task 962. Read the session research.md to understand the specific task
993. Write a detailed plan to \`.claude-flow/sessions/{sessionId}/plan.md\` 973. Write a detailed plan to session plan.md
1004. Include specific code snippets showing proposed changes 984. Include specific code snippets showing proposed changes
1015. Make the plan detailed enough that implementation is mechanical 995. Make the plan detailed enough that implementation is mechanical
102 100
@@ -134,47 +132,30 @@ FORMAT for plan.md:
134 132
135When the user adds annotations to plan.md and clicks Review, address each annotation and update the document. 133When the user adds annotations to plan.md and clicks Review, address each annotation and update the document.
136 134
137Remember: Your output goes in \`.claude-flow/sessions/{sessionId}/plan.md\`, not chat. Chat is for clarifying questions only.`, 135Remember: Your output goes in plan.md, not chat. Chat is for clarifying questions only.`,
138 }, 136 },
139 137
140 implement: { 138 implement: {
141 permissionMode: "acceptEdits", 139 permissionMode: "acceptEdits",
142 tools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"], 140 tools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
143 initialMessage: 141 initialMessage:
144 "Starting implementation. I'll follow the plan exactly, commit as I go, and mark tasks complete.", 142 "Starting implementation. I'll follow the plan exactly and mark tasks complete as I go.",
145 systemPrompt: `You are in IMPLEMENTATION mode. Execute the approved plan. 143 systemPrompt: `You are in IMPLEMENTATION mode. Execute the approved plan.
146 144
147CRITICAL RULES: 145CRITICAL RULES:
1481. Read \`.claude-flow/sessions/{sessionId}/plan.md\` and follow it exactly 1461. Read session plan.md and follow it exactly
1492. Mark tasks complete in plan.md as you finish them: - [ ] → - [x] 1472. Mark tasks complete in plan.md as you finish them: - [ ] → - [x]
1503. DO NOT deviate from the plan without asking 1483. DO NOT deviate from the plan without asking
1514. Run tests/typecheck if available 1494. Run tests/typecheck if available
1525. Make git commits as you complete logical chunks of work 1505. Stop and ask if you encounter issues not covered by the plan
1536. Stop and ask if you encounter issues not covered by the plan
154
155CONTEXT:
156- You are in a git worktree at \`.claude-flow/worktrees/{sessionId}/\`
157- This is an isolated branch: \`claude-flow/{sessionId}\`
158- Your commits will not affect the main branch until merged
159- The user can review your work in this worktree before accepting
160 151
161WORKFLOW: 152WORKFLOW:
1621. Read \`.claude-flow/sessions/{sessionId}/plan.md\` 1531. Read session plan.md
1632. Execute each task in order 1542. Execute each task in order
1643. Update plan.md to mark tasks complete 1553. Update plan.md to mark tasks complete
1654. Make git commits with clear messages as you finish chunks 1564. Continue until all tasks are done
1665. Continue until all tasks are done 157
167 158When complete, summarize what was done and any follow-up tasks.`,
168COMMIT GUIDELINES:
169- Commit after completing logical units of work
170- Use clear commit messages (e.g., "Add user authentication middleware")
171- Don't commit broken or incomplete code
172- Update CLAUDE.md at project root if you discover important architecture info
173
174When complete, summarize what was done and tell the user how to review:
175- The work is in worktree: \`.claude-flow/worktrees/{sessionId}/\`
176- Branch: \`claude-flow/{sessionId}\`
177- They can review, then merge or discard as needed`,
178 }, 159 },
179}; 160};
180 161
diff --git a/src/main/ipc/handlers.ts b/src/main/ipc/handlers.ts
index 2d5e3d3..d9beaf0 100644
--- a/src/main/ipc/handlers.ts
+++ b/src/main/ipc/handlers.ts
@@ -26,11 +26,8 @@ export function registerIpcHandlers(mainWindow: BrowserWindow): void {
26 ipcMain.handle("sessions:delete", (_, id: string) => { 26 ipcMain.handle("sessions:delete", (_, id: string) => {
27 const session = sessions.getSession(id); 27 const session = sessions.getSession(id);
28 if (session) { 28 if (session) {
29 const project = projects.getProject(session.project_id); 29 // Clean up session artifacts from global storage
30 if (project) { 30 claude.clearSessionArtifacts(session.project_id, id);
31 // Clean up session artifacts
32 claude.clearSessionArtifacts(project.path, id);
33 }
34 } 31 }
35 sessions.deleteSession(id); 32 sessions.deleteSession(id);
36 }); 33 });
@@ -103,22 +100,22 @@ export function registerIpcHandlers(mainWindow: BrowserWindow): void {
103 } 100 }
104 ); 101 );
105 102
106 // Session Artifacts (new session-specific API) 103 // Session Artifacts (stored in ~/.claude-flow/)
107 ipcMain.handle( 104 ipcMain.handle(
108 "artifact:readSession", 105 "artifact:readSession",
109 (_, projectPath: string, sessionId: string, filename: string) => { 106 (_, projectId: string, sessionId: string, filename: string) => {
110 return claude.readSessionArtifact(projectPath, sessionId, filename); 107 return claude.readSessionArtifact(projectId, sessionId, filename);
111 } 108 }
112 ); 109 );
113 110
114 ipcMain.handle( 111 ipcMain.handle(
115 "artifact:writeSession", 112 "artifact:writeSession",
116 (_, projectPath: string, sessionId: string, filename: string, content: string) => { 113 (_, projectId: string, sessionId: string, filename: string, content: string) => {
117 claude.writeSessionArtifact(projectPath, sessionId, filename, content); 114 claude.writeSessionArtifact(projectId, sessionId, filename, content);
118 } 115 }
119 ); 116 );
120 117
121 // CLAUDE.md 118 // CLAUDE.md (stored in project)
122 ipcMain.handle("claudemd:read", (_, projectPath: string) => { 119 ipcMain.handle("claudemd:read", (_, projectPath: string) => {
123 return claude.readClaudeMd(projectPath); 120 return claude.readClaudeMd(projectPath);
124 }); 121 });
@@ -127,21 +124,6 @@ export function registerIpcHandlers(mainWindow: BrowserWindow): void {
127 claude.writeClaudeMd(projectPath, content); 124 claude.writeClaudeMd(projectPath, content);
128 }); 125 });
129 126
130 // Legacy artifact API (for backward compatibility)
131 ipcMain.handle(
132 "artifact:read",
133 (_, projectPath: string, filename: string) => {
134 return claude.readArtifact(projectPath, filename);
135 }
136 );
137
138 ipcMain.handle(
139 "artifact:write",
140 (_, projectPath: string, filename: string, content: string) => {
141 claude.writeArtifact(projectPath, filename, content);
142 }
143 );
144
145 // Dialogs 127 // Dialogs
146 ipcMain.handle("dialog:selectDirectory", async () => { 128 ipcMain.handle("dialog:selectDirectory", async () => {
147 const result = await dialog.showOpenDialog(mainWindow, { 129 const result = await dialog.showOpenDialog(mainWindow, {
diff --git a/src/main/preload.ts b/src/main/preload.ts
index 7c1d634..2c228dd 100644
--- a/src/main/preload.ts
+++ b/src/main/preload.ts
@@ -31,34 +31,23 @@ export interface ClaudeFlowAPI {
31 mode: UserPermissionMode 31 mode: UserPermissionMode
32 ) => Promise<void>; 32 ) => Promise<void>;
33 33
34 // Session Artifacts (session-specific) 34 // Session Artifacts (stored in ~/.claude-flow/)
35 readSessionArtifact: ( 35 readSessionArtifact: (
36 projectPath: string, 36 projectId: string,
37 sessionId: string, 37 sessionId: string,
38 filename: string 38 filename: string
39 ) => Promise<string | null>; 39 ) => Promise<string | null>;
40 writeSessionArtifact: ( 40 writeSessionArtifact: (
41 projectPath: string, 41 projectId: string,
42 sessionId: string, 42 sessionId: string,
43 filename: string, 43 filename: string,
44 content: string 44 content: string
45 ) => Promise<void>; 45 ) => Promise<void>;
46 46
47 // CLAUDE.md 47 // CLAUDE.md (stored in project)
48 readClaudeMd: (projectPath: string) => Promise<string | null>; 48 readClaudeMd: (projectPath: string) => Promise<string | null>;
49 writeClaudeMd: (projectPath: string, content: string) => Promise<void>; 49 writeClaudeMd: (projectPath: string, content: string) => Promise<void>;
50 50
51 // Legacy Artifacts (backward compat)
52 readArtifact: (
53 projectPath: string,
54 filename: string
55 ) => Promise<string | null>;
56 writeArtifact: (
57 projectPath: string,
58 filename: string,
59 content: string
60 ) => Promise<void>;
61
62 // Events 51 // Events
63 onClaudeMessage: ( 52 onClaudeMessage: (
64 callback: (sessionId: string, message: SDKMessage) => void 53 callback: (sessionId: string, message: SDKMessage) => void
@@ -97,23 +86,17 @@ const api: ClaudeFlowAPI = {
97 setPermissionMode: (sessionId, mode) => 86 setPermissionMode: (sessionId, mode) =>
98 ipcRenderer.invoke("workflow:setPermissionMode", sessionId, mode), 87 ipcRenderer.invoke("workflow:setPermissionMode", sessionId, mode),
99 88
100 // Session Artifacts 89 // Session Artifacts (stored in ~/.claude-flow/)
101 readSessionArtifact: (projectPath, sessionId, filename) => 90 readSessionArtifact: (projectId, sessionId, filename) =>
102 ipcRenderer.invoke("artifact:readSession", projectPath, sessionId, filename), 91 ipcRenderer.invoke("artifact:readSession", projectId, sessionId, filename),
103 writeSessionArtifact: (projectPath, sessionId, filename, content) => 92 writeSessionArtifact: (projectId, sessionId, filename, content) =>
104 ipcRenderer.invoke("artifact:writeSession", projectPath, sessionId, filename, content), 93 ipcRenderer.invoke("artifact:writeSession", projectId, sessionId, filename, content),
105 94
106 // CLAUDE.md 95 // CLAUDE.md (stored in project)
107 readClaudeMd: (projectPath) => ipcRenderer.invoke("claudemd:read", projectPath), 96 readClaudeMd: (projectPath) => ipcRenderer.invoke("claudemd:read", projectPath),
108 writeClaudeMd: (projectPath, content) => 97 writeClaudeMd: (projectPath, content) =>
109 ipcRenderer.invoke("claudemd:write", projectPath, content), 98 ipcRenderer.invoke("claudemd:write", projectPath, content),
110 99
111 // Legacy Artifacts
112 readArtifact: (projectPath, filename) =>
113 ipcRenderer.invoke("artifact:read", projectPath, filename),
114 writeArtifact: (projectPath, filename, content) =>
115 ipcRenderer.invoke("artifact:write", projectPath, filename, content),
116
117 // Events 100 // Events
118 onClaudeMessage: (callback) => { 101 onClaudeMessage: (callback) => {
119 const handler = ( 102 const handler = (