sessioncast-cli 2.0.0 → 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/dist/agent/session-handler.d.ts +2 -1
- package/dist/agent/session-handler.js +77 -30
- package/dist/agent/tmux-executor.d.ts +33 -3
- package/dist/agent/tmux-executor.js +50 -3
- package/dist/agent/tmux.d.ts +6 -2
- package/dist/agent/tmux.js +9 -2
- package/dist/agent/types.d.ts +10 -0
- package/dist/agent/websocket.d.ts +18 -2
- package/dist/agent/websocket.js +26 -9
- package/dist/autopilot/index.d.ts +94 -0
- package/dist/autopilot/index.js +322 -0
- package/dist/autopilot/mission-analyzer.d.ts +27 -0
- package/dist/autopilot/mission-analyzer.js +232 -0
- package/dist/autopilot/project-detector.d.ts +12 -0
- package/dist/autopilot/project-detector.js +326 -0
- package/dist/autopilot/source-scanner.d.ts +26 -0
- package/dist/autopilot/source-scanner.js +285 -0
- package/dist/autopilot/speckit-generator.d.ts +60 -0
- package/dist/autopilot/speckit-generator.js +511 -0
- package/dist/autopilot/types.d.ts +110 -0
- package/dist/autopilot/types.js +6 -0
- package/dist/autopilot/workflow-generator.d.ts +33 -0
- package/dist/autopilot/workflow-generator.js +278 -0
- package/dist/project/executor.d.ts +73 -0
- package/dist/project/executor.js +437 -0
- package/dist/project/index.d.ts +4 -0
- package/dist/project/index.js +20 -0
- package/dist/project/manager.d.ts +66 -0
- package/dist/project/manager.js +290 -0
- package/dist/project/relay-client.d.ts +37 -0
- package/dist/project/relay-client.js +204 -0
- package/dist/project/types.d.ts +48 -0
- package/dist/project/types.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* AutoPilot - Single-prompt execution layer for SessionCast
|
|
4
|
+
*
|
|
5
|
+
* Enables opencode-style experience: one prompt → auto-detect → auto-analyze → auto-execute
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
41
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
42
|
+
};
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.AutoPilot = exports.saveSpeckit = exports.generateQuickSpeckit = exports.generateSpeckit = void 0;
|
|
45
|
+
exports.autoPilot = autoPilot;
|
|
46
|
+
exports.autoPilotQuick = autoPilotQuick;
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const events_1 = require("events");
|
|
50
|
+
const project_detector_1 = require("./project-detector");
|
|
51
|
+
const source_scanner_1 = require("./source-scanner");
|
|
52
|
+
const mission_analyzer_1 = require("./mission-analyzer");
|
|
53
|
+
const workflow_generator_1 = require("./workflow-generator");
|
|
54
|
+
const speckit_generator_1 = require("./speckit-generator");
|
|
55
|
+
// Re-export types
|
|
56
|
+
__exportStar(require("./types"), exports);
|
|
57
|
+
// Re-export Speckit functions
|
|
58
|
+
var speckit_generator_2 = require("./speckit-generator");
|
|
59
|
+
Object.defineProperty(exports, "generateSpeckit", { enumerable: true, get: function () { return speckit_generator_2.generateSpeckit; } });
|
|
60
|
+
Object.defineProperty(exports, "generateQuickSpeckit", { enumerable: true, get: function () { return speckit_generator_2.generateQuickSpeckit; } });
|
|
61
|
+
Object.defineProperty(exports, "saveSpeckit", { enumerable: true, get: function () { return speckit_generator_2.saveSpeckit; } });
|
|
62
|
+
class AutoPilot extends events_1.EventEmitter {
|
|
63
|
+
constructor(options) {
|
|
64
|
+
super();
|
|
65
|
+
this.options = options;
|
|
66
|
+
this.context = this.createInitialContext(options);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create initial context
|
|
70
|
+
*/
|
|
71
|
+
createInitialContext(options) {
|
|
72
|
+
return {
|
|
73
|
+
prompt: '',
|
|
74
|
+
workingDir: options.workingDir,
|
|
75
|
+
projectType: 'unknown',
|
|
76
|
+
projectName: path.basename(options.workingDir),
|
|
77
|
+
sources: [],
|
|
78
|
+
projectStructure: '',
|
|
79
|
+
mission: '',
|
|
80
|
+
status: 'idle'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Set LLM client for mission analysis
|
|
85
|
+
*/
|
|
86
|
+
setLlmClient(client) {
|
|
87
|
+
this.llmClient = client;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get current context
|
|
91
|
+
*/
|
|
92
|
+
getContext() {
|
|
93
|
+
return { ...this.context };
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Main entry point: execute a single prompt
|
|
97
|
+
*/
|
|
98
|
+
async execute(prompt) {
|
|
99
|
+
this.context.prompt = prompt;
|
|
100
|
+
this.context.mission = prompt;
|
|
101
|
+
try {
|
|
102
|
+
// Phase 1: Detect project type
|
|
103
|
+
await this.detectProject();
|
|
104
|
+
// Phase 2: Scan sources
|
|
105
|
+
await this.scanProject();
|
|
106
|
+
// Phase 3: Analyze mission
|
|
107
|
+
await this.analyzeMission();
|
|
108
|
+
// Phase 4: Generate workflow
|
|
109
|
+
const workflow = await this.generateWorkflow();
|
|
110
|
+
this.updateStatus('ready');
|
|
111
|
+
return workflow;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
this.context.error = error instanceof Error ? error.message : 'Unknown error';
|
|
115
|
+
this.updateStatus('error');
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Quick execute without LLM analysis
|
|
121
|
+
*/
|
|
122
|
+
async quickExecute(prompt) {
|
|
123
|
+
this.context.prompt = prompt;
|
|
124
|
+
this.context.mission = prompt;
|
|
125
|
+
try {
|
|
126
|
+
// Phase 1: Detect project type
|
|
127
|
+
await this.detectProject();
|
|
128
|
+
// Phase 2: Generate simple workflow
|
|
129
|
+
const workflow = (0, workflow_generator_1.createQuickWorkflow)(prompt, {
|
|
130
|
+
projectType: this.context.projectType,
|
|
131
|
+
projectName: this.context.projectName,
|
|
132
|
+
workingDir: this.context.workingDir
|
|
133
|
+
});
|
|
134
|
+
this.context.workflow = workflow;
|
|
135
|
+
this.updateStatus('ready');
|
|
136
|
+
return workflow;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
this.context.error = error instanceof Error ? error.message : 'Unknown error';
|
|
140
|
+
this.updateStatus('error');
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Phase 1: Detect project type
|
|
146
|
+
*/
|
|
147
|
+
async detectProject() {
|
|
148
|
+
this.updateStatus('detecting');
|
|
149
|
+
this.emit('phase', 'detecting', 'Detecting project type...');
|
|
150
|
+
const detection = await (0, project_detector_1.detectProjectType)(this.context.workingDir);
|
|
151
|
+
this.context.projectType = detection.type;
|
|
152
|
+
this.context.projectName = detection.name;
|
|
153
|
+
if (this.options.verbose) {
|
|
154
|
+
console.log(` Project: ${detection.name}`);
|
|
155
|
+
console.log(` Type: ${detection.type} (${(detection.confidence * 100).toFixed(0)}% confidence)`);
|
|
156
|
+
console.log(` Language: ${detection.mainLanguage || 'unknown'}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Phase 2: Scan sources
|
|
161
|
+
*/
|
|
162
|
+
async scanProject() {
|
|
163
|
+
this.updateStatus('scanning');
|
|
164
|
+
this.emit('phase', 'scanning', 'Scanning source files...');
|
|
165
|
+
// Get project structure
|
|
166
|
+
this.context.projectStructure = (0, project_detector_1.getProjectStructure)(this.context.workingDir);
|
|
167
|
+
// Scan source files
|
|
168
|
+
this.context.sources = await (0, source_scanner_1.scanSources)(this.context.workingDir, this.context.projectType, { maxFiles: 50, maxFileSize: 100 * 1024 });
|
|
169
|
+
if (this.options.verbose) {
|
|
170
|
+
console.log(` Found ${this.context.sources.length} relevant source files`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Phase 3: Analyze mission
|
|
175
|
+
*/
|
|
176
|
+
async analyzeMission() {
|
|
177
|
+
this.updateStatus('analyzing');
|
|
178
|
+
this.emit('phase', 'analyzing', 'Analyzing mission...');
|
|
179
|
+
const analyzerContext = {
|
|
180
|
+
projectType: this.context.projectType,
|
|
181
|
+
projectName: this.context.projectName,
|
|
182
|
+
projectStructure: this.context.projectStructure,
|
|
183
|
+
sources: this.context.sources,
|
|
184
|
+
keyFilesContent: (0, source_scanner_1.getKeyFilesContent)(this.context.sources)
|
|
185
|
+
};
|
|
186
|
+
if (this.llmClient) {
|
|
187
|
+
// Full LLM-powered analysis
|
|
188
|
+
this.context.analysis = await (0, mission_analyzer_1.analyzeMission)(this.context.prompt, analyzerContext, this.llmClient);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// Quick heuristic analysis
|
|
192
|
+
this.context.analysis = (0, mission_analyzer_1.analyzeQuick)(this.context.prompt, analyzerContext);
|
|
193
|
+
}
|
|
194
|
+
if (this.options.verbose && this.context.analysis) {
|
|
195
|
+
console.log(` Complexity: ${this.context.analysis.complexity}`);
|
|
196
|
+
console.log(` Steps: ${this.context.analysis.steps.length}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Phase 4: Generate workflow
|
|
201
|
+
*/
|
|
202
|
+
async generateWorkflow() {
|
|
203
|
+
this.updateStatus('generating');
|
|
204
|
+
this.emit('phase', 'generating', 'Generating workflow...');
|
|
205
|
+
if (!this.context.analysis) {
|
|
206
|
+
// Fallback: create simple workflow
|
|
207
|
+
return (0, workflow_generator_1.createQuickWorkflow)(this.context.prompt, {
|
|
208
|
+
projectType: this.context.projectType,
|
|
209
|
+
projectName: this.context.projectName,
|
|
210
|
+
workingDir: this.context.workingDir
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
const workflow = (0, workflow_generator_1.generateWorkflow)(this.context.analysis, {
|
|
214
|
+
projectType: this.context.projectType,
|
|
215
|
+
projectName: this.context.projectName,
|
|
216
|
+
workingDir: this.context.workingDir
|
|
217
|
+
});
|
|
218
|
+
this.context.workflow = workflow;
|
|
219
|
+
if (this.options.verbose) {
|
|
220
|
+
console.log(` Workflow: ${workflow.name}`);
|
|
221
|
+
console.log(` Agents: ${workflow.agents.length}`);
|
|
222
|
+
console.log(` Steps: ${workflow.steps.length}`);
|
|
223
|
+
}
|
|
224
|
+
return workflow;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Convert to executable workflow format (compatible with existing ProjectManager)
|
|
228
|
+
*/
|
|
229
|
+
toExecutableFormat() {
|
|
230
|
+
if (!this.context.workflow)
|
|
231
|
+
return null;
|
|
232
|
+
return (0, workflow_generator_1.toExecutableWorkflow)(this.context.workflow);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Convert to Speckit format (plan.md + tasks.md)
|
|
236
|
+
*/
|
|
237
|
+
toSpeckit() {
|
|
238
|
+
return (0, speckit_generator_1.generateSpeckit)(this.context);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Generate and save Speckit files
|
|
242
|
+
*/
|
|
243
|
+
saveSpeckit(outputDir) {
|
|
244
|
+
const speckit = this.toSpeckit();
|
|
245
|
+
if (outputDir) {
|
|
246
|
+
speckit.outputDir = outputDir;
|
|
247
|
+
}
|
|
248
|
+
return (0, speckit_generator_1.saveSpeckit)(speckit);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Update status and emit event
|
|
252
|
+
*/
|
|
253
|
+
updateStatus(status, message) {
|
|
254
|
+
this.context.status = status;
|
|
255
|
+
this.emit('status', status, message);
|
|
256
|
+
if (this.options.verbose) {
|
|
257
|
+
console.log(`[AutoPilot] ${status}${message ? ': ' + message : ''}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Get a summary of the analysis for display
|
|
262
|
+
*/
|
|
263
|
+
getSummary() {
|
|
264
|
+
const lines = [];
|
|
265
|
+
lines.push(`Project: ${this.context.projectName} (${this.context.projectType})`);
|
|
266
|
+
lines.push(`Mission: ${this.context.mission}`);
|
|
267
|
+
if (this.context.analysis) {
|
|
268
|
+
lines.push(`Complexity: ${this.context.analysis.complexity}`);
|
|
269
|
+
lines.push(`\nSteps:`);
|
|
270
|
+
for (let i = 0; i < this.context.analysis.steps.length; i++) {
|
|
271
|
+
const step = this.context.analysis.steps[i];
|
|
272
|
+
lines.push(` ${i + 1}. ${step.name} [${step.type}]`);
|
|
273
|
+
if (step.dependsOn?.length) {
|
|
274
|
+
lines.push(` depends on: ${step.dependsOn.join(', ')}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (this.context.workflow) {
|
|
279
|
+
lines.push(`\nWorkflow: ${this.context.workflow.name}`);
|
|
280
|
+
lines.push(`Agents:`);
|
|
281
|
+
for (const agent of this.context.workflow.agents) {
|
|
282
|
+
lines.push(` - ${agent.name} (${agent.id})`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return lines.join('\n');
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Save workflow to file
|
|
289
|
+
*/
|
|
290
|
+
async saveWorkflow(outputPath) {
|
|
291
|
+
if (!this.context.workflow) {
|
|
292
|
+
throw new Error('No workflow generated yet');
|
|
293
|
+
}
|
|
294
|
+
const workflow = this.toExecutableFormat();
|
|
295
|
+
if (!workflow) {
|
|
296
|
+
throw new Error('Failed to convert workflow');
|
|
297
|
+
}
|
|
298
|
+
const filePath = outputPath || path.join(this.context.workingDir, '.sessioncast', 'workflows', `${workflow.name}.json`);
|
|
299
|
+
// Ensure directory exists
|
|
300
|
+
const dir = path.dirname(filePath);
|
|
301
|
+
if (!fs.existsSync(dir)) {
|
|
302
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
303
|
+
}
|
|
304
|
+
fs.writeFileSync(filePath, JSON.stringify(workflow, null, 2));
|
|
305
|
+
return filePath;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
exports.AutoPilot = AutoPilot;
|
|
309
|
+
/**
|
|
310
|
+
* Convenience function for one-shot execution
|
|
311
|
+
*/
|
|
312
|
+
async function autoPilot(prompt, options) {
|
|
313
|
+
const pilot = new AutoPilot(options);
|
|
314
|
+
return pilot.execute(prompt);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Quick version without LLM
|
|
318
|
+
*/
|
|
319
|
+
async function autoPilotQuick(prompt, options) {
|
|
320
|
+
const pilot = new AutoPilot(options);
|
|
321
|
+
return pilot.quickExecute(prompt);
|
|
322
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MissionAnalyzer - LLM-based analysis of user prompts
|
|
3
|
+
* Breaks down a single prompt into structured workflow steps
|
|
4
|
+
*/
|
|
5
|
+
import { MissionAnalysis, ProjectType, SourceInfo } from './types';
|
|
6
|
+
interface AnalyzerContext {
|
|
7
|
+
projectType: ProjectType;
|
|
8
|
+
projectName: string;
|
|
9
|
+
projectStructure: string;
|
|
10
|
+
sources: SourceInfo[];
|
|
11
|
+
keyFilesContent?: string;
|
|
12
|
+
}
|
|
13
|
+
interface LlmClient {
|
|
14
|
+
chat: (messages: {
|
|
15
|
+
role: string;
|
|
16
|
+
content: string;
|
|
17
|
+
}[]) => Promise<string>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Analyze user prompt and generate mission breakdown
|
|
21
|
+
*/
|
|
22
|
+
export declare function analyzeMission(prompt: string, context: AnalyzerContext, llmClient: LlmClient): Promise<MissionAnalysis>;
|
|
23
|
+
/**
|
|
24
|
+
* Analyze mission without LLM (for quick mode or when LLM is unavailable)
|
|
25
|
+
*/
|
|
26
|
+
export declare function analyzeQuick(prompt: string, context: AnalyzerContext): MissionAnalysis;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MissionAnalyzer - LLM-based analysis of user prompts
|
|
4
|
+
* Breaks down a single prompt into structured workflow steps
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.analyzeMission = analyzeMission;
|
|
8
|
+
exports.analyzeQuick = analyzeQuick;
|
|
9
|
+
/**
|
|
10
|
+
* Analyze user prompt and generate mission breakdown
|
|
11
|
+
*/
|
|
12
|
+
async function analyzeMission(prompt, context, llmClient) {
|
|
13
|
+
const systemPrompt = buildSystemPrompt(context);
|
|
14
|
+
const userPrompt = buildUserPrompt(prompt, context);
|
|
15
|
+
try {
|
|
16
|
+
const response = await llmClient.chat([
|
|
17
|
+
{ role: 'system', content: systemPrompt },
|
|
18
|
+
{ role: 'user', content: userPrompt }
|
|
19
|
+
]);
|
|
20
|
+
return parseAnalysisResponse(response, prompt);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
// Fallback to simple analysis if LLM fails
|
|
24
|
+
return createFallbackAnalysis(prompt, context);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build system prompt for mission analysis
|
|
29
|
+
*/
|
|
30
|
+
function buildSystemPrompt(context) {
|
|
31
|
+
return `You are a software development task analyzer for SessionCast.
|
|
32
|
+
Your job is to analyze user requests and break them down into executable steps.
|
|
33
|
+
|
|
34
|
+
Project Information:
|
|
35
|
+
- Type: ${context.projectType}
|
|
36
|
+
- Name: ${context.projectName}
|
|
37
|
+
- Main Language: ${getMainLanguage(context.projectType)}
|
|
38
|
+
|
|
39
|
+
Rules:
|
|
40
|
+
1. Break down the task into clear, independent steps
|
|
41
|
+
2. Each step should be achievable by a single AI agent
|
|
42
|
+
3. Consider dependencies between steps
|
|
43
|
+
4. Estimate complexity based on the scope of changes
|
|
44
|
+
5. Identify required agent types: frontend, backend, fullstack, mobile, infra, test
|
|
45
|
+
|
|
46
|
+
Response Format (JSON):
|
|
47
|
+
{
|
|
48
|
+
"mission": "Brief summary of the mission",
|
|
49
|
+
"complexity": "simple|medium|complex",
|
|
50
|
+
"steps": [
|
|
51
|
+
{
|
|
52
|
+
"name": "Step name",
|
|
53
|
+
"description": "What this step accomplishes",
|
|
54
|
+
"type": "frontend|backend|fullstack|mobile|infra|test",
|
|
55
|
+
"prompt": "Detailed prompt for the AI agent",
|
|
56
|
+
"dependsOn": ["previous step names if any"]
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"requiredAgents": ["agent types needed"],
|
|
60
|
+
"estimatedFiles": ["likely files to be modified"]
|
|
61
|
+
}`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build user prompt with context
|
|
65
|
+
*/
|
|
66
|
+
function buildUserPrompt(prompt, context) {
|
|
67
|
+
let userContent = `Analyze this task and break it down into executable steps:
|
|
68
|
+
|
|
69
|
+
USER REQUEST: ${prompt}
|
|
70
|
+
|
|
71
|
+
PROJECT STRUCTURE:
|
|
72
|
+
${context.projectStructure}
|
|
73
|
+
|
|
74
|
+
SOURCE FILES:
|
|
75
|
+
${context.sources.map(s => `- ${s.relativePath} (${s.type})`).join('\n')}`;
|
|
76
|
+
if (context.keyFilesContent) {
|
|
77
|
+
userContent += `\n\nKEY FILES CONTENT:\n${context.keyFilesContent}`;
|
|
78
|
+
}
|
|
79
|
+
return userContent;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Parse LLM response into MissionAnalysis
|
|
83
|
+
*/
|
|
84
|
+
function parseAnalysisResponse(response, originalPrompt) {
|
|
85
|
+
try {
|
|
86
|
+
// Extract JSON from response
|
|
87
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
88
|
+
if (!jsonMatch) {
|
|
89
|
+
throw new Error('No JSON found in response');
|
|
90
|
+
}
|
|
91
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
92
|
+
// Validate and normalize
|
|
93
|
+
const analysis = {
|
|
94
|
+
mission: parsed.mission || originalPrompt,
|
|
95
|
+
complexity: validateComplexity(parsed.complexity),
|
|
96
|
+
steps: normalizeSteps(parsed.steps || []),
|
|
97
|
+
requiredAgents: parsed.requiredAgents || [],
|
|
98
|
+
estimatedFiles: parsed.estimatedFiles || []
|
|
99
|
+
};
|
|
100
|
+
// Ensure at least one step
|
|
101
|
+
if (analysis.steps.length === 0) {
|
|
102
|
+
analysis.steps = [{
|
|
103
|
+
name: 'Execute task',
|
|
104
|
+
description: originalPrompt,
|
|
105
|
+
type: 'fullstack',
|
|
106
|
+
prompt: originalPrompt
|
|
107
|
+
}];
|
|
108
|
+
}
|
|
109
|
+
return analysis;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
// Return basic analysis if parsing fails
|
|
113
|
+
return {
|
|
114
|
+
mission: originalPrompt,
|
|
115
|
+
complexity: 'simple',
|
|
116
|
+
steps: [{
|
|
117
|
+
name: 'Execute task',
|
|
118
|
+
description: originalPrompt,
|
|
119
|
+
type: 'fullstack',
|
|
120
|
+
prompt: originalPrompt
|
|
121
|
+
}],
|
|
122
|
+
requiredAgents: ['fullstack'],
|
|
123
|
+
estimatedFiles: []
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Validate complexity value
|
|
129
|
+
*/
|
|
130
|
+
function validateComplexity(value) {
|
|
131
|
+
const valid = ['simple', 'medium', 'complex'];
|
|
132
|
+
return valid.includes(value) ? value : 'simple';
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Normalize steps array
|
|
136
|
+
*/
|
|
137
|
+
function normalizeSteps(steps) {
|
|
138
|
+
return steps.map((step, index) => ({
|
|
139
|
+
name: step.name || `Step ${index + 1}`,
|
|
140
|
+
description: step.description || '',
|
|
141
|
+
type: validateStepType(step.type),
|
|
142
|
+
prompt: step.prompt || step.description || '',
|
|
143
|
+
dependsOn: Array.isArray(step.dependsOn) ? step.dependsOn : undefined
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Validate step type
|
|
148
|
+
*/
|
|
149
|
+
function validateStepType(value) {
|
|
150
|
+
const valid = ['frontend', 'backend', 'fullstack', 'mobile', 'infra', 'test'];
|
|
151
|
+
return valid.includes(value) ? value : 'fullstack';
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get main language for project type
|
|
155
|
+
*/
|
|
156
|
+
function getMainLanguage(projectType) {
|
|
157
|
+
const map = {
|
|
158
|
+
android: 'Kotlin/Java',
|
|
159
|
+
ios: 'Swift',
|
|
160
|
+
react: 'TypeScript/React',
|
|
161
|
+
next: 'TypeScript/Next.js',
|
|
162
|
+
vue: 'TypeScript/Vue',
|
|
163
|
+
node: 'JavaScript/Node.js',
|
|
164
|
+
python: 'Python',
|
|
165
|
+
spring: 'Java/Spring',
|
|
166
|
+
go: 'Go',
|
|
167
|
+
rust: 'Rust',
|
|
168
|
+
unknown: 'Unknown'
|
|
169
|
+
};
|
|
170
|
+
return map[projectType];
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Create fallback analysis without LLM
|
|
174
|
+
*/
|
|
175
|
+
function createFallbackAnalysis(prompt, context) {
|
|
176
|
+
// Simple heuristic-based analysis
|
|
177
|
+
const promptLower = prompt.toLowerCase();
|
|
178
|
+
let type = 'fullstack';
|
|
179
|
+
let complexity = 'simple';
|
|
180
|
+
// Detect type from keywords
|
|
181
|
+
if (promptLower.includes('ui') || promptLower.includes('화면') || promptLower.includes('button') || promptLower.includes('버튼')) {
|
|
182
|
+
type = 'frontend';
|
|
183
|
+
}
|
|
184
|
+
else if (promptLower.includes('api') || promptLower.includes('database') || promptLower.includes('db') || promptLower.includes('서버')) {
|
|
185
|
+
type = 'backend';
|
|
186
|
+
}
|
|
187
|
+
else if (promptLower.includes('test') || promptLower.includes('테스트')) {
|
|
188
|
+
type = 'test';
|
|
189
|
+
}
|
|
190
|
+
else if (promptLower.includes('deploy') || promptLower.includes('docker') || promptLower.includes('배포')) {
|
|
191
|
+
type = 'infra';
|
|
192
|
+
}
|
|
193
|
+
// Mobile detection based on project type
|
|
194
|
+
if (context.projectType === 'android' || context.projectType === 'ios') {
|
|
195
|
+
type = 'mobile';
|
|
196
|
+
}
|
|
197
|
+
// Detect complexity from keywords
|
|
198
|
+
if (promptLower.includes('refactor') ||
|
|
199
|
+
promptLower.includes('리팩토링') ||
|
|
200
|
+
promptLower.includes('architecture') ||
|
|
201
|
+
promptLower.includes('아키텍처') ||
|
|
202
|
+
promptLower.includes('전체') ||
|
|
203
|
+
promptLower.includes('모든')) {
|
|
204
|
+
complexity = 'complex';
|
|
205
|
+
}
|
|
206
|
+
else if (promptLower.includes('add') ||
|
|
207
|
+
promptLower.includes('추가') ||
|
|
208
|
+
promptLower.includes('create') ||
|
|
209
|
+
promptLower.includes('생성') ||
|
|
210
|
+
promptLower.includes('implement') ||
|
|
211
|
+
promptLower.includes('구현')) {
|
|
212
|
+
complexity = 'medium';
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
mission: prompt,
|
|
216
|
+
complexity,
|
|
217
|
+
steps: [{
|
|
218
|
+
name: 'Execute task',
|
|
219
|
+
description: prompt,
|
|
220
|
+
type,
|
|
221
|
+
prompt
|
|
222
|
+
}],
|
|
223
|
+
requiredAgents: [type === 'mobile' ? 'mobile' : type],
|
|
224
|
+
estimatedFiles: []
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Analyze mission without LLM (for quick mode or when LLM is unavailable)
|
|
229
|
+
*/
|
|
230
|
+
function analyzeQuick(prompt, context) {
|
|
231
|
+
return createFallbackAnalysis(prompt, context);
|
|
232
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProjectDetector - Auto-detect project type from directory
|
|
3
|
+
*/
|
|
4
|
+
import { ProjectDetectionResult } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Detect project type from directory
|
|
7
|
+
*/
|
|
8
|
+
export declare function detectProjectType(dir: string): Promise<ProjectDetectionResult>;
|
|
9
|
+
/**
|
|
10
|
+
* Get a summary of project structure for LLM context
|
|
11
|
+
*/
|
|
12
|
+
export declare function getProjectStructure(dir: string, maxDepth?: number): string;
|