@cloudcli-ai/cloudcli 1.31.5 → 1.32.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/assets/index-AaSE7-3t.js +1336 -0
- package/dist/assets/index-CKaku72N.css +32 -0
- package/dist/index.html +2 -2
- package/dist-server/server/claude-sdk.js +10 -5
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cursor-cli.js +0 -6
- package/dist-server/server/cursor-cli.js.map +1 -1
- package/dist-server/server/gemini-cli.js +160 -34
- package/dist-server/server/gemini-cli.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/projects-with-sessions-fetch.service.js +43 -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 +2 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
- 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 +2 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
- 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 +82 -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 +2 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
- 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 +189 -13
- 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 +2 -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-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 +7 -1
- 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 +2 -0
- package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +23 -2
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- 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/sessions-watcher.service.js +7 -5
- 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/skills.test.js +226 -0
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -0
- package/dist-server/server/openai-codex.js +56 -26
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/routes/commands.js +5 -5
- package/dist-server/server/routes/commands.js.map +1 -1
- 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 +82 -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/dist-server/shared/modelConstants.js +1 -0
- package/dist-server/shared/modelConstants.js.map +1 -1
- package/package.json +1 -1
- package/server/claude-sdk.js +10 -5
- package/server/cursor-cli.js +0 -8
- package/server/gemini-cli.js +190 -42
- 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/projects-with-sessions-fetch.service.ts +64 -0
- package/server/modules/providers/README.md +346 -0
- package/server/modules/providers/index.ts +2 -1
- package/server/modules/providers/list/claude/claude-auth.provider.ts +8 -7
- 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 +8 -1
- 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 +100 -0
- package/server/modules/providers/list/codex/codex.provider.ts +8 -1
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +17 -40
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +216 -13
- package/server/modules/providers/list/cursor/cursor-skills.provider.ts +31 -0
- package/server/modules/providers/list/cursor/cursor.provider.ts +8 -1
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +158 -2
- package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +24 -20
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +7 -1
- package/server/modules/providers/list/gemini/gemini-skills.provider.ts +36 -0
- package/server/modules/providers/list/gemini/gemini.provider.ts +8 -1
- package/server/modules/providers/provider.routes.ts +35 -2
- package/server/modules/providers/services/session-conversations-search.service.ts +162 -13
- package/server/modules/providers/services/sessions-watcher.service.ts +7 -5
- 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 +2 -0
- package/server/modules/providers/shared/skills/skills.provider.ts +64 -0
- package/server/modules/providers/tests/skills.test.ts +446 -0
- package/server/openai-codex.js +59 -27
- package/server/routes/commands.js +7 -5
- 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 +19 -0
- package/server/shared/types.ts +78 -0
- package/server/shared/utils.ts +94 -0
- package/server/utils/commandParser.js +5 -3
- package/shared/modelConstants.js +1 -0
- package/dist/assets/index-6rYG_mRo.css +0 -32
- package/dist/assets/index-DqMVUeZS.js +0 -1331
- package/dist-server/server/utils/frontmatter.js.map +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
resolveClaudeCodeExecutablePath,
|
|
6
|
+
type ResolveClaudeCodeExecutablePathDependencies,
|
|
7
|
+
} from '@/shared/claude-cli-path.js';
|
|
8
|
+
|
|
9
|
+
test('resolveClaudeCodeExecutablePath resolves the npm Claude wrapper to its native exe on Windows', () => {
|
|
10
|
+
const wrapperDir = 'C:\\nvm4w\\nodejs';
|
|
11
|
+
const nativePath = `${wrapperDir}\\node_modules\\@anthropic-ai\\claude-code\\bin\\claude.exe`;
|
|
12
|
+
const execFileSync =
|
|
13
|
+
(() => `${wrapperDir}\\claude\r\n${wrapperDir}\\claude.cmd\r\n`) as unknown as ResolveClaudeCodeExecutablePathDependencies['execFileSync'];
|
|
14
|
+
const readFileSync = (() => '') as unknown as ResolveClaudeCodeExecutablePathDependencies['readFileSync'];
|
|
15
|
+
|
|
16
|
+
const resolved = resolveClaudeCodeExecutablePath('claude', {
|
|
17
|
+
platform: 'win32',
|
|
18
|
+
execFileSync,
|
|
19
|
+
existsSync: (candidate) => candidate === nativePath,
|
|
20
|
+
readFileSync,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
assert.equal(resolved, nativePath);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('resolveClaudeCodeExecutablePath keeps an explicit JavaScript launcher path unchanged', () => {
|
|
27
|
+
const scriptPath = 'C:\\tools\\claude.js';
|
|
28
|
+
|
|
29
|
+
const resolved = resolveClaudeCodeExecutablePath(scriptPath, {
|
|
30
|
+
platform: 'win32',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
assert.equal(resolved, scriptPath);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('resolveClaudeCodeExecutablePath can parse a wrapper file path containing letters r and n before claude.exe', () => {
|
|
37
|
+
const wrapperPath = 'C:\\tools\\claude';
|
|
38
|
+
const nativePath = 'C:\\tools\\custom\\bin\\node_modules\\@anthropic-ai\\claude-code\\bin\\claude.exe';
|
|
39
|
+
const readFileSync = (() => `exec "$basedir/custom/bin/node_modules/@anthropic-ai/claude-code/bin/claude.exe" "$@"`) as unknown as ResolveClaudeCodeExecutablePathDependencies['readFileSync'];
|
|
40
|
+
|
|
41
|
+
const resolved = resolveClaudeCodeExecutablePath(wrapperPath, {
|
|
42
|
+
platform: 'win32',
|
|
43
|
+
existsSync: (candidate) => candidate === nativePath,
|
|
44
|
+
readFileSync,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
assert.equal(resolved, nativePath);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('resolveClaudeCodeExecutablePath falls back to the configured command when PATH lookup fails', () => {
|
|
51
|
+
const execFileSync = (() => {
|
|
52
|
+
throw new Error('not found');
|
|
53
|
+
}) as unknown as ResolveClaudeCodeExecutablePathDependencies['execFileSync'];
|
|
54
|
+
|
|
55
|
+
const resolved = resolveClaudeCodeExecutablePath('claude', {
|
|
56
|
+
platform: 'win32',
|
|
57
|
+
execFileSync,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
assert.equal(resolved, 'claude');
|
|
61
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const DEFAULT_CLAUDE_COMMAND = 'claude';
|
|
6
|
+
const CLAUDE_SCRIPT_EXTENSIONS = new Set(['.cjs', '.js', '.jsx', '.mjs', '.ts', '.tsx']);
|
|
7
|
+
const CLAUDE_WRAPPER_SEGMENTS = ['node_modules', '@anthropic-ai', 'claude-code', 'bin', 'claude.exe'] as const;
|
|
8
|
+
|
|
9
|
+
export type ResolveClaudeCodeExecutablePathDependencies = {
|
|
10
|
+
execFileSync?: typeof execFileSync;
|
|
11
|
+
existsSync?: typeof fs.existsSync;
|
|
12
|
+
platform?: NodeJS.Platform;
|
|
13
|
+
readFileSync?: typeof fs.readFileSync;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function getPathApi(platform: NodeJS.Platform) {
|
|
17
|
+
return platform === 'win32' ? path.win32 : path;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function stripWrappingQuotes(value: string): string {
|
|
21
|
+
const trimmed = value.trim();
|
|
22
|
+
if (
|
|
23
|
+
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
24
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
25
|
+
) {
|
|
26
|
+
return trimmed.slice(1, -1);
|
|
27
|
+
}
|
|
28
|
+
return trimmed;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isPathLike(value: string): boolean {
|
|
32
|
+
return value.includes('/') || value.includes('\\');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function resolveClaudeWrapperBinary(
|
|
36
|
+
wrapperPath: string,
|
|
37
|
+
deps: Required<ResolveClaudeCodeExecutablePathDependencies>,
|
|
38
|
+
): string | null {
|
|
39
|
+
const pathApi = getPathApi(deps.platform);
|
|
40
|
+
const directCandidate = pathApi.resolve(pathApi.dirname(wrapperPath), ...CLAUDE_WRAPPER_SEGMENTS);
|
|
41
|
+
|
|
42
|
+
if (deps.existsSync(directCandidate)) {
|
|
43
|
+
return directCandidate;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let content: string;
|
|
47
|
+
try {
|
|
48
|
+
content = deps.readFileSync(wrapperPath, 'utf8');
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const matches = content.matchAll(/["']([^"'\\\r\n]*claude\.exe)["']/gi);
|
|
54
|
+
for (const match of matches) {
|
|
55
|
+
const rawTarget = match[1]
|
|
56
|
+
.replace(/^\$basedir[\\/]/i, '')
|
|
57
|
+
.replace(/^%dp0%[\\/]/i, '')
|
|
58
|
+
.replace(/^%~dp0[\\/]/i, '');
|
|
59
|
+
const normalizedTarget = rawTarget.replace(/[\\/]/g, pathApi.sep);
|
|
60
|
+
const candidate = pathApi.isAbsolute(normalizedTarget)
|
|
61
|
+
? normalizedTarget
|
|
62
|
+
: pathApi.resolve(pathApi.dirname(wrapperPath), normalizedTarget);
|
|
63
|
+
|
|
64
|
+
if (deps.existsSync(candidate)) {
|
|
65
|
+
return candidate;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function resolveWindowsClaudeExecutablePath(
|
|
73
|
+
configuredPath: string,
|
|
74
|
+
deps: Required<ResolveClaudeCodeExecutablePathDependencies>,
|
|
75
|
+
): string {
|
|
76
|
+
const pathApi = getPathApi(deps.platform);
|
|
77
|
+
const extension = pathApi.extname(configuredPath).toLowerCase();
|
|
78
|
+
const explicitPath = isPathLike(configuredPath) || pathApi.isAbsolute(configuredPath);
|
|
79
|
+
|
|
80
|
+
if (CLAUDE_SCRIPT_EXTENSIONS.has(extension)) {
|
|
81
|
+
return configuredPath;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (explicitPath && extension === '.exe') {
|
|
85
|
+
return configuredPath;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (explicitPath) {
|
|
89
|
+
return resolveClaudeWrapperBinary(configuredPath, deps) ?? configuredPath;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const stdout = deps.execFileSync('where.exe', [configuredPath], {
|
|
94
|
+
encoding: 'utf8',
|
|
95
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
96
|
+
windowsHide: true,
|
|
97
|
+
});
|
|
98
|
+
const candidates = stdout
|
|
99
|
+
.split(/\r?\n/)
|
|
100
|
+
.map((entry) => entry.trim())
|
|
101
|
+
.filter(Boolean);
|
|
102
|
+
|
|
103
|
+
for (const candidate of candidates) {
|
|
104
|
+
if (pathApi.extname(candidate).toLowerCase() === '.exe') {
|
|
105
|
+
return candidate;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const candidate of candidates) {
|
|
110
|
+
const resolved = resolveClaudeWrapperBinary(candidate, deps);
|
|
111
|
+
if (resolved) {
|
|
112
|
+
return resolved;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
return configuredPath;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return configuredPath;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function resolveClaudeCodeExecutablePath(
|
|
123
|
+
configuredPath: string | undefined = process.env.CLAUDE_CLI_PATH,
|
|
124
|
+
dependencies: ResolveClaudeCodeExecutablePathDependencies = {},
|
|
125
|
+
): string {
|
|
126
|
+
const deps: Required<ResolveClaudeCodeExecutablePathDependencies> = {
|
|
127
|
+
execFileSync: dependencies.execFileSync ?? execFileSync,
|
|
128
|
+
existsSync: dependencies.existsSync ?? fs.existsSync,
|
|
129
|
+
platform: dependencies.platform ?? process.platform,
|
|
130
|
+
readFileSync: dependencies.readFileSync ?? fs.readFileSync,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const normalizedPath = stripWrappingQuotes(configuredPath || DEFAULT_CLAUDE_COMMAND);
|
|
134
|
+
if (deps.platform !== 'win32') {
|
|
135
|
+
return normalizedPath;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return resolveWindowsClaudeExecutablePath(normalizedPath, deps);
|
|
139
|
+
}
|
|
@@ -9,10 +9,10 @@ const frontmatterOptions = {
|
|
|
9
9
|
engines: {
|
|
10
10
|
js: disabledFrontmatterEngine,
|
|
11
11
|
javascript: disabledFrontmatterEngine,
|
|
12
|
-
json: disabledFrontmatterEngine
|
|
13
|
-
}
|
|
12
|
+
json: disabledFrontmatterEngine,
|
|
13
|
+
},
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export function
|
|
16
|
+
export function parseFrontMatter(content: string) {
|
|
17
17
|
return matter(content, frontmatterOptions);
|
|
18
18
|
}
|
|
@@ -4,6 +4,8 @@ import type {
|
|
|
4
4
|
LLMProvider,
|
|
5
5
|
McpScope,
|
|
6
6
|
NormalizedMessage,
|
|
7
|
+
ProviderSkill,
|
|
8
|
+
ProviderSkillListOptions,
|
|
7
9
|
ProviderAuthStatus,
|
|
8
10
|
ProviderMcpServer,
|
|
9
11
|
UpsertProviderMcpServerInput,
|
|
@@ -20,6 +22,7 @@ export interface IProvider {
|
|
|
20
22
|
readonly id: LLMProvider;
|
|
21
23
|
readonly mcp: IProviderMcp;
|
|
22
24
|
readonly auth: IProviderAuth;
|
|
25
|
+
readonly skills: IProviderSkills;
|
|
23
26
|
readonly sessions: IProviderSessions;
|
|
24
27
|
readonly sessionSynchronizer: IProviderSessionSynchronizer;
|
|
25
28
|
}
|
|
@@ -39,6 +42,22 @@ export interface IProviderAuth {
|
|
|
39
42
|
getStatus(): Promise<ProviderAuthStatus>;
|
|
40
43
|
}
|
|
41
44
|
|
|
45
|
+
// ---------------------------
|
|
46
|
+
//----------------- PROVIDER SKILLS INTERFACE ------------
|
|
47
|
+
/**
|
|
48
|
+
* Skills contract for one provider.
|
|
49
|
+
*
|
|
50
|
+
* Implementations discover provider-native skill markdown locations and return
|
|
51
|
+
* normalized skill records with the exact command syntax expected by that
|
|
52
|
+
* provider. Each skill is read from a `SKILL.md` file under its skill directory.
|
|
53
|
+
*/
|
|
54
|
+
export interface IProviderSkills {
|
|
55
|
+
/**
|
|
56
|
+
* Lists all skills visible to this provider for the optional workspace.
|
|
57
|
+
*/
|
|
58
|
+
listSkills(options?: ProviderSkillListOptions): Promise<ProviderSkill[]>;
|
|
59
|
+
}
|
|
60
|
+
|
|
42
61
|
// ---------------------------
|
|
43
62
|
//----------------- PROVIDER MCP INTERFACE ------------
|
|
44
63
|
/**
|
package/server/shared/types.ts
CHANGED
|
@@ -102,6 +102,21 @@ export type NormalizedMessage = {
|
|
|
102
102
|
kind: MessageKind;
|
|
103
103
|
role?: 'user' | 'assistant';
|
|
104
104
|
content?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Optional display-oriented metadata used by providers that need to expose
|
|
107
|
+
* richer transcript artifacts without introducing a brand-new message kind.
|
|
108
|
+
*
|
|
109
|
+
* Current Claude usage:
|
|
110
|
+
* - local slash commands expose parsed command fields
|
|
111
|
+
* - compact summaries are flagged so the UI can treat them differently later
|
|
112
|
+
*/
|
|
113
|
+
displayText?: string;
|
|
114
|
+
commandName?: string;
|
|
115
|
+
commandMessage?: string;
|
|
116
|
+
commandArgs?: string;
|
|
117
|
+
isLocalCommand?: boolean;
|
|
118
|
+
isLocalCommandStdout?: boolean;
|
|
119
|
+
isCompactSummary?: boolean;
|
|
105
120
|
images?: unknown;
|
|
106
121
|
toolName?: string;
|
|
107
122
|
toolInput?: unknown;
|
|
@@ -156,6 +171,69 @@ export type FetchHistoryResult = {
|
|
|
156
171
|
tokenUsage?: unknown;
|
|
157
172
|
};
|
|
158
173
|
|
|
174
|
+
// ---------------------------
|
|
175
|
+
//----------------- PROVIDER SKILL TYPES ------------
|
|
176
|
+
/**
|
|
177
|
+
* Scope where a provider skill definition was discovered.
|
|
178
|
+
*
|
|
179
|
+
* Provider skill adapters should use this to describe the origin of each
|
|
180
|
+
* skill markdown file without leaking provider-specific folder names into route
|
|
181
|
+
* contracts. `repo` is used for Codex repository lookup locations, while
|
|
182
|
+
* `project` is used for providers that treat workspace-local skills as project
|
|
183
|
+
* scoped.
|
|
184
|
+
*/
|
|
185
|
+
export type ProviderSkillScope = 'user' | 'project' | 'plugin' | 'repo' | 'admin' | 'system';
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Shared input accepted by provider skill listing operations.
|
|
189
|
+
*
|
|
190
|
+
* Routes pass `workspacePath` when a caller wants project/repository skills for
|
|
191
|
+
* a specific folder. Providers should fall back to the backend process cwd when
|
|
192
|
+
* this option is omitted.
|
|
193
|
+
*/
|
|
194
|
+
export type ProviderSkillListOptions = {
|
|
195
|
+
workspacePath?: string;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Normalized skill record returned by provider skill adapters.
|
|
200
|
+
*
|
|
201
|
+
* The `command` value is the exact invocation text the selected provider expects
|
|
202
|
+
* for this skill. Claude plugin skills use a namespaced command such as
|
|
203
|
+
* `/plugin-name:skill-name`, while Codex skills use the `$skill-name` form.
|
|
204
|
+
* `sourcePath` points to the skill markdown file that produced the record so
|
|
205
|
+
* callers can distinguish duplicate skill names across scopes.
|
|
206
|
+
*/
|
|
207
|
+
export type ProviderSkill = {
|
|
208
|
+
provider: LLMProvider;
|
|
209
|
+
name: string;
|
|
210
|
+
description: string;
|
|
211
|
+
command: string;
|
|
212
|
+
scope: ProviderSkillScope;
|
|
213
|
+
sourcePath: string;
|
|
214
|
+
pluginName?: string;
|
|
215
|
+
pluginId?: string;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Internal source descriptor consumed by shared provider skill discovery logic.
|
|
220
|
+
*
|
|
221
|
+
* Concrete provider adapters build these records from their native lookup rules.
|
|
222
|
+
* The shared skills provider then scans `rootDir` for child skill markdown files
|
|
223
|
+
* and uses `commandForSkill` or `commandPrefix` to produce the provider-specific
|
|
224
|
+
* invocation command. Set `recursive` only when a provider stores skills under
|
|
225
|
+
* arbitrary nested folders below the source root.
|
|
226
|
+
*/
|
|
227
|
+
export type ProviderSkillSource = {
|
|
228
|
+
scope: ProviderSkillScope;
|
|
229
|
+
rootDir: string;
|
|
230
|
+
recursive?: boolean;
|
|
231
|
+
commandPrefix?: '/' | '$';
|
|
232
|
+
commandForSkill?: (skillName: string) => string;
|
|
233
|
+
pluginName?: string;
|
|
234
|
+
pluginId?: string;
|
|
235
|
+
};
|
|
236
|
+
|
|
159
237
|
// ---------------------------
|
|
160
238
|
//----------------- SHARED ERROR TYPES ------------
|
|
161
239
|
/**
|
package/server/shared/utils.ts
CHANGED
|
@@ -17,6 +17,7 @@ import readline from 'node:readline';
|
|
|
17
17
|
|
|
18
18
|
import type { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
19
19
|
|
|
20
|
+
import { parseFrontMatter } from '@/shared/frontmatter.js';
|
|
20
21
|
import type {
|
|
21
22
|
AnyRecord,
|
|
22
23
|
ApiSuccessShape,
|
|
@@ -503,6 +504,99 @@ export const writeJsonConfig = async (filePath: string, data: Record<string, unk
|
|
|
503
504
|
await writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
|
|
504
505
|
};
|
|
505
506
|
|
|
507
|
+
// ---------------------------
|
|
508
|
+
//----------------- PROVIDER SKILL FILE UTILITIES ------------
|
|
509
|
+
/**
|
|
510
|
+
* Finds direct child skill markdown files under a provider skill root.
|
|
511
|
+
*
|
|
512
|
+
* Skill systems usually store one skill per child directory, so direct mode
|
|
513
|
+
* scans only `<root>/<skill-name>/SKILL.md`. Recursive mode is reserved for
|
|
514
|
+
* provider sources that can nest skills arbitrarily, and it returns every
|
|
515
|
+
* descendant `SKILL.md`. Missing or unreadable roots return an empty list
|
|
516
|
+
* because users may not have every provider installed or configured.
|
|
517
|
+
*/
|
|
518
|
+
export async function findProviderSkillMarkdownFiles(
|
|
519
|
+
rootDir: string,
|
|
520
|
+
options: { recursive?: boolean } = {},
|
|
521
|
+
): Promise<string[]> {
|
|
522
|
+
const skillFiles: string[] = [];
|
|
523
|
+
|
|
524
|
+
const collectRecursive = async (dirPath: string): Promise<void> => {
|
|
525
|
+
let entries;
|
|
526
|
+
try {
|
|
527
|
+
entries = await readdir(dirPath, { withFileTypes: true });
|
|
528
|
+
} catch {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
try {
|
|
533
|
+
const skillPath = path.join(dirPath, 'SKILL.md');
|
|
534
|
+
const skillStats = await stat(skillPath);
|
|
535
|
+
if (skillStats.isFile()) {
|
|
536
|
+
skillFiles.push(skillPath);
|
|
537
|
+
}
|
|
538
|
+
} catch {
|
|
539
|
+
// Directories without SKILL.md are expected while walking plugin trees.
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
for (const entry of entries) {
|
|
543
|
+
if (entry.isDirectory()) {
|
|
544
|
+
await collectRecursive(path.join(dirPath, entry.name));
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
if (options.recursive) {
|
|
550
|
+
await collectRecursive(rootDir);
|
|
551
|
+
return skillFiles.sort((left, right) => left.localeCompare(right));
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
try {
|
|
555
|
+
const entries = await readdir(rootDir, { withFileTypes: true });
|
|
556
|
+
|
|
557
|
+
for (const entry of entries) {
|
|
558
|
+
if (!entry.isDirectory()) {
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const skillPath = path.join(rootDir, entry.name, 'SKILL.md');
|
|
563
|
+
try {
|
|
564
|
+
const skillStats = await stat(skillPath);
|
|
565
|
+
if (skillStats.isFile()) {
|
|
566
|
+
skillFiles.push(skillPath);
|
|
567
|
+
}
|
|
568
|
+
} catch {
|
|
569
|
+
// A partial skill directory should not block discovery of sibling skills.
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return skillFiles.sort((left, right) => left.localeCompare(right));
|
|
574
|
+
} catch {
|
|
575
|
+
return [];
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Reads the `name` and `description` fields from a provider skill markdown file.
|
|
581
|
+
*
|
|
582
|
+
* The metadata is expected in markdown front matter. If a skill omits `name`, the
|
|
583
|
+
* parent directory name is used as a stable fallback so providers can still
|
|
584
|
+
* expose the skill. Missing descriptions are normalized to an empty string.
|
|
585
|
+
*/
|
|
586
|
+
export async function readProviderSkillMarkdownDefinition(
|
|
587
|
+
skillPath: string,
|
|
588
|
+
): Promise<{ name: string; description: string }> {
|
|
589
|
+
const content = await readFile(skillPath, 'utf8');
|
|
590
|
+
const parsed = parseFrontMatter(content);
|
|
591
|
+
const data = readObjectRecord(parsed.data) ?? {};
|
|
592
|
+
const fallbackName = path.basename(path.dirname(skillPath));
|
|
593
|
+
|
|
594
|
+
return {
|
|
595
|
+
name: readOptionalString(data.name) ?? fallbackName,
|
|
596
|
+
description: readOptionalString(data.description) ?? '',
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
|
|
506
600
|
// ---------------------------
|
|
507
601
|
//----------------- SESSION SYNCHRONIZER TITLE HELPERS ------------
|
|
508
602
|
/**
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { execFile } from 'child_process';
|
|
1
2
|
import { promises as fs } from 'fs';
|
|
2
3
|
import path from 'path';
|
|
3
|
-
import { execFile } from 'child_process';
|
|
4
4
|
import { promisify } from 'util';
|
|
5
|
+
|
|
5
6
|
import { parse as parseShellCommand } from 'shell-quote';
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
import { parseFrontMatter } from '../shared/frontmatter.js';
|
|
7
9
|
|
|
8
10
|
const execFileAsync = promisify(execFile);
|
|
9
11
|
|
|
@@ -32,7 +34,7 @@ const BASH_COMMAND_ALLOWLIST = [
|
|
|
32
34
|
*/
|
|
33
35
|
export function parseCommand(content) {
|
|
34
36
|
try {
|
|
35
|
-
const parsed =
|
|
37
|
+
const parsed = parseFrontMatter(content);
|
|
36
38
|
return {
|
|
37
39
|
data: parsed.data || {},
|
|
38
40
|
content: parsed.content || '',
|
package/shared/modelConstants.js
CHANGED
|
@@ -84,6 +84,7 @@ export const GEMINI_MODELS = {
|
|
|
84
84
|
{ value: "gemini-2.5-flash", label: "Gemini 2.5 Flash" },
|
|
85
85
|
{ value: "gemini-2.5-pro", label: "Gemini 2.5 Pro" },
|
|
86
86
|
{ value: "gemini-2.0-flash-lite", label: "Gemini 2.0 Flash Lite" },
|
|
87
|
+
{ value: "gemini-2.5-flash-lite", label: "Gemini 2.5 Flash Lite" },
|
|
87
88
|
{ value: "gemini-2.0-flash", label: "Gemini 2.0 Flash" },
|
|
88
89
|
{ value: "gemini-2.0-pro-exp", label: "Gemini 2.0 Pro Experimental" },
|
|
89
90
|
{
|