1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.sendMessage = sendMessage;
exports.interruptSession = interruptSession;
exports.triggerReview = triggerReview;
exports.advancePhase = advancePhase;
exports.readArtifact = readArtifact;
exports.writeArtifact = writeArtifact;
exports.getPhaseInitialMessage = getPhaseInitialMessage;
const claude_agent_sdk_1 = require("@anthropic-ai/claude-agent-sdk");
const phases_1 = require("./phases");
const projects_1 = require("../db/projects");
const sessions_1 = require("../db/sessions");
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
// Track active queries by session ID
const activeQueries = new Map();
function ensureArtifactDir(projectPath) {
const dir = node_path_1.default.join(projectPath, ".claude-flow");
if (!node_fs_1.default.existsSync(dir)) {
node_fs_1.default.mkdirSync(dir, { recursive: true });
}
}
async function sendMessage({ session, message, onMessage, }) {
const project = (0, projects_1.getProject)(session.project_id);
if (!project)
throw new Error("Project not found");
ensureArtifactDir(project.path);
const phaseConfig = (0, phases_1.getPhaseConfig)(session.phase, session.permission_mode);
const q = (0, claude_agent_sdk_1.query)({
prompt: message,
options: {
cwd: project.path,
resume: session.claude_session_id ?? undefined,
tools: phaseConfig.tools,
permissionMode: phaseConfig.permissionMode,
systemPrompt: phaseConfig.systemPrompt,
},
});
activeQueries.set(session.id, q);
try {
for await (const msg of q) {
// Capture session ID from init message
if (msg.type === "system" && msg.subtype === "init") {
if (!session.claude_session_id) {
(0, sessions_1.updateSession)(session.id, { claude_session_id: msg.session_id });
}
}
onMessage(msg);
}
}
finally {
activeQueries.delete(session.id);
}
}
function interruptSession(sessionId) {
const q = activeQueries.get(sessionId);
if (q) {
q.close();
activeQueries.delete(sessionId);
}
}
/**
* Trigger a review: Claude reads the document and addresses user annotations
*/
async function triggerReview(session, onMessage) {
const docName = (0, phases_1.getArtifactFilename)(session.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.`;
await sendMessage({ session, message, onMessage });
}
/**
* Advance to the next phase
*/
function advancePhase(session) {
const nextPhase = (0, phases_1.getNextPhase)(session.phase);
if (nextPhase) {
(0, sessions_1.updateSession)(session.id, { phase: nextPhase });
}
return nextPhase;
}
/**
* Read an artifact file from the project's .claude-flow directory
*/
function readArtifact(projectPath, filename) {
const filePath = node_path_1.default.join(projectPath, ".claude-flow", filename);
if (node_fs_1.default.existsSync(filePath)) {
return node_fs_1.default.readFileSync(filePath, "utf-8");
}
return null;
}
/**
* Write an artifact file to the project's .claude-flow directory
*/
function writeArtifact(projectPath, filename, content) {
const dir = node_path_1.default.join(projectPath, ".claude-flow");
if (!node_fs_1.default.existsSync(dir)) {
node_fs_1.default.mkdirSync(dir, { recursive: true });
}
node_fs_1.default.writeFileSync(node_path_1.default.join(dir, filename), content, "utf-8");
}
/**
* Get the initial message for a phase
*/
function getPhaseInitialMessage(phase) {
return (0, phases_1.getPhaseConfig)(phase).initialMessage;
}
|