diff options
Diffstat (limited to 'dist/main')
| -rw-r--r-- | dist/main/claude/index.js | 109 | ||||
| -rw-r--r-- | dist/main/claude/phases.js | 147 | ||||
| -rw-r--r-- | dist/main/db/index.js | 33 | ||||
| -rw-r--r-- | dist/main/db/projects.js | 28 | ||||
| -rw-r--r-- | dist/main/db/schema.js | 36 | ||||
| -rw-r--r-- | dist/main/db/sessions.js | 72 | ||||
| -rw-r--r-- | dist/main/index.js | 54 | ||||
| -rw-r--r-- | dist/main/index.sync-conflict-20260228-074140-M6474AW.js | 54 | ||||
| -rw-r--r-- | dist/main/ipc/handlers.js | 116 | ||||
| -rw-r--r-- | dist/main/preload.js | 38 | ||||
| -rw-r--r-- | dist/main/preload.sync-conflict-20260228-074140-M6474AW.js | 38 |
11 files changed, 725 insertions, 0 deletions
diff --git a/dist/main/claude/index.js b/dist/main/claude/index.js new file mode 100644 index 0000000..42a9652 --- /dev/null +++ b/dist/main/claude/index.js | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | "use strict"; | ||
| 2 | var __importDefault = (this && this.__importDefault) || function (mod) { | ||
| 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| 4 | }; | ||
| 5 | Object.defineProperty(exports, "__esModule", { value: true }); | ||
| 6 | exports.sendMessage = sendMessage; | ||
| 7 | exports.interruptSession = interruptSession; | ||
| 8 | exports.triggerReview = triggerReview; | ||
| 9 | exports.advancePhase = advancePhase; | ||
| 10 | exports.readArtifact = readArtifact; | ||
| 11 | exports.writeArtifact = writeArtifact; | ||
| 12 | exports.getPhaseInitialMessage = getPhaseInitialMessage; | ||
| 13 | const claude_agent_sdk_1 = require("@anthropic-ai/claude-agent-sdk"); | ||
| 14 | const phases_1 = require("./phases"); | ||
| 15 | const projects_1 = require("../db/projects"); | ||
| 16 | const sessions_1 = require("../db/sessions"); | ||
| 17 | const node_fs_1 = __importDefault(require("node:fs")); | ||
| 18 | const node_path_1 = __importDefault(require("node:path")); | ||
| 19 | // Track active queries by session ID | ||
| 20 | const activeQueries = new Map(); | ||
| 21 | function ensureArtifactDir(projectPath) { | ||
| 22 | const dir = node_path_1.default.join(projectPath, ".claude-flow"); | ||
| 23 | if (!node_fs_1.default.existsSync(dir)) { | ||
| 24 | node_fs_1.default.mkdirSync(dir, { recursive: true }); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | async function sendMessage({ session, message, onMessage, }) { | ||
| 28 | const project = (0, projects_1.getProject)(session.project_id); | ||
| 29 | if (!project) | ||
| 30 | throw new Error("Project not found"); | ||
| 31 | ensureArtifactDir(project.path); | ||
| 32 | const phaseConfig = (0, phases_1.getPhaseConfig)(session.phase, session.permission_mode); | ||
| 33 | const q = (0, claude_agent_sdk_1.query)({ | ||
| 34 | prompt: message, | ||
| 35 | options: { | ||
| 36 | cwd: project.path, | ||
| 37 | resume: session.claude_session_id ?? undefined, | ||
| 38 | tools: phaseConfig.tools, | ||
| 39 | permissionMode: phaseConfig.permissionMode, | ||
| 40 | systemPrompt: phaseConfig.systemPrompt, | ||
| 41 | }, | ||
| 42 | }); | ||
| 43 | activeQueries.set(session.id, q); | ||
| 44 | try { | ||
| 45 | for await (const msg of q) { | ||
| 46 | // Capture session ID from init message | ||
| 47 | if (msg.type === "system" && msg.subtype === "init") { | ||
| 48 | if (!session.claude_session_id) { | ||
| 49 | (0, sessions_1.updateSession)(session.id, { claude_session_id: msg.session_id }); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | onMessage(msg); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | finally { | ||
| 56 | activeQueries.delete(session.id); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | function interruptSession(sessionId) { | ||
| 60 | const q = activeQueries.get(sessionId); | ||
| 61 | if (q) { | ||
| 62 | q.close(); | ||
| 63 | activeQueries.delete(sessionId); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | /** | ||
| 67 | * Trigger a review: Claude reads the document and addresses user annotations | ||
| 68 | */ | ||
| 69 | async function triggerReview(session, onMessage) { | ||
| 70 | const docName = (0, phases_1.getArtifactFilename)(session.phase); | ||
| 71 | 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.`; | ||
| 72 | await sendMessage({ session, message, onMessage }); | ||
| 73 | } | ||
| 74 | /** | ||
| 75 | * Advance to the next phase | ||
| 76 | */ | ||
| 77 | function advancePhase(session) { | ||
| 78 | const nextPhase = (0, phases_1.getNextPhase)(session.phase); | ||
| 79 | if (nextPhase) { | ||
| 80 | (0, sessions_1.updateSession)(session.id, { phase: nextPhase }); | ||
| 81 | } | ||
| 82 | return nextPhase; | ||
| 83 | } | ||
| 84 | /** | ||
| 85 | * Read an artifact file from the project's .claude-flow directory | ||
| 86 | */ | ||
| 87 | function readArtifact(projectPath, filename) { | ||
| 88 | const filePath = node_path_1.default.join(projectPath, ".claude-flow", filename); | ||
| 89 | if (node_fs_1.default.existsSync(filePath)) { | ||
| 90 | return node_fs_1.default.readFileSync(filePath, "utf-8"); | ||
| 91 | } | ||
| 92 | return null; | ||
| 93 | } | ||
| 94 | /** | ||
| 95 | * Write an artifact file to the project's .claude-flow directory | ||
| 96 | */ | ||
| 97 | function writeArtifact(projectPath, filename, content) { | ||
| 98 | const dir = node_path_1.default.join(projectPath, ".claude-flow"); | ||
| 99 | if (!node_fs_1.default.existsSync(dir)) { | ||
| 100 | node_fs_1.default.mkdirSync(dir, { recursive: true }); | ||
| 101 | } | ||
| 102 | node_fs_1.default.writeFileSync(node_path_1.default.join(dir, filename), content, "utf-8"); | ||
| 103 | } | ||
| 104 | /** | ||
| 105 | * Get the initial message for a phase | ||
| 106 | */ | ||
| 107 | function getPhaseInitialMessage(phase) { | ||
| 108 | return (0, phases_1.getPhaseConfig)(phase).initialMessage; | ||
| 109 | } | ||
diff --git a/dist/main/claude/phases.js b/dist/main/claude/phases.js new file mode 100644 index 0000000..65ea46d --- /dev/null +++ b/dist/main/claude/phases.js | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | "use strict"; | ||
| 2 | Object.defineProperty(exports, "__esModule", { value: true }); | ||
| 3 | exports.phaseConfigs = void 0; | ||
| 4 | exports.getPhaseConfig = getPhaseConfig; | ||
| 5 | exports.getNextPhase = getNextPhase; | ||
| 6 | exports.getArtifactFilename = getArtifactFilename; | ||
| 7 | exports.phaseConfigs = { | ||
| 8 | research: { | ||
| 9 | permissionMode: "acceptEdits", | ||
| 10 | tools: ["Read", "Glob", "Grep", "Bash", "Write"], | ||
| 11 | initialMessage: "What areas of the codebase should I research? What are you trying to build?", | ||
| 12 | systemPrompt: `You are in RESEARCH mode. Your ONLY job is to understand the codebase. | ||
| 13 | |||
| 14 | CRITICAL RULES: | ||
| 15 | 1. You MUST write ALL findings to .claude-flow/research.md — this is your PRIMARY output | ||
| 16 | 2. DO NOT just respond in chat. The document viewer shows research.md, so write there. | ||
| 17 | 3. DO NOT suggest moving to planning or implementation | ||
| 18 | 4. DO NOT ask "are you ready to implement?" or similar | ||
| 19 | 5. DO NOT modify any source code files | ||
| 20 | 6. The user controls phase transitions via UI buttons — never prompt them about it | ||
| 21 | |||
| 22 | WORKFLOW: | ||
| 23 | 1. Ask what to research (if unclear) | ||
| 24 | 2. Read files thoroughly using Read, Glob, Grep | ||
| 25 | 3. Write structured findings to .claude-flow/research.md | ||
| 26 | 4. Keep updating research.md as you learn more | ||
| 27 | |||
| 28 | FORMAT for research.md: | ||
| 29 | \`\`\`markdown | ||
| 30 | # Research Findings | ||
| 31 | |||
| 32 | ## Overview | ||
| 33 | [High-level summary of what you found] | ||
| 34 | |||
| 35 | ## Architecture | ||
| 36 | [Key components, patterns, structure] | ||
| 37 | |||
| 38 | ## Relevant Files | ||
| 39 | [List of important files with descriptions] | ||
| 40 | |||
| 41 | ## Key Insights | ||
| 42 | [Important discoveries, patterns, potential issues] | ||
| 43 | |||
| 44 | ## Questions/Unknowns | ||
| 45 | [Things that need clarification] | ||
| 46 | \`\`\` | ||
| 47 | |||
| 48 | When the user adds annotations (// REVIEW:, // NOTE:, TODO:) to research.md and clicks Review, address each annotation and update the document. | ||
| 49 | |||
| 50 | Remember: Your output goes in research.md, not chat. Chat is for clarifying questions only.`, | ||
| 51 | }, | ||
| 52 | plan: { | ||
| 53 | permissionMode: "acceptEdits", | ||
| 54 | tools: ["Read", "Glob", "Grep", "Write"], | ||
| 55 | initialMessage: "I'll create a detailed implementation plan based on my research. Writing to plan.md...", | ||
| 56 | systemPrompt: `You are in PLANNING mode. Your ONLY job is to create an implementation plan. | ||
| 57 | |||
| 58 | CRITICAL RULES: | ||
| 59 | 1. You MUST write the plan to .claude-flow/plan.md — this is your PRIMARY output | ||
| 60 | 2. DO NOT just respond in chat. The document viewer shows plan.md, so write there. | ||
| 61 | 3. DO NOT implement anything — no code changes to source files | ||
| 62 | 4. DO NOT ask "should I start implementing?" or similar | ||
| 63 | 5. The user controls phase transitions via UI buttons — never prompt them about it | ||
| 64 | 6. Base your plan on .claude-flow/research.md | ||
| 65 | |||
| 66 | WORKFLOW: | ||
| 67 | 1. Read .claude-flow/research.md to understand the codebase | ||
| 68 | 2. Write a detailed plan to .claude-flow/plan.md | ||
| 69 | 3. Include specific code snippets showing proposed changes | ||
| 70 | 4. Make the plan detailed enough that implementation is mechanical | ||
| 71 | |||
| 72 | FORMAT for plan.md: | ||
| 73 | \`\`\`markdown | ||
| 74 | # Implementation Plan | ||
| 75 | |||
| 76 | ## Goal | ||
| 77 | [What we're building/changing] | ||
| 78 | |||
| 79 | ## Approach | ||
| 80 | [High-level strategy] | ||
| 81 | |||
| 82 | ## Changes | ||
| 83 | |||
| 84 | ### 1. [First change] | ||
| 85 | **File:** path/to/file.ts | ||
| 86 | **What:** [Description] | ||
| 87 | **Code:** | ||
| 88 | \\\`\\\`\\\`typescript | ||
| 89 | // Proposed code | ||
| 90 | \\\`\\\`\\\` | ||
| 91 | |||
| 92 | ### 2. [Second change] | ||
| 93 | ... | ||
| 94 | |||
| 95 | ## TODO | ||
| 96 | - [ ] Task 1 | ||
| 97 | - [ ] Task 2 | ||
| 98 | - [ ] Task 3 | ||
| 99 | |||
| 100 | ## Risks/Considerations | ||
| 101 | [Potential issues, edge cases] | ||
| 102 | \`\`\` | ||
| 103 | |||
| 104 | When the user adds annotations to plan.md and clicks Review, address each annotation and update the document. | ||
| 105 | |||
| 106 | Remember: Your output goes in plan.md, not chat. Chat is for clarifying questions only.`, | ||
| 107 | }, | ||
| 108 | implement: { | ||
| 109 | permissionMode: "acceptEdits", | ||
| 110 | tools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"], | ||
| 111 | initialMessage: "Starting implementation. I'll follow the plan exactly and mark tasks complete as I go.", | ||
| 112 | systemPrompt: `You are in IMPLEMENTATION mode. Execute the approved plan. | ||
| 113 | |||
| 114 | CRITICAL RULES: | ||
| 115 | 1. Read .claude-flow/plan.md and follow it exactly | ||
| 116 | 2. Mark tasks complete in plan.md as you finish them: - [ ] → - [x] | ||
| 117 | 3. DO NOT deviate from the plan without asking | ||
| 118 | 4. Run tests/typecheck if available | ||
| 119 | 5. Stop and ask if you encounter issues not covered by the plan | ||
| 120 | |||
| 121 | WORKFLOW: | ||
| 122 | 1. Read .claude-flow/plan.md | ||
| 123 | 2. Execute each task in order | ||
| 124 | 3. Update plan.md to mark tasks complete | ||
| 125 | 4. Continue until all tasks are done | ||
| 126 | |||
| 127 | If something in the plan is unclear or problematic, ask before proceeding.`, | ||
| 128 | }, | ||
| 129 | }; | ||
| 130 | function getPhaseConfig(phase, userPermissionMode) { | ||
| 131 | const config = { ...exports.phaseConfigs[phase] }; | ||
| 132 | if (phase === "implement" && userPermissionMode) { | ||
| 133 | config.permissionMode = userPermissionMode; | ||
| 134 | } | ||
| 135 | return config; | ||
| 136 | } | ||
| 137 | function getNextPhase(phase) { | ||
| 138 | const transitions = { | ||
| 139 | research: "plan", | ||
| 140 | plan: "implement", | ||
| 141 | implement: null, | ||
| 142 | }; | ||
| 143 | return transitions[phase]; | ||
| 144 | } | ||
| 145 | function getArtifactFilename(phase) { | ||
| 146 | return phase === "research" ? "research.md" : "plan.md"; | ||
| 147 | } | ||
diff --git a/dist/main/db/index.js b/dist/main/db/index.js new file mode 100644 index 0000000..652d189 --- /dev/null +++ b/dist/main/db/index.js | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | "use strict"; | ||
| 2 | var __importDefault = (this && this.__importDefault) || function (mod) { | ||
| 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| 4 | }; | ||
| 5 | Object.defineProperty(exports, "__esModule", { value: true }); | ||
| 6 | exports.getDb = getDb; | ||
| 7 | exports.closeDb = closeDb; | ||
| 8 | const better_sqlite3_1 = __importDefault(require("better-sqlite3")); | ||
| 9 | const electron_1 = require("electron"); | ||
| 10 | const node_path_1 = __importDefault(require("node:path")); | ||
| 11 | const node_fs_1 = __importDefault(require("node:fs")); | ||
| 12 | const schema_1 = require("./schema"); | ||
| 13 | let db = null; | ||
| 14 | function getDb() { | ||
| 15 | if (db) | ||
| 16 | return db; | ||
| 17 | const dbDir = electron_1.app.getPath("userData"); | ||
| 18 | if (!node_fs_1.default.existsSync(dbDir)) { | ||
| 19 | node_fs_1.default.mkdirSync(dbDir, { recursive: true }); | ||
| 20 | } | ||
| 21 | const dbPath = node_path_1.default.join(dbDir, "claude-flow.db"); | ||
| 22 | db = new better_sqlite3_1.default(dbPath); | ||
| 23 | db.pragma("journal_mode = WAL"); | ||
| 24 | db.pragma("foreign_keys = ON"); | ||
| 25 | (0, schema_1.initSchema)(db); | ||
| 26 | return db; | ||
| 27 | } | ||
| 28 | function closeDb() { | ||
| 29 | if (db) { | ||
| 30 | db.close(); | ||
| 31 | db = null; | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/dist/main/db/projects.js b/dist/main/db/projects.js new file mode 100644 index 0000000..af36cc0 --- /dev/null +++ b/dist/main/db/projects.js | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | "use strict"; | ||
| 2 | Object.defineProperty(exports, "__esModule", { value: true }); | ||
| 3 | exports.listProjects = listProjects; | ||
| 4 | exports.getProject = getProject; | ||
| 5 | exports.createProject = createProject; | ||
| 6 | exports.deleteProject = deleteProject; | ||
| 7 | const index_1 = require("./index"); | ||
| 8 | const uuid_1 = require("uuid"); | ||
| 9 | function listProjects() { | ||
| 10 | return (0, index_1.getDb)() | ||
| 11 | .prepare("SELECT * FROM projects ORDER BY updated_at DESC") | ||
| 12 | .all(); | ||
| 13 | } | ||
| 14 | function getProject(id) { | ||
| 15 | return (0, index_1.getDb)() | ||
| 16 | .prepare("SELECT * FROM projects WHERE id = ?") | ||
| 17 | .get(id); | ||
| 18 | } | ||
| 19 | function createProject(name, projectPath) { | ||
| 20 | const db = (0, index_1.getDb)(); | ||
| 21 | const id = (0, uuid_1.v4)(); | ||
| 22 | const now = Math.floor(Date.now() / 1000); | ||
| 23 | db.prepare("INSERT INTO projects (id, name, path, created_at, updated_at) VALUES (?, ?, ?, ?, ?)").run(id, name, projectPath, now, now); | ||
| 24 | return { id, name, path: projectPath, created_at: now, updated_at: now }; | ||
| 25 | } | ||
| 26 | function deleteProject(id) { | ||
| 27 | (0, index_1.getDb)().prepare("DELETE FROM projects WHERE id = ?").run(id); | ||
| 28 | } | ||
diff --git a/dist/main/db/schema.js b/dist/main/db/schema.js new file mode 100644 index 0000000..974e593 --- /dev/null +++ b/dist/main/db/schema.js | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | "use strict"; | ||
| 2 | Object.defineProperty(exports, "__esModule", { value: true }); | ||
| 3 | exports.initSchema = initSchema; | ||
| 4 | function initSchema(db) { | ||
| 5 | db.exec(` | ||
| 6 | CREATE TABLE IF NOT EXISTS projects ( | ||
| 7 | id TEXT PRIMARY KEY, | ||
| 8 | name TEXT NOT NULL, | ||
| 9 | path TEXT NOT NULL, | ||
| 10 | created_at INTEGER NOT NULL DEFAULT (unixepoch()), | ||
| 11 | updated_at INTEGER NOT NULL DEFAULT (unixepoch()) | ||
| 12 | ); | ||
| 13 | |||
| 14 | CREATE TABLE IF NOT EXISTS sessions ( | ||
| 15 | id TEXT PRIMARY KEY, | ||
| 16 | project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE, | ||
| 17 | name TEXT NOT NULL, | ||
| 18 | phase TEXT NOT NULL DEFAULT 'research', | ||
| 19 | claude_session_id TEXT, | ||
| 20 | permission_mode TEXT NOT NULL DEFAULT 'acceptEdits', | ||
| 21 | created_at INTEGER NOT NULL DEFAULT (unixepoch()), | ||
| 22 | updated_at INTEGER NOT NULL DEFAULT (unixepoch()) | ||
| 23 | ); | ||
| 24 | |||
| 25 | CREATE TABLE IF NOT EXISTS messages ( | ||
| 26 | id TEXT PRIMARY KEY, | ||
| 27 | session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE, | ||
| 28 | role TEXT NOT NULL, | ||
| 29 | content TEXT NOT NULL, | ||
| 30 | created_at INTEGER NOT NULL DEFAULT (unixepoch()) | ||
| 31 | ); | ||
| 32 | |||
| 33 | CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_id); | ||
| 34 | CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id); | ||
| 35 | `); | ||
| 36 | } | ||
diff --git a/dist/main/db/sessions.js b/dist/main/db/sessions.js new file mode 100644 index 0000000..b554898 --- /dev/null +++ b/dist/main/db/sessions.js | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | "use strict"; | ||
| 2 | Object.defineProperty(exports, "__esModule", { value: true }); | ||
| 3 | exports.listSessions = listSessions; | ||
| 4 | exports.getSession = getSession; | ||
| 5 | exports.createSession = createSession; | ||
| 6 | exports.updateSession = updateSession; | ||
| 7 | exports.deleteSession = deleteSession; | ||
| 8 | exports.listMessages = listMessages; | ||
| 9 | exports.addMessage = addMessage; | ||
| 10 | const index_1 = require("./index"); | ||
| 11 | const uuid_1 = require("uuid"); | ||
| 12 | function listSessions(projectId) { | ||
| 13 | return (0, index_1.getDb)() | ||
| 14 | .prepare("SELECT * FROM sessions WHERE project_id = ? ORDER BY updated_at DESC") | ||
| 15 | .all(projectId); | ||
| 16 | } | ||
| 17 | function getSession(id) { | ||
| 18 | return (0, index_1.getDb)() | ||
| 19 | .prepare("SELECT * FROM sessions WHERE id = ?") | ||
| 20 | .get(id); | ||
| 21 | } | ||
| 22 | function createSession(projectId, name) { | ||
| 23 | const db = (0, index_1.getDb)(); | ||
| 24 | const id = (0, uuid_1.v4)(); | ||
| 25 | const now = Math.floor(Date.now() / 1000); | ||
| 26 | db.prepare(`INSERT INTO sessions (id, project_id, name, phase, permission_mode, created_at, updated_at) | ||
| 27 | VALUES (?, ?, ?, 'research', 'acceptEdits', ?, ?)`).run(id, projectId, name, now, now); | ||
| 28 | return { | ||
| 29 | id, | ||
| 30 | project_id: projectId, | ||
| 31 | name, | ||
| 32 | phase: "research", | ||
| 33 | claude_session_id: null, | ||
| 34 | permission_mode: "acceptEdits", | ||
| 35 | created_at: now, | ||
| 36 | updated_at: now, | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | function updateSession(id, updates) { | ||
| 40 | const db = (0, index_1.getDb)(); | ||
| 41 | const sets = []; | ||
| 42 | const values = []; | ||
| 43 | for (const [key, value] of Object.entries(updates)) { | ||
| 44 | if (value !== undefined) { | ||
| 45 | sets.push(`${key} = ?`); | ||
| 46 | values.push(value); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | if (sets.length > 0) { | ||
| 50 | sets.push("updated_at = ?"); | ||
| 51 | values.push(Math.floor(Date.now() / 1000)); | ||
| 52 | values.push(id); | ||
| 53 | db.prepare(`UPDATE sessions SET ${sets.join(", ")} WHERE id = ?`).run(...values); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | function deleteSession(id) { | ||
| 57 | (0, index_1.getDb)().prepare("DELETE FROM sessions WHERE id = ?").run(id); | ||
| 58 | } | ||
| 59 | // Messages | ||
| 60 | function listMessages(sessionId) { | ||
| 61 | return (0, index_1.getDb)() | ||
| 62 | .prepare("SELECT * FROM messages WHERE session_id = ? ORDER BY created_at ASC") | ||
| 63 | .all(sessionId); | ||
| 64 | } | ||
| 65 | function addMessage(sessionId, role, content) { | ||
| 66 | const db = (0, index_1.getDb)(); | ||
| 67 | const id = (0, uuid_1.v4)(); | ||
| 68 | const now = Math.floor(Date.now() / 1000); | ||
| 69 | db.prepare("INSERT INTO messages (id, session_id, role, content, created_at) VALUES (?, ?, ?, ?, ?)").run(id, sessionId, role, content, now); | ||
| 70 | db.prepare("UPDATE sessions SET updated_at = ? WHERE id = ?").run(now, sessionId); | ||
| 71 | return { id, session_id: sessionId, role, content, created_at: now }; | ||
| 72 | } | ||
diff --git a/dist/main/index.js b/dist/main/index.js new file mode 100644 index 0000000..a10a371 --- /dev/null +++ b/dist/main/index.js | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | "use strict"; | ||
| 2 | var __importDefault = (this && this.__importDefault) || function (mod) { | ||
| 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| 4 | }; | ||
| 5 | Object.defineProperty(exports, "__esModule", { value: true }); | ||
| 6 | const electron_1 = require("electron"); | ||
| 7 | const node_path_1 = __importDefault(require("node:path")); | ||
| 8 | const db_1 = require("./db"); | ||
| 9 | const handlers_1 = require("./ipc/handlers"); | ||
| 10 | const isDev = !electron_1.app.isPackaged; | ||
| 11 | let mainWindow = null; | ||
| 12 | function createWindow() { | ||
| 13 | mainWindow = new electron_1.BrowserWindow({ | ||
| 14 | width: 1400, | ||
| 15 | height: 900, | ||
| 16 | minWidth: 1000, | ||
| 17 | minHeight: 600, | ||
| 18 | show: false, | ||
| 19 | titleBarStyle: "hiddenInset", | ||
| 20 | webPreferences: { | ||
| 21 | contextIsolation: true, | ||
| 22 | nodeIntegration: false, | ||
| 23 | preload: node_path_1.default.join(__dirname, "preload.js"), | ||
| 24 | }, | ||
| 25 | }); | ||
| 26 | (0, handlers_1.registerIpcHandlers)(mainWindow); | ||
| 27 | if (isDev) { | ||
| 28 | const url = process.env.VITE_DEV_SERVER_URL ?? "http://localhost:5173"; | ||
| 29 | mainWindow.loadURL(url).finally(() => { | ||
| 30 | mainWindow.show(); | ||
| 31 | mainWindow.webContents.openDevTools({ mode: "detach" }); | ||
| 32 | }); | ||
| 33 | } | ||
| 34 | else { | ||
| 35 | const indexHtml = node_path_1.default.join(electron_1.app.getAppPath(), "renderer", "dist", "index.html"); | ||
| 36 | mainWindow.loadFile(indexHtml).finally(() => mainWindow.show()); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | electron_1.app.whenReady().then(() => { | ||
| 40 | // Initialize database | ||
| 41 | (0, db_1.getDb)(); | ||
| 42 | createWindow(); | ||
| 43 | electron_1.app.on("activate", () => { | ||
| 44 | if (electron_1.BrowserWindow.getAllWindows().length === 0) { | ||
| 45 | createWindow(); | ||
| 46 | } | ||
| 47 | }); | ||
| 48 | }); | ||
| 49 | electron_1.app.on("window-all-closed", () => { | ||
| 50 | (0, db_1.closeDb)(); | ||
| 51 | if (process.platform !== "darwin") { | ||
| 52 | electron_1.app.quit(); | ||
| 53 | } | ||
| 54 | }); | ||
diff --git a/dist/main/index.sync-conflict-20260228-074140-M6474AW.js b/dist/main/index.sync-conflict-20260228-074140-M6474AW.js new file mode 100644 index 0000000..a10a371 --- /dev/null +++ b/dist/main/index.sync-conflict-20260228-074140-M6474AW.js | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | "use strict"; | ||
| 2 | var __importDefault = (this && this.__importDefault) || function (mod) { | ||
| 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
| 4 | }; | ||
| 5 | Object.defineProperty(exports, "__esModule", { value: true }); | ||
| 6 | const electron_1 = require("electron"); | ||
| 7 | const node_path_1 = __importDefault(require("node:path")); | ||
| 8 | const db_1 = require("./db"); | ||
| 9 | const handlers_1 = require("./ipc/handlers"); | ||
| 10 | const isDev = !electron_1.app.isPackaged; | ||
| 11 | let mainWindow = null; | ||
| 12 | function createWindow() { | ||
| 13 | mainWindow = new electron_1.BrowserWindow({ | ||
| 14 | width: 1400, | ||
| 15 | height: 900, | ||
| 16 | minWidth: 1000, | ||
| 17 | minHeight: 600, | ||
| 18 | show: false, | ||
| 19 | titleBarStyle: "hiddenInset", | ||
| 20 | webPreferences: { | ||
| 21 | contextIsolation: true, | ||
| 22 | nodeIntegration: false, | ||
| 23 | preload: node_path_1.default.join(__dirname, "preload.js"), | ||
| 24 | }, | ||
| 25 | }); | ||
| 26 | (0, handlers_1.registerIpcHandlers)(mainWindow); | ||
| 27 | if (isDev) { | ||
| 28 | const url = process.env.VITE_DEV_SERVER_URL ?? "http://localhost:5173"; | ||
| 29 | mainWindow.loadURL(url).finally(() => { | ||
| 30 | mainWindow.show(); | ||
| 31 | mainWindow.webContents.openDevTools({ mode: "detach" }); | ||
| 32 | }); | ||
| 33 | } | ||
| 34 | else { | ||
| 35 | const indexHtml = node_path_1.default.join(electron_1.app.getAppPath(), "renderer", "dist", "index.html"); | ||
| 36 | mainWindow.loadFile(indexHtml).finally(() => mainWindow.show()); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | electron_1.app.whenReady().then(() => { | ||
| 40 | // Initialize database | ||
| 41 | (0, db_1.getDb)(); | ||
| 42 | createWindow(); | ||
| 43 | electron_1.app.on("activate", () => { | ||
| 44 | if (electron_1.BrowserWindow.getAllWindows().length === 0) { | ||
| 45 | createWindow(); | ||
| 46 | } | ||
| 47 | }); | ||
| 48 | }); | ||
| 49 | electron_1.app.on("window-all-closed", () => { | ||
| 50 | (0, db_1.closeDb)(); | ||
| 51 | if (process.platform !== "darwin") { | ||
| 52 | electron_1.app.quit(); | ||
| 53 | } | ||
| 54 | }); | ||
diff --git a/dist/main/ipc/handlers.js b/dist/main/ipc/handlers.js new file mode 100644 index 0000000..c452cee --- /dev/null +++ b/dist/main/ipc/handlers.js | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | "use strict"; | ||
| 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
| 3 | if (k2 === undefined) k2 = k; | ||
| 4 | var desc = Object.getOwnPropertyDescriptor(m, k); | ||
| 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
| 6 | desc = { enumerable: true, get: function() { return m[k]; } }; | ||
| 7 | } | ||
| 8 | Object.defineProperty(o, k2, desc); | ||
| 9 | }) : (function(o, m, k, k2) { | ||
| 10 | if (k2 === undefined) k2 = k; | ||
| 11 | o[k2] = m[k]; | ||
| 12 | })); | ||
| 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
| 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
| 15 | }) : function(o, v) { | ||
| 16 | o["default"] = v; | ||
| 17 | }); | ||
| 18 | var __importStar = (this && this.__importStar) || (function () { | ||
| 19 | var ownKeys = function(o) { | ||
| 20 | ownKeys = Object.getOwnPropertyNames || function (o) { | ||
| 21 | var ar = []; | ||
| 22 | for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; | ||
| 23 | return ar; | ||
| 24 | }; | ||
| 25 | return ownKeys(o); | ||
| 26 | }; | ||
| 27 | return function (mod) { | ||
| 28 | if (mod && mod.__esModule) return mod; | ||
| 29 | var result = {}; | ||
| 30 | if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); | ||
| 31 | __setModuleDefault(result, mod); | ||
| 32 | return result; | ||
| 33 | }; | ||
| 34 | })(); | ||
| 35 | Object.defineProperty(exports, "__esModule", { value: true }); | ||
| 36 | exports.registerIpcHandlers = registerIpcHandlers; | ||
| 37 | const electron_1 = require("electron"); | ||
| 38 | const projects = __importStar(require("../db/projects")); | ||
| 39 | const sessions = __importStar(require("../db/sessions")); | ||
| 40 | const claude = __importStar(require("../claude")); | ||
| 41 | function registerIpcHandlers(mainWindow) { | ||
| 42 | // Projects | ||
| 43 | electron_1.ipcMain.handle("projects:list", () => projects.listProjects()); | ||
| 44 | electron_1.ipcMain.handle("projects:create", (_, name, path) => projects.createProject(name, path)); | ||
| 45 | electron_1.ipcMain.handle("projects:delete", (_, id) => projects.deleteProject(id)); | ||
| 46 | // Sessions | ||
| 47 | electron_1.ipcMain.handle("sessions:list", (_, projectId) => sessions.listSessions(projectId)); | ||
| 48 | electron_1.ipcMain.handle("sessions:create", (_, projectId, name) => sessions.createSession(projectId, name)); | ||
| 49 | electron_1.ipcMain.handle("sessions:delete", (_, id) => sessions.deleteSession(id)); | ||
| 50 | electron_1.ipcMain.handle("sessions:get", (_, id) => sessions.getSession(id)); | ||
| 51 | // Messages | ||
| 52 | electron_1.ipcMain.handle("messages:list", (_, sessionId) => sessions.listMessages(sessionId)); | ||
| 53 | // Chat | ||
| 54 | electron_1.ipcMain.handle("chat:send", async (_, sessionId, message) => { | ||
| 55 | const session = sessions.getSession(sessionId); | ||
| 56 | if (!session) | ||
| 57 | throw new Error("Session not found"); | ||
| 58 | // Store user message | ||
| 59 | sessions.addMessage(sessionId, "user", message); | ||
| 60 | await claude.sendMessage({ | ||
| 61 | session, | ||
| 62 | message, | ||
| 63 | onMessage: (msg) => { | ||
| 64 | // Forward all messages to renderer | ||
| 65 | mainWindow.webContents.send("claude:message", sessionId, msg); | ||
| 66 | // Store assistant text messages | ||
| 67 | if (msg.type === "assistant") { | ||
| 68 | const content = msg.message.content | ||
| 69 | .filter((c) => c.type === "text" && c.text) | ||
| 70 | .map((c) => c.text) | ||
| 71 | .join("\n"); | ||
| 72 | if (content) { | ||
| 73 | sessions.addMessage(sessionId, "assistant", content); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | }, | ||
| 77 | }); | ||
| 78 | }); | ||
| 79 | electron_1.ipcMain.handle("chat:interrupt", (_, sessionId) => { | ||
| 80 | claude.interruptSession(sessionId); | ||
| 81 | }); | ||
| 82 | // Workflow | ||
| 83 | electron_1.ipcMain.handle("workflow:review", async (_, sessionId) => { | ||
| 84 | const session = sessions.getSession(sessionId); | ||
| 85 | if (!session) | ||
| 86 | throw new Error("Session not found"); | ||
| 87 | await claude.triggerReview(session, (msg) => { | ||
| 88 | mainWindow.webContents.send("claude:message", sessionId, msg); | ||
| 89 | }); | ||
| 90 | }); | ||
| 91 | electron_1.ipcMain.handle("workflow:advance", (_, sessionId) => { | ||
| 92 | const session = sessions.getSession(sessionId); | ||
| 93 | if (!session) | ||
| 94 | throw new Error("Session not found"); | ||
| 95 | return claude.advancePhase(session); | ||
| 96 | }); | ||
| 97 | electron_1.ipcMain.handle("workflow:setPermissionMode", (_, sessionId, mode) => { | ||
| 98 | sessions.updateSession(sessionId, { | ||
| 99 | permission_mode: mode, | ||
| 100 | }); | ||
| 101 | }); | ||
| 102 | // Artifacts | ||
| 103 | electron_1.ipcMain.handle("artifact:read", (_, projectPath, filename) => { | ||
| 104 | return claude.readArtifact(projectPath, filename); | ||
| 105 | }); | ||
| 106 | electron_1.ipcMain.handle("artifact:write", (_, projectPath, filename, content) => { | ||
| 107 | claude.writeArtifact(projectPath, filename, content); | ||
| 108 | }); | ||
| 109 | // Dialogs | ||
| 110 | electron_1.ipcMain.handle("dialog:selectDirectory", async () => { | ||
| 111 | const result = await electron_1.dialog.showOpenDialog(mainWindow, { | ||
| 112 | properties: ["openDirectory"], | ||
| 113 | }); | ||
| 114 | return result.canceled ? null : result.filePaths[0]; | ||
| 115 | }); | ||
| 116 | } | ||
diff --git a/dist/main/preload.js b/dist/main/preload.js new file mode 100644 index 0000000..7978b53 --- /dev/null +++ b/dist/main/preload.js | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | "use strict"; | ||
| 2 | Object.defineProperty(exports, "__esModule", { value: true }); | ||
| 3 | const electron_1 = require("electron"); | ||
| 4 | const api = { | ||
| 5 | // Projects | ||
| 6 | listProjects: () => electron_1.ipcRenderer.invoke("projects:list"), | ||
| 7 | createProject: (name, path) => electron_1.ipcRenderer.invoke("projects:create", name, path), | ||
| 8 | deleteProject: (id) => electron_1.ipcRenderer.invoke("projects:delete", id), | ||
| 9 | // Sessions | ||
| 10 | listSessions: (projectId) => electron_1.ipcRenderer.invoke("sessions:list", projectId), | ||
| 11 | createSession: (projectId, name) => electron_1.ipcRenderer.invoke("sessions:create", projectId, name), | ||
| 12 | deleteSession: (id) => electron_1.ipcRenderer.invoke("sessions:delete", id), | ||
| 13 | getSession: (id) => electron_1.ipcRenderer.invoke("sessions:get", id), | ||
| 14 | // Messages | ||
| 15 | listMessages: (sessionId) => electron_1.ipcRenderer.invoke("messages:list", sessionId), | ||
| 16 | // Chat | ||
| 17 | sendMessage: (sessionId, message) => electron_1.ipcRenderer.invoke("chat:send", sessionId, message), | ||
| 18 | interruptSession: (sessionId) => electron_1.ipcRenderer.invoke("chat:interrupt", sessionId), | ||
| 19 | // Workflow | ||
| 20 | triggerReview: (sessionId) => electron_1.ipcRenderer.invoke("workflow:review", sessionId), | ||
| 21 | advancePhase: (sessionId) => electron_1.ipcRenderer.invoke("workflow:advance", sessionId), | ||
| 22 | setPermissionMode: (sessionId, mode) => electron_1.ipcRenderer.invoke("workflow:setPermissionMode", sessionId, mode), | ||
| 23 | // Artifacts | ||
| 24 | readArtifact: (projectPath, filename) => electron_1.ipcRenderer.invoke("artifact:read", projectPath, filename), | ||
| 25 | writeArtifact: (projectPath, filename, content) => electron_1.ipcRenderer.invoke("artifact:write", projectPath, filename, content), | ||
| 26 | // Events | ||
| 27 | onClaudeMessage: (callback) => { | ||
| 28 | const handler = (_, sessionId, message) => callback(sessionId, message); | ||
| 29 | electron_1.ipcRenderer.on("claude:message", handler); | ||
| 30 | return () => electron_1.ipcRenderer.removeListener("claude:message", handler); | ||
| 31 | }, | ||
| 32 | // Dialogs | ||
| 33 | selectDirectory: async () => { | ||
| 34 | const result = await electron_1.ipcRenderer.invoke("dialog:selectDirectory"); | ||
| 35 | return result; | ||
| 36 | }, | ||
| 37 | }; | ||
| 38 | electron_1.contextBridge.exposeInMainWorld("api", api); | ||
diff --git a/dist/main/preload.sync-conflict-20260228-074140-M6474AW.js b/dist/main/preload.sync-conflict-20260228-074140-M6474AW.js new file mode 100644 index 0000000..7978b53 --- /dev/null +++ b/dist/main/preload.sync-conflict-20260228-074140-M6474AW.js | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | "use strict"; | ||
| 2 | Object.defineProperty(exports, "__esModule", { value: true }); | ||
| 3 | const electron_1 = require("electron"); | ||
| 4 | const api = { | ||
| 5 | // Projects | ||
| 6 | listProjects: () => electron_1.ipcRenderer.invoke("projects:list"), | ||
| 7 | createProject: (name, path) => electron_1.ipcRenderer.invoke("projects:create", name, path), | ||
| 8 | deleteProject: (id) => electron_1.ipcRenderer.invoke("projects:delete", id), | ||
| 9 | // Sessions | ||
| 10 | listSessions: (projectId) => electron_1.ipcRenderer.invoke("sessions:list", projectId), | ||
| 11 | createSession: (projectId, name) => electron_1.ipcRenderer.invoke("sessions:create", projectId, name), | ||
| 12 | deleteSession: (id) => electron_1.ipcRenderer.invoke("sessions:delete", id), | ||
| 13 | getSession: (id) => electron_1.ipcRenderer.invoke("sessions:get", id), | ||
| 14 | // Messages | ||
| 15 | listMessages: (sessionId) => electron_1.ipcRenderer.invoke("messages:list", sessionId), | ||
| 16 | // Chat | ||
| 17 | sendMessage: (sessionId, message) => electron_1.ipcRenderer.invoke("chat:send", sessionId, message), | ||
| 18 | interruptSession: (sessionId) => electron_1.ipcRenderer.invoke("chat:interrupt", sessionId), | ||
| 19 | // Workflow | ||
| 20 | triggerReview: (sessionId) => electron_1.ipcRenderer.invoke("workflow:review", sessionId), | ||
| 21 | advancePhase: (sessionId) => electron_1.ipcRenderer.invoke("workflow:advance", sessionId), | ||
| 22 | setPermissionMode: (sessionId, mode) => electron_1.ipcRenderer.invoke("workflow:setPermissionMode", sessionId, mode), | ||
| 23 | // Artifacts | ||
| 24 | readArtifact: (projectPath, filename) => electron_1.ipcRenderer.invoke("artifact:read", projectPath, filename), | ||
| 25 | writeArtifact: (projectPath, filename, content) => electron_1.ipcRenderer.invoke("artifact:write", projectPath, filename, content), | ||
| 26 | // Events | ||
| 27 | onClaudeMessage: (callback) => { | ||
| 28 | const handler = (_, sessionId, message) => callback(sessionId, message); | ||
| 29 | electron_1.ipcRenderer.on("claude:message", handler); | ||
| 30 | return () => electron_1.ipcRenderer.removeListener("claude:message", handler); | ||
| 31 | }, | ||
| 32 | // Dialogs | ||
| 33 | selectDirectory: async () => { | ||
| 34 | const result = await electron_1.ipcRenderer.invoke("dialog:selectDirectory"); | ||
| 35 | return result; | ||
| 36 | }, | ||
| 37 | }; | ||
| 38 | electron_1.contextBridge.exposeInMainWorld("api", api); | ||
