@cloudcli-ai/cloudcli 1.31.5 → 1.33.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/README.de.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.ru.md +1 -1
- package/README.tr.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/api-docs.html +6 -7
- package/dist/assets/index-MOIvpy53.css +32 -0
- package/dist/assets/{index-DqMVUeZS.js → index-skXSjBje.js} +290 -270
- package/dist/index.html +2 -2
- package/dist/modelConstants.js +841 -0
- package/dist-server/server/claude-sdk.js +67 -39
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cursor-cli.js +6 -9
- package/dist-server/server/cursor-cli.js.map +1 -1
- package/dist-server/server/gemini-cli.js +163 -35
- package/dist-server/server/gemini-cli.js.map +1 -1
- package/dist-server/server/gemini-response-handler.js +34 -0
- package/dist-server/server/gemini-response-handler.js.map +1 -1
- package/dist-server/server/index.js +131 -19
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/database/index.js +1 -0
- package/dist-server/server/modules/database/index.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +11 -0
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/projects.db.js +12 -0
- package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -1
- package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js +61 -0
- package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +49 -8
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/schema.js +1 -0
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/projects/projects.routes.js +12 -3
- package/dist-server/server/modules/projects/projects.routes.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js +14 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-management.service.js +1 -0
- package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +47 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
- package/dist-server/server/modules/providers/index.js +1 -0
- package/dist-server/server/modules/providers/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +27 -7
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +141 -0
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +55 -2
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +153 -14
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +198 -0
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +4 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +84 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +54 -2
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +21 -6
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js +50 -0
- package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +4 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +754 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +8 -30
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +187 -24
- package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js +28 -0
- package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +4 -0
- package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +125 -2
- package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js +27 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +24 -8
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +10 -10
- package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js +33 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +4 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js +92 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-mcp.provider.js +181 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-mcp.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +267 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +115 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +410 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-skills.provider.js +62 -0
- package/dist-server/server/modules/providers/list/opencode/opencode-skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/opencode/opencode.provider.js +19 -0
- package/dist-server/server/modules/providers/list/opencode/opencode.provider.js.map +1 -0
- package/dist-server/server/modules/providers/provider.registry.js +2 -0
- package/dist-server/server/modules/providers/provider.registry.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +65 -3
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/mcp.service.js +1 -9
- package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js +199 -0
- package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +129 -13
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js +1 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +13 -4
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +81 -7
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/skills.service.js +11 -0
- package/dist-server/server/modules/providers/services/skills.service.js.map +1 -0
- package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -1
- package/dist-server/server/modules/providers/shared/skills/skills.provider.js +45 -0
- package/dist-server/server/modules/providers/shared/skills/skills.provider.js.map +1 -0
- package/dist-server/server/modules/providers/tests/mcp.test.js +73 -6
- package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +66 -0
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +262 -0
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js +270 -0
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/skills.test.js +259 -0
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -0
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +12 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js +4 -4
- package/dist-server/server/modules/websocket/services/plugin-websocket-proxy.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +9 -1
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
- package/dist-server/server/openai-codex.js +87 -29
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/opencode-cli.js +287 -0
- package/dist-server/server/opencode-cli.js.map +1 -0
- package/dist-server/server/opencode-cli.test.js +84 -0
- package/dist-server/server/opencode-cli.test.js.map +1 -0
- package/dist-server/server/routes/agent.js +21 -8
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/routes/commands.js +204 -211
- package/dist-server/server/routes/commands.js.map +1 -1
- package/dist-server/server/routes/cursor.js +2 -2
- package/dist-server/server/routes/cursor.js.map +1 -1
- package/dist-server/server/routes/settings.js +0 -10
- package/dist-server/server/routes/settings.js.map +1 -1
- package/dist-server/server/routes/tests/commands.test.js +76 -0
- package/dist-server/server/routes/tests/commands.test.js.map +1 -0
- package/dist-server/server/shared/claude-cli-path.js +103 -0
- package/dist-server/server/shared/claude-cli-path.js.map +1 -0
- package/dist-server/server/shared/claude-cli-path.test.js +45 -0
- package/dist-server/server/shared/claude-cli-path.test.js.map +1 -0
- package/dist-server/server/{utils → shared}/frontmatter.js +3 -3
- package/dist-server/server/shared/frontmatter.js.map +1 -0
- package/dist-server/server/shared/utils.js +368 -0
- package/dist-server/server/shared/utils.js.map +1 -1
- package/dist-server/server/utils/commandParser.js +3 -3
- package/dist-server/server/utils/commandParser.js.map +1 -1
- package/package.json +3 -1
- package/public/api-docs.html +878 -0
- package/public/modelConstants.js +841 -0
- package/server/claude-sdk.js +74 -40
- package/server/cursor-cli.js +6 -11
- package/server/gemini-cli.js +197 -43
- package/server/gemini-response-handler.js +38 -0
- package/server/index.js +144 -19
- package/server/modules/database/index.ts +1 -0
- package/server/modules/database/migrations.ts +12 -0
- package/server/modules/database/repositories/projects.db.ts +13 -0
- package/server/modules/database/repositories/sessions.db.integration.test.ts +72 -0
- package/server/modules/database/repositories/sessions.db.ts +60 -9
- package/server/modules/database/schema.ts +1 -0
- package/server/modules/projects/projects.routes.ts +20 -3
- package/server/modules/projects/services/project-delete.service.ts +16 -1
- package/server/modules/projects/services/project-management.service.ts +2 -0
- package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +71 -1
- package/server/modules/providers/README.md +354 -0
- package/server/modules/providers/index.ts +2 -1
- package/server/modules/providers/list/claude/claude-auth.provider.ts +38 -13
- package/server/modules/providers/list/claude/claude-models.provider.ts +191 -0
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +68 -2
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +175 -14
- package/server/modules/providers/list/claude/claude-skills.provider.ts +257 -0
- package/server/modules/providers/list/claude/claude.provider.ts +11 -1
- package/server/modules/providers/list/codex/codex-models.provider.ts +125 -0
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +62 -2
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +25 -6
- package/server/modules/providers/list/codex/codex-skills.provider.ts +60 -0
- package/server/modules/providers/list/codex/codex.provider.ts +11 -1
- package/server/modules/providers/list/cursor/cursor-models.provider.ts +820 -0
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +17 -40
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +216 -26
- package/server/modules/providers/list/cursor/cursor-skills.provider.ts +31 -0
- package/server/modules/providers/list/cursor/cursor.provider.ts +11 -1
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +158 -2
- package/server/modules/providers/list/gemini/gemini-models.provider.ts +42 -0
- package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +24 -20
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +10 -11
- package/server/modules/providers/list/gemini/gemini-skills.provider.ts +36 -0
- package/server/modules/providers/list/gemini/gemini.provider.ts +11 -1
- package/server/modules/providers/list/opencode/opencode-auth.provider.ts +111 -0
- package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +228 -0
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +339 -0
- package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +157 -0
- package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +506 -0
- package/server/modules/providers/list/opencode/opencode-skills.provider.ts +78 -0
- package/server/modules/providers/list/opencode/opencode.provider.ts +27 -0
- package/server/modules/providers/provider.registry.ts +2 -0
- package/server/modules/providers/provider.routes.ts +97 -4
- package/server/modules/providers/services/mcp.service.ts +1 -12
- package/server/modules/providers/services/provider-models.service.ts +325 -0
- package/server/modules/providers/services/session-conversations-search.service.ts +162 -13
- package/server/modules/providers/services/session-synchronizer.service.ts +1 -0
- package/server/modules/providers/services/sessions-watcher.service.ts +14 -4
- package/server/modules/providers/services/sessions.service.ts +112 -9
- package/server/modules/providers/services/skills.service.ts +15 -0
- package/server/modules/providers/shared/base/abstract.provider.ts +4 -0
- package/server/modules/providers/shared/skills/skills.provider.ts +64 -0
- package/server/modules/providers/tests/mcp.test.ts +93 -6
- package/server/modules/providers/tests/opencode-models.test.ts +73 -0
- package/server/modules/providers/tests/opencode-sessions.test.ts +334 -0
- package/server/modules/providers/tests/provider-models.service.test.ts +318 -0
- package/server/modules/providers/tests/skills.test.ts +512 -0
- package/server/modules/websocket/services/chat-websocket.service.ts +15 -1
- package/server/modules/websocket/services/plugin-websocket-proxy.service.ts +4 -4
- package/server/modules/websocket/services/shell-websocket.service.ts +9 -0
- package/server/openai-codex.js +99 -30
- package/server/opencode-cli.js +336 -0
- package/server/opencode-cli.test.js +95 -0
- package/server/routes/agent.js +22 -8
- package/server/routes/commands.js +256 -233
- package/server/routes/cursor.js +2 -2
- package/server/routes/settings.js +1 -10
- package/server/routes/tests/commands.test.js +82 -0
- package/server/shared/claude-cli-path.test.ts +61 -0
- package/server/shared/claude-cli-path.ts +139 -0
- package/server/{utils/frontmatter.js → shared/frontmatter.ts} +3 -3
- package/server/shared/interfaces.ts +64 -0
- package/server/shared/types.ts +166 -1
- package/server/shared/utils.ts +478 -0
- package/server/utils/commandParser.js +5 -3
- package/dist/assets/index-6rYG_mRo.css +0 -32
- package/dist-server/server/utils/frontmatter.js.map +0 -1
- package/dist-server/shared/modelConstants.js +0 -98
- package/dist-server/shared/modelConstants.js.map +0 -1
- package/shared/modelConstants.js +0 -106
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
|
|
7
|
+
import { providerSkillsService } from '@/modules/providers/services/skills.service.js';
|
|
8
|
+
|
|
9
|
+
const patchHomeDir = (nextHomeDir: string) => {
|
|
10
|
+
const original = os.homedir;
|
|
11
|
+
(os as any).homedir = () => nextHomeDir;
|
|
12
|
+
return () => {
|
|
13
|
+
(os as any).homedir = original;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const writeSkill = async (
|
|
18
|
+
skillsRoot: string,
|
|
19
|
+
directoryName: string,
|
|
20
|
+
name: string,
|
|
21
|
+
description: string,
|
|
22
|
+
): Promise<string> => {
|
|
23
|
+
const skillDir = path.join(skillsRoot, directoryName);
|
|
24
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
25
|
+
const skillPath = path.join(skillDir, 'SKILL.md');
|
|
26
|
+
await fs.writeFile(
|
|
27
|
+
skillPath,
|
|
28
|
+
`---\nname: ${name}\ndescription: ${description}\n---\n\n`,
|
|
29
|
+
'utf8',
|
|
30
|
+
);
|
|
31
|
+
return skillPath;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const writeClaudePluginManifest = async (
|
|
35
|
+
installPath: string,
|
|
36
|
+
name: string,
|
|
37
|
+
): Promise<void> => {
|
|
38
|
+
const pluginConfigDir = path.join(installPath, '.claude-plugin');
|
|
39
|
+
await fs.mkdir(pluginConfigDir, { recursive: true });
|
|
40
|
+
await fs.writeFile(
|
|
41
|
+
path.join(pluginConfigDir, 'plugin.json'),
|
|
42
|
+
JSON.stringify(
|
|
43
|
+
{
|
|
44
|
+
name,
|
|
45
|
+
version: '0.1.0',
|
|
46
|
+
description: `${name} test plugin`,
|
|
47
|
+
},
|
|
48
|
+
null,
|
|
49
|
+
2,
|
|
50
|
+
),
|
|
51
|
+
'utf8',
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const writeClaudePluginCommand = async (
|
|
56
|
+
commandsRoot: string,
|
|
57
|
+
commandName: string,
|
|
58
|
+
description: string,
|
|
59
|
+
): Promise<string> => {
|
|
60
|
+
await fs.mkdir(commandsRoot, { recursive: true });
|
|
61
|
+
const commandPath = path.join(commandsRoot, `${commandName}.md`);
|
|
62
|
+
await fs.writeFile(
|
|
63
|
+
commandPath,
|
|
64
|
+
`---\ndescription: ${description}\nargument-hint: 'test args'\n---\n\nCommand body.\n`,
|
|
65
|
+
'utf8',
|
|
66
|
+
);
|
|
67
|
+
return commandPath;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* This test covers Claude user/project skill folders plus plugin discovery from
|
|
72
|
+
* installed plugin command files and fallback plugin skill files.
|
|
73
|
+
*/
|
|
74
|
+
test('providerSkillsService lists claude user, project, and enabled plugin skills', { concurrency: false }, async () => {
|
|
75
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'llm-skills-claude-'));
|
|
76
|
+
const workspacePath = path.join(tempRoot, 'workspace');
|
|
77
|
+
const commandPluginInstallPath = path.join(
|
|
78
|
+
tempRoot,
|
|
79
|
+
'.claude',
|
|
80
|
+
'plugins',
|
|
81
|
+
'cache',
|
|
82
|
+
'notion-plugin',
|
|
83
|
+
'notion',
|
|
84
|
+
'abc123',
|
|
85
|
+
);
|
|
86
|
+
const skillPluginInstallPath = path.join(
|
|
87
|
+
tempRoot,
|
|
88
|
+
'.claude',
|
|
89
|
+
'plugins',
|
|
90
|
+
'cache',
|
|
91
|
+
'anthropic-agent-skills',
|
|
92
|
+
'example-skills',
|
|
93
|
+
'def456',
|
|
94
|
+
);
|
|
95
|
+
const disabledPluginInstallPath = path.join(
|
|
96
|
+
tempRoot,
|
|
97
|
+
'.claude',
|
|
98
|
+
'plugins',
|
|
99
|
+
'cache',
|
|
100
|
+
'disabled-marketplace',
|
|
101
|
+
'disabled-skills',
|
|
102
|
+
'ghi789',
|
|
103
|
+
);
|
|
104
|
+
const emptyIdPluginInstallPath = path.join(
|
|
105
|
+
tempRoot,
|
|
106
|
+
'.claude',
|
|
107
|
+
'plugins',
|
|
108
|
+
'cache',
|
|
109
|
+
'invalid-empty-plugin',
|
|
110
|
+
'empty',
|
|
111
|
+
'000',
|
|
112
|
+
);
|
|
113
|
+
const atIdPluginInstallPath = path.join(
|
|
114
|
+
tempRoot,
|
|
115
|
+
'.claude',
|
|
116
|
+
'plugins',
|
|
117
|
+
'cache',
|
|
118
|
+
'invalid-at-plugin',
|
|
119
|
+
'at',
|
|
120
|
+
'000',
|
|
121
|
+
);
|
|
122
|
+
const siblingSkillPluginPath = path.join(path.dirname(skillPluginInstallPath), 'legacy777');
|
|
123
|
+
await fs.mkdir(workspacePath, { recursive: true });
|
|
124
|
+
|
|
125
|
+
const restoreHomeDir = patchHomeDir(tempRoot);
|
|
126
|
+
try {
|
|
127
|
+
await writeSkill(
|
|
128
|
+
path.join(tempRoot, '.claude', 'skills'),
|
|
129
|
+
'claude-user-dir',
|
|
130
|
+
'claude-user',
|
|
131
|
+
'Claude user skill',
|
|
132
|
+
);
|
|
133
|
+
await writeSkill(
|
|
134
|
+
path.join(workspacePath, '.claude', 'skills'),
|
|
135
|
+
'claude-project-dir',
|
|
136
|
+
'claude-project',
|
|
137
|
+
'Claude project skill',
|
|
138
|
+
);
|
|
139
|
+
await writeClaudePluginManifest(commandPluginInstallPath, 'Notion');
|
|
140
|
+
await writeClaudePluginCommand(
|
|
141
|
+
path.join(commandPluginInstallPath, 'commands'),
|
|
142
|
+
'insert-row',
|
|
143
|
+
'Insert a Notion database row',
|
|
144
|
+
);
|
|
145
|
+
await writeSkill(
|
|
146
|
+
path.join(commandPluginInstallPath, 'skills'),
|
|
147
|
+
'ignored-command-plugin-skill-dir',
|
|
148
|
+
'ignored-command-plugin-skill',
|
|
149
|
+
'Command plugin fallback skill should be ignored',
|
|
150
|
+
);
|
|
151
|
+
await writeClaudePluginManifest(skillPluginInstallPath, 'ExampleSkills');
|
|
152
|
+
await writeSkill(
|
|
153
|
+
path.join(skillPluginInstallPath, 'skills'),
|
|
154
|
+
'claude-plugin-dir',
|
|
155
|
+
'claude-plugin',
|
|
156
|
+
'Claude plugin skill',
|
|
157
|
+
);
|
|
158
|
+
await writeSkill(
|
|
159
|
+
path.join(skillPluginInstallPath, 'skills'),
|
|
160
|
+
'claude-plugin-second-dir',
|
|
161
|
+
'claude-plugin-second',
|
|
162
|
+
'Second Claude plugin skill',
|
|
163
|
+
);
|
|
164
|
+
await writeSkill(
|
|
165
|
+
path.join(skillPluginInstallPath, 'skills', 'nested', 'collection'),
|
|
166
|
+
'claude-plugin-nested-dir',
|
|
167
|
+
'claude-plugin-nested',
|
|
168
|
+
'Nested Claude plugin skill',
|
|
169
|
+
);
|
|
170
|
+
await writeSkill(
|
|
171
|
+
path.join(siblingSkillPluginPath, 'skills'),
|
|
172
|
+
'claude-plugin-sibling-dir',
|
|
173
|
+
'claude-plugin-sibling',
|
|
174
|
+
'Sibling Claude plugin skill',
|
|
175
|
+
);
|
|
176
|
+
await writeClaudePluginManifest(disabledPluginInstallPath, 'DisabledSkills');
|
|
177
|
+
await writeClaudePluginCommand(
|
|
178
|
+
path.join(disabledPluginInstallPath, 'commands'),
|
|
179
|
+
'disabled-command',
|
|
180
|
+
'Disabled plugin command',
|
|
181
|
+
);
|
|
182
|
+
await writeClaudePluginCommand(
|
|
183
|
+
path.join(emptyIdPluginInstallPath, 'commands'),
|
|
184
|
+
'invalid-empty-command',
|
|
185
|
+
'Invalid empty id command',
|
|
186
|
+
);
|
|
187
|
+
await writeClaudePluginCommand(
|
|
188
|
+
path.join(atIdPluginInstallPath, 'commands'),
|
|
189
|
+
'invalid-at-command',
|
|
190
|
+
'Invalid at id command',
|
|
191
|
+
);
|
|
192
|
+
await writeSkill(
|
|
193
|
+
path.join(
|
|
194
|
+
disabledPluginInstallPath,
|
|
195
|
+
'skills',
|
|
196
|
+
),
|
|
197
|
+
'disabled-plugin-dir',
|
|
198
|
+
'disabled-plugin',
|
|
199
|
+
'Disabled plugin skill',
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
await fs.writeFile(
|
|
203
|
+
path.join(tempRoot, '.claude', 'settings.json'),
|
|
204
|
+
JSON.stringify(
|
|
205
|
+
{
|
|
206
|
+
enabledPlugins: {
|
|
207
|
+
'': true,
|
|
208
|
+
'@': true,
|
|
209
|
+
'notion@notion-marketplace': true,
|
|
210
|
+
'example-skills@anthropic-agent-skills': true,
|
|
211
|
+
'disabled-skills@disabled-marketplace': false,
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
null,
|
|
215
|
+
2,
|
|
216
|
+
),
|
|
217
|
+
'utf8',
|
|
218
|
+
);
|
|
219
|
+
await fs.writeFile(
|
|
220
|
+
path.join(tempRoot, '.claude', 'plugins', 'installed_plugins.json'),
|
|
221
|
+
JSON.stringify(
|
|
222
|
+
{
|
|
223
|
+
version: 2,
|
|
224
|
+
plugins: {
|
|
225
|
+
'': [
|
|
226
|
+
{
|
|
227
|
+
scope: 'user',
|
|
228
|
+
installPath: emptyIdPluginInstallPath,
|
|
229
|
+
version: '000',
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
'@': [
|
|
233
|
+
{
|
|
234
|
+
scope: 'user',
|
|
235
|
+
installPath: atIdPluginInstallPath,
|
|
236
|
+
version: '000',
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
'notion@notion-marketplace': [
|
|
240
|
+
{
|
|
241
|
+
scope: 'user',
|
|
242
|
+
installPath: commandPluginInstallPath,
|
|
243
|
+
version: 'abc123',
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
'example-skills@anthropic-agent-skills': [
|
|
247
|
+
{
|
|
248
|
+
scope: 'user',
|
|
249
|
+
installPath: skillPluginInstallPath,
|
|
250
|
+
version: 'def456',
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
'disabled-skills@disabled-marketplace': [
|
|
254
|
+
{
|
|
255
|
+
scope: 'user',
|
|
256
|
+
installPath: disabledPluginInstallPath,
|
|
257
|
+
version: 'ghi789',
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
null,
|
|
263
|
+
2,
|
|
264
|
+
),
|
|
265
|
+
'utf8',
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const skills = await providerSkillsService.listProviderSkills('claude', { workspacePath });
|
|
269
|
+
const byName = new Map(skills.map((skill) => [skill.name, skill]));
|
|
270
|
+
|
|
271
|
+
assert.equal(byName.get('claude-user')?.scope, 'user');
|
|
272
|
+
assert.equal(byName.get('claude-user')?.command, '/claude-user');
|
|
273
|
+
assert.equal(byName.get('claude-project')?.scope, 'project');
|
|
274
|
+
assert.equal(byName.get('claude-project')?.command, '/claude-project');
|
|
275
|
+
|
|
276
|
+
const pluginCommand = byName.get('insert-row');
|
|
277
|
+
assert.equal(pluginCommand?.scope, 'plugin');
|
|
278
|
+
assert.equal(pluginCommand?.pluginName, 'Notion');
|
|
279
|
+
assert.equal(pluginCommand?.pluginId, 'notion@notion-marketplace');
|
|
280
|
+
assert.equal(pluginCommand?.command, '/Notion:insert-row');
|
|
281
|
+
assert.equal(pluginCommand?.description, 'Insert a Notion database row');
|
|
282
|
+
assert.match(pluginCommand?.sourcePath ?? '', /commands[\\/]insert-row\.md$/);
|
|
283
|
+
assert.equal(byName.has('ignored-command-plugin-skill'), false);
|
|
284
|
+
|
|
285
|
+
const pluginSkill = byName.get('claude-plugin');
|
|
286
|
+
assert.equal(pluginSkill?.scope, 'plugin');
|
|
287
|
+
assert.equal(pluginSkill?.pluginName, 'ExampleSkills');
|
|
288
|
+
assert.equal(pluginSkill?.pluginId, 'example-skills@anthropic-agent-skills');
|
|
289
|
+
assert.equal(pluginSkill?.command, '/ExampleSkills:claude-plugin');
|
|
290
|
+
assert.equal(pluginSkill?.description, 'Claude plugin skill');
|
|
291
|
+
assert.match(
|
|
292
|
+
pluginSkill?.sourcePath ?? '',
|
|
293
|
+
/cache[\\/]anthropic-agent-skills[\\/]example-skills[\\/]def456[\\/]skills[\\/]/,
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const secondPluginSkill = byName.get('claude-plugin-second');
|
|
297
|
+
assert.equal(secondPluginSkill?.scope, 'plugin');
|
|
298
|
+
assert.equal(secondPluginSkill?.command, '/ExampleSkills:claude-plugin-second');
|
|
299
|
+
|
|
300
|
+
const nestedPluginSkill = byName.get('claude-plugin-nested');
|
|
301
|
+
assert.equal(nestedPluginSkill?.scope, 'plugin');
|
|
302
|
+
assert.equal(nestedPluginSkill?.command, '/ExampleSkills:claude-plugin-nested');
|
|
303
|
+
assert.equal(nestedPluginSkill?.description, 'Nested Claude plugin skill');
|
|
304
|
+
|
|
305
|
+
const siblingPluginSkill = byName.get('claude-plugin-sibling');
|
|
306
|
+
assert.equal(siblingPluginSkill?.scope, 'plugin');
|
|
307
|
+
assert.equal(siblingPluginSkill?.pluginName, 'example-skills');
|
|
308
|
+
assert.equal(siblingPluginSkill?.command, '/example-skills:claude-plugin-sibling');
|
|
309
|
+
assert.equal(siblingPluginSkill?.description, 'Sibling Claude plugin skill');
|
|
310
|
+
assert.equal(byName.has('disabled-command'), false);
|
|
311
|
+
assert.equal(byName.has('disabled-plugin'), false);
|
|
312
|
+
assert.equal(byName.has('invalid-empty-command'), false);
|
|
313
|
+
assert.equal(byName.has('invalid-at-command'), false);
|
|
314
|
+
assert.equal(skills.some((skill) => skill.command.startsWith('/:')), false);
|
|
315
|
+
} finally {
|
|
316
|
+
restoreHomeDir();
|
|
317
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* This test covers Codex repository/user/system skill folders and verifies that
|
|
323
|
+
* repository lookup includes cwd, parent, and git root skill locations.
|
|
324
|
+
*/
|
|
325
|
+
test('providerSkillsService lists codex repository, user, and system skills', { concurrency: false }, async () => {
|
|
326
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'llm-skills-codex-'));
|
|
327
|
+
const repoRoot = path.join(tempRoot, 'repo');
|
|
328
|
+
const workspacePath = path.join(repoRoot, 'packages', 'app');
|
|
329
|
+
await fs.mkdir(path.join(repoRoot, '.git'), { recursive: true });
|
|
330
|
+
await fs.mkdir(workspacePath, { recursive: true });
|
|
331
|
+
|
|
332
|
+
const restoreHomeDir = patchHomeDir(tempRoot);
|
|
333
|
+
try {
|
|
334
|
+
await writeSkill(
|
|
335
|
+
path.join(workspacePath, '.agents', 'skills'),
|
|
336
|
+
'codex-cwd-dir',
|
|
337
|
+
'codex-cwd',
|
|
338
|
+
'Codex cwd skill',
|
|
339
|
+
);
|
|
340
|
+
await writeSkill(
|
|
341
|
+
path.join(repoRoot, 'packages', '.agents', 'skills'),
|
|
342
|
+
'codex-parent-dir',
|
|
343
|
+
'codex-parent',
|
|
344
|
+
'Codex parent skill',
|
|
345
|
+
);
|
|
346
|
+
await writeSkill(
|
|
347
|
+
path.join(repoRoot, '.agents', 'skills'),
|
|
348
|
+
'codex-root-dir',
|
|
349
|
+
'codex-root',
|
|
350
|
+
'Codex root skill',
|
|
351
|
+
);
|
|
352
|
+
await writeSkill(
|
|
353
|
+
path.join(tempRoot, '.agents', 'skills'),
|
|
354
|
+
'codex-user-dir',
|
|
355
|
+
'codex-user',
|
|
356
|
+
'Codex user skill',
|
|
357
|
+
);
|
|
358
|
+
await writeSkill(
|
|
359
|
+
path.join(tempRoot, '.codex', 'skills', '.system'),
|
|
360
|
+
'codex-system-dir',
|
|
361
|
+
'codex-system',
|
|
362
|
+
'Codex system skill',
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
const skills = await providerSkillsService.listProviderSkills('codex', { workspacePath });
|
|
366
|
+
const byName = new Map(skills.map((skill) => [skill.name, skill]));
|
|
367
|
+
|
|
368
|
+
assert.equal(byName.get('codex-cwd')?.scope, 'repo');
|
|
369
|
+
assert.equal(byName.get('codex-parent')?.scope, 'repo');
|
|
370
|
+
assert.equal(byName.get('codex-root')?.scope, 'repo');
|
|
371
|
+
assert.equal(byName.get('codex-user')?.scope, 'user');
|
|
372
|
+
assert.equal(byName.get('codex-system')?.scope, 'system');
|
|
373
|
+
assert.equal(byName.get('codex-root')?.command, '$codex-root');
|
|
374
|
+
} finally {
|
|
375
|
+
restoreHomeDir();
|
|
376
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* This test covers OpenCode skill lookup across cwd-to-git-root project folders
|
|
382
|
+
* plus the global OpenCode/Claude/Agents compatibility locations.
|
|
383
|
+
*/
|
|
384
|
+
test('providerSkillsService lists opencode project and user compatibility skills', { concurrency: false }, async () => {
|
|
385
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'llm-skills-opencode-'));
|
|
386
|
+
const repoRoot = path.join(tempRoot, 'repo');
|
|
387
|
+
const workspacePath = path.join(repoRoot, 'packages', 'app');
|
|
388
|
+
await fs.mkdir(path.join(repoRoot, '.git'), { recursive: true });
|
|
389
|
+
await fs.mkdir(workspacePath, { recursive: true });
|
|
390
|
+
|
|
391
|
+
const restoreHomeDir = patchHomeDir(tempRoot);
|
|
392
|
+
try {
|
|
393
|
+
await writeSkill(
|
|
394
|
+
path.join(workspacePath, '.opencode', 'skills'),
|
|
395
|
+
'opencode-cwd-dir',
|
|
396
|
+
'opencode-cwd',
|
|
397
|
+
'OpenCode cwd skill',
|
|
398
|
+
);
|
|
399
|
+
await writeSkill(
|
|
400
|
+
path.join(repoRoot, 'packages', '.claude', 'skills'),
|
|
401
|
+
'opencode-claude-parent-dir',
|
|
402
|
+
'opencode-claude-parent',
|
|
403
|
+
'OpenCode Claude parent skill',
|
|
404
|
+
);
|
|
405
|
+
await writeSkill(
|
|
406
|
+
path.join(repoRoot, '.agents', 'skills'),
|
|
407
|
+
'opencode-agents-root-dir',
|
|
408
|
+
'opencode-agents-root',
|
|
409
|
+
'OpenCode Agents root skill',
|
|
410
|
+
);
|
|
411
|
+
await writeSkill(
|
|
412
|
+
path.join(tempRoot, '.config', 'opencode', 'skills'),
|
|
413
|
+
'opencode-user-dir',
|
|
414
|
+
'opencode-user',
|
|
415
|
+
'OpenCode user skill',
|
|
416
|
+
);
|
|
417
|
+
await writeSkill(
|
|
418
|
+
path.join(tempRoot, '.claude', 'skills'),
|
|
419
|
+
'opencode-claude-user-dir',
|
|
420
|
+
'opencode-claude-user',
|
|
421
|
+
'OpenCode Claude user skill',
|
|
422
|
+
);
|
|
423
|
+
await writeSkill(
|
|
424
|
+
path.join(tempRoot, '.agents', 'skills'),
|
|
425
|
+
'opencode-agents-user-dir',
|
|
426
|
+
'opencode-agents-user',
|
|
427
|
+
'OpenCode Agents user skill',
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
const skills = await providerSkillsService.listProviderSkills('opencode', { workspacePath });
|
|
431
|
+
const byName = new Map(skills.map((skill) => [skill.name, skill]));
|
|
432
|
+
|
|
433
|
+
assert.equal(byName.get('opencode-cwd')?.scope, 'project');
|
|
434
|
+
assert.equal(byName.get('opencode-claude-parent')?.scope, 'project');
|
|
435
|
+
assert.equal(byName.get('opencode-agents-root')?.scope, 'project');
|
|
436
|
+
assert.equal(byName.get('opencode-user')?.scope, 'user');
|
|
437
|
+
assert.equal(byName.get('opencode-claude-user')?.scope, 'user');
|
|
438
|
+
assert.equal(byName.get('opencode-agents-user')?.scope, 'user');
|
|
439
|
+
assert.equal(byName.get('opencode-cwd')?.command, '/opencode-cwd');
|
|
440
|
+
} finally {
|
|
441
|
+
restoreHomeDir();
|
|
442
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* This test covers Gemini and Cursor skill directory rules, including shared
|
|
448
|
+
* `.agents/skills` project support.
|
|
449
|
+
*/
|
|
450
|
+
test('providerSkillsService lists gemini and cursor skills from their configured directories', { concurrency: false }, async () => {
|
|
451
|
+
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'llm-skills-gc-'));
|
|
452
|
+
const workspacePath = path.join(tempRoot, 'workspace');
|
|
453
|
+
await fs.mkdir(workspacePath, { recursive: true });
|
|
454
|
+
|
|
455
|
+
const restoreHomeDir = patchHomeDir(tempRoot);
|
|
456
|
+
try {
|
|
457
|
+
await writeSkill(
|
|
458
|
+
path.join(tempRoot, '.gemini', 'skills'),
|
|
459
|
+
'gemini-user-dir',
|
|
460
|
+
'gemini-user',
|
|
461
|
+
'Gemini user skill',
|
|
462
|
+
);
|
|
463
|
+
await writeSkill(
|
|
464
|
+
path.join(tempRoot, '.agents', 'skills'),
|
|
465
|
+
'agents-user-dir',
|
|
466
|
+
'agents-user',
|
|
467
|
+
'Agents user skill',
|
|
468
|
+
);
|
|
469
|
+
await writeSkill(
|
|
470
|
+
path.join(workspacePath, '.gemini', 'skills'),
|
|
471
|
+
'gemini-project-dir',
|
|
472
|
+
'gemini-project',
|
|
473
|
+
'Gemini project skill',
|
|
474
|
+
);
|
|
475
|
+
await writeSkill(
|
|
476
|
+
path.join(workspacePath, '.agents', 'skills'),
|
|
477
|
+
'agents-project-dir',
|
|
478
|
+
'agents-project',
|
|
479
|
+
'Agents project skill',
|
|
480
|
+
);
|
|
481
|
+
await writeSkill(
|
|
482
|
+
path.join(workspacePath, '.cursor', 'skills'),
|
|
483
|
+
'cursor-project-dir',
|
|
484
|
+
'cursor-project',
|
|
485
|
+
'Cursor project skill',
|
|
486
|
+
);
|
|
487
|
+
await writeSkill(
|
|
488
|
+
path.join(tempRoot, '.cursor', 'skills'),
|
|
489
|
+
'cursor-user-dir',
|
|
490
|
+
'cursor-user',
|
|
491
|
+
'Cursor user skill',
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
const geminiSkills = await providerSkillsService.listProviderSkills('gemini', { workspacePath });
|
|
495
|
+
const geminiByName = new Map(geminiSkills.map((skill) => [skill.name, skill]));
|
|
496
|
+
assert.equal(geminiByName.get('gemini-user')?.scope, 'user');
|
|
497
|
+
assert.equal(geminiByName.get('agents-user')?.scope, 'user');
|
|
498
|
+
assert.equal(geminiByName.get('gemini-project')?.scope, 'project');
|
|
499
|
+
assert.equal(geminiByName.get('agents-project')?.scope, 'project');
|
|
500
|
+
assert.equal(geminiByName.get('gemini-project')?.command, '/gemini-project');
|
|
501
|
+
|
|
502
|
+
const cursorSkills = await providerSkillsService.listProviderSkills('cursor', { workspacePath });
|
|
503
|
+
const cursorByName = new Map(cursorSkills.map((skill) => [skill.name, skill]));
|
|
504
|
+
assert.equal(cursorByName.get('agents-project')?.scope, 'project');
|
|
505
|
+
assert.equal(cursorByName.get('cursor-project')?.scope, 'project');
|
|
506
|
+
assert.equal(cursorByName.get('cursor-user')?.scope, 'user');
|
|
507
|
+
assert.equal(cursorByName.get('cursor-user')?.command, '/cursor-user');
|
|
508
|
+
} finally {
|
|
509
|
+
restoreHomeDir();
|
|
510
|
+
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
511
|
+
}
|
|
512
|
+
});
|
|
@@ -29,10 +29,12 @@ type ChatWebSocketDependencies = {
|
|
|
29
29
|
spawnCursor: (command: string, options: unknown, writer: WebSocketWriter) => Promise<unknown>;
|
|
30
30
|
queryCodex: (command: string, options: unknown, writer: WebSocketWriter) => Promise<unknown>;
|
|
31
31
|
spawnGemini: (command: string, options: unknown, writer: WebSocketWriter) => Promise<unknown>;
|
|
32
|
+
spawnOpenCode: (command: string, options: unknown, writer: WebSocketWriter) => Promise<unknown>;
|
|
32
33
|
abortClaudeSDKSession: (sessionId: string) => Promise<boolean>;
|
|
33
34
|
abortCursorSession: (sessionId: string) => boolean;
|
|
34
35
|
abortCodexSession: (sessionId: string) => boolean;
|
|
35
36
|
abortGeminiSession: (sessionId: string) => boolean;
|
|
37
|
+
abortOpenCodeSession: (sessionId: string) => boolean;
|
|
36
38
|
resolveToolApproval: (
|
|
37
39
|
requestId: string,
|
|
38
40
|
payload: {
|
|
@@ -46,19 +48,21 @@ type ChatWebSocketDependencies = {
|
|
|
46
48
|
isCursorSessionActive: (sessionId: string) => boolean;
|
|
47
49
|
isCodexSessionActive: (sessionId: string) => boolean;
|
|
48
50
|
isGeminiSessionActive: (sessionId: string) => boolean;
|
|
51
|
+
isOpenCodeSessionActive: (sessionId: string) => boolean;
|
|
49
52
|
reconnectSessionWriter: (sessionId: string, ws: WebSocket) => boolean;
|
|
50
53
|
getPendingApprovalsForSession: (sessionId: string) => unknown[];
|
|
51
54
|
getActiveClaudeSDKSessions: () => unknown;
|
|
52
55
|
getActiveCursorSessions: () => unknown;
|
|
53
56
|
getActiveCodexSessions: () => unknown;
|
|
54
57
|
getActiveGeminiSessions: () => unknown;
|
|
58
|
+
getActiveOpenCodeSessions: () => unknown;
|
|
55
59
|
};
|
|
56
60
|
|
|
57
61
|
/**
|
|
58
62
|
* Normalizes potentially invalid provider names coming from websocket payloads.
|
|
59
63
|
*/
|
|
60
64
|
function readProvider(value: unknown): LLMProvider {
|
|
61
|
-
if (value === 'claude' || value === 'cursor' || value === 'codex' || value === 'gemini') {
|
|
65
|
+
if (value === 'claude' || value === 'cursor' || value === 'codex' || value === 'gemini' || value === 'opencode') {
|
|
62
66
|
return value;
|
|
63
67
|
}
|
|
64
68
|
|
|
@@ -134,6 +138,11 @@ export function handleChatConnection(
|
|
|
134
138
|
return;
|
|
135
139
|
}
|
|
136
140
|
|
|
141
|
+
if (messageType === 'opencode-command') {
|
|
142
|
+
await dependencies.spawnOpenCode(data.command ?? '', data.options, writer);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
137
146
|
if (messageType === 'cursor-resume') {
|
|
138
147
|
await dependencies.spawnCursor(
|
|
139
148
|
'',
|
|
@@ -158,6 +167,8 @@ export function handleChatConnection(
|
|
|
158
167
|
success = dependencies.abortCodexSession(sessionId);
|
|
159
168
|
} else if (provider === 'gemini') {
|
|
160
169
|
success = dependencies.abortGeminiSession(sessionId);
|
|
170
|
+
} else if (provider === 'opencode') {
|
|
171
|
+
success = dependencies.abortOpenCodeSession(sessionId);
|
|
161
172
|
} else {
|
|
162
173
|
success = await dependencies.abortClaudeSDKSession(sessionId);
|
|
163
174
|
}
|
|
@@ -214,6 +225,8 @@ export function handleChatConnection(
|
|
|
214
225
|
isActive = dependencies.isCodexSessionActive(sessionId);
|
|
215
226
|
} else if (provider === 'gemini') {
|
|
216
227
|
isActive = dependencies.isGeminiSessionActive(sessionId);
|
|
228
|
+
} else if (provider === 'opencode') {
|
|
229
|
+
isActive = dependencies.isOpenCodeSessionActive(sessionId);
|
|
217
230
|
} else {
|
|
218
231
|
isActive = dependencies.isClaudeSDKSessionActive(sessionId);
|
|
219
232
|
if (isActive) {
|
|
@@ -251,6 +264,7 @@ export function handleChatConnection(
|
|
|
251
264
|
cursor: dependencies.getActiveCursorSessions(),
|
|
252
265
|
codex: dependencies.getActiveCodexSessions(),
|
|
253
266
|
gemini: dependencies.getActiveGeminiSessions(),
|
|
267
|
+
opencode: dependencies.getActiveOpenCodeSessions(),
|
|
254
268
|
},
|
|
255
269
|
});
|
|
256
270
|
}
|
|
@@ -26,15 +26,15 @@ export function handlePluginWsProxy(
|
|
|
26
26
|
console.log(`[Plugins] WS proxy connected to "${pluginName}" on port ${port}`);
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
upstream.on('message', (data) => {
|
|
29
|
+
upstream.on('message', (data, isBinary) => {
|
|
30
30
|
if (clientWs.readyState === WebSocket.OPEN) {
|
|
31
|
-
clientWs.send(data);
|
|
31
|
+
clientWs.send(data, { binary: isBinary });
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
clientWs.on('message', (data) => {
|
|
35
|
+
clientWs.on('message', (data, isBinary) => {
|
|
36
36
|
if (upstream.readyState === WebSocket.OPEN) {
|
|
37
|
-
upstream.send(data);
|
|
37
|
+
upstream.send(data, { binary: isBinary });
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
40
|
|
|
@@ -136,6 +136,13 @@ function buildShellCommand(
|
|
|
136
136
|
return command;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
if (provider === 'opencode') {
|
|
140
|
+
if (hasSession && sessionId) {
|
|
141
|
+
return `opencode --session "${sessionId}"`;
|
|
142
|
+
}
|
|
143
|
+
return initialCommand || 'opencode';
|
|
144
|
+
}
|
|
145
|
+
|
|
139
146
|
const command = initialCommand || 'claude';
|
|
140
147
|
if (hasSession && sessionId) {
|
|
141
148
|
if (os.platform() === 'win32') {
|
|
@@ -389,6 +396,8 @@ export function handleShellConnection(
|
|
|
389
396
|
? 'Codex'
|
|
390
397
|
: provider === 'gemini'
|
|
391
398
|
? 'Gemini'
|
|
399
|
+
: provider === 'opencode'
|
|
400
|
+
? 'OpenCode'
|
|
392
401
|
: 'Claude';
|
|
393
402
|
welcomeMsg = hasSession
|
|
394
403
|
? `\x1b[36mResuming ${providerName} session ${sessionId} in: ${projectPath}\x1b[0m\r\n`
|