runwork 0.5.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/__tests__/claude-code-stats.test.d.ts +1 -0
- package/dist/agents/__tests__/claude-code-stats.test.js +153 -0
- package/dist/agents/__tests__/claude-desktop-stats.test.d.ts +1 -0
- package/dist/agents/__tests__/claude-desktop-stats.test.js +280 -0
- package/dist/agents/__tests__/codex-stats.test.d.ts +1 -0
- package/dist/agents/__tests__/codex-stats.test.js +262 -0
- package/dist/agents/__tests__/cursor-stats.test.d.ts +1 -0
- package/dist/agents/__tests__/cursor-stats.test.js +183 -0
- package/dist/agents/__tests__/graceful-degradation.test.d.ts +1 -0
- package/dist/agents/__tests__/graceful-degradation.test.js +123 -0
- package/dist/agents/__tests__/instruction-hint.test.d.ts +1 -0
- package/dist/agents/__tests__/instruction-hint.test.js +69 -0
- package/dist/agents/__tests__/intro-skill.test.d.ts +1 -0
- package/dist/agents/__tests__/intro-skill.test.js +148 -0
- package/dist/agents/__tests__/schema-validation.test.d.ts +1 -0
- package/dist/agents/__tests__/schema-validation.test.js +212 -0
- package/dist/agents/claude-code.d.ts +16 -1
- package/dist/agents/claude-code.js +344 -21
- package/dist/agents/claude-desktop.d.ts +10 -1
- package/dist/agents/claude-desktop.js +250 -8
- package/dist/agents/cline.d.ts +13 -0
- package/dist/agents/cline.js +88 -0
- package/dist/agents/codex.d.ts +7 -2
- package/dist/agents/codex.js +130 -18
- package/dist/agents/cursor.d.ts +6 -1
- package/dist/agents/cursor.js +164 -7
- package/dist/agents/detect.js +30 -7
- package/dist/agents/gemini.d.ts +6 -3
- package/dist/agents/gemini.js +57 -20
- package/dist/agents/generic-adapter.d.ts +23 -0
- package/dist/agents/generic-adapter.js +106 -0
- package/dist/agents/intro-skill.d.ts +36 -0
- package/dist/agents/intro-skill.js +358 -0
- package/dist/agents/registry.d.ts +53 -0
- package/dist/agents/registry.js +219 -0
- package/dist/agents/types.d.ts +44 -1
- package/dist/agents/types.js +11 -4
- package/dist/agents/utils/instruction-hint.d.ts +11 -0
- package/dist/agents/utils/instruction-hint.js +62 -0
- package/dist/agents/utils/json-config.js +3 -3
- package/dist/agents/windsurf.d.ts +2 -0
- package/dist/agents/windsurf.js +26 -9
- package/dist/api/client.d.ts +85 -2
- package/dist/api/client.js +40 -10
- package/dist/auth/__tests__/login-flow.test.d.ts +1 -0
- package/dist/auth/__tests__/login-flow.test.js +125 -0
- package/dist/auth/login-flow.d.ts +16 -0
- package/dist/auth/login-flow.js +101 -1
- package/dist/commands/clone.d.ts +9 -1
- package/dist/commands/clone.js +40 -12
- package/dist/commands/deploy.js +27 -4
- package/dist/commands/dev.js +35 -1
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +82 -0
- package/dist/commands/info.js +6 -2
- package/dist/commands/init.d.ts +10 -2
- package/dist/commands/init.js +58 -15
- package/dist/commands/integrations.js +47 -10
- package/dist/commands/login.js +31 -4
- package/dist/commands/mcp.js +143 -15
- package/dist/commands/setup.js +47 -122
- package/dist/commands/skills.js +230 -12
- package/dist/commands/sync.d.ts +8 -0
- package/dist/commands/sync.js +398 -65
- package/dist/commands/welcome.js +6 -3
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/health/__tests__/checks.test.d.ts +1 -0
- package/dist/health/__tests__/checks.test.js +223 -0
- package/dist/health/checks.d.ts +20 -0
- package/dist/health/checks.js +392 -0
- package/dist/health/runner.d.ts +2 -0
- package/dist/health/runner.js +26 -0
- package/dist/health/types.d.ts +12 -0
- package/dist/health/types.js +1 -0
- package/dist/index.js +32 -0
- package/dist/sync/__tests__/change-detect.test.d.ts +1 -0
- package/dist/sync/__tests__/change-detect.test.js +123 -0
- package/dist/sync/__tests__/hash.test.d.ts +1 -0
- package/dist/sync/__tests__/hash.test.js +18 -0
- package/dist/sync/change-detect.d.ts +19 -0
- package/dist/sync/change-detect.js +136 -0
- package/dist/sync/conflict-ui.d.ts +5 -0
- package/dist/sync/conflict-ui.js +78 -0
- package/dist/sync/executor.d.ts +21 -0
- package/dist/sync/executor.js +98 -0
- package/dist/sync/hash.d.ts +1 -0
- package/dist/sync/hash.js +4 -0
- package/dist/sync/types.d.ts +24 -0
- package/dist/sync/types.js +1 -0
- package/dist/types.d.ts +12 -0
- package/dist/ui/__tests__/banner.test.js +17 -1
- package/dist/ui/banner.d.ts +1 -1
- package/dist/ui/banner.js +27 -2
- package/dist/utils/__tests__/resolve.test.d.ts +1 -0
- package/dist/utils/__tests__/resolve.test.js +95 -0
- package/dist/utils/__tests__/sqlite.test.d.ts +1 -0
- package/dist/utils/__tests__/sqlite.test.js +77 -0
- package/dist/utils/agent-guidance.d.ts +32 -0
- package/dist/utils/agent-guidance.js +106 -0
- package/dist/utils/curl-parser.d.ts +1 -1
- package/dist/utils/curl-parser.js +144 -26
- package/dist/utils/fs.js +2 -2
- package/dist/utils/help-json.d.ts +56 -0
- package/dist/utils/help-json.js +85 -0
- package/dist/utils/resolve.d.ts +12 -0
- package/dist/utils/resolve.js +52 -0
- package/dist/utils/setup-state.d.ts +3 -0
- package/dist/utils/setup-state.js +19 -0
- package/dist/utils/sqlite.d.ts +7 -0
- package/dist/utils/sqlite.js +24 -0
- package/dist/utils/which.d.ts +6 -0
- package/dist/utils/which.js +18 -0
- package/package.json +7 -4
- package/dist/agents/copilot-cli.d.ts +0 -10
- package/dist/agents/copilot-cli.js +0 -46
- package/dist/agents/copilot-vscode.d.ts +0 -10
- package/dist/agents/copilot-vscode.js +0 -37
|
@@ -1,20 +1,45 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
|
-
import { homedir } from 'os';
|
|
3
|
+
import { homedir, platform } from 'os';
|
|
4
4
|
import { execFileSync } from 'child_process';
|
|
5
|
+
import { whichBinary } from '../utils/which.js';
|
|
5
6
|
import { buildSkillMd } from './types.js';
|
|
6
|
-
import { mergeJsonMcpServers } from './utils/json-config.js';
|
|
7
|
+
import { mergeJsonMcpServers, readJsonConfig, writeJsonConfig } from './utils/json-config.js';
|
|
8
|
+
import { writeHintToFile, writeTeamInstructionsToFile } from './utils/instruction-hint.js';
|
|
9
|
+
const PLUGIN_NAME = 'runwork';
|
|
10
|
+
const PLUGIN_VERSION = '1.0.0';
|
|
11
|
+
function getPluginJson() {
|
|
12
|
+
return {
|
|
13
|
+
name: PLUGIN_NAME,
|
|
14
|
+
version: PLUGIN_VERSION,
|
|
15
|
+
description: 'Skills and tools from your Runwork workspace',
|
|
16
|
+
author: { name: 'Runwork' },
|
|
17
|
+
homepage: 'https://www.runwork.ai',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function getMarketplaceJson() {
|
|
21
|
+
return {
|
|
22
|
+
name: 'runwork',
|
|
23
|
+
owner: { name: 'Runwork' },
|
|
24
|
+
plugins: [
|
|
25
|
+
{
|
|
26
|
+
name: PLUGIN_NAME,
|
|
27
|
+
source: `./plugins/${PLUGIN_NAME}`,
|
|
28
|
+
description: 'Skills and tools from your Runwork workspace',
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
7
33
|
export class ClaudeCodeAdapter {
|
|
8
34
|
name = 'Claude Code';
|
|
9
35
|
slug = 'claude-code';
|
|
10
36
|
async detect() {
|
|
11
|
-
|
|
12
|
-
execFileSync('which', ['claude'], { stdio: 'pipe' });
|
|
37
|
+
if (whichBinary('claude'))
|
|
13
38
|
return true;
|
|
14
|
-
|
|
15
|
-
|
|
39
|
+
// Fallback: check app bundle on macOS
|
|
40
|
+
if (platform() === 'darwin')
|
|
16
41
|
return existsSync('/Applications/Claude.app');
|
|
17
|
-
|
|
42
|
+
return false;
|
|
18
43
|
}
|
|
19
44
|
supportsMcpScope(_scope) {
|
|
20
45
|
return true;
|
|
@@ -23,28 +48,326 @@ export class ClaudeCodeAdapter {
|
|
|
23
48
|
return true;
|
|
24
49
|
}
|
|
25
50
|
async writeMcpServers(servers, scope) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
51
|
+
if (scope === 'project') {
|
|
52
|
+
// Project scope: write to .mcp.json (standard Claude Code location)
|
|
53
|
+
const filePath = join(process.cwd(), '.mcp.json');
|
|
54
|
+
const entries = {};
|
|
55
|
+
for (const s of servers) {
|
|
56
|
+
entries[s.name] = {
|
|
57
|
+
type: 'http',
|
|
58
|
+
url: s.url,
|
|
59
|
+
...(s.headers ? { headers: s.headers } : {}),
|
|
60
|
+
...(s.description ? { description: s.description } : {}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
mergeJsonMcpServers(filePath, entries, 'mcpServers');
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// User scope: write MCP config both in global settings and inside the plugin
|
|
67
|
+
const settingsPath = join(homedir(), '.claude', 'settings.json');
|
|
68
|
+
const entries = {};
|
|
69
|
+
for (const s of servers) {
|
|
70
|
+
entries[s.name] = {
|
|
71
|
+
type: 'http',
|
|
72
|
+
url: s.url,
|
|
73
|
+
...(s.headers ? { headers: s.headers } : {}),
|
|
74
|
+
...(s.description ? { description: s.description } : {}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
mergeJsonMcpServers(settingsPath, entries, 'mcpServers');
|
|
78
|
+
// Also write .mcp.json in the plugin directory
|
|
79
|
+
const pluginDir = this.getPluginDir();
|
|
80
|
+
mkdirSync(pluginDir, { recursive: true });
|
|
81
|
+
const pluginMcpPath = join(pluginDir, '.mcp.json');
|
|
82
|
+
const pluginEntries = {};
|
|
83
|
+
for (const s of servers) {
|
|
84
|
+
pluginEntries[s.name] = {
|
|
85
|
+
type: 'http',
|
|
86
|
+
url: s.url,
|
|
87
|
+
...(s.headers ? { headers: s.headers } : {}),
|
|
88
|
+
...(s.description ? { description: s.description } : {}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
writeJsonConfig(pluginMcpPath, pluginEntries);
|
|
36
92
|
}
|
|
37
|
-
mergeJsonMcpServers(filePath, entries, 'mcpServers');
|
|
38
93
|
}
|
|
39
94
|
async writeSkills(skills, scope) {
|
|
95
|
+
// Always write to the standard skills directory (guaranteed to work)
|
|
40
96
|
const baseDir = scope === 'project'
|
|
41
97
|
? join(process.cwd(), '.claude', 'skills')
|
|
42
98
|
: join(homedir(), '.claude', 'skills');
|
|
43
99
|
for (const skill of skills) {
|
|
44
100
|
const skillDir = join(baseDir, skill.name);
|
|
45
101
|
mkdirSync(skillDir, { recursive: true });
|
|
46
|
-
|
|
47
|
-
|
|
102
|
+
writeFileSync(join(skillDir, 'SKILL.md'), buildSkillMd(skill));
|
|
103
|
+
}
|
|
104
|
+
// User scope: also write as a plugin for native plugin UI support
|
|
105
|
+
if (scope === 'user') {
|
|
106
|
+
const pluginDir = this.getPluginDir();
|
|
107
|
+
// Create plugin.json
|
|
108
|
+
const pluginJsonDir = join(pluginDir, '.claude-plugin');
|
|
109
|
+
mkdirSync(pluginJsonDir, { recursive: true });
|
|
110
|
+
writeFileSync(join(pluginJsonDir, 'plugin.json'), JSON.stringify(getPluginJson(), null, 2));
|
|
111
|
+
// Write skill files inside the plugin
|
|
112
|
+
for (const skill of skills) {
|
|
113
|
+
const skillDir = join(pluginDir, 'skills', skill.name);
|
|
114
|
+
mkdirSync(skillDir, { recursive: true });
|
|
115
|
+
writeFileSync(join(skillDir, 'SKILL.md'), buildSkillMd(skill));
|
|
116
|
+
}
|
|
117
|
+
// Also write to the marketplace directory
|
|
118
|
+
const marketDir = this.getMarketplaceDir();
|
|
119
|
+
const marketPluginJsonDir = join(marketDir, '.claude-plugin');
|
|
120
|
+
mkdirSync(marketPluginJsonDir, { recursive: true });
|
|
121
|
+
writeFileSync(join(marketPluginJsonDir, 'plugin.json'), JSON.stringify(getPluginJson(), null, 2));
|
|
122
|
+
for (const skill of skills) {
|
|
123
|
+
const skillDir = join(marketDir, 'skills', skill.name);
|
|
124
|
+
mkdirSync(skillDir, { recursive: true });
|
|
125
|
+
writeFileSync(join(skillDir, 'SKILL.md'), buildSkillMd(skill));
|
|
126
|
+
}
|
|
127
|
+
// Register in installed_plugins.json
|
|
128
|
+
this.registerPlugin(pluginDir);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async writeInstructionHint(hint, scope) {
|
|
132
|
+
const filePath = scope === 'project'
|
|
133
|
+
? join(process.cwd(), '.claude', 'CLAUDE.md')
|
|
134
|
+
: join(homedir(), '.claude', 'CLAUDE.md');
|
|
135
|
+
writeHintToFile(filePath, hint);
|
|
136
|
+
}
|
|
137
|
+
async writeTeamInstructions(instructions, scope) {
|
|
138
|
+
// Write to CLAUDE.md via markers (direct instruction file approach)
|
|
139
|
+
const filePath = scope === 'project'
|
|
140
|
+
? join(process.cwd(), '.claude', 'CLAUDE.md')
|
|
141
|
+
: join(homedir(), '.claude', 'CLAUDE.md');
|
|
142
|
+
writeTeamInstructionsToFile(filePath, instructions);
|
|
143
|
+
// Also write as a plugin skill (same mechanism as Claude Desktop)
|
|
144
|
+
if (scope === 'user') {
|
|
145
|
+
const skillContent = `---\nname: runwork-team-instructions\ndescription: Team instructions from your Runwork workspace. Always follow these guidelines.\n---\n\n${instructions}`;
|
|
146
|
+
const pluginDir = this.getPluginDir();
|
|
147
|
+
const marketDir = this.getMarketplaceDir();
|
|
148
|
+
for (const dir of [
|
|
149
|
+
join(pluginDir, 'skills', 'runwork-team-instructions'),
|
|
150
|
+
join(marketDir, 'skills', 'runwork-team-instructions'),
|
|
151
|
+
]) {
|
|
152
|
+
mkdirSync(dir, { recursive: true });
|
|
153
|
+
writeFileSync(join(dir, 'SKILL.md'), skillContent);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// ── Agent config (model, permissions) ──────────────────────────────
|
|
158
|
+
async writeAgentConfig(config, scope) {
|
|
159
|
+
const settingsPath = scope === 'project'
|
|
160
|
+
? join(process.cwd(), '.claude', 'settings.json')
|
|
161
|
+
: join(homedir(), '.claude', 'settings.json');
|
|
162
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
163
|
+
let settings = {};
|
|
164
|
+
if (existsSync(settingsPath)) {
|
|
165
|
+
try {
|
|
166
|
+
settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
167
|
+
}
|
|
168
|
+
catch { /* start fresh */ }
|
|
169
|
+
}
|
|
170
|
+
if (config.modelPreference) {
|
|
171
|
+
settings.model = config.modelPreference;
|
|
172
|
+
}
|
|
173
|
+
if (config.permissionRules) {
|
|
174
|
+
if (!settings.permissions)
|
|
175
|
+
settings.permissions = {};
|
|
176
|
+
const perms = settings.permissions;
|
|
177
|
+
const MANAGED_MARKER = '# runwork-managed';
|
|
178
|
+
if (config.permissionRules.allow) {
|
|
179
|
+
const cleanExisting = this.removeAfterMarker(perms.allow ?? [], MANAGED_MARKER);
|
|
180
|
+
perms.allow = [...cleanExisting, MANAGED_MARKER, ...config.permissionRules.allow];
|
|
181
|
+
}
|
|
182
|
+
if (config.permissionRules.deny) {
|
|
183
|
+
const cleanExisting = this.removeAfterMarker(perms.deny ?? [], MANAGED_MARKER);
|
|
184
|
+
perms.deny = [...cleanExisting, MANAGED_MARKER, ...config.permissionRules.deny];
|
|
185
|
+
}
|
|
186
|
+
if (config.permissionRules.defaultMode) {
|
|
187
|
+
perms.defaultMode = config.permissionRules.defaultMode;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
mkdirSync(join(settingsPath, '..'), { recursive: true });
|
|
191
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
192
|
+
}
|
|
193
|
+
/** Remove the managed marker and all entries after it from a permissions array */
|
|
194
|
+
removeAfterMarker(arr, marker) {
|
|
195
|
+
const idx = arr.indexOf(marker);
|
|
196
|
+
return idx >= 0 ? arr.slice(0, idx) : [...arr];
|
|
197
|
+
}
|
|
198
|
+
// ── Usage stats (passive, best-effort) ─────────────────────────────
|
|
199
|
+
async readUsageStats(lastSyncAt) {
|
|
200
|
+
try {
|
|
201
|
+
const claudeDir = join(homedir(), '.claude');
|
|
202
|
+
if (!existsSync(claudeDir))
|
|
203
|
+
return null;
|
|
204
|
+
// Primary: stats-cache.json has pre-computed daily activity
|
|
205
|
+
// Format: { version, lastComputedDate, dailyActivity: [{ date, messageCount, sessionCount, toolCallCount }] }
|
|
206
|
+
const statsCachePath = join(claudeDir, 'stats-cache.json');
|
|
207
|
+
if (existsSync(statsCachePath)) {
|
|
208
|
+
const statsCache = JSON.parse(readFileSync(statsCachePath, 'utf-8'));
|
|
209
|
+
if (statsCache.dailyActivity && Array.isArray(statsCache.dailyActivity)) {
|
|
210
|
+
const sinceDate = lastSyncAt ? lastSyncAt.slice(0, 10) : '1970-01-01';
|
|
211
|
+
const recentDays = statsCache.dailyActivity.filter((d) => d.date > sinceDate);
|
|
212
|
+
const totalMessages = recentDays.reduce((sum, d) => sum + (d.messageCount ?? 0), 0);
|
|
213
|
+
const totalSessions = recentDays.reduce((sum, d) => sum + (d.sessionCount ?? 0), 0);
|
|
214
|
+
const totalToolCalls = recentDays.reduce((sum, d) => sum + (d.toolCallCount ?? 0), 0);
|
|
215
|
+
if (totalMessages > 0 || totalSessions > 0) {
|
|
216
|
+
return {
|
|
217
|
+
hasNewActivity: true,
|
|
218
|
+
sessionCount: totalSessions,
|
|
219
|
+
messageCount: totalMessages,
|
|
220
|
+
toolCallCount: totalToolCalls,
|
|
221
|
+
tokensUsed: 0,
|
|
222
|
+
aiLinesAdded: 0,
|
|
223
|
+
aiLinesRemoved: 0,
|
|
224
|
+
lastActiveAt: recentDays.length > 0
|
|
225
|
+
? new Date(recentDays[recentDays.length - 1].date).toISOString()
|
|
226
|
+
: null,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Fallback: count session directories modified since last sync
|
|
232
|
+
const sessionEnvDir = join(claudeDir, 'session-env');
|
|
233
|
+
if (existsSync(sessionEnvDir)) {
|
|
234
|
+
const entries = readdirSync(sessionEnvDir);
|
|
235
|
+
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
236
|
+
let recentCount = 0;
|
|
237
|
+
let latestMtime = 0;
|
|
238
|
+
for (const entry of entries) {
|
|
239
|
+
try {
|
|
240
|
+
const stat = statSync(join(sessionEnvDir, entry));
|
|
241
|
+
if (stat.mtimeMs > sinceMs)
|
|
242
|
+
recentCount++;
|
|
243
|
+
if (stat.mtimeMs > latestMtime)
|
|
244
|
+
latestMtime = stat.mtimeMs;
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (recentCount > 0) {
|
|
251
|
+
return {
|
|
252
|
+
hasNewActivity: true,
|
|
253
|
+
sessionCount: recentCount,
|
|
254
|
+
messageCount: 0,
|
|
255
|
+
toolCallCount: 0,
|
|
256
|
+
tokensUsed: 0,
|
|
257
|
+
aiLinesAdded: 0,
|
|
258
|
+
aiLinesRemoved: 0,
|
|
259
|
+
lastActiveAt: new Date(latestMtime).toISOString(),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async readSkillUsage(lastSyncAt) {
|
|
270
|
+
try {
|
|
271
|
+
const results = [];
|
|
272
|
+
const sinceMs = lastSyncAt ? new Date(lastSyncAt).getTime() : 0;
|
|
273
|
+
const skillDirs = [
|
|
274
|
+
join(homedir(), '.claude', 'skills'),
|
|
275
|
+
join(process.cwd(), '.claude', 'skills'),
|
|
276
|
+
];
|
|
277
|
+
for (const dir of skillDirs) {
|
|
278
|
+
if (!existsSync(dir))
|
|
279
|
+
continue;
|
|
280
|
+
for (const entry of readdirSync(dir)) {
|
|
281
|
+
if (entry === 'runwork')
|
|
282
|
+
continue;
|
|
283
|
+
const skillDir = join(dir, entry);
|
|
284
|
+
try {
|
|
285
|
+
const stat = statSync(skillDir);
|
|
286
|
+
if (stat.mtimeMs > sinceMs) {
|
|
287
|
+
results.push({
|
|
288
|
+
skillName: entry,
|
|
289
|
+
count: 1,
|
|
290
|
+
lastUsedAt: new Date(stat.mtimeMs).toISOString(),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return results.length > 0 ? results : null;
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async readVersion() {
|
|
306
|
+
try {
|
|
307
|
+
const output = execFileSync('claude', ['--version'], { stdio: 'pipe' }).toString().trim();
|
|
308
|
+
return output;
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
// ── Plugin helpers ─────────────────────────────────────────────────
|
|
315
|
+
getPluginsBaseDir() {
|
|
316
|
+
return join(homedir(), '.claude', 'plugins');
|
|
317
|
+
}
|
|
318
|
+
getPluginDir() {
|
|
319
|
+
return join(this.getPluginsBaseDir(), 'cache', 'runwork', PLUGIN_NAME, PLUGIN_VERSION);
|
|
320
|
+
}
|
|
321
|
+
/** The marketplace root (contains marketplace.json and plugins/) */
|
|
322
|
+
getMarketplaceRoot() {
|
|
323
|
+
return join(this.getPluginsBaseDir(), 'marketplaces', 'runwork');
|
|
324
|
+
}
|
|
325
|
+
/** The plugin directory within the marketplace */
|
|
326
|
+
getMarketplaceDir() {
|
|
327
|
+
return join(this.getMarketplaceRoot(), 'plugins', PLUGIN_NAME);
|
|
328
|
+
}
|
|
329
|
+
registerPlugin(pluginDir) {
|
|
330
|
+
const pluginsBase = this.getPluginsBaseDir();
|
|
331
|
+
mkdirSync(pluginsBase, { recursive: true });
|
|
332
|
+
// Register in installed_plugins.json
|
|
333
|
+
const installedPath = join(pluginsBase, 'installed_plugins.json');
|
|
334
|
+
const installed = readJsonConfig(installedPath);
|
|
335
|
+
if (!installed.version)
|
|
336
|
+
installed.version = 2;
|
|
337
|
+
if (!installed.plugins)
|
|
338
|
+
installed.plugins = {};
|
|
339
|
+
installed.plugins[`${PLUGIN_NAME}@runwork`] = [{
|
|
340
|
+
scope: 'user',
|
|
341
|
+
installPath: pluginDir,
|
|
342
|
+
version: PLUGIN_VERSION,
|
|
343
|
+
installedAt: new Date().toISOString(),
|
|
344
|
+
lastUpdated: new Date().toISOString(),
|
|
345
|
+
}];
|
|
346
|
+
writeJsonConfig(installedPath, installed);
|
|
347
|
+
// Write marketplace.json catalog
|
|
348
|
+
const marketRoot = this.getMarketplaceRoot();
|
|
349
|
+
const marketCatalogDir = join(marketRoot, '.claude-plugin');
|
|
350
|
+
mkdirSync(marketCatalogDir, { recursive: true });
|
|
351
|
+
writeFileSync(join(marketCatalogDir, 'marketplace.json'), JSON.stringify(getMarketplaceJson(), null, 2));
|
|
352
|
+
// Register in known_marketplaces.json
|
|
353
|
+
const marketplacesPath = join(pluginsBase, 'known_marketplaces.json');
|
|
354
|
+
const marketplaces = readJsonConfig(marketplacesPath);
|
|
355
|
+
marketplaces['runwork'] = {
|
|
356
|
+
source: { source: 'directory', path: marketRoot },
|
|
357
|
+
installLocation: marketRoot,
|
|
358
|
+
lastUpdated: new Date().toISOString(),
|
|
359
|
+
};
|
|
360
|
+
writeJsonConfig(marketplacesPath, marketplaces);
|
|
361
|
+
// Enable the plugin in settings.json
|
|
362
|
+
const settingsPath = join(homedir(), '.claude', 'settings.json');
|
|
363
|
+
const settings = readJsonConfig(settingsPath);
|
|
364
|
+
if (!settings['enabledPlugins']) {
|
|
365
|
+
settings['enabledPlugins'] = {};
|
|
366
|
+
}
|
|
367
|
+
const enabled = settings['enabledPlugins'];
|
|
368
|
+
if (!enabled[`${PLUGIN_NAME}@runwork`]) {
|
|
369
|
+
enabled[`${PLUGIN_NAME}@runwork`] = true;
|
|
370
|
+
writeJsonConfig(settingsPath, settings);
|
|
48
371
|
}
|
|
49
372
|
}
|
|
50
373
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentAdapter, McpServerEntry, SkillFile } from './types.js';
|
|
1
|
+
import type { AgentAdapter, AgentConfigOverride, AgentUsageStats, McpServerEntry, SkillFile } from './types.js';
|
|
2
2
|
export declare class ClaudeDesktopAdapter implements AgentAdapter {
|
|
3
3
|
name: string;
|
|
4
4
|
slug: string;
|
|
@@ -7,4 +7,13 @@ export declare class ClaudeDesktopAdapter implements AgentAdapter {
|
|
|
7
7
|
supportsSkills(): boolean;
|
|
8
8
|
writeMcpServers(servers: McpServerEntry[], _scope: 'project' | 'user'): Promise<void>;
|
|
9
9
|
writeSkills(skills: SkillFile[], _scope: 'project' | 'user'): Promise<void>;
|
|
10
|
+
writeInstructionHint(_hint: string, _scope: 'project' | 'user'): Promise<void>;
|
|
11
|
+
writeTeamInstructions(instructions: string, _scope: 'project' | 'user'): Promise<void>;
|
|
12
|
+
writeAgentConfig(config: AgentConfigOverride, _scope: 'project' | 'user'): Promise<void>;
|
|
13
|
+
readUsageStats(lastSyncAt: string | null): Promise<AgentUsageStats | null>;
|
|
14
|
+
readVersion(): Promise<string | null>;
|
|
15
|
+
/**
|
|
16
|
+
* Walk the org/user/session directory tree and invoke callback for each session JSON.
|
|
17
|
+
*/
|
|
18
|
+
private walkSessionDirs;
|
|
10
19
|
}
|