sam-coder-cli 1.0.69 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/agi-cli.js +287 -95
- package/bin/agi-cli.js.bak +352 -0
- package/bin/core/brainstorm.js +198 -0
- package/bin/core/edit_finished_brainstorm.js +294 -0
- package/bin/core/finish_brainstorm.js +217 -0
- package/bin/core/index.js +37 -0
- package/bin/core/models.js +290 -0
- package/bin/core/templates.js +567 -0
- package/package.json +14 -4
- package/ANIMATION_ENHANCEMENTS.md +0 -86
- package/media/ai-icon.png +0 -0
- package/media/ai-icon.svg +0 -5
- package/media/infinity-icon.svg +0 -4
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fetch = require('node-fetch');
|
|
4
|
+
const readline = require('readline');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { CLIAgentUtils } = require('../src/cliAgentUtils');
|
|
7
|
+
|
|
8
|
+
// Initialize CLIAgentUtils
|
|
9
|
+
const agentUtils = new CLIAgentUtils();
|
|
10
|
+
|
|
11
|
+
// OpenRouter configuration
|
|
12
|
+
const OPENROUTER_API_KEY = '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';
|
|
13
|
+
const MODEL = 'deepseek/deepseek-chat-v3-0324:free';
|
|
14
|
+
const API_BASE_URL = 'https://openrouter.ai/api/v1';
|
|
15
|
+
|
|
16
|
+
// System prompt matching the VS Code extension
|
|
17
|
+
const SYSTEM_PROMPT = `You are a VS Code AI Assistant with agency capabilities. You can perform actions on the user's workspace.
|
|
18
|
+
|
|
19
|
+
ENVIRONMENT CONTEXT:
|
|
20
|
+
- OS: ${process.platform}
|
|
21
|
+
- Working Directory: ${process.cwd()}
|
|
22
|
+
|
|
23
|
+
When you need to perform actions, respond with JSON in the following format:
|
|
24
|
+
\`\`\`json
|
|
25
|
+
{
|
|
26
|
+
"thoughts": "Your reasoning about what needs to be done",
|
|
27
|
+
"actions": [
|
|
28
|
+
{
|
|
29
|
+
"type": "read|write|search|command|analyze|execute|stop",
|
|
30
|
+
"data": { ... action specific data ... }
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
\`\`\`
|
|
35
|
+
|
|
36
|
+
Action types and their data:
|
|
37
|
+
- read: { "path": "relative/or/absolute/path" }
|
|
38
|
+
- write: { "path": "relative/or/absolute/path", "content": "file content" }
|
|
39
|
+
- search: { "type": "files", "pattern": "glob pattern" } or { "type": "text", "text": "search text" }
|
|
40
|
+
- command: { "command": "command string to execute in terminal" }
|
|
41
|
+
- execute: { "language": "js|python|bash|...", "code": "code to execute" }
|
|
42
|
+
- analyze: { "code": "code to analyze", "question": "what you want to analyze" }
|
|
43
|
+
- browse: { "query": "search query", "numResults": 5 } (free web search using DuckDuckGo, optional numResults)
|
|
44
|
+
- edit: {
|
|
45
|
+
"path": "relative/or/absolute/path",
|
|
46
|
+
"edits": {
|
|
47
|
+
"operations": [
|
|
48
|
+
{ "type": "replace", "startLine": 10, "endLine": 15, "newText": "new code here" },
|
|
49
|
+
{ "type": "replace", "pattern": "oldFunction\\(\\)", "replacement": "newFunction()", "flags": "g" },
|
|
50
|
+
{ "type": "insert", "line": 20, "text": "new line of code here" },
|
|
51
|
+
{ "type": "insert", "position": "start", "text": "// Header comment" },
|
|
52
|
+
{ "type": "insert", "position": "end", "text": "// Footer comment" },
|
|
53
|
+
{ "type": "delete", "startLine": 25, "endLine": 30 }
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
} (edit specific parts of an existing file)
|
|
57
|
+
- stop: {} (use this to indicate you're done with the task and no more actions are needed)
|
|
58
|
+
|
|
59
|
+
By default, you will continue to take actions in a loop until you decide to stop with the 'stop' action type.
|
|
60
|
+
Always wrap your JSON in markdown code blocks with the json language specifier.
|
|
61
|
+
When executing code or commands that might be potentially harmful, explain what the code does before executing it.`;
|
|
62
|
+
|
|
63
|
+
async function callOpenRouter(messages, toolCalls = null) {
|
|
64
|
+
const body = {
|
|
65
|
+
model: MODEL,
|
|
66
|
+
messages: messages.map(msg => ({
|
|
67
|
+
role: msg.role,
|
|
68
|
+
content: msg.content || '',
|
|
69
|
+
...(msg.name && { name: msg.name })
|
|
70
|
+
})),
|
|
71
|
+
...(!toolCalls && { tools }),
|
|
72
|
+
...(toolCalls && { tool_calls: toolCalls })
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Remove any undefined values
|
|
76
|
+
const cleanBody = JSON.parse(JSON.stringify(body));
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
console.log('Sending request to OpenRouter API...');
|
|
80
|
+
console.log('Model:', MODEL);
|
|
81
|
+
console.log('Headers:', {
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
'Authorization': 'Bearer ' + (OPENROUTER_API_KEY ? '***' + OPENROUTER_API_KEY.slice(-4) : 'undefined'),
|
|
84
|
+
'HTTP-Referer': 'https://github.com/yourusername/agi-cli',
|
|
85
|
+
'X-Title': 'AGI-CLI'
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
console.log('Request body:', JSON.stringify(cleanBody, null, 2));
|
|
89
|
+
|
|
90
|
+
const response = await fetch(`${API_BASE_URL}/chat/completions`, {
|
|
91
|
+
method: 'POST',
|
|
92
|
+
headers: {
|
|
93
|
+
'Content-Type': 'application/json',
|
|
94
|
+
'Authorization': `Bearer ${OPENROUTER_API_KEY}`,
|
|
95
|
+
'HTTP-Referer': 'https://github.com/yourusername/agi-cli',
|
|
96
|
+
'X-Title': 'AGI-CLI'
|
|
97
|
+
},
|
|
98
|
+
body: JSON.stringify(cleanBody)
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
console.log('Response status:', response.status, response.statusText);
|
|
102
|
+
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
let errorText;
|
|
105
|
+
try {
|
|
106
|
+
const errorData = await response.text();
|
|
107
|
+
console.error('Error response body:', errorData);
|
|
108
|
+
const parsedError = JSON.parse(errorData);
|
|
109
|
+
errorText = parsedError.error?.message || JSON.stringify(parsedError);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
errorText = await response.text();
|
|
112
|
+
}
|
|
113
|
+
throw new Error(`API request failed with status ${response.status}: ${errorText}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const responseData = await response.json();
|
|
117
|
+
console.log('API Response:', JSON.stringify(responseData, null, 2));
|
|
118
|
+
return responseData;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error('Error in callOpenRouter:', error);
|
|
121
|
+
throw new Error(`Failed to call OpenRouter API: ${error.message}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function handleToolCalls(toolCalls, messages) {
|
|
126
|
+
const results = [];
|
|
127
|
+
|
|
128
|
+
for (const toolCall of toolCalls) {
|
|
129
|
+
const { name, arguments: args } = toolCall.function;
|
|
130
|
+
let result;
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
switch (name) {
|
|
134
|
+
case 'readFile':
|
|
135
|
+
result = await agentUtils.readFile(args.path);
|
|
136
|
+
break;
|
|
137
|
+
case 'writeFile':
|
|
138
|
+
await agentUtils.writeFile(args.path, args.content);
|
|
139
|
+
|
|
140
|
+
results.push({
|
|
141
|
+
tool_call_id: toolCall.id,
|
|
142
|
+
role: 'tool',
|
|
143
|
+
name: functionName,
|
|
144
|
+
content: JSON.stringify(result)
|
|
145
|
+
});
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error('❌ Tool execution failed:', error);
|
|
148
|
+
|
|
149
|
+
results.push({
|
|
150
|
+
tool_call_id: toolCall.id,
|
|
151
|
+
role: 'tool',
|
|
152
|
+
name: functionName,
|
|
153
|
+
content: JSON.stringify({ error: error.message })
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return results;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function extractJsonFromMarkdown(text) {
|
|
162
|
+
// Try to find a markdown code block with JSON content
|
|
163
|
+
const codeBlockRegex = /```json\n([\s\S]*?)\n```/g;
|
|
164
|
+
const match = codeBlockRegex.exec(text);
|
|
165
|
+
|
|
166
|
+
if (match) {
|
|
167
|
+
try {
|
|
168
|
+
return JSON.parse(match[1]);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error('Error parsing JSON from markdown:', error);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// If no code block, try to parse the entire text as JSON
|
|
175
|
+
try {
|
|
176
|
+
return JSON.parse(text);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error('Error parsing JSON:', error);
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function processQuery(query, conversation = []) {
|
|
184
|
+
try {
|
|
185
|
+
// Add user message to conversation
|
|
186
|
+
const userMessage = { role: 'user', content: query };
|
|
187
|
+
const messages = [...conversation, userMessage];
|
|
188
|
+
|
|
189
|
+
// Add system message if this is the first message
|
|
190
|
+
if (conversation.length === 0) {
|
|
191
|
+
messages.unshift({
|
|
192
|
+
role: 'system',
|
|
193
|
+
content: SYSTEM_PROMPT
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let shouldContinue = true;
|
|
198
|
+
let iteration = 0;
|
|
199
|
+
const maxIterations = 10; // Prevent infinite loops
|
|
200
|
+
let finalResponse = '';
|
|
201
|
+
|
|
202
|
+
while (shouldContinue && iteration < maxIterations) {
|
|
203
|
+
iteration++;
|
|
204
|
+
console.log('🤖 Thinking...');
|
|
205
|
+
|
|
206
|
+
const response = await callOpenRouter(messages);
|
|
207
|
+
const assistantMessage = response.choices[0].message;
|
|
208
|
+
|
|
209
|
+
// Add assistant's message to the conversation
|
|
210
|
+
messages.push(assistantMessage);
|
|
211
|
+
|
|
212
|
+
// Check if the response contains actions
|
|
213
|
+
const actionData = await extractJsonFromMarkdown(assistantMessage.content);
|
|
214
|
+
|
|
215
|
+
if (actionData && actionData.actions && Array.isArray(actionData.actions)) {
|
|
216
|
+
console.log(`🔧 Processing ${actionData.actions.length} actions...`);
|
|
217
|
+
if (actionData.thoughts) {
|
|
218
|
+
console.log(`💭 ${actionData.thoughts}`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const actionResults = [];
|
|
222
|
+
|
|
223
|
+
for (const action of actionData.actions) {
|
|
224
|
+
console.log(`🛠️ Executing action: ${action.type}`);
|
|
225
|
+
|
|
226
|
+
// Handle stop action
|
|
227
|
+
if (action.type === 'stop') {
|
|
228
|
+
console.log('🛑 Stop action received, ending action processing');
|
|
229
|
+
shouldContinue = false;
|
|
230
|
+
finalResponse = 'Task completed successfully.';
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
let result;
|
|
236
|
+
|
|
237
|
+
switch (action.type) {
|
|
238
|
+
case 'read':
|
|
239
|
+
result = await agentUtils.readFile(action.data.path);
|
|
240
|
+
break;
|
|
241
|
+
|
|
242
|
+
case 'write':
|
|
243
|
+
await agentUtils.writeFile(action.data.path, action.data.content);
|
|
244
|
+
result = `Successfully wrote to ${action.data.path}`;
|
|
245
|
+
break;
|
|
246
|
+
|
|
247
|
+
case 'command':
|
|
248
|
+
result = await agentUtils.runCommand(action.data.command);
|
|
249
|
+
break;
|
|
250
|
+
|
|
251
|
+
case 'search':
|
|
252
|
+
if (action.data.type === 'files') {
|
|
253
|
+
result = await agentUtils.searchFiles(action.data.pattern);
|
|
254
|
+
} else {
|
|
255
|
+
result = 'Text search not yet implemented';
|
|
256
|
+
}
|
|
257
|
+
break;
|
|
258
|
+
|
|
259
|
+
default:
|
|
260
|
+
result = `Action type '${action.type}' is not supported yet.`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
actionResults.push({
|
|
264
|
+
type: action.type,
|
|
265
|
+
success: true,
|
|
266
|
+
result: result
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
console.log(`✅ Action ${action.type} completed successfully`);
|
|
270
|
+
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.error(`❌ Action ${action.type} failed:`, error);
|
|
273
|
+
actionResults.push({
|
|
274
|
+
type: action.type,
|
|
275
|
+
success: false,
|
|
276
|
+
error: error.message
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Add action results to the conversation
|
|
282
|
+
messages.push({
|
|
283
|
+
role: 'system',
|
|
284
|
+
content: `Action results:\n\`\`\`json\n${JSON.stringify(actionResults, null, 2)}\n\`\`\`\n` +
|
|
285
|
+
`Based on these results, determine what to do next. You can:\n` +
|
|
286
|
+
`1. Continue with more actions by returning a new JSON with "actions" array\n` +
|
|
287
|
+
`2. Stop the iteration by including an action with "type": "stop" if the task is completed\n` +
|
|
288
|
+
`3. Provide a final response to the user with your findings`
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
} else {
|
|
292
|
+
// No actions, this is a regular response
|
|
293
|
+
shouldContinue = false;
|
|
294
|
+
finalResponse = assistantMessage.content;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// If we hit max iterations, add a note
|
|
299
|
+
if (iteration >= maxIterations) {
|
|
300
|
+
finalResponse += '\n\n⚠️ Reached maximum number of iterations. Stopping execution.';
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
response: finalResponse,
|
|
305
|
+
conversation: messages
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
} catch (error) {
|
|
309
|
+
console.error('Error processing query:', error);
|
|
310
|
+
return {
|
|
311
|
+
response: `Error: ${error.message}`,
|
|
312
|
+
conversation
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async function chat() {
|
|
318
|
+
const conversation = [];
|
|
319
|
+
console.log('Welcome to AGI-CLI. Type your message, or "exit" to quit.');
|
|
320
|
+
|
|
321
|
+
const rl = readline.createInterface({
|
|
322
|
+
input: process.stdin,
|
|
323
|
+
output: process.stdout,
|
|
324
|
+
prompt: '> '
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
rl.prompt();
|
|
328
|
+
|
|
329
|
+
rl.on('line', async (line) => {
|
|
330
|
+
const input = line.trim();
|
|
331
|
+
|
|
332
|
+
if (input.toLowerCase() === 'exit') {
|
|
333
|
+
rl.close();
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (!input) {
|
|
338
|
+
rl.prompt();
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const result = await processQuery(input, conversation);
|
|
343
|
+
console.log(result.response);
|
|
344
|
+
rl.prompt();
|
|
345
|
+
}).on('close', () => {
|
|
346
|
+
console.log('Goodbye!');
|
|
347
|
+
process.exit(0);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Start the chat
|
|
352
|
+
chat();
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brainstorm Module - Start a New Brainstorm Session
|
|
3
|
+
*
|
|
4
|
+
* Initializes a new brainstorm session and generates all project coordination files.
|
|
5
|
+
* Ported from Python to JavaScript.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs').promises;
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { ProjectConfig, BrainstormSession } = require('./models');
|
|
11
|
+
const templates = require('./templates');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* File templates mapping
|
|
15
|
+
*/
|
|
16
|
+
const FILE_TEMPLATES = {
|
|
17
|
+
'CLAUDE.md': templates.generateClaudeMd,
|
|
18
|
+
'PROJECT_STATUS.md': templates.generateProjectStatusMd,
|
|
19
|
+
'PROGRESS_REPORT.md': templates.generateProgressReportMd,
|
|
20
|
+
'GETTING_STARTED.md': templates.generateGettingStartedMd,
|
|
21
|
+
'SELF_IMPROVEMENT.md': templates.generateSelfImprovementMd,
|
|
22
|
+
'CEO-UPDATES-TODAY.md': templates.generateCeoUpdatesMd,
|
|
23
|
+
'CODE_OF_CONDUCT_OF_AI_WORKERS.md': templates.generateCodeOfConductMd
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const MULTI_AGENT_TEMPLATES = {
|
|
27
|
+
'SECONDARY-AI-CHAT.md': templates.generateSecondaryAiChatMd
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Start a new brainstorm session
|
|
32
|
+
*
|
|
33
|
+
* @param {Object} options
|
|
34
|
+
* @param {ProjectConfig|Object} options.config - Project configuration
|
|
35
|
+
* @param {string} options.outputDir - Directory where files will be generated
|
|
36
|
+
* @param {string[]} options.agents - List of agent identities (e.g., ["CLAUDE-1", "CLAUDE-2"])
|
|
37
|
+
* @returns {Promise<BrainstormSession>}
|
|
38
|
+
*/
|
|
39
|
+
async function startBrainstorm({ config, outputDir, agents = ['CLAUDE-1'] }) {
|
|
40
|
+
// Convert to ProjectConfig if needed
|
|
41
|
+
const project = config instanceof ProjectConfig ? config : new ProjectConfig(config);
|
|
42
|
+
|
|
43
|
+
// Create output directory
|
|
44
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
45
|
+
|
|
46
|
+
// Create new session
|
|
47
|
+
const session = BrainstormSession.create({
|
|
48
|
+
project,
|
|
49
|
+
outputDirectory: outputDir,
|
|
50
|
+
agents
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const generatedFiles = [];
|
|
54
|
+
|
|
55
|
+
// Generate standard files
|
|
56
|
+
for (const [fileName, templateFn] of Object.entries(FILE_TEMPLATES)) {
|
|
57
|
+
const content = templateFn({ project, agents, session });
|
|
58
|
+
const filePath = path.join(outputDir, fileName);
|
|
59
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
60
|
+
generatedFiles.push(fileName);
|
|
61
|
+
session.fileVersions[fileName] = 1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Generate multi-agent files if more than one agent
|
|
65
|
+
if (agents.length > 1) {
|
|
66
|
+
for (const [fileName, templateFn] of Object.entries(MULTI_AGENT_TEMPLATES)) {
|
|
67
|
+
const content = templateFn({ project, agents, session });
|
|
68
|
+
const filePath = path.join(outputDir, fileName);
|
|
69
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
70
|
+
generatedFiles.push(fileName);
|
|
71
|
+
session.fileVersions[fileName] = 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Create directory structure if defined
|
|
76
|
+
if (project.directoryStructure && Object.keys(project.directoryStructure).length > 0) {
|
|
77
|
+
await createDirectoryStructure(outputDir, project.directoryStructure);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Add event for file generation
|
|
81
|
+
session.addEvent({
|
|
82
|
+
eventType: 'UPDATED',
|
|
83
|
+
actor: 'BRAINSTORM',
|
|
84
|
+
description: `Generated ${generatedFiles.length} project files`,
|
|
85
|
+
affectedFiles: generatedFiles
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Save session state
|
|
89
|
+
await session.save();
|
|
90
|
+
|
|
91
|
+
return session;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Create directory structure recursively
|
|
96
|
+
*/
|
|
97
|
+
async function createDirectoryStructure(baseDir, structure, currentPath = null) {
|
|
98
|
+
const current = currentPath || baseDir;
|
|
99
|
+
|
|
100
|
+
for (const [name, children] of Object.entries(structure)) {
|
|
101
|
+
const itemPath = path.join(current, name);
|
|
102
|
+
|
|
103
|
+
if (typeof children === 'object' && children !== null && Object.keys(children).length > 0) {
|
|
104
|
+
// Directory with children
|
|
105
|
+
await fs.mkdir(itemPath, { recursive: true });
|
|
106
|
+
await createDirectoryStructure(baseDir, children, itemPath);
|
|
107
|
+
} else if (children === null || (typeof children === 'object' && Object.keys(children).length === 0)) {
|
|
108
|
+
// Empty directory
|
|
109
|
+
await fs.mkdir(itemPath, { recursive: true });
|
|
110
|
+
} else {
|
|
111
|
+
// File placeholder
|
|
112
|
+
await fs.mkdir(path.dirname(itemPath), { recursive: true });
|
|
113
|
+
try {
|
|
114
|
+
await fs.access(itemPath);
|
|
115
|
+
} catch {
|
|
116
|
+
await fs.writeFile(itemPath, '', 'utf-8');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Load an existing session from a directory
|
|
124
|
+
*
|
|
125
|
+
* @param {string} sessionDir - Directory containing SESSION.json
|
|
126
|
+
* @returns {Promise<BrainstormSession|null>}
|
|
127
|
+
*/
|
|
128
|
+
async function getSession(sessionDir) {
|
|
129
|
+
const sessionFile = path.join(sessionDir, 'SESSION.json');
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
await fs.access(sessionFile);
|
|
133
|
+
return await BrainstormSession.load(sessionFile);
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* List all files in a brainstorm session with their versions
|
|
141
|
+
*
|
|
142
|
+
* @param {BrainstormSession} session
|
|
143
|
+
* @returns {Array<Object>}
|
|
144
|
+
*/
|
|
145
|
+
async function listFiles(session) {
|
|
146
|
+
const files = [];
|
|
147
|
+
|
|
148
|
+
for (const [fileName, version] of Object.entries(session.fileVersions)) {
|
|
149
|
+
const filePath = path.join(session.outputDirectory, fileName);
|
|
150
|
+
let exists = false;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
await fs.access(filePath);
|
|
154
|
+
exists = true;
|
|
155
|
+
} catch { }
|
|
156
|
+
|
|
157
|
+
files.push({
|
|
158
|
+
name: fileName,
|
|
159
|
+
path: filePath,
|
|
160
|
+
version,
|
|
161
|
+
exists
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return files;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Quick start function for creating a brainstorm session
|
|
170
|
+
*
|
|
171
|
+
* @param {string} name - Project name
|
|
172
|
+
* @param {string} description - Project description
|
|
173
|
+
* @param {string} outputDir - Output directory
|
|
174
|
+
* @param {string[]} agents - List of agent identities
|
|
175
|
+
* @param {Object} options - Additional ProjectConfig fields
|
|
176
|
+
* @returns {Promise<BrainstormSession>}
|
|
177
|
+
*/
|
|
178
|
+
async function quickStart(name, description, outputDir, agents = ['CLAUDE-1'], options = {}) {
|
|
179
|
+
const config = new ProjectConfig({
|
|
180
|
+
name,
|
|
181
|
+
description,
|
|
182
|
+
...options
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return startBrainstorm({
|
|
186
|
+
config,
|
|
187
|
+
outputDir,
|
|
188
|
+
agents
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports = {
|
|
193
|
+
startBrainstorm,
|
|
194
|
+
getSession,
|
|
195
|
+
listFiles,
|
|
196
|
+
quickStart,
|
|
197
|
+
createDirectoryStructure
|
|
198
|
+
};
|