@sylix/coworker 1.4.2 → 1.5.0
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/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +56 -12
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/slash/branch.d.ts +3 -0
- package/dist/commands/slash/branch.d.ts.map +1 -0
- package/dist/commands/slash/branch.js +45 -0
- package/dist/commands/slash/branch.js.map +1 -0
- package/dist/commands/slash/config.d.ts.map +1 -1
- package/dist/commands/slash/config.js +43 -0
- package/dist/commands/slash/config.js.map +1 -1
- package/dist/commands/slash/core.d.ts.map +1 -1
- package/dist/commands/slash/core.js +36 -6
- package/dist/commands/slash/core.js.map +1 -1
- package/dist/commands/slash/registry.d.ts.map +1 -1
- package/dist/commands/slash/registry.js +2 -0
- package/dist/commands/slash/registry.js.map +1 -1
- package/dist/core/CoWorkerAgent.d.ts +2 -21
- package/dist/core/CoWorkerAgent.d.ts.map +1 -1
- package/dist/core/CoWorkerAgent.js +149 -297
- package/dist/core/CoWorkerAgent.js.map +1 -1
- package/dist/core/ContextReader.d.ts +10 -0
- package/dist/core/ContextReader.d.ts.map +1 -0
- package/dist/core/ContextReader.js +122 -0
- package/dist/core/ContextReader.js.map +1 -0
- package/dist/session/SessionManager.d.ts +5 -0
- package/dist/session/SessionManager.d.ts.map +1 -1
- package/dist/session/SessionManager.js +3 -4
- package/dist/session/SessionManager.js.map +1 -1
- package/dist/utils/inputbar.d.ts.map +1 -1
- package/dist/utils/inputbar.js +2 -1
- package/dist/utils/inputbar.js.map +1 -1
- package/dist/utils/output.d.ts +2 -1
- package/dist/utils/output.d.ts.map +1 -1
- package/dist/utils/output.js +5 -1
- package/dist/utils/output.js.map +1 -1
- package/dist/utils/string.d.ts +6 -0
- package/dist/utils/string.d.ts.map +1 -0
- package/dist/utils/string.js +18 -0
- package/dist/utils/string.js.map +1 -0
- package/dist/utils/string.test.d.ts +2 -0
- package/dist/utils/string.test.d.ts.map +1 -0
- package/dist/utils/string.test.js +19 -0
- package/dist/utils/string.test.js.map +1 -0
- package/dist/utils/welcome.d.ts.map +1 -1
- package/dist/utils/welcome.js +2 -0
- package/dist/utils/welcome.js.map +1 -1
- package/package.json +4 -3
|
@@ -52,94 +52,17 @@ const fs = __importStar(require("fs"));
|
|
|
52
52
|
const path = __importStar(require("path"));
|
|
53
53
|
const os = __importStar(require("os"));
|
|
54
54
|
const child_process_1 = require("child_process");
|
|
55
|
+
const ContextReader_1 = require("./ContextReader");
|
|
55
56
|
const MAX_TOOL_TURNS = 25;
|
|
56
|
-
// Resolve the agents directory relative to the project root
|
|
57
|
-
function resolveAgentsDir() {
|
|
58
|
-
const candidates = [
|
|
59
|
-
path.join(process.cwd(), 'agents/system-prompts-and-models-of-ai-tools/Anthropic/Claude Code'),
|
|
60
|
-
path.join(__dirname, '../../agents/system-prompts-and-models-of-ai-tools/Anthropic/Claude Code'),
|
|
61
|
-
path.join(__dirname, '../../../agents/system-prompts-and-models-of-ai-tools/Anthropic/Claude Code'),
|
|
62
|
-
];
|
|
63
|
-
for (const candidate of candidates) {
|
|
64
|
-
if (fs.existsSync(candidate))
|
|
65
|
-
return candidate;
|
|
66
|
-
}
|
|
67
|
-
return candidates[0];
|
|
68
|
-
}
|
|
69
|
-
const FALLBACK_SYSTEM_PROMPT = `You are CoWorker, an autonomous AI coding agent by Sylix.
|
|
70
|
-
You help developers write, debug, and understand code.
|
|
71
|
-
You have access to tools to search files, read the web, and write code on the user's local machine.
|
|
72
|
-
Always be concise, direct, and helpful. When creating files, use the executeWrite tool.
|
|
73
|
-
When searching for files, use executeGlob. When searching inside files, use executeGrep.
|
|
74
|
-
Never refuse a reasonable coding request. Execute tools to accomplish the task.`;
|
|
75
|
-
function getGitContext(cwd) {
|
|
76
|
-
try {
|
|
77
|
-
const isRepo = (0, child_process_1.execSync)('git rev-parse --is-inside-work-tree', { cwd, encoding: 'utf8', stdio: 'ignore' });
|
|
78
|
-
const branch = (0, child_process_1.execSync)('git branch --show-current', { cwd, encoding: 'utf8' }).trim();
|
|
79
|
-
const status = (0, child_process_1.execSync)('git status --short', { cwd, encoding: 'utf8' }).trim() || '(clean)';
|
|
80
|
-
const commits = (0, child_process_1.execSync)('git log -n 5 --oneline', { cwd, encoding: 'utf8' }).trim();
|
|
81
|
-
return { isRepo: true, branch, status, commits };
|
|
82
|
-
}
|
|
83
|
-
catch (err) {
|
|
84
|
-
return { isRepo: false, branch: 'unknown', status: 'unknown', commits: 'No git info provided.' };
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
async function getSystemPrompt(cwd) {
|
|
88
|
-
const agentsDir = resolveAgentsDir();
|
|
89
|
-
const promptPath = path.join(agentsDir, 'Prompt.txt');
|
|
90
|
-
let promptText = FALLBACK_SYSTEM_PROMPT;
|
|
91
|
-
try {
|
|
92
|
-
promptText = fs.readFileSync(promptPath, 'utf8');
|
|
93
|
-
// Global branding override — catch all variants
|
|
94
|
-
promptText = promptText
|
|
95
|
-
.replace(/Claude Code/gi, 'CoWorker')
|
|
96
|
-
.replace(/Claude/gi, 'CoWorker')
|
|
97
|
-
.replace(/Anthropic/gi, 'Sylix');
|
|
98
|
-
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
return FALLBACK_SYSTEM_PROMPT;
|
|
101
|
-
}
|
|
102
|
-
// Hydrate environment variables
|
|
103
|
-
const gitContext = getGitContext(cwd);
|
|
104
|
-
const today = new Date().toISOString().split('T')[0];
|
|
105
|
-
promptText = promptText
|
|
106
|
-
.replace('${Working directory}', cwd)
|
|
107
|
-
.replace('Is directory a git repo: Yes', `Is directory a git repo: ${gitContext.isRepo ? 'Yes' : 'No'}`)
|
|
108
|
-
.replace('Platform: darwin', `Platform: ${os.platform()}`)
|
|
109
|
-
.replace(/OS Version: [^\n]+/, `OS Version: ${os.type()} ${os.release()}`)
|
|
110
|
-
.replace(/Today's date: [^\n]+/, `Today's date: ${today}`)
|
|
111
|
-
.replace(/Current branch: [^\n]+/, `Current branch: ${gitContext.branch}`)
|
|
112
|
-
.replace(/\(clean\)/, gitContext.status)
|
|
113
|
-
.replace('${Last 5 Recent commits}', gitContext.commits);
|
|
114
|
-
// Project memory support (.coworker.md / COWORKER.md)
|
|
115
|
-
let projectMemory = '';
|
|
116
|
-
const memoryFiles = ['.coworker.md', 'COWORKER.md'];
|
|
117
|
-
for (const file of memoryFiles) {
|
|
118
|
-
const memPath = path.join(cwd, file);
|
|
119
|
-
if (fs.existsSync(memPath)) {
|
|
120
|
-
try {
|
|
121
|
-
projectMemory = `\n\nPROJECT CONTEXT (from ${file}):\n${fs.readFileSync(memPath, 'utf8')}`;
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
catch { }
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return promptText + projectMemory;
|
|
128
|
-
}
|
|
129
|
-
async function getSkillsPromptFragment(hooks) {
|
|
130
|
-
return hooks.getSkillsSummary();
|
|
131
|
-
}
|
|
132
57
|
/**
|
|
133
|
-
* THE SDK LAYER — Real Agentic Tool Loop.
|
|
134
|
-
* Calls the LLM, parses tool_calls, executes them locally via NativeTools,
|
|
135
|
-
* feeds results back, and loops until the LLM produces a final text response.
|
|
58
|
+
* THE SDK LAYER — Real Agentic Tool Loop for CoWorker v1.5.0.
|
|
136
59
|
*/
|
|
137
60
|
class CoWorkerAgent {
|
|
138
61
|
constructor(options) {
|
|
139
|
-
this.systemPrompt =
|
|
62
|
+
this.systemPrompt = '';
|
|
140
63
|
this.model = options.model || 'helix-1.2';
|
|
141
64
|
this.endpoint = options.endpoint || 'https://ollama-ubuntu.sylixide.com';
|
|
142
|
-
this.ollamaModel = 'glm-4.7-flash:q4_K_M';
|
|
65
|
+
this.ollamaModel = 'glm-4.7-flash:q4_K_M';
|
|
143
66
|
this.cwd = options.cwd || process.cwd();
|
|
144
67
|
this.tasks = new TaskEngine_1.TaskEngine();
|
|
145
68
|
this.sessions = new SessionManager_1.SessionManager((0, crypto_1.randomUUID)());
|
|
@@ -150,28 +73,24 @@ class CoWorkerAgent {
|
|
|
150
73
|
}
|
|
151
74
|
async boot() {
|
|
152
75
|
await this.hooks.loadSkillsFromDirectory();
|
|
153
|
-
|
|
76
|
+
const context = await ContextReader_1.ContextReader.getProjectContext(this.cwd);
|
|
77
|
+
this.systemPrompt = await getSystemPrompt(this.cwd, context);
|
|
154
78
|
this.systemPrompt += await getSkillsPromptFragment(this.hooks);
|
|
155
|
-
// Load existing session if applicable
|
|
156
79
|
await this.sessions.loadCheckpoint();
|
|
157
80
|
const config = await this.settings.loadSettings();
|
|
158
81
|
this.permissions = new PermissionInterceptor_1.PermissionInterceptor({
|
|
159
82
|
allowedTools: config.allow,
|
|
160
83
|
deniedTools: config.deny
|
|
161
84
|
});
|
|
162
|
-
await this.hooks.loadSkillsFromDirectory();
|
|
163
85
|
for (const [name, server] of Object.entries(config.mcpServers)) {
|
|
164
86
|
await this.mcp.connectServer(name, server.command, server.args);
|
|
165
87
|
}
|
|
166
88
|
}
|
|
167
|
-
/**
|
|
168
|
-
* The real agentic loop. Streams LLM responses in real-time.
|
|
169
|
-
* Uses a single streaming call that detects both text and tool_calls
|
|
170
|
-
* from the same SSE stream — no double-request pattern.
|
|
171
|
-
*/
|
|
172
89
|
async *chat(prompt) {
|
|
173
90
|
await this.sessions.rollupMemory();
|
|
174
|
-
|
|
91
|
+
const liveContext = await ContextReader_1.ContextReader.getProjectContext(this.cwd);
|
|
92
|
+
const enrichedPrompt = this.enrichMessage(prompt, this.sessions.state.messages);
|
|
93
|
+
let fullSystemPrompt = this.systemPrompt + `\n\n## LIVE CONTEXT (Current State)\n${liveContext}`;
|
|
175
94
|
if (this.hooks.activeSkill) {
|
|
176
95
|
const skillPrompt = this.hooks.getSkillPrompt(this.hooks.activeSkill);
|
|
177
96
|
if (skillPrompt)
|
|
@@ -179,26 +98,21 @@ class CoWorkerAgent {
|
|
|
179
98
|
}
|
|
180
99
|
const messages = [
|
|
181
100
|
{ role: 'system', content: fullSystemPrompt },
|
|
182
|
-
...this.sessions.state.messages
|
|
101
|
+
...this.sessions.state.messages,
|
|
102
|
+
{ role: 'user', content: enrichedPrompt }
|
|
183
103
|
];
|
|
184
|
-
|
|
185
|
-
messages.push(userMsg);
|
|
186
|
-
await this.sessions.appendMessage(userMsg);
|
|
104
|
+
await this.sessions.appendMessage({ role: 'user', content: prompt });
|
|
187
105
|
let turnCount = 0;
|
|
188
106
|
while (turnCount < MAX_TOOL_TURNS) {
|
|
189
107
|
turnCount++;
|
|
190
|
-
// Single streaming call — yields text AND detects tool_calls
|
|
191
108
|
const collected = { content: '', tool_calls: [] };
|
|
192
109
|
yield* this.callLLMStreamUnified(messages, collected);
|
|
193
|
-
// If no tool_calls, we're done — text was already streamed
|
|
194
110
|
if (!collected.tool_calls || collected.tool_calls.length === 0) {
|
|
195
|
-
// Save the streamed content to session
|
|
196
111
|
if (collected.content) {
|
|
197
112
|
await this.sessions.appendMessage({ role: 'assistant', content: collected.content });
|
|
198
113
|
}
|
|
199
114
|
break;
|
|
200
115
|
}
|
|
201
|
-
// Has tool calls — content was already streamed, now execute tools
|
|
202
116
|
const assistantMsg = {
|
|
203
117
|
role: 'assistant',
|
|
204
118
|
content: collected.content || '',
|
|
@@ -230,15 +144,7 @@ class CoWorkerAgent {
|
|
|
230
144
|
}
|
|
231
145
|
}
|
|
232
146
|
await this.hooks.firePreToolHook(toolName, args);
|
|
233
|
-
|
|
234
|
-
let toolTitle = `\n ◈ ${output_1.theme.brand(toolName)}`;
|
|
235
|
-
if (args.path || args.dir || args.filePath) {
|
|
236
|
-
toolTitle += output_1.theme.dim(` \u00b7 ${args.path || args.dir || args.filePath}`);
|
|
237
|
-
}
|
|
238
|
-
else if (args.pattern) {
|
|
239
|
-
toolTitle += output_1.theme.dim(` \u00b7 ${args.pattern}`);
|
|
240
|
-
}
|
|
241
|
-
yield toolTitle + '\n';
|
|
147
|
+
yield `\n ◈ ${output_1.theme.brand(toolName)}` + (args.path || args.dir || args.filePath || args.pattern ? output_1.theme.dim(` \u00b7 ${args.path || args.dir || args.filePath || args.pattern}`) : '') + '\n';
|
|
242
148
|
let result;
|
|
243
149
|
try {
|
|
244
150
|
result = await this.executeToolLocally(toolName, args);
|
|
@@ -250,117 +156,66 @@ class CoWorkerAgent {
|
|
|
250
156
|
const toolMsg = { role: 'tool', content: result, tool_call_id: toolId };
|
|
251
157
|
messages.push(toolMsg);
|
|
252
158
|
await this.sessions.appendMessage(toolMsg);
|
|
253
|
-
// Inline error detection
|
|
254
|
-
if (this.hooks.lastDiagnostic?.hasErrors) {
|
|
255
|
-
const diag = this.hooks.lastDiagnostic;
|
|
256
|
-
const errorMsg = {
|
|
257
|
-
role: 'system',
|
|
258
|
-
content: `[DIAGNOSTICS] After your ${toolName} on ${diag.filePath}, errors detected by ${diag.tool}:\n${diag.errors}\n\nYou MUST fix these errors before proceeding.`
|
|
259
|
-
};
|
|
260
|
-
messages.push(errorMsg);
|
|
261
|
-
await this.sessions.appendMessage(errorMsg);
|
|
262
|
-
yield ` ✗ ${output_1.theme.error(`[${diag.tool}]`)} ${output_1.theme.dim('errors detected \u2014 agent will self-correct')}\n`;
|
|
263
|
-
}
|
|
264
|
-
// ── TOOL SUMMARIZATION ──
|
|
265
159
|
const resultLines = result.split('\n').filter(l => l.trim());
|
|
266
|
-
if (
|
|
267
|
-
const
|
|
268
|
-
yield ` ${output_1.theme.dim('\u2514')} ${output_1.theme.dim(`${count} items found`)}\n`;
|
|
269
|
-
}
|
|
270
|
-
else if (toolName === 'executeGlob') {
|
|
271
|
-
const count = resultLines.length;
|
|
272
|
-
yield ` ${output_1.theme.dim('\u2514')} ${output_1.theme.dim(`${count} files matched`)}\n`;
|
|
273
|
-
}
|
|
274
|
-
else if (result.includes('[ToolError]') || result.includes('\u2717')) {
|
|
275
|
-
yield ` ${output_1.theme.dim('\u2514')} ${output_1.theme.error(result.split('\n')[0].substring(0, 80))}\n`;
|
|
276
|
-
}
|
|
277
|
-
else if (resultLines.length <= 2) {
|
|
278
|
-
for (const line of resultLines) {
|
|
160
|
+
if (resultLines.length <= 2) {
|
|
161
|
+
for (const line of resultLines)
|
|
279
162
|
yield ` ${output_1.theme.dim('\u2514')} ${output_1.theme.dim(line.substring(0, 80))}\n`;
|
|
280
|
-
}
|
|
281
163
|
}
|
|
282
164
|
else {
|
|
283
|
-
|
|
284
|
-
for (const line of resultLines.slice(0, 2)) {
|
|
165
|
+
for (const line of resultLines.slice(0, 2))
|
|
285
166
|
yield ` ${output_1.theme.dim('\u2514')} ${output_1.theme.dim(line.substring(0, 80))}\n`;
|
|
286
|
-
}
|
|
287
167
|
yield ` ${output_1.theme.dim(`...and ${resultLines.length - 2} more lines`)}\n`;
|
|
288
168
|
}
|
|
289
169
|
}
|
|
290
170
|
}
|
|
291
|
-
if (turnCount >= MAX_TOOL_TURNS) {
|
|
292
|
-
yield `\n \u2717 Reached maximum tool turns (${MAX_TOOL_TURNS}). Stopping.\n`;
|
|
293
|
-
}
|
|
294
171
|
}
|
|
295
|
-
/**
|
|
296
|
-
* UNIFIED STREAMING — yields text chunks as they arrive AND collects tool_calls.
|
|
297
|
-
* The `collected` object is populated with accumulated content and tool_calls
|
|
298
|
-
* so the caller can inspect them after the stream completes.
|
|
299
|
-
*/
|
|
300
172
|
async *callLLMStreamUnified(messages, collected) {
|
|
301
173
|
const url = `${this.endpoint}/v1/chat/completions`;
|
|
302
|
-
const body = {
|
|
303
|
-
model: this.ollamaModel,
|
|
304
|
-
messages,
|
|
305
|
-
tools: Schemas_1.CoWorkerToolSchemas,
|
|
306
|
-
tool_choice: 'auto',
|
|
307
|
-
stream: true,
|
|
308
|
-
options: { num_ctx: 32768, num_predict: 4096, temperature: 0.7 }
|
|
309
|
-
};
|
|
310
174
|
const res = await (0, node_fetch_1.default)(url, {
|
|
311
175
|
method: 'POST',
|
|
312
176
|
headers: { 'Content-Type': 'application/json' },
|
|
313
|
-
body: JSON.stringify(
|
|
177
|
+
body: JSON.stringify({
|
|
178
|
+
model: this.ollamaModel,
|
|
179
|
+
messages,
|
|
180
|
+
tools: Schemas_1.CoWorkerToolSchemas,
|
|
181
|
+
tool_choice: 'auto',
|
|
182
|
+
stream: true,
|
|
183
|
+
options: { num_ctx: 32768, num_predict: 4096, temperature: 0.7 }
|
|
184
|
+
})
|
|
314
185
|
});
|
|
315
|
-
if (!res.ok)
|
|
316
|
-
|
|
317
|
-
throw new Error(`LLM call failed (${res.status}): ${errText}`);
|
|
318
|
-
}
|
|
186
|
+
if (!res.ok)
|
|
187
|
+
throw new Error(`LLM call failed (${res.status}): ${await res.text()}`);
|
|
319
188
|
const nodeStream = res.body;
|
|
320
189
|
if (!nodeStream)
|
|
321
190
|
throw new Error('No response body');
|
|
322
191
|
let buffer = '';
|
|
323
|
-
// Track tool call assembly from streaming deltas
|
|
324
192
|
const toolCallMap = new Map();
|
|
325
|
-
// Read the SSE stream chunk by chunk
|
|
326
193
|
for await (const rawChunk of nodeStream) {
|
|
327
194
|
buffer += rawChunk.toString();
|
|
328
|
-
// Process complete SSE lines
|
|
329
195
|
const lines = buffer.split('\n');
|
|
330
|
-
buffer = lines.pop() || '';
|
|
196
|
+
buffer = lines.pop() || '';
|
|
331
197
|
for (const line of lines) {
|
|
332
|
-
|
|
333
|
-
if (!trimmed || !trimmed.startsWith('data: '))
|
|
198
|
+
if (!line.trim().startsWith('data: '))
|
|
334
199
|
continue;
|
|
335
|
-
const data =
|
|
200
|
+
const data = line.trim().slice(6).trim();
|
|
336
201
|
if (data === '[DONE]') {
|
|
337
|
-
|
|
338
|
-
if (toolCallMap.size > 0) {
|
|
202
|
+
if (toolCallMap.size > 0)
|
|
339
203
|
collected.tool_calls = Array.from(toolCallMap.values());
|
|
340
|
-
}
|
|
341
204
|
return;
|
|
342
205
|
}
|
|
343
206
|
try {
|
|
344
207
|
const parsed = JSON.parse(data);
|
|
345
|
-
|
|
346
|
-
const content = parsed.choices?.[0]?.delta?.content ||
|
|
347
|
-
parsed.message?.content ||
|
|
348
|
-
parsed.response ||
|
|
349
|
-
'';
|
|
208
|
+
const content = parsed.choices?.[0]?.delta?.content || '';
|
|
350
209
|
if (content) {
|
|
351
210
|
collected.content += content;
|
|
352
|
-
yield content;
|
|
211
|
+
yield content;
|
|
353
212
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
for (const tc of deltaToolCalls) {
|
|
213
|
+
const deltas = parsed.choices?.[0]?.delta?.tool_calls;
|
|
214
|
+
if (deltas) {
|
|
215
|
+
for (const tc of deltas) {
|
|
358
216
|
const idx = tc.index ?? 0;
|
|
359
217
|
if (!toolCallMap.has(idx)) {
|
|
360
|
-
toolCallMap.set(idx, {
|
|
361
|
-
id: tc.id || `call_${(0, crypto_1.randomUUID)().substring(0, 8)}`,
|
|
362
|
-
function: { name: '', arguments: '' }
|
|
363
|
-
});
|
|
218
|
+
toolCallMap.set(idx, { id: tc.id || `call_${(0, crypto_1.randomUUID)().substring(0, 8)}`, function: { name: '', arguments: '' } });
|
|
364
219
|
}
|
|
365
220
|
const existing = toolCallMap.get(idx);
|
|
366
221
|
if (tc.id)
|
|
@@ -371,141 +226,138 @@ class CoWorkerAgent {
|
|
|
371
226
|
existing.function.arguments += tc.function.arguments;
|
|
372
227
|
}
|
|
373
228
|
}
|
|
374
|
-
// Handle non-streaming tool_calls (full response in single message)
|
|
375
|
-
const msgToolCalls = parsed.choices?.[0]?.message?.tool_calls || parsed.message?.tool_calls;
|
|
376
|
-
if (msgToolCalls && Array.isArray(msgToolCalls)) {
|
|
377
|
-
collected.tool_calls = msgToolCalls;
|
|
378
|
-
}
|
|
379
|
-
// Handle non-streaming full content
|
|
380
|
-
const msgContent = parsed.choices?.[0]?.message?.content;
|
|
381
|
-
if (msgContent && !content) {
|
|
382
|
-
collected.content += msgContent;
|
|
383
|
-
yield msgContent;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
catch {
|
|
387
|
-
// Partial JSON or non-JSON line — skip
|
|
388
229
|
}
|
|
230
|
+
catch { }
|
|
389
231
|
}
|
|
390
232
|
}
|
|
391
|
-
// Finalize any tool calls assembled from deltas
|
|
392
233
|
if (toolCallMap.size > 0 && collected.tool_calls.length === 0) {
|
|
393
234
|
collected.tool_calls = Array.from(toolCallMap.values());
|
|
394
235
|
}
|
|
395
236
|
}
|
|
396
|
-
/**
|
|
397
|
-
* Local tool dispatcher. Maps tool names to NativeTools methods.
|
|
398
|
-
* This is what makes CoWorker actually create files on the user's machine.
|
|
399
|
-
* ALL 35 tools are wired here.
|
|
400
|
-
*/
|
|
401
237
|
async executeToolLocally(toolName, args) {
|
|
402
238
|
switch (toolName) {
|
|
403
|
-
|
|
404
|
-
case '
|
|
405
|
-
|
|
406
|
-
case '
|
|
407
|
-
|
|
408
|
-
case '
|
|
409
|
-
|
|
410
|
-
case '
|
|
411
|
-
|
|
412
|
-
case '
|
|
413
|
-
|
|
414
|
-
case '
|
|
415
|
-
|
|
416
|
-
case '
|
|
417
|
-
|
|
418
|
-
case '
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
case '
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
case '
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const result = await engine.review(args.baseBranch);
|
|
428
|
-
return CodeReviewEngine.formatForTerminal(result);
|
|
429
|
-
}
|
|
430
|
-
// ── Shell & Process ──
|
|
431
|
-
case 'executeBash':
|
|
432
|
-
return NativeTools_1.NativeTools.executeBash(args.command, args.cwd);
|
|
433
|
-
case 'bashOutput':
|
|
434
|
-
return NativeTools_1.NativeTools.executeBashOutput(args.pid);
|
|
435
|
-
case 'killShell':
|
|
436
|
-
return NativeTools_1.NativeTools.executeKillShell(args.pid);
|
|
437
|
-
// ── Git ──
|
|
438
|
-
case 'gitStatus':
|
|
439
|
-
return NativeTools_1.NativeTools.executeGitStatus(args.cwd);
|
|
440
|
-
case 'gitDiff':
|
|
441
|
-
return NativeTools_1.NativeTools.executeGitDiff(args.filePath, args.staged, args.cwd);
|
|
442
|
-
case 'gitCommit':
|
|
443
|
-
return NativeTools_1.NativeTools.executeGitCommit(args.message, args.files, args.cwd);
|
|
444
|
-
// ── Memory (COWORKER.md) ──
|
|
445
|
-
case 'memoryRead':
|
|
446
|
-
return NativeTools_1.NativeTools.executeMemoryRead();
|
|
447
|
-
case 'memoryWrite':
|
|
448
|
-
return NativeTools_1.NativeTools.executeMemoryWrite(args.content, args.mode);
|
|
449
|
-
// ── Web ──
|
|
450
|
-
case 'executeWebSearch':
|
|
451
|
-
return NativeTools_1.NativeTools.executeWebSearch(args.query);
|
|
452
|
-
case 'executeWebFetch':
|
|
453
|
-
return NativeTools_1.NativeTools.executeWebFetch(args.url);
|
|
454
|
-
case 'executeOpenBrowser':
|
|
455
|
-
return NativeTools_1.NativeTools.executeOpenBrowser(args.url);
|
|
456
|
-
// ── Task Management ──
|
|
457
|
-
case 'todoRead':
|
|
458
|
-
return NativeTools_1.NativeTools.executeTodoRead();
|
|
459
|
-
case 'todoWrite':
|
|
460
|
-
return NativeTools_1.NativeTools.executeTodoWrite(args.todos);
|
|
461
|
-
case 'taskCreate': {
|
|
462
|
-
const id = await this.tasks.createTask(args.description, args.dependencies || []);
|
|
463
|
-
return `Task created with ID: ${id}`;
|
|
464
|
-
}
|
|
465
|
-
case 'taskUpdate': {
|
|
239
|
+
case 'executeRead': return NativeTools_1.NativeTools.executeRead(args.filePath);
|
|
240
|
+
case 'executeWrite': return NativeTools_1.NativeTools.executeWrite(args.filePath, args.content);
|
|
241
|
+
case 'executeEdit': return NativeTools_1.NativeTools.executeEdit(args.filePath, args.oldString, args.newString, args.replaceAll);
|
|
242
|
+
case 'executeMultiEdit': return NativeTools_1.NativeTools.executeMultiEdit(args.filePath, args.edits);
|
|
243
|
+
case 'executeLS': return NativeTools_1.NativeTools.executeLS(args.path, args.recursive);
|
|
244
|
+
case 'executeListDir': return NativeTools_1.NativeTools.executeListDir(args.dirPath);
|
|
245
|
+
case 'executeGlob': return (await NativeTools_1.NativeTools.executeGlob(args.pattern, args.dir)).join('\n');
|
|
246
|
+
case 'executeGrep': return NativeTools_1.NativeTools.executeGrep(args.regexQuery, args.filesToSearch);
|
|
247
|
+
case 'diagnostics': return this.hooks.runManualDiagnostics(args.filePath);
|
|
248
|
+
case 'executeBash': return NativeTools_1.NativeTools.executeBash(args.command, args.cwd);
|
|
249
|
+
case 'bashOutput': return NativeTools_1.NativeTools.executeBashOutput(args.pid);
|
|
250
|
+
case 'killShell': return NativeTools_1.NativeTools.executeKillShell(args.pid);
|
|
251
|
+
case 'gitStatus': return NativeTools_1.NativeTools.executeGitStatus(args.cwd);
|
|
252
|
+
case 'gitDiff': return NativeTools_1.NativeTools.executeGitDiff(args.filePath, args.staged, args.cwd);
|
|
253
|
+
case 'gitCommit': return NativeTools_1.NativeTools.executeGitCommit(args.message, args.files, args.cwd);
|
|
254
|
+
case 'memoryRead': return NativeTools_1.NativeTools.executeMemoryRead();
|
|
255
|
+
case 'memoryWrite': return NativeTools_1.NativeTools.executeMemoryWrite(args.content, args.mode);
|
|
256
|
+
case 'executeWebSearch': return NativeTools_1.NativeTools.executeWebSearch(args.query);
|
|
257
|
+
case 'executeWebFetch': return NativeTools_1.NativeTools.executeWebFetch(args.url);
|
|
258
|
+
case 'executeOpenBrowser': return NativeTools_1.NativeTools.executeOpenBrowser(args.url);
|
|
259
|
+
case 'todoRead': return NativeTools_1.NativeTools.executeTodoRead();
|
|
260
|
+
case 'todoWrite': return NativeTools_1.NativeTools.executeTodoWrite(args.todos);
|
|
261
|
+
case 'taskCreate': return `Task created with ID: ${await this.tasks.createTask(args.description, args.dependencies || [])}`;
|
|
262
|
+
case 'taskUpdate':
|
|
466
263
|
await this.tasks.updateTaskState(args.taskId, args.state, args.notes);
|
|
467
264
|
return `Task ${args.taskId} updated to ${args.state}`;
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
case '
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
case '
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
return NativeTools_1.NativeTools.executeNotebookEdit(args.notebookPath, args.newSource, args.cellId, args.cellType);
|
|
477
|
-
// ── Agents ──
|
|
478
|
-
case 'agent':
|
|
479
|
-
return NativeTools_1.NativeTools.executeAgent(args.description, args.subagentType);
|
|
480
|
-
case 'returnSubagentResult':
|
|
481
|
-
return NativeTools_1.NativeTools.executeReturnSubagentResult(args.result);
|
|
482
|
-
// ── Plan Mode (handled by permission interceptor state) ──
|
|
483
|
-
case 'enterPlanMode':
|
|
484
|
-
return 'Plan mode activated. Mutation tools (bash, write, edit) are now blocked until you call exitPlanMode.';
|
|
485
|
-
case 'exitPlanMode':
|
|
486
|
-
return 'Plan mode deactivated. Mutation tools are re-enabled.';
|
|
487
|
-
// ── Cron (stub — uses executeBash internally) ──
|
|
488
|
-
case 'cronCreate':
|
|
489
|
-
return `Cron scheduled: "${args.description}" every ${args.intervalMinutes} minutes. (Note: cron runs in-process only while CoWorker is active.)`;
|
|
490
|
-
case 'cronList':
|
|
491
|
-
return 'No active crons. (Cron persistence is not yet implemented.)';
|
|
492
|
-
case 'cronDelete':
|
|
493
|
-
return `Cron ${args.cronId} deleted.`;
|
|
494
|
-
default:
|
|
495
|
-
return `[Unknown Tool] "${toolName}" is not registered in CoWorker's local executor.`;
|
|
265
|
+
case 'executeAskUserQuestion': return NativeTools_1.NativeTools.executeAskUserQuestion(args.question);
|
|
266
|
+
case 'executeNotebookRead': return NativeTools_1.NativeTools.executeNotebookRead(args.notebookPath);
|
|
267
|
+
case 'executeNotebookEdit': return NativeTools_1.NativeTools.executeNotebookEdit(args.notebookPath, args.newSource, args.cellId, args.cellType);
|
|
268
|
+
case 'agent': return NativeTools_1.NativeTools.executeAgent(args.description, args.subagentType);
|
|
269
|
+
case 'returnSubagentResult': return NativeTools_1.NativeTools.executeReturnSubagentResult(args.result);
|
|
270
|
+
case 'enterPlanMode': return 'Plan mode activated. Mutation tools are blocked.';
|
|
271
|
+
case 'exitPlanMode': return 'Plan mode deactivated.';
|
|
272
|
+
default: return `[Unknown Tool] "${toolName}" is not registered.`;
|
|
496
273
|
}
|
|
497
274
|
}
|
|
498
|
-
/**
|
|
499
|
-
* Subagent: creates a restricted child agent for delegated subtasks.
|
|
500
|
-
*/
|
|
501
275
|
async createSubagent(profileName, subtask) {
|
|
502
276
|
const subAgent = new CoWorkerAgent({ model: this.model, role: 'CodeReviewer' });
|
|
503
277
|
let output = '';
|
|
504
|
-
for await (const chunk of subAgent.chat(subtask))
|
|
278
|
+
for await (const chunk of subAgent.chat(subtask))
|
|
505
279
|
output += chunk;
|
|
506
|
-
}
|
|
507
280
|
return `[SUBAGENT OUTPUT]: ${output}`;
|
|
508
281
|
}
|
|
282
|
+
enrichMessage(prompt, history) {
|
|
283
|
+
const lastAi = history.filter(m => m.role === 'assistant').pop();
|
|
284
|
+
const trimmed = prompt.trim();
|
|
285
|
+
if (/^\d+$/.test(trimmed) && lastAi) {
|
|
286
|
+
return `${prompt} (Refers to step ${trimmed} from: "${lastAi.content.substring(0, 80).replace(/\\n/g, ' ')}...")`;
|
|
287
|
+
}
|
|
288
|
+
if (/^run.*test/i.test(trimmed))
|
|
289
|
+
return `${prompt} (Identify framework and run tests automatically)`;
|
|
290
|
+
if (/^fix (it|this|the error|that)/i.test(trimmed))
|
|
291
|
+
return `${prompt} (Refer to the previous context or error logs)`;
|
|
292
|
+
return prompt;
|
|
293
|
+
}
|
|
509
294
|
}
|
|
510
295
|
exports.CoWorkerAgent = CoWorkerAgent;
|
|
296
|
+
function resolveAgentsDir() {
|
|
297
|
+
const candidates = [
|
|
298
|
+
path.join(process.cwd(), 'agents/system-prompts-and-models-of-ai-tools/Anthropic/Claude Code'),
|
|
299
|
+
path.join(__dirname, '../../agents/system-prompts-and-models-of-ai-tools/Anthropic/Claude Code'),
|
|
300
|
+
path.join(__dirname, '../../../agents/system-prompts-and-models-of-ai-tools/Anthropic/Claude Code'),
|
|
301
|
+
];
|
|
302
|
+
for (const candidate of candidates)
|
|
303
|
+
if (fs.existsSync(candidate))
|
|
304
|
+
return candidate;
|
|
305
|
+
return candidates[0];
|
|
306
|
+
}
|
|
307
|
+
function getGitContext(cwd) {
|
|
308
|
+
try {
|
|
309
|
+
const branch = (0, child_process_1.execSync)('git branch --show-current', { cwd, encoding: 'utf8' }).trim();
|
|
310
|
+
const status = (0, child_process_1.execSync)('git status --short', { cwd, encoding: 'utf8' }).trim() || '(clean)';
|
|
311
|
+
const commits = (0, child_process_1.execSync)('git log -n 5 --oneline', { cwd, encoding: 'utf8' }).trim();
|
|
312
|
+
return { isRepo: true, branch, status, commits };
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
return { isRepo: false, branch: 'unknown', status: 'unknown', commits: 'No git info provided.' };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
async function getSystemPrompt(cwd, autoContext) {
|
|
319
|
+
const agentsDir = resolveAgentsDir();
|
|
320
|
+
const promptPath = path.join(agentsDir, 'Prompt.txt');
|
|
321
|
+
let promptText = FALLBACK_SYSTEM_PROMPT;
|
|
322
|
+
try {
|
|
323
|
+
promptText = fs.readFileSync(promptPath, 'utf8')
|
|
324
|
+
.replace(/Claude Code/gi, 'CoWorker').replace(/Claude/gi, 'CoWorker').replace(/Anthropic/gi, 'Sylix');
|
|
325
|
+
}
|
|
326
|
+
catch { }
|
|
327
|
+
const gitContext = getGitContext(cwd);
|
|
328
|
+
promptText = promptText
|
|
329
|
+
.replace('${Working directory}', cwd)
|
|
330
|
+
.replace(/Is directory a git repo: [^\n]+/, `Is directory a git repo: ${gitContext.isRepo ? 'Yes' : 'No'}`)
|
|
331
|
+
.replace('Platform: darwin', `Platform: ${os.platform()}`)
|
|
332
|
+
.replace(/OS Version: [^\n]+/, `OS Version: ${os.type()} ${os.release()}`)
|
|
333
|
+
.replace(/Today's date: [^\n]+/, `Today's date: ${new Date().toISOString().split('T')[0]}`)
|
|
334
|
+
.replace(/Current branch: [^\n]+/, `Current branch: ${gitContext.branch}`)
|
|
335
|
+
.replace('${Last 5 Recent commits}', gitContext.commits);
|
|
336
|
+
if (autoContext)
|
|
337
|
+
promptText += `\n\n## Automated Project Context\n${autoContext}`;
|
|
338
|
+
const memoryFiles = ['.coworker.md', 'COWORKER.md'];
|
|
339
|
+
for (const file of memoryFiles) {
|
|
340
|
+
const memPath = path.join(cwd, file);
|
|
341
|
+
if (fs.existsSync(memPath)) {
|
|
342
|
+
try {
|
|
343
|
+
promptText += `\n\nPROJECT MEMORY (${file}):\n${fs.readFileSync(memPath, 'utf8')}`;
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
catch { }
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
promptText += `
|
|
350
|
+
\n## CRITICAL COLLABORATION RULES
|
|
351
|
+
1. NEVER ask for information you can fetch yourself (CWD, files, project type).
|
|
352
|
+
2. If the user refers to a number, it's a step from your previous list.
|
|
353
|
+
3. If the user says "run the test", check package.json and execute bash automatically.
|
|
354
|
+
4. Don't ask 'which project?' — look at the file tree in the context.
|
|
355
|
+
5. Always look at the conversation history to maintain context.
|
|
356
|
+
`;
|
|
357
|
+
return promptText;
|
|
358
|
+
}
|
|
359
|
+
async function getSkillsPromptFragment(hooks) {
|
|
360
|
+
return hooks.getSkillsSummary();
|
|
361
|
+
}
|
|
362
|
+
const FALLBACK_SYSTEM_PROMPT = `You are CoWorker, an autonomous AI coding agent by Sylix.`;
|
|
511
363
|
//# sourceMappingURL=CoWorkerAgent.js.map
|