@openagents-org/agent-launcher 0.2.130 → 0.2.132
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/package.json +1 -1
- package/registry.json +7 -5
- package/src/adapters/base.js +130 -7
- package/src/adapters/claude.js +342 -255
- package/src/adapters/cursor.js +27 -8
- package/src/adapters/workspace-prompt.js +133 -28
- package/src/cli.js +85 -0
- package/src/daemon.js +12 -3
- package/src/github-client.js +165 -0
- package/src/index.js +13 -1
- package/src/installer.js +151 -19
- package/src/mcp-server.js +279 -91
- package/src/paths.js +31 -3
- package/src/registry.js +4 -4
- package/src/skill-catalog.js +125 -0
- package/src/tui.js +1 -16
- package/src/update-check.js +5 -25
- package/src/workspace-client.js +101 -6
package/src/adapters/cursor.js
CHANGED
|
@@ -171,40 +171,59 @@ class CursorAdapter extends BaseAdapter {
|
|
|
171
171
|
_findCursorBinary() {
|
|
172
172
|
const home = os.homedir();
|
|
173
173
|
const ext = IS_WINDOWS ? '.cmd' : '';
|
|
174
|
+
const names = ['cursor-agent', 'agent'];
|
|
174
175
|
|
|
175
176
|
// Tier 0: Isolated runtime prefix
|
|
176
|
-
|
|
177
|
-
|
|
177
|
+
for (const name of names) {
|
|
178
|
+
const runtimeCandidate = path.join(home, '.openagents', 'runtimes', 'cursor', 'node_modules', '.bin', `${name}${ext}`);
|
|
179
|
+
if (fs.existsSync(runtimeCandidate)) return runtimeCandidate;
|
|
180
|
+
}
|
|
178
181
|
|
|
179
182
|
// Tier 0b: Legacy portable install
|
|
180
|
-
|
|
181
|
-
|
|
183
|
+
for (const name of names) {
|
|
184
|
+
const portableCandidate = path.join(home, '.openagents', 'nodejs', 'node_modules', '.bin', `${name}${ext}`);
|
|
185
|
+
if (fs.existsSync(portableCandidate)) return portableCandidate;
|
|
186
|
+
}
|
|
182
187
|
|
|
183
188
|
// Tier 1: PATH search
|
|
184
189
|
try {
|
|
185
190
|
if (IS_WINDOWS) {
|
|
186
|
-
const r = execSync('where agent.cmd 2>nul || where agent.exe 2>nul || where agent 2>nul', {
|
|
191
|
+
const r = execSync('where cursor-agent.cmd 2>nul || where cursor-agent.exe 2>nul || where cursor-agent 2>nul || where agent.cmd 2>nul || where agent.exe 2>nul || where agent 2>nul', {
|
|
187
192
|
encoding: 'utf-8', timeout: 5000,
|
|
188
193
|
});
|
|
189
194
|
return r.split(/\r?\n/)[0].trim();
|
|
190
195
|
} else {
|
|
191
|
-
return execSync('which agent', { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
196
|
+
return execSync('which cursor-agent || which agent', { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
192
197
|
}
|
|
193
198
|
} catch {}
|
|
194
199
|
|
|
195
200
|
// Tier 2: Next to current Node.js interpreter (npm global)
|
|
196
201
|
const nodeBinDir = path.dirname(process.execPath);
|
|
197
|
-
const
|
|
198
|
-
|
|
202
|
+
for (const name of names) {
|
|
203
|
+
const nearNode = path.join(nodeBinDir, `${name}${ext}`);
|
|
204
|
+
if (fs.existsSync(nearNode)) return nearNode;
|
|
205
|
+
}
|
|
199
206
|
|
|
200
207
|
// Tier 3: Common install locations
|
|
201
208
|
const candidates = IS_WINDOWS ? [
|
|
209
|
+
path.join(home, '.cursor', 'bin', 'cursor-agent.cmd'),
|
|
210
|
+
path.join(home, '.cursor', 'bin', 'cursor-agent.exe'),
|
|
211
|
+
path.join(home, '.cursor', 'bin', 'cursor-agent'),
|
|
212
|
+
path.join(home, '.cursor', 'bin', 'agent.cmd'),
|
|
213
|
+
path.join(home, '.cursor', 'bin', 'agent.exe'),
|
|
214
|
+
path.join(home, '.cursor', 'bin', 'agent'),
|
|
215
|
+
path.join(process.env.APPDATA || '', 'npm', 'cursor-agent.cmd'),
|
|
202
216
|
path.join(process.env.APPDATA || '', 'npm', 'agent.cmd'),
|
|
203
217
|
] : [
|
|
218
|
+
path.join(home, '.local', 'bin', 'cursor-agent'),
|
|
204
219
|
path.join(home, '.local', 'bin', 'agent'),
|
|
220
|
+
path.join(home, '.npm-global', 'bin', 'cursor-agent'),
|
|
205
221
|
path.join(home, '.npm-global', 'bin', 'agent'),
|
|
222
|
+
path.join(home, '.cursor', 'bin', 'cursor-agent'),
|
|
206
223
|
path.join(home, '.cursor', 'bin', 'agent'),
|
|
224
|
+
'/opt/homebrew/bin/cursor-agent',
|
|
207
225
|
'/opt/homebrew/bin/agent',
|
|
226
|
+
'/usr/local/bin/cursor-agent',
|
|
208
227
|
'/usr/local/bin/agent',
|
|
209
228
|
];
|
|
210
229
|
for (const c of candidates) {
|
|
@@ -11,6 +11,51 @@
|
|
|
11
11
|
|
|
12
12
|
'use strict';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Strong directive forcing agents to use the workspace browser when the
|
|
16
|
+
* workspace has Browser Fabric enabled. Emitted high in the system prompt
|
|
17
|
+
* so it wins against any earlier guidance that suggests local browsing
|
|
18
|
+
* tools.
|
|
19
|
+
*
|
|
20
|
+
* Returns '' when the toggle is off; the caller can unconditionally
|
|
21
|
+
* concatenate the result.
|
|
22
|
+
*
|
|
23
|
+
* Mirrors `build_browser_directive` in
|
|
24
|
+
* sdk/src/openagents/adapters/workspace_prompt.py.
|
|
25
|
+
*/
|
|
26
|
+
function buildBrowserDirective(browserEnabled) {
|
|
27
|
+
if (!browserEnabled) return '';
|
|
28
|
+
return (
|
|
29
|
+
'\n## Browser Use (MANDATORY)\n' +
|
|
30
|
+
'This workspace has the **shared Browser Fabric session** enabled. ' +
|
|
31
|
+
'All web browsing MUST go through it so the user can watch the ' +
|
|
32
|
+
'session live in their right-side panel and so cookies / state ' +
|
|
33
|
+
'persist across agents.\n\n' +
|
|
34
|
+
'**Use ONLY these tools for any web browsing:**\n' +
|
|
35
|
+
'- `mcp__openagents-workspace__workspace_browser_open`\n' +
|
|
36
|
+
'- `mcp__openagents-workspace__workspace_browser_navigate`\n' +
|
|
37
|
+
'- `mcp__openagents-workspace__workspace_browser_click`\n' +
|
|
38
|
+
'- `mcp__openagents-workspace__workspace_browser_type`\n' +
|
|
39
|
+
'- `mcp__openagents-workspace__workspace_browser_snapshot`\n' +
|
|
40
|
+
'- `mcp__openagents-workspace__workspace_browser_screenshot`\n' +
|
|
41
|
+
'- `mcp__openagents-workspace__workspace_browser_list_tabs`\n' +
|
|
42
|
+
'- `mcp__openagents-workspace__workspace_browser_close`\n' +
|
|
43
|
+
'\n' +
|
|
44
|
+
'If you don\'t have these MCP tools, use `Bash` + `curl` against ' +
|
|
45
|
+
'`/v1/browser/tabs` (documented below in Shared Browser).\n\n' +
|
|
46
|
+
'**FORBIDDEN — do NOT call any of these:**\n' +
|
|
47
|
+
'- `mcp__browsermcp__*` (any local Browser MCP extension tool)\n' +
|
|
48
|
+
'- `mcp__playwright__*`, `mcp__puppeteer__*`, `mcp__chrome-devtools__*`, or any other local-browser MCP\n' +
|
|
49
|
+
'- `WebFetch`, `WebSearch`, `web_fetch`, `web_search`, or any built-in network/browser tool\n' +
|
|
50
|
+
'\n' +
|
|
51
|
+
'If a local browser tool errors with "extension isn\'t connected" or ' +
|
|
52
|
+
'"connect your browser", do NOT ask the user to connect anything — ' +
|
|
53
|
+
'the local extension is irrelevant here. Immediately switch to the ' +
|
|
54
|
+
'workspace browser tools above. The Browser Fabric session is already ' +
|
|
55
|
+
'running on the backend.\n'
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
14
59
|
/**
|
|
15
60
|
* Build the identity section common to all adapters.
|
|
16
61
|
*/
|
|
@@ -91,6 +136,7 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
91
136
|
const caps = [];
|
|
92
137
|
if (!disabled.has('files')) caps.push('share and read files with other agents and users');
|
|
93
138
|
if (!disabled.has('browser')) caps.push('browse websites in a shared browser');
|
|
139
|
+
if (!disabled.has('knowledge')) caps.push('create and access a shared knowledge base');
|
|
94
140
|
caps.push('discover other agents in the workspace');
|
|
95
141
|
|
|
96
142
|
sections.push(
|
|
@@ -247,7 +293,7 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
247
293
|
}
|
|
248
294
|
|
|
249
295
|
// To-Dos (planning)
|
|
250
|
-
if (!isPlan) {
|
|
296
|
+
if (!isPlan && !disabled.has('todos')) {
|
|
251
297
|
sections.push(
|
|
252
298
|
'\n### To-Do List (Planning)\n\n' +
|
|
253
299
|
'Create or update your to-do list to track progress. The entire list ' +
|
|
@@ -270,7 +316,7 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
270
316
|
}
|
|
271
317
|
|
|
272
318
|
// Timers
|
|
273
|
-
if (!isPlan) {
|
|
319
|
+
if (!isPlan && !disabled.has('timers')) {
|
|
274
320
|
sections.push(
|
|
275
321
|
'\n### Timers\n\n' +
|
|
276
322
|
'Set a timer that will send you a message after a delay, waking you up ' +
|
|
@@ -291,33 +337,82 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
291
337
|
}
|
|
292
338
|
|
|
293
339
|
// Routines (recurring scheduled tasks)
|
|
294
|
-
if (!isPlan) {
|
|
340
|
+
if (!isPlan && !disabled.has('routines')) {
|
|
295
341
|
sections.push(
|
|
296
342
|
'\n### Routines (Recurring Tasks)\n\n' +
|
|
297
|
-
'Create a recurring routine that fires on a schedule
|
|
298
|
-
'
|
|
299
|
-
'
|
|
300
|
-
'**
|
|
301
|
-
'
|
|
302
|
-
'
|
|
343
|
+
'Create a recurring routine that fires on a schedule. Each routine gets ' +
|
|
344
|
+
'**its own dedicated thread** (`routine:<id>`) so different routines never ' +
|
|
345
|
+
'interfere, and the full context is preserved.\n\n' +
|
|
346
|
+
'**`context` is required** — provide a thorough description of what the ' +
|
|
347
|
+
'routine should do, any background info, and relevant details from the ' +
|
|
348
|
+
'current conversation. This context is posted at the start of the routine\'s ' +
|
|
349
|
+
'thread every time it fires, so you have full background.\n\n' +
|
|
350
|
+
'**Two schedule modes:**\n' +
|
|
351
|
+
'- **Daily**: `hour` (0-23 UTC) + `minute` (0-59), optional `days` ' +
|
|
352
|
+
'array (0=Mon, 6=Sun). Omit `days` for every day.\n' +
|
|
353
|
+
'- **Interval**: `interval_minutes` (1-1440). Fires every N minutes. ' +
|
|
354
|
+
'Mutually exclusive with `hour`/`minute`.\n\n' +
|
|
355
|
+
'**Create a daily routine:**\n' +
|
|
303
356
|
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json" ` +
|
|
304
357
|
`${baseUrl}/v1/routines -d '{"name":"Daily PR Review","message":"Review open PRs",` +
|
|
358
|
+
`"context":"Review all open pull requests on the main repo. Check for merge conflicts, ` +
|
|
359
|
+
`CI failures, and stale PRs older than 3 days. Post a summary to the workspace.",` +
|
|
305
360
|
`"hour":8,"minute":0,` +
|
|
306
|
-
`"network":"${workspaceId}"
|
|
307
|
-
`"source":"openagents:${agentName}"}'\`\n\n` +
|
|
308
|
-
'**Create a weekday-only routine (Mon-Fri):**\n' +
|
|
309
|
-
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json" ` +
|
|
310
|
-
`${baseUrl}/v1/routines -d '{"name":"Morning Standup","message":"Post standup summary",` +
|
|
311
|
-
`"hour":9,"minute":0,"days":[0,1,2,3,4],` +
|
|
312
|
-
`"network":"${workspaceId}","channel":"${channelName}",` +
|
|
361
|
+
`"network":"${workspaceId}",` +
|
|
313
362
|
`"source":"openagents:${agentName}"}'\`\n\n` +
|
|
314
363
|
'**List active routines:**\n' +
|
|
315
|
-
`\`${curl} -s -H "${h}" "${baseUrl}/v1/routines?network=${workspaceId}
|
|
364
|
+
`\`${curl} -s -H "${h}" "${baseUrl}/v1/routines?network=${workspaceId}"\`\n\n` +
|
|
316
365
|
'**Cancel a routine:**\n' +
|
|
317
366
|
`\`${curl} -s -X DELETE -H "${h}" ${baseUrl}/v1/routines/ROUTINE_ID\`\n`
|
|
318
367
|
);
|
|
319
368
|
}
|
|
320
369
|
|
|
370
|
+
// Notifications / Inbox
|
|
371
|
+
if (!isPlan && !disabled.has('notifications')) {
|
|
372
|
+
sections.push(
|
|
373
|
+
'\n### Notifications (Inbox)\n\n' +
|
|
374
|
+
'Send notifications to the workspace inbox. Notifications appear in a ' +
|
|
375
|
+
'dedicated panel separate from the chat stream. Use for task completions, ' +
|
|
376
|
+
'important findings, or anything that needs human attention.\n\n' +
|
|
377
|
+
'**Send a notification:**\n' +
|
|
378
|
+
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json" ` +
|
|
379
|
+
`${baseUrl}/v1/notifications -d '{"title":"Task Complete","message":"The analysis is ready.",` +
|
|
380
|
+
`"priority":"normal","channel":"${channelName}",` +
|
|
381
|
+
`"network":"${workspaceId}",` +
|
|
382
|
+
`"source":"openagents:${agentName}"}'\`\n\n` +
|
|
383
|
+
'**Priority values:** `low`, `normal`, `high`\n\n' +
|
|
384
|
+
'**List notifications:**\n' +
|
|
385
|
+
`\`${curl} -s -H "${h}" "${baseUrl}/v1/notifications?network=${workspaceId}"\`\n`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Knowledge Base
|
|
390
|
+
if (!isPlan && !disabled.has('knowledge')) {
|
|
391
|
+
sections.push(
|
|
392
|
+
'\n### Knowledge Base\n\n' +
|
|
393
|
+
'The workspace has a shared knowledge base of markdown documents. ' +
|
|
394
|
+
'Use it to store and retrieve shared information like API docs, ' +
|
|
395
|
+
'design decisions, project conventions, and other reference material. ' +
|
|
396
|
+
'Knowledge entries are accessible to all agents via @knowledge:slug mentions.\n\n' +
|
|
397
|
+
'**Create a knowledge entry:**\n' +
|
|
398
|
+
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json" ` +
|
|
399
|
+
`${baseUrl}/v1/knowledge -d '{"title":"API Design Patterns","content":"# API Design Patterns\\n\\n...",` +
|
|
400
|
+
`"description":"Common API patterns used in this project",` +
|
|
401
|
+
`"network":"${workspaceId}",` +
|
|
402
|
+
`"source":"openagents:${agentName}"}'\`\n\n` +
|
|
403
|
+
'**List knowledge entries:**\n' +
|
|
404
|
+
`\`${curl} -s -H "${h}" "${baseUrl}/v1/knowledge?network=${workspaceId}"\`\n\n` +
|
|
405
|
+
'**Read a knowledge entry by slug:**\n' +
|
|
406
|
+
`\`${curl} -s -H "${h}" "${baseUrl}/v1/knowledge/by-slug/api-design-patterns?network=${workspaceId}"\`\n\n` +
|
|
407
|
+
'**Update a knowledge entry:**\n' +
|
|
408
|
+
`\`${curl} -s -X PUT -H "${h}" -H "Content-Type: application/json" ` +
|
|
409
|
+
`${baseUrl}/v1/knowledge/ENTRY_ID -d '{"title":"Updated Title","content":"# Updated\\n\\n...",` +
|
|
410
|
+
`"network":"${workspaceId}","source":"openagents:${agentName}"}'\`\n\n` +
|
|
411
|
+
'**Delete a knowledge entry:**\n' +
|
|
412
|
+
`\`${curl} -s -X DELETE -H "${h}" "${baseUrl}/v1/knowledge/ENTRY_ID?network=${workspaceId}"\`\n`
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
321
416
|
// Discovery
|
|
322
417
|
sections.push(
|
|
323
418
|
'\n### Discover Agents\n\n' +
|
|
@@ -352,7 +447,7 @@ function buildGuardrails() {
|
|
|
352
447
|
* Build the system prompt for Claude adapter (MCP-based).
|
|
353
448
|
* Claude gets identity + collaboration instructions but NOT API skills.
|
|
354
449
|
*/
|
|
355
|
-
function buildClaudeSystemPrompt({ agentName, workspaceId, channelName, mode = 'execute' }) {
|
|
450
|
+
function buildClaudeSystemPrompt({ agentName, workspaceId, channelName, mode = 'execute', browserEnabled = false }) {
|
|
356
451
|
const parts = [];
|
|
357
452
|
parts.push(buildWorkspaceIdentity(agentName, workspaceId, channelName, mode));
|
|
358
453
|
parts.push(
|
|
@@ -360,8 +455,12 @@ function buildClaudeSystemPrompt({ agentName, workspaceId, channelName, mode = '
|
|
|
360
455
|
'Use workspace_get_agents to see other agents.\n' +
|
|
361
456
|
'Use workspace_put_todos to track your progress. ALWAYS create a to-do list when given multiple tasks or multi-step work.\n' +
|
|
362
457
|
'Use workspace_create_timer to set a reminder that wakes you up later.\n' +
|
|
363
|
-
'Use workspace_create_routine to set up recurring scheduled tasks (e.g. daily reviews).\n'
|
|
458
|
+
'Use workspace_create_routine to set up recurring scheduled tasks (e.g. daily reviews).\n' +
|
|
459
|
+
'Use workspace_send_notification to send a notification to the workspace inbox when you complete a task or have important results.\n' +
|
|
460
|
+
'Use workspace_write_knowledge to create or update shared knowledge base entries that persist across conversations.\n' +
|
|
461
|
+
'Use workspace_read_knowledge to read knowledge entries by ID or slug (from @knowledge:slug mentions).\n'
|
|
364
462
|
);
|
|
463
|
+
parts.push(buildBrowserDirective(browserEnabled));
|
|
365
464
|
parts.push(buildCollaborationPrompt());
|
|
366
465
|
parts.push(buildA2UIPrompt());
|
|
367
466
|
|
|
@@ -457,9 +556,10 @@ function buildA2UIPrompt() {
|
|
|
457
556
|
/**
|
|
458
557
|
* Build the full system prompt for OpenClaw/non-MCP agents.
|
|
459
558
|
*/
|
|
460
|
-
function buildOpenclawSystemPrompt({ agentName, workspaceId, channelName, endpoint, token, mode = 'execute', disabledModules }) {
|
|
559
|
+
function buildOpenclawSystemPrompt({ agentName, workspaceId, channelName, endpoint, token, mode = 'execute', disabledModules, browserEnabled = false }) {
|
|
461
560
|
const parts = [];
|
|
462
561
|
parts.push(buildWorkspaceIdentity(agentName, workspaceId, channelName, mode));
|
|
562
|
+
parts.push(buildBrowserDirective(browserEnabled));
|
|
463
563
|
parts.push(buildCollaborationPrompt());
|
|
464
564
|
parts.push(buildA2UIPrompt());
|
|
465
565
|
parts.push(buildModePrompt(mode));
|
|
@@ -473,12 +573,13 @@ function buildOpenclawSystemPrompt({ agentName, workspaceId, channelName, endpoi
|
|
|
473
573
|
/**
|
|
474
574
|
* Build a SKILL.md file for OpenClaw's skill auto-discovery.
|
|
475
575
|
*/
|
|
476
|
-
function buildOpenclawSkillMd({ endpoint, workspaceId, token, agentName, channelName, disabledModules }) {
|
|
576
|
+
function buildOpenclawSkillMd({ endpoint, workspaceId, token, agentName, channelName, disabledModules, browserEnabled = false }) {
|
|
477
577
|
const body = buildApiSkillsPrompt({
|
|
478
578
|
endpoint, workspaceId, token, agentName, channelName, disabledModules, mode: 'execute',
|
|
479
579
|
});
|
|
480
580
|
|
|
481
581
|
const identity = buildWorkspaceIdentity(agentName, workspaceId, channelName, 'execute');
|
|
582
|
+
const directive = buildBrowserDirective(browserEnabled);
|
|
482
583
|
const collab = buildCollaborationPrompt();
|
|
483
584
|
|
|
484
585
|
const frontmatter = (
|
|
@@ -495,18 +596,19 @@ function buildOpenclawSkillMd({ endpoint, workspaceId, token, agentName, channel
|
|
|
495
596
|
'---\n\n'
|
|
496
597
|
);
|
|
497
598
|
|
|
498
|
-
return frontmatter + identity + '\n' + collab + '\n' + body + '\n' + buildGuardrails();
|
|
599
|
+
return frontmatter + identity + directive + '\n' + collab + '\n' + body + '\n' + buildGuardrails();
|
|
499
600
|
}
|
|
500
601
|
|
|
501
602
|
/**
|
|
502
603
|
* Build system prompt for OpenCode adapter.
|
|
503
604
|
*/
|
|
504
|
-
function buildOpenCodeSystemPrompt({ agentName, workspaceId, channelName, endpoint, token, mode = 'execute', disabledModules }) {
|
|
605
|
+
function buildOpenCodeSystemPrompt({ agentName, workspaceId, channelName, endpoint, token, mode = 'execute', disabledModules, browserEnabled = false }) {
|
|
505
606
|
const identity = buildWorkspaceIdentity(agentName, workspaceId, channelName, mode);
|
|
607
|
+
const directive = buildBrowserDirective(browserEnabled);
|
|
506
608
|
const collab = buildCollaborationPrompt();
|
|
507
609
|
const modePrompt = buildModePrompt(mode);
|
|
508
610
|
const api = buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channelName, disabledModules, mode });
|
|
509
|
-
return identity + '\n' + collab + '\n' + modePrompt + '\n' + api + '\n' + buildGuardrails();
|
|
611
|
+
return identity + directive + '\n' + collab + '\n' + modePrompt + '\n' + api + '\n' + buildGuardrails();
|
|
510
612
|
}
|
|
511
613
|
|
|
512
614
|
/**
|
|
@@ -540,7 +642,7 @@ function buildOpenCodeSkillMd({ endpoint, workspaceId, token, agentName, channel
|
|
|
540
642
|
* of spawning an MCP server. Claude Code discovers the skill via its
|
|
541
643
|
* .claude/skills/ directory and uses Bash + curl to call workspace APIs.
|
|
542
644
|
*/
|
|
543
|
-
function buildClaudeSkillMd({ endpoint, workspaceId, token, agentName, channelName, disabledModules }) {
|
|
645
|
+
function buildClaudeSkillMd({ endpoint, workspaceId, token, agentName, channelName, disabledModules, browserEnabled = false }) {
|
|
544
646
|
const api = buildApiSkillsPrompt({
|
|
545
647
|
endpoint, workspaceId, token, agentName,
|
|
546
648
|
channelName: channelName || 'general',
|
|
@@ -549,6 +651,7 @@ function buildClaudeSkillMd({ endpoint, workspaceId, token, agentName, channelNa
|
|
|
549
651
|
});
|
|
550
652
|
|
|
551
653
|
const identity = buildWorkspaceIdentity(agentName, workspaceId, channelName, 'execute');
|
|
654
|
+
const directive = buildBrowserDirective(browserEnabled);
|
|
552
655
|
const collab = buildCollaborationPrompt();
|
|
553
656
|
|
|
554
657
|
const frontmatter =
|
|
@@ -561,7 +664,7 @@ function buildClaudeSkillMd({ endpoint, workspaceId, token, agentName, channelNa
|
|
|
561
664
|
' or collaborating with other agents via @mentions.\n' +
|
|
562
665
|
'---\n\n';
|
|
563
666
|
|
|
564
|
-
return frontmatter + identity + '\n' + collab + '\n' + api + '\n' + buildGuardrails();
|
|
667
|
+
return frontmatter + identity + directive + '\n' + collab + '\n' + api + '\n' + buildGuardrails();
|
|
565
668
|
}
|
|
566
669
|
|
|
567
670
|
/**
|
|
@@ -570,7 +673,7 @@ function buildClaudeSkillMd({ endpoint, workspaceId, token, agentName, channelNa
|
|
|
570
673
|
* Written to .cursor/skills/openagents-workspace.md before each CLI spawn.
|
|
571
674
|
* Cursor discovers skills from the .cursor/skills/ directory automatically.
|
|
572
675
|
*/
|
|
573
|
-
function buildCursorSkillMd({ endpoint, workspaceId, token, agentName, channelName, disabledModules }) {
|
|
676
|
+
function buildCursorSkillMd({ endpoint, workspaceId, token, agentName, channelName, disabledModules, browserEnabled = false }) {
|
|
574
677
|
const api = buildApiSkillsPrompt({
|
|
575
678
|
endpoint, workspaceId, token, agentName,
|
|
576
679
|
channelName: channelName || 'general',
|
|
@@ -579,6 +682,7 @@ function buildCursorSkillMd({ endpoint, workspaceId, token, agentName, channelNa
|
|
|
579
682
|
});
|
|
580
683
|
|
|
581
684
|
const identity = buildWorkspaceIdentity(agentName, workspaceId, channelName, 'execute');
|
|
685
|
+
const directive = buildBrowserDirective(browserEnabled);
|
|
582
686
|
const collab = buildCollaborationPrompt();
|
|
583
687
|
|
|
584
688
|
const frontmatter =
|
|
@@ -591,11 +695,12 @@ function buildCursorSkillMd({ endpoint, workspaceId, token, agentName, channelNa
|
|
|
591
695
|
' or collaborating with other agents via @mentions.\n' +
|
|
592
696
|
'---\n\n';
|
|
593
697
|
|
|
594
|
-
return frontmatter + identity + '\n' + collab + '\n' + api + '\n' + buildGuardrails();
|
|
698
|
+
return frontmatter + identity + directive + '\n' + collab + '\n' + api + '\n' + buildGuardrails();
|
|
595
699
|
}
|
|
596
700
|
|
|
597
701
|
module.exports = {
|
|
598
702
|
buildWorkspaceIdentity,
|
|
703
|
+
buildBrowserDirective,
|
|
599
704
|
buildCollaborationPrompt,
|
|
600
705
|
buildModePrompt,
|
|
601
706
|
buildGuardrails,
|
package/src/cli.js
CHANGED
|
@@ -502,6 +502,89 @@ async function cmdToolMode(connector, _flags, positional) {
|
|
|
502
502
|
}
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
+
async function cmdSkills(connector, _flags, positional) {
|
|
506
|
+
const { SKILL_CATALOG, getSkillDefaults } = require('./skill-catalog');
|
|
507
|
+
const toggleable = SKILL_CATALOG.filter(s => s.toggleable);
|
|
508
|
+
const first = positional[0];
|
|
509
|
+
const second = positional[1];
|
|
510
|
+
const third = positional[2];
|
|
511
|
+
|
|
512
|
+
// agn skills → list skills for all agents
|
|
513
|
+
if (!first) {
|
|
514
|
+
const agents = connector.config.getAgents();
|
|
515
|
+
if (agents.length === 0) { print('No agents configured'); return; }
|
|
516
|
+
for (const a of agents) {
|
|
517
|
+
const defaults = getSkillDefaults();
|
|
518
|
+
const skills = a.skills || {};
|
|
519
|
+
const parts = toggleable.map(s => {
|
|
520
|
+
const enabled = skills[s.id] !== undefined ? skills[s.id] : defaults[s.id];
|
|
521
|
+
return `${enabled ? '+' : '-'}${s.id}`;
|
|
522
|
+
});
|
|
523
|
+
print(` ${a.name}: ${parts.join(' ')}`);
|
|
524
|
+
}
|
|
525
|
+
print('\nUsage: agn skills <agent> [enable|disable <skill>]');
|
|
526
|
+
print('Available skills: ' + toggleable.map(s => s.id).join(', '));
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// agn skills catalog → show full catalog
|
|
531
|
+
if (first === 'catalog') {
|
|
532
|
+
print('Skill Hub — Available Skills:\n');
|
|
533
|
+
for (const s of SKILL_CATALOG) {
|
|
534
|
+
const tag = s.toggleable ? (s.defaultEnabled ? '[on]' : '[off]') : '[always]';
|
|
535
|
+
print(` ${s.id.padEnd(16)} ${tag.padEnd(10)} ${s.name}`);
|
|
536
|
+
print(` ${''.padEnd(16)} ${''.padEnd(10)} ${s.description}`);
|
|
537
|
+
print('');
|
|
538
|
+
}
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const agent = connector.config.getAgent(first);
|
|
543
|
+
if (!agent) { print(`Agent '${first}' not found`); process.exitCode = 1; return; }
|
|
544
|
+
|
|
545
|
+
// agn skills <agent> → show agent's skills
|
|
546
|
+
if (!second) {
|
|
547
|
+
const defaults = getSkillDefaults();
|
|
548
|
+
const skills = agent.skills || {};
|
|
549
|
+
print(`Skills for ${first}:\n`);
|
|
550
|
+
for (const s of toggleable) {
|
|
551
|
+
const enabled = skills[s.id] !== undefined ? skills[s.id] : defaults[s.id];
|
|
552
|
+
const marker = enabled ? ' ✓' : ' ✗';
|
|
553
|
+
print(`${marker} ${s.id.padEnd(14)} ${s.name} — ${s.description}`);
|
|
554
|
+
}
|
|
555
|
+
print('\nUsage: agn skills <agent> enable|disable <skill>');
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// agn skills <agent> enable|disable <skill>
|
|
560
|
+
if (second !== 'enable' && second !== 'disable') {
|
|
561
|
+
print(`Unknown action: ${second}. Use 'enable' or 'disable'.`);
|
|
562
|
+
process.exitCode = 1;
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
if (!third) {
|
|
566
|
+
print(`Usage: agn skills ${first} ${second} <skill>`);
|
|
567
|
+
print('Available skills: ' + toggleable.map(s => s.id).join(', '));
|
|
568
|
+
process.exitCode = 1;
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const skillDef = toggleable.find(s => s.id === third);
|
|
573
|
+
if (!skillDef) {
|
|
574
|
+
print(`Unknown skill: ${third}`);
|
|
575
|
+
print('Available skills: ' + toggleable.map(s => s.id).join(', '));
|
|
576
|
+
process.exitCode = 1;
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const current = agent.skills || {};
|
|
581
|
+
const updated = { ...current, [third]: second === 'enable' };
|
|
582
|
+
connector.config.updateAgent(first, { skills: updated });
|
|
583
|
+
try { connector.sendDaemonCommand('reload'); } catch {}
|
|
584
|
+
const verb = second === 'enable' ? 'Enabled' : 'Disabled';
|
|
585
|
+
print(`${verb} '${skillDef.name}' for ${first}`);
|
|
586
|
+
}
|
|
587
|
+
|
|
505
588
|
async function cmdTestLLM(connector, _flags, positional) {
|
|
506
589
|
const type = positional[0];
|
|
507
590
|
if (!type) { print('Usage: agn test-llm <type>'); return; }
|
|
@@ -566,6 +649,7 @@ Commands:
|
|
|
566
649
|
connect <agent> <token> Connect agent to workspace
|
|
567
650
|
disconnect <agent> Disconnect agent from workspace
|
|
568
651
|
env <type> [--set K=V] View/set env vars for agent type
|
|
652
|
+
skills [agent] [action] Manage agent skills (enable/disable)
|
|
569
653
|
tool-mode [agent] [mode] View/set tool mode (mcp or skills)
|
|
570
654
|
autostart [--disable] Enable/disable auto-start on login
|
|
571
655
|
test-llm <type> Test LLM connection
|
|
@@ -652,6 +736,7 @@ async function main() {
|
|
|
652
736
|
autostart: () => cmdAutostart(connector, flags),
|
|
653
737
|
workspace: () => cmdWorkspace(connector, flags, positional),
|
|
654
738
|
env: () => cmdEnv(connector, flags, positional),
|
|
739
|
+
skills: () => cmdSkills(connector, flags, positional),
|
|
655
740
|
'tool-mode': () => cmdToolMode(connector, flags, positional),
|
|
656
741
|
'test-llm': () => cmdTestLLM(connector, flags, positional),
|
|
657
742
|
update: () => cmdUpdate(),
|
package/src/daemon.js
CHANGED
|
@@ -58,12 +58,20 @@ class Daemon {
|
|
|
58
58
|
// Write PID file
|
|
59
59
|
this._writePid();
|
|
60
60
|
|
|
61
|
-
// Periodic status +
|
|
61
|
+
// Periodic status (heavy: JSON serialize + write) every 5s.
|
|
62
62
|
this._statusInterval = setInterval(() => {
|
|
63
63
|
this._writeStatus();
|
|
64
|
-
this._processCommands();
|
|
65
64
|
}, 5000);
|
|
66
65
|
|
|
66
|
+
// Command file poll (cheap: existsSync on a tiny file) every 200ms so
|
|
67
|
+
// start/stop/restart from the launcher feels responsive. With a 5s
|
|
68
|
+
// combined interval, users saw up to 5s before the daemon even noticed
|
|
69
|
+
// a Stop click — this was especially painful on Windows where there's
|
|
70
|
+
// no SIGHUP shortcut and Stop landed near the end of a tick.
|
|
71
|
+
this._cmdInterval = setInterval(() => {
|
|
72
|
+
this._processCommands();
|
|
73
|
+
}, 200);
|
|
74
|
+
|
|
67
75
|
// Watch config file for hot-reload
|
|
68
76
|
this._watchConfig();
|
|
69
77
|
|
|
@@ -429,6 +437,7 @@ class Daemon {
|
|
|
429
437
|
|
|
430
438
|
async _adapterLoop(name, agentCfg, info, network) {
|
|
431
439
|
const { createAdapter } = require('./adapters');
|
|
440
|
+
const { skillsToDisabledModules } = require('./skill-catalog');
|
|
432
441
|
const agentType = agentCfg.type || 'openclaw';
|
|
433
442
|
const endpoint = network.endpoint || 'https://workspace-endpoint.openagents.org';
|
|
434
443
|
|
|
@@ -442,7 +451,7 @@ class Daemon {
|
|
|
442
451
|
endpoint,
|
|
443
452
|
agentType,
|
|
444
453
|
openclawAgentId: agentCfg.openclaw_agent_id || 'main',
|
|
445
|
-
disabledModules:
|
|
454
|
+
disabledModules: skillsToDisabledModules(agentCfg.skills),
|
|
446
455
|
agentEnv: this._buildAgentEnv(agentCfg),
|
|
447
456
|
workingDir: agentCfg.path || undefined,
|
|
448
457
|
toolMode: agentCfg.tool_mode || 'skills',
|