@staticpayload/zai-code 1.4.12 → 1.5.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/orchestrator.d.ts +2 -2
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +325 -430
- package/dist/orchestrator.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +245 -147
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
package/dist/orchestrator.js
CHANGED
|
@@ -39,15 +39,109 @@ const session_1 = require("./session");
|
|
|
39
39
|
const ui_1 = require("./ui");
|
|
40
40
|
const runtime_1 = require("./runtime");
|
|
41
41
|
const auth_1 = require("./auth");
|
|
42
|
-
const mode_prompts_1 = require("./mode_prompts");
|
|
43
42
|
const apply_1 = require("./apply");
|
|
44
|
-
//
|
|
43
|
+
// Analyze task to determine best execution strategy
|
|
44
|
+
function analyzeTask(input) {
|
|
45
|
+
const lower = input.toLowerCase().trim();
|
|
46
|
+
const words = lower.split(/\s+/);
|
|
47
|
+
const wordCount = words.length;
|
|
48
|
+
// CHAT - greetings, small talk
|
|
49
|
+
const chatPatterns = [
|
|
50
|
+
/^h+i+!*$/i, /^he+y+!*$/i, /^y+o+!*$/i,
|
|
51
|
+
/^(hello|hola|howdy|greetings|sup|wassup)[\s!.,]*$/i,
|
|
52
|
+
/^(good\s*(morning|afternoon|evening|night))[\s!.,]*$/i,
|
|
53
|
+
/^(what'?s?\s*up|how\s*are\s*you)[\s!?.,]*$/i,
|
|
54
|
+
/^(thanks|thank\s*you|thx|ty|bye|goodbye|ok|okay|sure|yes|no|yep|nope|yeah|nah|cool|nice|great|awesome|lol)[\s!.,]*$/i,
|
|
55
|
+
];
|
|
56
|
+
if (chatPatterns.some(p => p.test(lower)) || (wordCount <= 3 && !lower.includes('create') && !lower.includes('make') && !lower.includes('add'))) {
|
|
57
|
+
return { type: 'chat', complexity: 'trivial', needsPlan: false, confidence: 0.95, keywords: [] };
|
|
58
|
+
}
|
|
59
|
+
// QUESTION - explicit questions
|
|
60
|
+
const questionPatterns = [
|
|
61
|
+
/^(what|why|how|when|where|who|which|explain|clarify|describe|tell me|show me)/i,
|
|
62
|
+
/\?$/,
|
|
63
|
+
/^(is|are|can|could|would|should|do|does|did|has|have|will)\s/i,
|
|
64
|
+
];
|
|
65
|
+
if (questionPatterns.some(p => p.test(lower))) {
|
|
66
|
+
return { type: 'question', complexity: 'simple', needsPlan: false, confidence: 0.9, keywords: [] };
|
|
67
|
+
}
|
|
68
|
+
// Extract action keywords
|
|
69
|
+
const actionKeywords = {
|
|
70
|
+
create: ['create', 'make', 'new', 'add', 'generate', 'build', 'write', 'init', 'setup', 'scaffold'],
|
|
71
|
+
modify: ['update', 'change', 'modify', 'edit', 'fix', 'improve', 'enhance', 'refactor', 'rename', 'move'],
|
|
72
|
+
delete: ['delete', 'remove', 'clean', 'clear', 'drop'],
|
|
73
|
+
debug: ['fix', 'debug', 'error', 'bug', 'issue', 'broken', 'failing', 'crash', 'problem', 'wrong', 'not working'],
|
|
74
|
+
refactor: ['refactor', 'reorganize', 'restructure', 'extract', 'split', 'merge', 'consolidate', 'simplify', 'clean up'],
|
|
75
|
+
};
|
|
76
|
+
const foundKeywords = [];
|
|
77
|
+
let taskType = 'simple_edit';
|
|
78
|
+
for (const [category, keywords] of Object.entries(actionKeywords)) {
|
|
79
|
+
for (const kw of keywords) {
|
|
80
|
+
if (lower.includes(kw)) {
|
|
81
|
+
foundKeywords.push(kw);
|
|
82
|
+
if (category === 'debug')
|
|
83
|
+
taskType = 'debug';
|
|
84
|
+
else if (category === 'refactor')
|
|
85
|
+
taskType = 'refactor';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Complexity indicators
|
|
90
|
+
const complexityIndicators = {
|
|
91
|
+
simple: ['file', 'function', 'variable', 'class', 'method', 'component', 'test', 'readme', 'config'],
|
|
92
|
+
medium: ['feature', 'module', 'service', 'api', 'endpoint', 'page', 'route', 'handler', 'middleware'],
|
|
93
|
+
complex: ['system', 'architecture', 'database', 'migration', 'integration', 'authentication', 'authorization', 'deployment', 'infrastructure', 'full', 'complete', 'entire', 'whole', 'all'],
|
|
94
|
+
};
|
|
95
|
+
let complexity = 'simple';
|
|
96
|
+
// Check for complexity indicators
|
|
97
|
+
for (const kw of complexityIndicators.complex) {
|
|
98
|
+
if (lower.includes(kw)) {
|
|
99
|
+
complexity = 'complex';
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (complexity === 'simple') {
|
|
104
|
+
for (const kw of complexityIndicators.medium) {
|
|
105
|
+
if (lower.includes(kw)) {
|
|
106
|
+
complexity = 'medium';
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Word count affects complexity
|
|
112
|
+
if (wordCount > 30)
|
|
113
|
+
complexity = 'complex';
|
|
114
|
+
else if (wordCount > 15 && complexity === 'simple')
|
|
115
|
+
complexity = 'medium';
|
|
116
|
+
// Multiple files mentioned = more complex
|
|
117
|
+
const filePatterns = /\.(ts|js|py|rs|go|java|cpp|c|h|css|html|json|yaml|yml|md|txt|sql|sh|bash|zsh)/gi;
|
|
118
|
+
const fileMatches = lower.match(filePatterns);
|
|
119
|
+
if (fileMatches && fileMatches.length > 2) {
|
|
120
|
+
complexity = complexity === 'simple' ? 'medium' : 'complex';
|
|
121
|
+
}
|
|
122
|
+
// "and" chains indicate complexity
|
|
123
|
+
const andCount = (lower.match(/\band\b/g) || []).length;
|
|
124
|
+
if (andCount >= 2) {
|
|
125
|
+
complexity = complexity === 'simple' ? 'medium' : 'complex';
|
|
126
|
+
taskType = 'complex_task';
|
|
127
|
+
}
|
|
128
|
+
// Determine if plan is needed
|
|
129
|
+
const needsPlan = complexity === 'complex' || (complexity === 'medium' && wordCount > 20);
|
|
130
|
+
return {
|
|
131
|
+
type: taskType,
|
|
132
|
+
complexity,
|
|
133
|
+
needsPlan,
|
|
134
|
+
confidence: foundKeywords.length > 0 ? 0.85 : 0.7,
|
|
135
|
+
keywords: foundKeywords,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// RESPONSE PARSING
|
|
140
|
+
// ============================================================================
|
|
45
141
|
function extractTextFromResponse(response) {
|
|
46
142
|
if (typeof response === 'string') {
|
|
47
|
-
// Strip markdown code blocks
|
|
48
143
|
let text = response;
|
|
49
144
|
text = text.replace(/```json\n?/gi, '').replace(/```\n?/g, '');
|
|
50
|
-
// Try to parse as JSON and extract explanation
|
|
51
145
|
try {
|
|
52
146
|
const parsed = JSON.parse(text);
|
|
53
147
|
if (parsed.explanation)
|
|
@@ -56,14 +150,8 @@ function extractTextFromResponse(response) {
|
|
|
56
150
|
return parsed.output;
|
|
57
151
|
if (parsed.message)
|
|
58
152
|
return parsed.message;
|
|
59
|
-
if (parsed.summary)
|
|
60
|
-
return parsed.summary;
|
|
61
|
-
if (parsed.status === 'error' && parsed.explanation)
|
|
62
|
-
return parsed.explanation;
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// Not JSON, return as-is
|
|
66
153
|
}
|
|
154
|
+
catch { }
|
|
67
155
|
return text.trim();
|
|
68
156
|
}
|
|
69
157
|
if (typeof response === 'object' && response !== null) {
|
|
@@ -74,497 +162,304 @@ function extractTextFromResponse(response) {
|
|
|
74
162
|
return String(obj.output);
|
|
75
163
|
if (obj.message)
|
|
76
164
|
return String(obj.message);
|
|
77
|
-
if (obj.summary)
|
|
78
|
-
return String(obj.summary);
|
|
79
165
|
return JSON.stringify(response, null, 2);
|
|
80
166
|
}
|
|
81
167
|
return String(response);
|
|
82
168
|
}
|
|
83
|
-
// Try to parse file operations from response
|
|
84
169
|
function parseFileOperations(response) {
|
|
85
170
|
if (typeof response === 'string') {
|
|
86
171
|
let text = response.trim();
|
|
87
|
-
// Try to extract JSON from markdown code blocks (handle various formats)
|
|
88
172
|
const jsonMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
89
|
-
if (jsonMatch)
|
|
173
|
+
if (jsonMatch)
|
|
90
174
|
text = jsonMatch[1].trim();
|
|
91
|
-
}
|
|
92
|
-
// Also try to find raw JSON object
|
|
93
175
|
if (!text.startsWith('{')) {
|
|
94
176
|
const jsonStart = text.indexOf('{');
|
|
95
177
|
const jsonEnd = text.lastIndexOf('}');
|
|
96
|
-
if (jsonStart !== -1 && jsonEnd
|
|
178
|
+
if (jsonStart !== -1 && jsonEnd > jsonStart) {
|
|
97
179
|
text = text.substring(jsonStart, jsonEnd + 1);
|
|
98
180
|
}
|
|
99
181
|
}
|
|
100
182
|
try {
|
|
101
183
|
const parsed = JSON.parse(text);
|
|
102
|
-
if (parsed.files && Array.isArray(parsed.files))
|
|
184
|
+
if (parsed.files && Array.isArray(parsed.files))
|
|
103
185
|
return parsed;
|
|
104
|
-
|
|
105
|
-
if (parsed.status) {
|
|
186
|
+
if (parsed.status)
|
|
106
187
|
return parsed;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
catch (e) {
|
|
110
|
-
// JSON parse failed - try to be more lenient
|
|
111
|
-
// Sometimes the API returns malformed JSON with extra text
|
|
112
|
-
console.log((0, ui_1.dim)('Note: Could not parse response as JSON'));
|
|
113
188
|
}
|
|
189
|
+
catch { }
|
|
114
190
|
}
|
|
115
191
|
if (typeof response === 'object' && response !== null) {
|
|
116
192
|
const obj = response;
|
|
117
|
-
if (obj.files && Array.isArray(obj.files))
|
|
193
|
+
if (obj.files && Array.isArray(obj.files))
|
|
118
194
|
return obj;
|
|
119
|
-
|
|
120
|
-
if (obj.status) {
|
|
195
|
+
if (obj.status)
|
|
121
196
|
return obj;
|
|
122
|
-
}
|
|
123
197
|
}
|
|
124
198
|
return null;
|
|
125
199
|
}
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (shortGreetings.some(g => lower.startsWith(g))) {
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
const casualPatterns = [
|
|
137
|
-
/^h+i+!*$/i, // hi, hii, hiii, etc
|
|
138
|
-
/^he+y+!*$/i, // hey, heyy, heyyy
|
|
139
|
-
/^y+o+!*$/i, // yo, yoo, yooo
|
|
140
|
-
/^(hello|hola|howdy|greetings)[\s!.,]*$/i,
|
|
141
|
-
/^(good\s*(morning|afternoon|evening|night))[\s!.,]*$/i,
|
|
142
|
-
/^(what'?s?\s*up|how\s*are\s*you|how'?s?\s*it\s*going)[\s!?.,]*$/i,
|
|
143
|
-
/^(thanks|thank\s*you|thx|ty)[\s!.,]*$/i,
|
|
144
|
-
/^(bye|goodbye|see\s*ya|later|cya)[\s!.,]*$/i,
|
|
145
|
-
/^(ok|okay|sure|yes|no|yep|nope|yeah|nah)[\s!.,]*$/i,
|
|
146
|
-
/^(cool|nice|great|awesome|perfect|lol|lmao)[\s!.,]*$/i,
|
|
147
|
-
/^(sup|wassup|wazzup)[\s!.,]*$/i,
|
|
148
|
-
];
|
|
149
|
-
return casualPatterns.some(pattern => pattern.test(lower));
|
|
150
|
-
}
|
|
151
|
-
// Check if input is a short simple question
|
|
152
|
-
function isSimpleQuestion(input) {
|
|
153
|
-
const lower = input.toLowerCase().trim();
|
|
154
|
-
// Short questions that don't need code context
|
|
155
|
-
if (input.length < 50 && /\?$/.test(input)) {
|
|
156
|
-
return true;
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// EXECUTION HANDLERS
|
|
202
|
+
// ============================================================================
|
|
203
|
+
async function getApiKey() {
|
|
204
|
+
try {
|
|
205
|
+
return await (0, auth_1.ensureAuthenticated)();
|
|
157
206
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return
|
|
207
|
+
catch {
|
|
208
|
+
console.log((0, ui_1.error)('Authentication required. Run: zcode auth'));
|
|
209
|
+
return null;
|
|
161
210
|
}
|
|
162
|
-
return false;
|
|
163
211
|
}
|
|
164
|
-
//
|
|
165
|
-
function
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
// DEBUG
|
|
178
|
-
if (/(fix|debug|error|bug|issue|broken|failing|crash|exception|problem|wrong|doesn'?t work|not working)/i.test(lower)) {
|
|
179
|
-
return 'DEBUG';
|
|
180
|
-
}
|
|
181
|
-
// REFACTOR
|
|
182
|
-
if (/(refactor|rename|reorganize|restructure|extract|move|split|merge|consolidate|clean up|cleanup|simplify)/i.test(lower)) {
|
|
183
|
-
return 'REFACTOR';
|
|
184
|
-
}
|
|
185
|
-
// REVIEW
|
|
186
|
-
if (/(review|analyze|audit|check|inspect|examine|assess|evaluate|look at|understand|read|show me)/i.test(lower)) {
|
|
187
|
-
return 'REVIEW';
|
|
188
|
-
}
|
|
189
|
-
// CODE_EDIT - file operations
|
|
190
|
-
if (/(add|create|implement|update|change|modify|write|build|make|generate|new|feature|function|component|edit|insert|append|remove|delete|replace|file|folder|directory)/i.test(lower)) {
|
|
191
|
-
return 'CODE_EDIT';
|
|
212
|
+
// Handle casual chat
|
|
213
|
+
async function handleChat(input) {
|
|
214
|
+
const apiKey = await getApiKey();
|
|
215
|
+
if (!apiKey)
|
|
216
|
+
return;
|
|
217
|
+
const chatPrompt = `You are a friendly AI coding assistant. Respond naturally and briefly (1-2 sentences). Be warm and helpful. If the user wants to code, suggest they describe their task.`;
|
|
218
|
+
const result = await (0, runtime_1.execute)({
|
|
219
|
+
instruction: `${chatPrompt}\n\nUser: ${input}\n\nRespond briefly:`,
|
|
220
|
+
enforceSchema: false
|
|
221
|
+
}, apiKey);
|
|
222
|
+
if (result.success && result.output) {
|
|
223
|
+
console.log(extractTextFromResponse(result.output));
|
|
192
224
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return 'QUESTION';
|
|
225
|
+
else {
|
|
226
|
+
console.log("Hey! What would you like to build today?");
|
|
196
227
|
}
|
|
197
|
-
return 'CODE_EDIT';
|
|
198
228
|
}
|
|
199
|
-
//
|
|
200
|
-
function
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return 'ask_question';
|
|
213
|
-
}
|
|
214
|
-
// Auto mode - execute directly without manual steps
|
|
215
|
-
if (mode === 'auto') {
|
|
216
|
-
return 'auto_execute';
|
|
229
|
+
// Handle questions
|
|
230
|
+
async function handleQuestion(input) {
|
|
231
|
+
const apiKey = await getApiKey();
|
|
232
|
+
if (!apiKey)
|
|
233
|
+
return;
|
|
234
|
+
const session = (0, session_1.getSession)();
|
|
235
|
+
console.log((0, ui_1.dim)('Thinking...'));
|
|
236
|
+
const result = await (0, runtime_1.execute)({
|
|
237
|
+
instruction: `You are a helpful coding assistant. Answer this question clearly and concisely:\n\n${input}`,
|
|
238
|
+
enforceSchema: false
|
|
239
|
+
}, apiKey);
|
|
240
|
+
if (result.success && result.output) {
|
|
241
|
+
console.log(extractTextFromResponse(result.output));
|
|
217
242
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
return 'ask_question';
|
|
243
|
+
else {
|
|
244
|
+
console.log((0, ui_1.error)(result.error || 'Failed to get answer'));
|
|
221
245
|
}
|
|
222
|
-
return 'capture_intent';
|
|
223
246
|
}
|
|
224
|
-
// Handle
|
|
225
|
-
async function
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (!apiKey) {
|
|
236
|
-
console.log((0, ui_1.error)('No API key configured. Run "zcode auth" to set up.'));
|
|
237
|
-
return { handled: true };
|
|
238
|
-
}
|
|
239
|
-
const session = (0, session_1.getSession)();
|
|
240
|
-
const mode = (0, session_1.getMode)();
|
|
241
|
-
const modePrompt = (0, mode_prompts_1.buildSystemPrompt)(mode, session.workingDirectory);
|
|
242
|
-
const instruction = `${modePrompt}
|
|
247
|
+
// Handle simple edits - direct execution
|
|
248
|
+
async function handleSimpleEdit(input) {
|
|
249
|
+
const apiKey = await getApiKey();
|
|
250
|
+
if (!apiKey)
|
|
251
|
+
return;
|
|
252
|
+
const session = (0, session_1.getSession)();
|
|
253
|
+
console.log((0, ui_1.info)('⚡ Executing...'));
|
|
254
|
+
const { buildContext, formatContextForModel } = await Promise.resolve().then(() => __importStar(require('./context/context_builder')));
|
|
255
|
+
const context = buildContext(session.workingDirectory, input, 'CODE_EDIT', session.openFiles);
|
|
256
|
+
const filesContext = formatContextForModel(context);
|
|
257
|
+
const instruction = `You are an autonomous coding agent. Execute this task completely.
|
|
243
258
|
|
|
244
|
-
|
|
259
|
+
Task: ${input}
|
|
260
|
+
Working directory: ${session.workingDirectory}
|
|
261
|
+
${filesContext ? `\nRelevant files:\n${filesContext}` : ''}
|
|
245
262
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
263
|
+
RESPOND WITH JSON ONLY:
|
|
264
|
+
{
|
|
265
|
+
"status": "success",
|
|
266
|
+
"files": [{"path": "path/to/file", "operation": "create|modify|delete", "content": "full content"}],
|
|
267
|
+
"output": "Brief explanation"
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
RULES:
|
|
271
|
+
- Create folders by creating files inside them (folders auto-create)
|
|
272
|
+
- Provide COMPLETE file content, never partial
|
|
273
|
+
- Only modify files directly related to the task`;
|
|
274
|
+
const result = await (0, runtime_1.execute)({ instruction }, apiKey);
|
|
275
|
+
if (result.success && result.output) {
|
|
276
|
+
const response = parseFileOperations(result.output) ||
|
|
277
|
+
(typeof result.output === 'object' ? result.output : null);
|
|
278
|
+
if (response?.files?.length) {
|
|
279
|
+
await applyFiles(response.files, session.workingDirectory, input);
|
|
280
|
+
if (response.output)
|
|
281
|
+
console.log('\n' + response.output);
|
|
282
|
+
}
|
|
283
|
+
else if (response?.output) {
|
|
284
|
+
console.log(response.output);
|
|
252
285
|
}
|
|
253
286
|
else {
|
|
254
|
-
|
|
287
|
+
const text = extractTextFromResponse(result.output);
|
|
288
|
+
if (text && !text.startsWith('{'))
|
|
289
|
+
console.log(text);
|
|
255
290
|
}
|
|
256
|
-
return { handled: true };
|
|
257
291
|
}
|
|
258
|
-
|
|
259
|
-
console.log((0, ui_1.error)(
|
|
260
|
-
return { handled: true };
|
|
292
|
+
else {
|
|
293
|
+
console.log((0, ui_1.error)(result.error || 'Execution failed'));
|
|
261
294
|
}
|
|
262
295
|
}
|
|
263
|
-
// Handle
|
|
264
|
-
async function
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
try {
|
|
277
|
-
apiKey = await (0, auth_1.ensureAuthenticated)();
|
|
278
|
-
}
|
|
279
|
-
catch (authError) {
|
|
280
|
-
console.log((0, ui_1.error)(`Authentication required: ${authError?.message || 'Run zcode auth'}`));
|
|
281
|
-
return { handled: true };
|
|
282
|
-
}
|
|
283
|
-
if (!apiKey) {
|
|
284
|
-
console.log((0, ui_1.error)('No API key configured. Run "zcode auth" to set up.'));
|
|
285
|
-
return { handled: true };
|
|
286
|
-
}
|
|
287
|
-
const session = (0, session_1.getSession)();
|
|
288
|
-
const modePrompt = (0, mode_prompts_1.buildSystemPrompt)('auto', session.workingDirectory);
|
|
289
|
-
console.log((0, ui_1.info)('Executing autonomously...'));
|
|
290
|
-
// Build context from workspace for better results
|
|
291
|
-
const { buildContext, formatContextForModel } = await Promise.resolve().then(() => __importStar(require('./context/context_builder')));
|
|
292
|
-
const context = buildContext(session.workingDirectory, input, 'CODE_EDIT', session.openFiles.map(f => require('path').join(session.workingDirectory, f)));
|
|
293
|
-
const filesContext = formatContextForModel(context);
|
|
294
|
-
const instruction = `${modePrompt}
|
|
296
|
+
// Handle complex tasks - plan then execute
|
|
297
|
+
async function handleComplexTask(input) {
|
|
298
|
+
const apiKey = await getApiKey();
|
|
299
|
+
if (!apiKey)
|
|
300
|
+
return;
|
|
301
|
+
const session = (0, session_1.getSession)();
|
|
302
|
+
(0, session_1.setIntent)(input);
|
|
303
|
+
console.log((0, ui_1.info)('📋 Complex task detected - creating plan...'));
|
|
304
|
+
const { buildContext, formatContextForModel } = await Promise.resolve().then(() => __importStar(require('./context/context_builder')));
|
|
305
|
+
const context = buildContext(session.workingDirectory, input, 'CODE_EDIT', session.openFiles);
|
|
306
|
+
const filesContext = formatContextForModel(context);
|
|
307
|
+
// Step 1: Generate plan
|
|
308
|
+
const planInstruction = `Create a step-by-step plan for this task.
|
|
295
309
|
|
|
296
310
|
Task: ${input}
|
|
297
|
-
|
|
298
311
|
Working directory: ${session.workingDirectory}
|
|
312
|
+
${filesContext ? `\nFiles:\n${filesContext}` : ''}
|
|
313
|
+
|
|
314
|
+
Respond with JSON:
|
|
315
|
+
{
|
|
316
|
+
"status": "success",
|
|
317
|
+
"plan": [
|
|
318
|
+
{"id": "1", "description": "Step description", "files": ["affected/files"]}
|
|
319
|
+
],
|
|
320
|
+
"output": "Plan summary"
|
|
321
|
+
}`;
|
|
322
|
+
console.log((0, ui_1.dim)('Planning...'));
|
|
323
|
+
const planResult = await (0, runtime_1.execute)({ instruction: planInstruction }, apiKey);
|
|
324
|
+
if (!planResult.success) {
|
|
325
|
+
console.log((0, ui_1.error)('Planning failed: ' + planResult.error));
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const planResponse = parseFileOperations(planResult.output);
|
|
329
|
+
const plan = planResponse?.plan;
|
|
330
|
+
if (!plan || !Array.isArray(plan) || plan.length === 0) {
|
|
331
|
+
console.log((0, ui_1.dim)('No plan generated, executing directly...'));
|
|
332
|
+
return handleSimpleEdit(input);
|
|
333
|
+
}
|
|
334
|
+
// Show plan
|
|
335
|
+
console.log((0, ui_1.success)(`Plan: ${plan.length} steps`));
|
|
336
|
+
plan.forEach((step, i) => {
|
|
337
|
+
console.log(` ${i + 1}. ${step.description}`);
|
|
338
|
+
});
|
|
339
|
+
(0, session_1.setLastPlan)(plan.map((s) => ({ id: s.id, description: s.description, status: 'pending' })));
|
|
340
|
+
// Step 2: Execute plan
|
|
341
|
+
console.log('');
|
|
342
|
+
console.log((0, ui_1.dim)('Executing plan...'));
|
|
343
|
+
const executeInstruction = `Execute this plan and generate all file changes.
|
|
344
|
+
|
|
345
|
+
Task: ${input}
|
|
299
346
|
|
|
300
|
-
|
|
347
|
+
Plan:
|
|
348
|
+
${plan.map((s, i) => `${i + 1}. ${s.description}`).join('\n')}
|
|
301
349
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
2. To create a folder, create a file inside it (folders are created automatically)
|
|
305
|
-
3. NEVER just describe what you would do - actually provide the file operations
|
|
306
|
-
4. For "create folder X", respond with a file like "X/.gitkeep" or "X/README.md"
|
|
350
|
+
Working directory: ${session.workingDirectory}
|
|
351
|
+
${filesContext ? `\nFiles:\n${filesContext}` : ''}
|
|
307
352
|
|
|
308
|
-
|
|
353
|
+
Respond with JSON containing ALL file operations:
|
|
309
354
|
{
|
|
310
355
|
"status": "success",
|
|
311
|
-
"files": [
|
|
312
|
-
|
|
313
|
-
],
|
|
314
|
-
"output": "Brief explanation"
|
|
356
|
+
"files": [{"path": "path/to/file", "operation": "create|modify|delete", "content": "COMPLETE content"}],
|
|
357
|
+
"output": "Summary of changes"
|
|
315
358
|
}
|
|
316
359
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
Respond with JSON only. No markdown, no explanation outside the JSON.`;
|
|
324
|
-
const result = await (0, runtime_1.execute)({ instruction }, apiKey);
|
|
325
|
-
if (result.success && result.output) {
|
|
326
|
-
// Try to parse as ResponseSchema first
|
|
327
|
-
let response = parseFileOperations(result.output);
|
|
328
|
-
// If direct parsing failed, try the output object
|
|
329
|
-
if (!response && typeof result.output === 'object') {
|
|
330
|
-
response = result.output;
|
|
331
|
-
}
|
|
332
|
-
// If no files array, just show the output
|
|
333
|
-
if (!response || !response.files || response.files.length === 0) {
|
|
334
|
-
if (response && response.output) {
|
|
335
|
-
console.log(response.output);
|
|
336
|
-
}
|
|
337
|
-
else {
|
|
338
|
-
const text = extractTextFromResponse(result.output);
|
|
339
|
-
if (text && !text.startsWith('{')) {
|
|
340
|
-
console.log(text);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return { handled: true };
|
|
344
|
-
}
|
|
345
|
-
// Check if there are file operations
|
|
346
|
-
if (response && response.files && response.files.length > 0) {
|
|
347
|
-
// SAFETY: Filter out suspicious file operations
|
|
348
|
-
const safeFiles = response.files.filter(file => {
|
|
349
|
-
const filePath = file.path.toLowerCase();
|
|
350
|
-
// Block creating files in src/ unless task explicitly mentions it
|
|
351
|
-
if (filePath.startsWith('src/') && !input.toLowerCase().includes('src')) {
|
|
352
|
-
console.log((0, ui_1.dim)(` Skipped ${file.path} (not in task scope)`));
|
|
353
|
-
return false;
|
|
354
|
-
}
|
|
355
|
-
// Block creating files outside project
|
|
356
|
-
if (filePath.startsWith('/') || filePath.startsWith('..')) {
|
|
357
|
-
console.log((0, ui_1.dim)(` Skipped ${file.path} (outside project)`));
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
return true;
|
|
361
|
-
});
|
|
362
|
-
if (safeFiles.length > 0) {
|
|
363
|
-
console.log((0, ui_1.success)(`Applying ${safeFiles.length} file(s)...`));
|
|
364
|
-
let applied = 0;
|
|
365
|
-
let failed = 0;
|
|
366
|
-
for (const file of safeFiles) {
|
|
367
|
-
try {
|
|
368
|
-
// Normalize the path
|
|
369
|
-
let filePath = file.path;
|
|
370
|
-
if (filePath.startsWith('./')) {
|
|
371
|
-
filePath = filePath.substring(2);
|
|
372
|
-
}
|
|
373
|
-
const opResult = (0, apply_1.applyFileOperation)(file.operation, filePath, file.content, {
|
|
374
|
-
basePath: session.workingDirectory
|
|
375
|
-
});
|
|
376
|
-
if (opResult.success) {
|
|
377
|
-
console.log((0, ui_1.success)(` ${file.operation}: ${filePath}`));
|
|
378
|
-
applied++;
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
console.log((0, ui_1.error)(` Failed ${filePath}: ${opResult.error}`));
|
|
382
|
-
failed++;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
catch (e) {
|
|
386
|
-
console.log((0, ui_1.error)(` Failed ${file.path}: ${e?.message}`));
|
|
387
|
-
failed++;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
console.log('');
|
|
391
|
-
if (applied > 0) {
|
|
392
|
-
console.log((0, ui_1.success)(`Applied ${applied} file(s)`));
|
|
393
|
-
}
|
|
394
|
-
if (failed > 0) {
|
|
395
|
-
console.log((0, ui_1.error)(`Failed ${failed} file(s)`));
|
|
396
|
-
}
|
|
397
|
-
console.log((0, ui_1.hint)('/undo to rollback'));
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
// Show output/explanation
|
|
401
|
-
if (response && response.output) {
|
|
402
|
-
console.log('');
|
|
403
|
-
console.log(response.output);
|
|
404
|
-
}
|
|
405
|
-
else if (response && response.error) {
|
|
406
|
-
console.log((0, ui_1.error)(response.error));
|
|
407
|
-
}
|
|
408
|
-
else if (!response) {
|
|
409
|
-
// No structured response - try to show something useful
|
|
410
|
-
const text = extractTextFromResponse(result.output);
|
|
411
|
-
// Don't dump raw JSON to the user
|
|
412
|
-
if (text && !text.startsWith('{') && !text.startsWith('[')) {
|
|
413
|
-
console.log(text);
|
|
414
|
-
}
|
|
415
|
-
else if (text) {
|
|
416
|
-
// It's JSON that we couldn't parse - show a friendly message
|
|
417
|
-
console.log((0, ui_1.dim)('Response received but could not be processed.'));
|
|
418
|
-
console.log((0, ui_1.dim)('Try rephrasing your request or use /do for step-by-step mode.'));
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
else {
|
|
423
|
-
console.log((0, ui_1.error)(`Failed: ${result.error || 'Unknown error'}`));
|
|
424
|
-
}
|
|
425
|
-
return { handled: true };
|
|
360
|
+
IMPORTANT: Include COMPLETE file content for every file. Never use placeholders.`;
|
|
361
|
+
const execResult = await (0, runtime_1.execute)({ instruction: executeInstruction }, apiKey);
|
|
362
|
+
if (!execResult.success) {
|
|
363
|
+
console.log((0, ui_1.error)('Execution failed: ' + execResult.error));
|
|
364
|
+
return;
|
|
426
365
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
366
|
+
const execResponse = parseFileOperations(execResult.output) ||
|
|
367
|
+
(typeof execResult.output === 'object' ? execResult.output : null);
|
|
368
|
+
if (execResponse?.files?.length) {
|
|
369
|
+
await applyFiles(execResponse.files, session.workingDirectory, input);
|
|
370
|
+
if (execResponse.output)
|
|
371
|
+
console.log('\n' + execResponse.output);
|
|
430
372
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
await (0, commands_1.executeCommand)(parsed);
|
|
437
|
-
return { handled: true };
|
|
438
|
-
case 'chat':
|
|
439
|
-
return handleChat(input);
|
|
440
|
-
case 'ask_question':
|
|
441
|
-
return handleAskQuestion(input);
|
|
442
|
-
case 'auto_execute':
|
|
443
|
-
return handleAutoExecute(input);
|
|
444
|
-
case 'capture_intent':
|
|
445
|
-
(0, session_1.setIntent)(input);
|
|
446
|
-
(0, session_1.setIntentType)(intent);
|
|
447
|
-
// Provide clear feedback about what was captured
|
|
448
|
-
const intentLabel = intent.toLowerCase().replace('_', ' ');
|
|
449
|
-
console.log(`Task captured: "${input.substring(0, 60)}${input.length > 60 ? '...' : ''}"`);
|
|
450
|
-
console.log(`Type: ${intentLabel}`);
|
|
451
|
-
console.log('');
|
|
452
|
-
console.log((0, ui_1.hint)('Type /plan to create execution plan'));
|
|
453
|
-
return { handled: true };
|
|
454
|
-
case 'append_context':
|
|
455
|
-
const existing = (0, session_1.getIntent)();
|
|
456
|
-
if (existing) {
|
|
457
|
-
(0, session_1.setIntent)(`${existing}\n\nClarification: ${input}`);
|
|
458
|
-
console.log((0, ui_1.dim)('Context updated.'));
|
|
459
|
-
console.log((0, ui_1.hint)('/plan'));
|
|
460
|
-
return { handled: true };
|
|
461
|
-
}
|
|
462
|
-
(0, session_1.setIntent)(input);
|
|
463
|
-
console.log((0, ui_1.dim)('Intent captured.'));
|
|
464
|
-
console.log((0, ui_1.hint)('/plan'));
|
|
465
|
-
return { handled: true };
|
|
466
|
-
case 'confirm_action':
|
|
467
|
-
const session = (0, session_1.getSession)();
|
|
468
|
-
if (session.pendingActions) {
|
|
469
|
-
console.log((0, ui_1.hint)('/diff or /apply'));
|
|
470
|
-
return { handled: true };
|
|
471
|
-
}
|
|
472
|
-
console.log((0, ui_1.dim)('Nothing pending.'));
|
|
473
|
-
return { handled: true };
|
|
474
|
-
case 'ignore':
|
|
475
|
-
return { handled: true };
|
|
476
|
-
default:
|
|
477
|
-
return { handled: false };
|
|
373
|
+
else if (execResponse?.output) {
|
|
374
|
+
console.log(execResponse.output);
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
console.log((0, ui_1.dim)('No file changes generated.'));
|
|
478
378
|
}
|
|
479
379
|
}
|
|
480
|
-
//
|
|
481
|
-
async function
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
console.log((0, ui_1.
|
|
489
|
-
return
|
|
380
|
+
// Apply files helper
|
|
381
|
+
async function applyFiles(files, basePath, input) {
|
|
382
|
+
if (!files || files.length === 0)
|
|
383
|
+
return;
|
|
384
|
+
// Safety filter
|
|
385
|
+
const safeFiles = files.filter(file => {
|
|
386
|
+
const p = file.path.toLowerCase();
|
|
387
|
+
if (p.startsWith('src/') && !input.toLowerCase().includes('src')) {
|
|
388
|
+
console.log((0, ui_1.dim)(` Skipped ${file.path} (not in task scope)`));
|
|
389
|
+
return false;
|
|
490
390
|
}
|
|
491
|
-
if (
|
|
492
|
-
console.log((0, ui_1.
|
|
493
|
-
return
|
|
391
|
+
if (p.startsWith('/') || p.startsWith('..')) {
|
|
392
|
+
console.log((0, ui_1.dim)(` Skipped ${file.path} (outside project)`));
|
|
393
|
+
return false;
|
|
494
394
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
395
|
+
return true;
|
|
396
|
+
});
|
|
397
|
+
if (safeFiles.length === 0)
|
|
398
|
+
return;
|
|
399
|
+
console.log((0, ui_1.success)(`Applying ${safeFiles.length} file(s)...`));
|
|
400
|
+
let applied = 0, failed = 0;
|
|
401
|
+
for (const file of safeFiles) {
|
|
402
|
+
try {
|
|
403
|
+
let filePath = file.path.startsWith('./') ? file.path.substring(2) : file.path;
|
|
404
|
+
const result = (0, apply_1.applyFileOperation)(file.operation, filePath, file.content, { basePath });
|
|
405
|
+
if (result.success) {
|
|
406
|
+
console.log((0, ui_1.success)(` ✓ ${file.operation}: ${filePath}`));
|
|
407
|
+
applied++;
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
console.log((0, ui_1.error)(` ✗ ${filePath}: ${result.error}`));
|
|
411
|
+
failed++;
|
|
412
|
+
}
|
|
509
413
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
'hi': 'Hey! What would you like to build today?',
|
|
514
|
-
'hello': 'Hello! Ready to code something awesome?',
|
|
515
|
-
'hey': 'Hey there! What can I help you with?',
|
|
516
|
-
'yo': 'Yo! What are we building?',
|
|
517
|
-
'sup': 'Not much! What are you working on?',
|
|
518
|
-
'thanks': 'You\'re welcome! Need anything else?',
|
|
519
|
-
'thank you': 'Happy to help! What\'s next?',
|
|
520
|
-
'bye': 'See you! Happy coding!',
|
|
521
|
-
'ok': 'Great! Let me know if you need anything.',
|
|
522
|
-
'cool': 'Awesome! What\'s next?',
|
|
523
|
-
};
|
|
524
|
-
const lower = input.toLowerCase().trim().replace(/[!.,?]/g, '');
|
|
525
|
-
const response = greetings[lower] || 'Hey! Describe what you\'d like to build.';
|
|
526
|
-
console.log(response);
|
|
414
|
+
catch (e) {
|
|
415
|
+
console.log((0, ui_1.error)(` ✗ ${file.path}: ${e?.message}`));
|
|
416
|
+
failed++;
|
|
527
417
|
}
|
|
528
|
-
return { handled: true };
|
|
529
|
-
}
|
|
530
|
-
catch (e) {
|
|
531
|
-
// Fallback on any error
|
|
532
|
-
console.log('Hey! What would you like to build today?');
|
|
533
|
-
return { handled: true };
|
|
534
418
|
}
|
|
419
|
+
console.log('');
|
|
420
|
+
if (applied > 0)
|
|
421
|
+
console.log((0, ui_1.success)(`Applied ${applied} file(s)`));
|
|
422
|
+
if (failed > 0)
|
|
423
|
+
console.log((0, ui_1.error)(`Failed ${failed} file(s)`));
|
|
424
|
+
console.log((0, ui_1.hint)('/undo to rollback'));
|
|
535
425
|
}
|
|
536
|
-
// Main orchestration entry
|
|
537
426
|
async function orchestrate(input) {
|
|
538
427
|
const trimmed = input.trim();
|
|
539
428
|
if (!trimmed) {
|
|
540
|
-
return {
|
|
541
|
-
inputType: 'free_text',
|
|
542
|
-
intent: 'COMMAND',
|
|
543
|
-
workflow: 'ignore',
|
|
544
|
-
handled: true,
|
|
545
|
-
};
|
|
429
|
+
return { inputType: 'free_text', intent: 'COMMAND', workflow: 'ignore', handled: true };
|
|
546
430
|
}
|
|
431
|
+
// Handle slash commands
|
|
547
432
|
const parsed = (0, commands_1.parseInput)(trimmed);
|
|
548
|
-
// Slash commands
|
|
549
433
|
if (parsed.isSlashCommand) {
|
|
550
434
|
await (0, commands_1.executeCommand)(parsed);
|
|
551
|
-
return {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
435
|
+
return { inputType: 'slash', intent: 'COMMAND', workflow: 'slash_command', handled: true };
|
|
436
|
+
}
|
|
437
|
+
// Analyze the task
|
|
438
|
+
const analysis = analyzeTask(trimmed);
|
|
439
|
+
// Route based on analysis
|
|
440
|
+
switch (analysis.type) {
|
|
441
|
+
case 'chat':
|
|
442
|
+
await handleChat(trimmed);
|
|
443
|
+
return { inputType: 'free_text', intent: 'QUESTION', workflow: 'chat', handled: true };
|
|
444
|
+
case 'question':
|
|
445
|
+
await handleQuestion(trimmed);
|
|
446
|
+
return { inputType: 'free_text', intent: 'QUESTION', workflow: 'question', handled: true };
|
|
447
|
+
case 'debug':
|
|
448
|
+
case 'refactor':
|
|
449
|
+
case 'simple_edit':
|
|
450
|
+
if (analysis.needsPlan) {
|
|
451
|
+
await handleComplexTask(trimmed);
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
await handleSimpleEdit(trimmed);
|
|
455
|
+
}
|
|
456
|
+
return { inputType: 'free_text', intent: 'CODE_EDIT', workflow: 'auto_execute', handled: true };
|
|
457
|
+
case 'complex_task':
|
|
458
|
+
await handleComplexTask(trimmed);
|
|
459
|
+
return { inputType: 'free_text', intent: 'CODE_EDIT', workflow: 'planned_execute', handled: true };
|
|
460
|
+
default:
|
|
461
|
+
await handleSimpleEdit(trimmed);
|
|
462
|
+
return { inputType: 'free_text', intent: 'CODE_EDIT', workflow: 'auto_execute', handled: true };
|
|
557
463
|
}
|
|
558
|
-
// Free text - classify and route
|
|
559
|
-
const intent = classifyIntent(trimmed);
|
|
560
|
-
const workflow = determineWorkflow(intent, trimmed);
|
|
561
|
-
const result = await handleWorkflow(workflow, trimmed, parsed, intent);
|
|
562
|
-
return {
|
|
563
|
-
inputType: 'free_text',
|
|
564
|
-
intent,
|
|
565
|
-
workflow,
|
|
566
|
-
handled: result.handled,
|
|
567
|
-
message: result.message,
|
|
568
|
-
};
|
|
569
464
|
}
|
|
570
465
|
//# sourceMappingURL=orchestrator.js.map
|