devworkspace 0.1.0 → 0.2.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/CHANGELOG.md +28 -0
- package/README.md +16 -2
- package/dist/agents/claude/adapter.d.ts +5 -0
- package/dist/agents/claude/adapter.js +10 -0
- package/dist/agents/claude/adapter.js.map +1 -1
- package/dist/agents/claude/content.js +3 -3
- package/dist/agents/claude/content.js.map +1 -1
- package/dist/cli/args.d.ts +22 -0
- package/dist/cli/args.js +7 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/help.js +54 -7
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/render.d.ts +9 -0
- package/dist/cli/render.js +105 -13
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/run.d.ts +6 -0
- package/dist/cli/run.js +75 -3
- package/dist/cli/run.js.map +1 -1
- package/dist/core/workspace.js +2 -1
- package/dist/core/workspace.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/integrations/adapters/agent-skills.d.ts +8 -0
- package/dist/integrations/adapters/agent-skills.js +25 -0
- package/dist/integrations/adapters/agent-skills.js.map +1 -0
- package/dist/integrations/adapters/claudePlugin.d.ts +41 -0
- package/dist/integrations/adapters/claudePlugin.js +129 -0
- package/dist/integrations/adapters/claudePlugin.js.map +1 -0
- package/dist/integrations/adapters/graphify.d.ts +2 -0
- package/dist/integrations/adapters/graphify.js +239 -0
- package/dist/integrations/adapters/graphify.js.map +1 -0
- package/dist/integrations/adapters/omniroute.d.ts +6 -0
- package/dist/integrations/adapters/omniroute.js +32 -0
- package/dist/integrations/adapters/omniroute.js.map +1 -0
- package/dist/integrations/adapters/pluginSkill.d.ts +28 -0
- package/dist/integrations/adapters/pluginSkill.js +195 -0
- package/dist/integrations/adapters/pluginSkill.js.map +1 -0
- package/dist/integrations/adapters/ponytail.d.ts +8 -0
- package/dist/integrations/adapters/ponytail.js +33 -0
- package/dist/integrations/adapters/ponytail.js.map +1 -0
- package/dist/integrations/exec.d.ts +26 -0
- package/dist/integrations/exec.js +167 -0
- package/dist/integrations/exec.js.map +1 -0
- package/dist/integrations/index.d.ts +5 -6
- package/dist/integrations/index.js +3 -29
- package/dist/integrations/index.js.map +1 -1
- package/dist/integrations/manager.d.ts +62 -0
- package/dist/integrations/manager.js +248 -0
- package/dist/integrations/manager.js.map +1 -0
- package/dist/integrations/notices.d.ts +13 -0
- package/dist/integrations/notices.js +68 -0
- package/dist/integrations/notices.js.map +1 -0
- package/dist/integrations/protect.d.ts +20 -0
- package/dist/integrations/protect.js +131 -0
- package/dist/integrations/protect.js.map +1 -0
- package/dist/integrations/state.d.ts +10 -0
- package/dist/integrations/state.js +81 -0
- package/dist/integrations/state.js.map +1 -0
- package/dist/integrations/types.d.ts +133 -16
- package/dist/integrations/types.js +13 -1
- package/dist/integrations/types.js.map +1 -1
- package/dist/prompts/onboarding.d.ts +3 -3
- package/dist/prompts/onboarding.js +5 -4
- package/dist/prompts/onboarding.js.map +1 -1
- package/dist/services/analysis.d.ts +2 -2
- package/dist/services/analysis.js +4 -5
- package/dist/services/analysis.js.map +1 -1
- package/dist/services/common.d.ts +8 -5
- package/dist/services/common.js +2 -5
- package/dist/services/common.js.map +1 -1
- package/dist/services/doctor.d.ts +10 -0
- package/dist/services/doctor.js +9 -15
- package/dist/services/doctor.js.map +1 -1
- package/dist/services/init.d.ts +9 -2
- package/dist/services/init.js +10 -7
- package/dist/services/init.js.map +1 -1
- package/dist/services/integrations.d.ts +26 -0
- package/dist/services/integrations.js +35 -0
- package/dist/services/integrations.js.map +1 -0
- package/dist/services/reset.d.ts +23 -0
- package/dist/services/reset.js +96 -0
- package/dist/services/reset.js.map +1 -0
- package/dist/services/status.d.ts +3 -2
- package/dist/services/status.js +3 -3
- package/dist/services/status.js.map +1 -1
- package/dist/utils/lock.d.ts +2 -0
- package/dist/utils/lock.js +2 -2
- package/dist/utils/lock.js.map +1 -1
- package/dist/utils/proc.d.ts +8 -1
- package/dist/utils/proc.js +31 -8
- package/dist/utils/proc.js.map +1 -1
- package/docs/architecture.md +1 -1
- package/docs/claude.md +4 -0
- package/docs/getting-started.md +13 -2
- package/docs/integrations.md +98 -33
- package/docs/security.md +5 -1
- package/docs/testing.md +2 -0
- package/docs/troubleshooting.md +9 -1
- package/package.json +1 -1
- package/dist/integrations/agent-skills.d.ts +0 -10
- package/dist/integrations/agent-skills.js +0 -45
- package/dist/integrations/agent-skills.js.map +0 -1
- package/dist/integrations/graphify.d.ts +0 -7
- package/dist/integrations/graphify.js +0 -54
- package/dist/integrations/graphify.js.map +0 -1
- package/dist/integrations/omniroute.d.ts +0 -7
- package/dist/integrations/omniroute.js +0 -24
- package/dist/integrations/omniroute.js.map +0 -1
- package/dist/integrations/ponytail.d.ts +0 -10
- package/dist/integrations/ponytail.js +0 -58
- package/dist/integrations/ponytail.js.map +0 -1
- package/dist/integrations/probe.d.ts +0 -6
- package/dist/integrations/probe.js +0 -41
- package/dist/integrations/probe.js.map +0 -1
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { compareVersions, findPython, findTool } from '../exec.js';
|
|
4
|
+
import { GRAPHIFY_NOTICE, writeThirdPartyNotice } from '../notices.js';
|
|
5
|
+
import { pathExists } from '../../utils/fs.js';
|
|
6
|
+
/**
|
|
7
|
+
* Graphify. Verified against the published package (graphifyy 0.9.82) and its CLI help on 2026-10-09:
|
|
8
|
+
* - PyPI package `graphifyy`, executable `graphify`; README documents pip and pipx (pipx recommended on Windows)
|
|
9
|
+
* - `graphify --version` prints "graphify <version>"
|
|
10
|
+
* - `graphify install --project` writes .claude/skills/graphify/, appends a graphify section to CLAUDE.md,
|
|
11
|
+
* creates .claude/CLAUDE.md and registers PreToolUse hooks (`graphify hook-guard ...`) in .claude/settings.json.
|
|
12
|
+
* It merges and is idempotent, but it does change files the user owns, so every run is snapshotted and verified.
|
|
13
|
+
* uv is not mentioned in the README; `uv tool install` is used because it is the equivalent of pipx for console tools.
|
|
14
|
+
*/
|
|
15
|
+
const EXE = process.platform === 'win32' ? 'graphify.exe' : 'graphify';
|
|
16
|
+
const MIN_PYTHON = '3.10.0';
|
|
17
|
+
async function exists(p) {
|
|
18
|
+
return pathExists(p);
|
|
19
|
+
}
|
|
20
|
+
async function locate(ctx, deep) {
|
|
21
|
+
const onPath = await ctx.runner.which('graphify');
|
|
22
|
+
if (onPath)
|
|
23
|
+
return { path: onPath, onPath: true };
|
|
24
|
+
const dirs = [path.join(ctx.home, '.local', 'bin')];
|
|
25
|
+
if (deep)
|
|
26
|
+
dirs.push(...(await toolBinDirs(ctx)));
|
|
27
|
+
for (const d of dirs) {
|
|
28
|
+
const candidate = path.join(d, ctx.platform === 'win32' ? 'graphify.exe' : EXE);
|
|
29
|
+
if (await exists(candidate))
|
|
30
|
+
return { path: candidate, onPath: false };
|
|
31
|
+
}
|
|
32
|
+
return { path: null, onPath: false };
|
|
33
|
+
}
|
|
34
|
+
/** Where uv, pipx and `pip --user` put console scripts, asked from the tools themselves (they differ by OS and version). */
|
|
35
|
+
async function toolBinDirs(ctx) {
|
|
36
|
+
const out = [];
|
|
37
|
+
const uv = await findTool(ctx.runner, 'uv', ctx.signal);
|
|
38
|
+
if (uv) {
|
|
39
|
+
const r = await ctx.runner.run({ command: uv.command, args: ['tool', 'dir', '--bin'], timeoutMs: 10_000, signal: ctx.signal });
|
|
40
|
+
if (r.ok && r.stdout.trim())
|
|
41
|
+
out.push(r.stdout.trim().split(/\r?\n/)[0]);
|
|
42
|
+
}
|
|
43
|
+
const pipx = await findTool(ctx.runner, 'pipx', ctx.signal);
|
|
44
|
+
if (pipx) {
|
|
45
|
+
const r = await ctx.runner.run({ command: pipx.command, args: ['environment', '--value', 'PIPX_BIN_DIR'], timeoutMs: 10_000, signal: ctx.signal });
|
|
46
|
+
if (r.ok && r.stdout.trim())
|
|
47
|
+
out.push(r.stdout.trim().split(/\r?\n/)[0]);
|
|
48
|
+
}
|
|
49
|
+
const py = await findPython(ctx.runner, ctx.platform, ctx.signal);
|
|
50
|
+
if (py) {
|
|
51
|
+
const code = "import sysconfig,os;print(sysconfig.get_path('scripts', os.name + '_user'))";
|
|
52
|
+
const r = await ctx.runner.run({ command: py.command, args: [...py.args, '-c', code], timeoutMs: 10_000, signal: ctx.signal });
|
|
53
|
+
if (r.ok && r.stdout.trim())
|
|
54
|
+
out.push(r.stdout.trim().split(/\r?\n/)[0]);
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
async function readText(p) {
|
|
59
|
+
try {
|
|
60
|
+
return await fs.readFile(p, 'utf8');
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function pathAdvice(ctx, dir) {
|
|
67
|
+
const where = dir ? `Add ${dir} to your PATH` : 'Add the folder containing graphify to your PATH';
|
|
68
|
+
return ctx.platform === 'win32'
|
|
69
|
+
? [`${where} (Settings > System > About > Advanced system settings > Environment Variables), or run \`pipx ensurepath\` / \`uv tool update-shell\`, then open a new terminal.`]
|
|
70
|
+
: [`${where}, or run \`pipx ensurepath\` / \`uv tool update-shell\`, then open a new terminal.`];
|
|
71
|
+
}
|
|
72
|
+
export const graphify = {
|
|
73
|
+
id: 'graphify',
|
|
74
|
+
name: 'Graphify',
|
|
75
|
+
required: true,
|
|
76
|
+
docsUrl: 'https://github.com/Graphify-Labs/graphify',
|
|
77
|
+
usage: 'For "where is X / what depends on Y" questions, query the Graphify graph first (graphify query "<question>", graphify path "<A>" "<B>", graphify explain "<concept>") ' +
|
|
78
|
+
'or use the /graphify skill. The graph is a navigation aid, not a source of truth: confirm in the actual files before relying on it. After editing code, `graphify update .` refreshes it (no LLM cost).',
|
|
79
|
+
async inspect(ctx, deep) {
|
|
80
|
+
const loc = await locate(ctx, deep);
|
|
81
|
+
const projectSkill = await exists(path.join(ctx.root, '.claude', 'skills', 'graphify', 'SKILL.md'));
|
|
82
|
+
const userSkill = !projectSkill && (await exists(path.join(ctx.home, '.claude', 'skills', 'graphify', 'SKILL.md')));
|
|
83
|
+
const settings = (await readText(path.join(ctx.root, '.claude', 'settings.json'))) ?? '';
|
|
84
|
+
const hooks = /graphify\s+hook-guard/.test(settings);
|
|
85
|
+
const graph = await exists(path.join(ctx.root, 'graphify-out', 'graph.json'));
|
|
86
|
+
const skillVersion = ((await readText(path.join(ctx.root, '.claude', 'skills', 'graphify', '.graphify_version'))) ?? '').trim() || null;
|
|
87
|
+
let version = null;
|
|
88
|
+
let runnable = null;
|
|
89
|
+
if (deep && loc.path) {
|
|
90
|
+
const r = await ctx.runner.run({ command: loc.path, args: ['--version'], timeoutMs: 15_000, signal: ctx.signal });
|
|
91
|
+
const m = /graphify\s+(\S+)/i.exec(r.stdout);
|
|
92
|
+
runnable = r.ok && !!m;
|
|
93
|
+
version = m?.[1] ?? null;
|
|
94
|
+
}
|
|
95
|
+
const checks = [
|
|
96
|
+
{ name: 'graphify executable', ok: !!loc.path, detail: loc.path ? (loc.onPath ? 'on PATH' : 'found, but not on PATH') : 'not found' },
|
|
97
|
+
];
|
|
98
|
+
if (deep && loc.path)
|
|
99
|
+
checks.push({ name: 'graphify --version', ok: !!runnable, detail: version ? `graphify ${version}` : 'did not run' });
|
|
100
|
+
checks.push({ name: 'Claude skill', ok: projectSkill || userSkill, detail: projectSkill ? 'project (.claude/skills/graphify)' : userSkill ? 'user (~/.claude/skills/graphify)' : 'missing' });
|
|
101
|
+
if (projectSkill && version && skillVersion)
|
|
102
|
+
checks.push({ name: 'skill matches CLI version', ok: skillVersion === version, detail: `skill ${skillVersion}, CLI ${version}` });
|
|
103
|
+
const capabilities = { cli: !!loc.path, skill: projectSkill || userSkill, hooks, graphBuilt: graph };
|
|
104
|
+
const warnings = [];
|
|
105
|
+
const nextSteps = [];
|
|
106
|
+
if (!graph)
|
|
107
|
+
warnings.push('No graph built yet. In Claude Code run `/graphify .` (graph building uses the model, so DevWorkspace does not do it).');
|
|
108
|
+
const base = { id: 'graphify', name: 'Graphify', required: true, deep, capabilities, checks };
|
|
109
|
+
const scope = projectSkill ? 'project' : userSkill ? 'user' : null;
|
|
110
|
+
if (!loc.path && !projectSkill && !userSkill) {
|
|
111
|
+
nextSteps.push('devworkspace setup --integrations --yes');
|
|
112
|
+
return { ...base, status: 'NOT_INSTALLED', variant: null, scope: null, version: null, warnings, nextSteps };
|
|
113
|
+
}
|
|
114
|
+
if (!loc.path) {
|
|
115
|
+
warnings.push('The Claude skill/hooks exist but the graphify command is missing, so they cannot work.');
|
|
116
|
+
nextSteps.push('devworkspace integrations repair --yes');
|
|
117
|
+
return { ...base, status: 'DEGRADED', variant: 'project skill without CLI', scope, version: null, warnings, nextSteps };
|
|
118
|
+
}
|
|
119
|
+
if (deep && runnable === false) {
|
|
120
|
+
warnings.push('The graphify executable was found but `graphify --version` failed.');
|
|
121
|
+
nextSteps.push('devworkspace integrations repair --yes');
|
|
122
|
+
return { ...base, status: 'DEGRADED', variant: 'cli (not runnable)', scope, version, warnings, nextSteps };
|
|
123
|
+
}
|
|
124
|
+
if (!projectSkill && !userSkill) {
|
|
125
|
+
warnings.push('The graphify CLI is installed but its Claude skill is not set up for this project.');
|
|
126
|
+
nextSteps.push('devworkspace setup --integrations --yes (runs `graphify install --project`)');
|
|
127
|
+
return { ...base, status: 'DEGRADED', variant: 'cli without project skill', scope: null, version, warnings, nextSteps };
|
|
128
|
+
}
|
|
129
|
+
if (version && skillVersion && compareVersions(version.replace(/[^0-9.]/g, '') || '0.0.0', skillVersion.replace(/[^0-9.]/g, '') || '0.0.0') !== 0) {
|
|
130
|
+
warnings.push(`The project skill is from graphify ${skillVersion} but the CLI is ${version}.`);
|
|
131
|
+
nextSteps.push('devworkspace integrations repair --yes');
|
|
132
|
+
return { ...base, status: 'DEGRADED', variant: 'cli + stale project skill', scope, version, warnings, nextSteps };
|
|
133
|
+
}
|
|
134
|
+
if (hooks && !loc.onPath) {
|
|
135
|
+
warnings.push('Claude Code hooks call `graphify` by name, but it is not on PATH in this terminal, so the hooks would fail.');
|
|
136
|
+
nextSteps.push(...pathAdvice(ctx, loc.path ? path.dirname(loc.path) : null));
|
|
137
|
+
return { ...base, status: 'DEGRADED', variant: 'cli + project skill (PATH missing)', scope, version, warnings, nextSteps };
|
|
138
|
+
}
|
|
139
|
+
if (!loc.onPath)
|
|
140
|
+
warnings.push(`graphify is installed but not on PATH. ${pathAdvice(ctx, path.dirname(loc.path))[0]}`);
|
|
141
|
+
return { ...base, status: deep ? 'VERIFIED' : 'INSTALLED', variant: 'cli + project skill', scope, version, warnings, nextSteps };
|
|
142
|
+
},
|
|
143
|
+
async plan(ctx, report) {
|
|
144
|
+
const protectPaths = ['CLAUDE.md', '.claude/CLAUDE.md', '.claude/settings.json', '.claude/settings.local.json', 'AGENTS.md'];
|
|
145
|
+
const blockers = [];
|
|
146
|
+
const notes = [];
|
|
147
|
+
const steps = [];
|
|
148
|
+
if (report.status === 'VERIFIED' && !ctx.setup.repair)
|
|
149
|
+
return { steps, blockers, protectPaths, notes: ['already verified'] };
|
|
150
|
+
const loc = await locate(ctx, true);
|
|
151
|
+
const exeRef = { path: loc.path };
|
|
152
|
+
const scope = ctx.setup.scope;
|
|
153
|
+
if (!loc.path) {
|
|
154
|
+
const uv = await findTool(ctx.runner, 'uv', ctx.signal);
|
|
155
|
+
const pipx = await findTool(ctx.runner, 'pipx', ctx.signal);
|
|
156
|
+
const py = await findPython(ctx.runner, ctx.platform, ctx.signal);
|
|
157
|
+
const pyOk = !!py && compareVersions(py.version, MIN_PYTHON) >= 0;
|
|
158
|
+
const choice = uv
|
|
159
|
+
? { command: [uv.command, 'tool', 'install', 'graphifyy'], how: 'uv tool install' }
|
|
160
|
+
: pipx
|
|
161
|
+
? { command: [pipx.command, 'install', 'graphifyy'], how: 'pipx install' }
|
|
162
|
+
: pyOk && py
|
|
163
|
+
? { command: [py.command, ...py.args, '-m', 'pip', 'install', '--user', 'graphifyy'], how: 'pip install --user' }
|
|
164
|
+
: null;
|
|
165
|
+
if (choice && !uv && !pipx)
|
|
166
|
+
notes.push('uv and pipx were not found; falling back to the documented `pip install --user` (no administrator rights needed).');
|
|
167
|
+
if (!choice) {
|
|
168
|
+
if (py && !pyOk)
|
|
169
|
+
blockers.push(`Python ${py.version} is too old; graphify needs Python 3.10 or newer.`);
|
|
170
|
+
blockers.push('No installer found. Graphify is a Python tool: install ONE of these, then re-run setup (nothing else is required):', ctx.platform === 'win32'
|
|
171
|
+
? ' - uv: winget install --id astral-sh.uv (or https://docs.astral.sh/uv/getting-started/installation/)'
|
|
172
|
+
: ' - uv: https://docs.astral.sh/uv/getting-started/installation/', ' - Python 3.10+ from https://www.python.org/downloads/ (the Microsoft Store "python" shortcut on Windows is not a real install)', ' - pipx: https://pipx.pypa.io/stable/installation/');
|
|
173
|
+
return { steps, blockers, protectPaths, notes };
|
|
174
|
+
}
|
|
175
|
+
const { command, how } = choice;
|
|
176
|
+
const [cmd, ...args] = command;
|
|
177
|
+
steps.push({
|
|
178
|
+
id: 'graphify:tool',
|
|
179
|
+
description: `Install the graphifyy package for your user account (${how}; no administrator rights, nothing system-wide)`,
|
|
180
|
+
command: command.map((c, i) => (i === 0 ? path.basename(c, path.extname(c)) : c)).join(' '),
|
|
181
|
+
level: 'machine',
|
|
182
|
+
async run() {
|
|
183
|
+
const r = await ctx.runner.run({ command: cmd, args, cwd: ctx.root, timeoutMs: 600_000, signal: ctx.signal });
|
|
184
|
+
if (r.aborted)
|
|
185
|
+
return { ok: false, detail: 'cancelled' };
|
|
186
|
+
if (r.timedOut)
|
|
187
|
+
return { ok: false, detail: 'timed out after 10 minutes (network?)' };
|
|
188
|
+
if (!r.ok) {
|
|
189
|
+
const text = `${r.stderr}\n${r.stdout}`;
|
|
190
|
+
if (/externally[- ]managed/i.test(text))
|
|
191
|
+
return { ok: false, detail: 'pip refuses to install into this Python (externally-managed environment). Install pipx or uv instead, then re-run.' };
|
|
192
|
+
if (/permission denied|access is denied|EACCES|WinError 5/i.test(text))
|
|
193
|
+
return { ok: false, detail: 'permission denied while installing. Run the command yourself from a terminal with the right permissions.' };
|
|
194
|
+
const tail = text.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).slice(-3).join(' ');
|
|
195
|
+
return { ok: false, detail: (tail || r.error || `exit ${r.code}`).slice(0, 400) };
|
|
196
|
+
}
|
|
197
|
+
const found = await locate(ctx, true);
|
|
198
|
+
if (!found.path)
|
|
199
|
+
return { ok: false, detail: 'graphifyy was installed but the graphify executable could not be located. Check the Scripts/bin folder of your Python tool.' };
|
|
200
|
+
exeRef.path = found.path;
|
|
201
|
+
return { ok: true, detail: found.onPath ? 'installed' : 'installed (not on PATH in this terminal)' };
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
steps.push({
|
|
206
|
+
id: 'graphify:project-skill',
|
|
207
|
+
description: scope === 'user'
|
|
208
|
+
? 'Install the Graphify skill for your user account (graphify install)'
|
|
209
|
+
: 'Install the Graphify project skill (graphify install --project): adds .claude/skills/graphify/, appends a graphify section to CLAUDE.md and registers Claude Code hooks in .claude/settings.json (existing content is preserved and verified)',
|
|
210
|
+
command: scope === 'user' ? 'graphify install' : 'graphify install --project',
|
|
211
|
+
level: scope === 'user' ? 'user-config' : 'project',
|
|
212
|
+
async run() {
|
|
213
|
+
if (!exeRef.path)
|
|
214
|
+
return { ok: false, detail: 'the graphify executable is not available' };
|
|
215
|
+
const r = await ctx.runner.run({ command: exeRef.path, args: scope === 'user' ? ['install'] : ['install', '--project'], cwd: ctx.root, timeoutMs: 120_000, signal: ctx.signal });
|
|
216
|
+
if (r.aborted)
|
|
217
|
+
return { ok: false, detail: 'cancelled' };
|
|
218
|
+
if (r.timedOut)
|
|
219
|
+
return { ok: false, detail: 'timed out after 120 s' };
|
|
220
|
+
if (!r.ok) {
|
|
221
|
+
const tail = `${r.stderr}\n${r.stdout}`.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).slice(-3).join(' ');
|
|
222
|
+
return { ok: false, detail: (tail || r.error || `exit ${r.code}`).slice(0, 400) };
|
|
223
|
+
}
|
|
224
|
+
return { ok: true, detail: 'project skill installed', restartRequired: true, managedPaths: scope === 'user' ? [] : ['.claude/skills/graphify'] };
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
steps.push({
|
|
228
|
+
id: 'graphify:notice',
|
|
229
|
+
description: 'Record attribution in .devworkspace/THIRD_PARTY_NOTICES.md',
|
|
230
|
+
level: 'project',
|
|
231
|
+
async run() {
|
|
232
|
+
await writeThirdPartyNotice(ctx.root, GRAPHIFY_NOTICE);
|
|
233
|
+
return { ok: true };
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
return { steps, blockers, protectPaths, notes };
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
//# sourceMappingURL=graphify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphify.js","sourceRoot":"","sources":["../../../src/integrations/adapters/graphify.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { compareVersions, findPython, findTool } from '../exec.js';\nimport { GRAPHIFY_NOTICE, writeThirdPartyNotice } from '../notices.js';\nimport type { AdapterContext, Check, IntegrationAdapter, IntegrationReport, PlanResult, PlanStep, StepOutcome } from '../types.js';\nimport { pathExists } from '../../utils/fs.js';\n\n/**\n * Graphify. Verified against the published package (graphifyy 0.9.82) and its CLI help on 2026-10-09:\n * - PyPI package `graphifyy`, executable `graphify`; README documents pip and pipx (pipx recommended on Windows)\n * - `graphify --version` prints \"graphify <version>\"\n * - `graphify install --project` writes .claude/skills/graphify/, appends a graphify section to CLAUDE.md,\n * creates .claude/CLAUDE.md and registers PreToolUse hooks (`graphify hook-guard ...`) in .claude/settings.json.\n * It merges and is idempotent, but it does change files the user owns, so every run is snapshotted and verified.\n * uv is not mentioned in the README; `uv tool install` is used because it is the equivalent of pipx for console tools.\n */\n\nconst EXE = process.platform === 'win32' ? 'graphify.exe' : 'graphify';\nconst MIN_PYTHON = '3.10.0';\n\ninterface Located {\n path: string | null;\n onPath: boolean;\n}\n\nasync function exists(p: string): Promise<boolean> {\n return pathExists(p);\n}\n\nasync function locate(ctx: AdapterContext, deep: boolean): Promise<Located> {\n const onPath = await ctx.runner.which('graphify');\n if (onPath) return { path: onPath, onPath: true };\n const dirs = [path.join(ctx.home, '.local', 'bin')];\n if (deep) dirs.push(...(await toolBinDirs(ctx)));\n for (const d of dirs) {\n const candidate = path.join(d, ctx.platform === 'win32' ? 'graphify.exe' : EXE);\n if (await exists(candidate)) return { path: candidate, onPath: false };\n }\n return { path: null, onPath: false };\n}\n\n/** Where uv, pipx and `pip --user` put console scripts, asked from the tools themselves (they differ by OS and version). */\nasync function toolBinDirs(ctx: AdapterContext): Promise<string[]> {\n const out: string[] = [];\n const uv = await findTool(ctx.runner, 'uv', ctx.signal);\n if (uv) {\n const r = await ctx.runner.run({ command: uv.command, args: ['tool', 'dir', '--bin'], timeoutMs: 10_000, signal: ctx.signal });\n if (r.ok && r.stdout.trim()) out.push(r.stdout.trim().split(/\\r?\\n/)[0]!);\n }\n const pipx = await findTool(ctx.runner, 'pipx', ctx.signal);\n if (pipx) {\n const r = await ctx.runner.run({ command: pipx.command, args: ['environment', '--value', 'PIPX_BIN_DIR'], timeoutMs: 10_000, signal: ctx.signal });\n if (r.ok && r.stdout.trim()) out.push(r.stdout.trim().split(/\\r?\\n/)[0]!);\n }\n const py = await findPython(ctx.runner, ctx.platform, ctx.signal);\n if (py) {\n const code = \"import sysconfig,os;print(sysconfig.get_path('scripts', os.name + '_user'))\";\n const r = await ctx.runner.run({ command: py.command, args: [...py.args, '-c', code], timeoutMs: 10_000, signal: ctx.signal });\n if (r.ok && r.stdout.trim()) out.push(r.stdout.trim().split(/\\r?\\n/)[0]!);\n }\n return out;\n}\n\nasync function readText(p: string): Promise<string | null> {\n try {\n return await fs.readFile(p, 'utf8');\n } catch {\n return null;\n }\n}\n\nfunction pathAdvice(ctx: AdapterContext, dir: string | null): string[] {\n const where = dir ? `Add ${dir} to your PATH` : 'Add the folder containing graphify to your PATH';\n return ctx.platform === 'win32'\n ? [`${where} (Settings > System > About > Advanced system settings > Environment Variables), or run \\`pipx ensurepath\\` / \\`uv tool update-shell\\`, then open a new terminal.`]\n : [`${where}, or run \\`pipx ensurepath\\` / \\`uv tool update-shell\\`, then open a new terminal.`];\n}\n\nexport const graphify: IntegrationAdapter = {\n id: 'graphify',\n name: 'Graphify',\n required: true,\n docsUrl: 'https://github.com/Graphify-Labs/graphify',\n usage:\n 'For \"where is X / what depends on Y\" questions, query the Graphify graph first (graphify query \"<question>\", graphify path \"<A>\" \"<B>\", graphify explain \"<concept>\") ' +\n 'or use the /graphify skill. The graph is a navigation aid, not a source of truth: confirm in the actual files before relying on it. After editing code, `graphify update .` refreshes it (no LLM cost).',\n\n async inspect(ctx, deep): Promise<IntegrationReport> {\n const loc = await locate(ctx, deep);\n const projectSkill = await exists(path.join(ctx.root, '.claude', 'skills', 'graphify', 'SKILL.md'));\n const userSkill = !projectSkill && (await exists(path.join(ctx.home, '.claude', 'skills', 'graphify', 'SKILL.md')));\n const settings = (await readText(path.join(ctx.root, '.claude', 'settings.json'))) ?? '';\n const hooks = /graphify\\s+hook-guard/.test(settings);\n const graph = await exists(path.join(ctx.root, 'graphify-out', 'graph.json'));\n const skillVersion = ((await readText(path.join(ctx.root, '.claude', 'skills', 'graphify', '.graphify_version'))) ?? '').trim() || null;\n\n let version: string | null = null;\n let runnable: boolean | null = null;\n if (deep && loc.path) {\n const r = await ctx.runner.run({ command: loc.path, args: ['--version'], timeoutMs: 15_000, signal: ctx.signal });\n const m = /graphify\\s+(\\S+)/i.exec(r.stdout);\n runnable = r.ok && !!m;\n version = m?.[1] ?? null;\n }\n\n const checks: Check[] = [\n { name: 'graphify executable', ok: !!loc.path, detail: loc.path ? (loc.onPath ? 'on PATH' : 'found, but not on PATH') : 'not found' },\n ];\n if (deep && loc.path) checks.push({ name: 'graphify --version', ok: !!runnable, detail: version ? `graphify ${version}` : 'did not run' });\n checks.push({ name: 'Claude skill', ok: projectSkill || userSkill, detail: projectSkill ? 'project (.claude/skills/graphify)' : userSkill ? 'user (~/.claude/skills/graphify)' : 'missing' });\n if (projectSkill && version && skillVersion) checks.push({ name: 'skill matches CLI version', ok: skillVersion === version, detail: `skill ${skillVersion}, CLI ${version}` });\n const capabilities = { cli: !!loc.path, skill: projectSkill || userSkill, hooks, graphBuilt: graph };\n const warnings: string[] = [];\n const nextSteps: string[] = [];\n if (!graph) warnings.push('No graph built yet. In Claude Code run `/graphify .` (graph building uses the model, so DevWorkspace does not do it).');\n\n const base = { id: 'graphify', name: 'Graphify', required: true, deep, capabilities, checks } as const;\n const scope = projectSkill ? 'project' : userSkill ? 'user' : null;\n\n if (!loc.path && !projectSkill && !userSkill) {\n nextSteps.push('devworkspace setup --integrations --yes');\n return { ...base, status: 'NOT_INSTALLED', variant: null, scope: null, version: null, warnings, nextSteps };\n }\n if (!loc.path) {\n warnings.push('The Claude skill/hooks exist but the graphify command is missing, so they cannot work.');\n nextSteps.push('devworkspace integrations repair --yes');\n return { ...base, status: 'DEGRADED', variant: 'project skill without CLI', scope, version: null, warnings, nextSteps };\n }\n if (deep && runnable === false) {\n warnings.push('The graphify executable was found but `graphify --version` failed.');\n nextSteps.push('devworkspace integrations repair --yes');\n return { ...base, status: 'DEGRADED', variant: 'cli (not runnable)', scope, version, warnings, nextSteps };\n }\n if (!projectSkill && !userSkill) {\n warnings.push('The graphify CLI is installed but its Claude skill is not set up for this project.');\n nextSteps.push('devworkspace setup --integrations --yes (runs `graphify install --project`)');\n return { ...base, status: 'DEGRADED', variant: 'cli without project skill', scope: null, version, warnings, nextSteps };\n }\n if (version && skillVersion && compareVersions(version.replace(/[^0-9.]/g, '') || '0.0.0', skillVersion.replace(/[^0-9.]/g, '') || '0.0.0') !== 0) {\n warnings.push(`The project skill is from graphify ${skillVersion} but the CLI is ${version}.`);\n nextSteps.push('devworkspace integrations repair --yes');\n return { ...base, status: 'DEGRADED', variant: 'cli + stale project skill', scope, version, warnings, nextSteps };\n }\n if (hooks && !loc.onPath) {\n warnings.push('Claude Code hooks call `graphify` by name, but it is not on PATH in this terminal, so the hooks would fail.');\n nextSteps.push(...pathAdvice(ctx, loc.path ? path.dirname(loc.path) : null));\n return { ...base, status: 'DEGRADED', variant: 'cli + project skill (PATH missing)', scope, version, warnings, nextSteps };\n }\n if (!loc.onPath) warnings.push(`graphify is installed but not on PATH. ${pathAdvice(ctx, path.dirname(loc.path))[0]}`);\n return { ...base, status: deep ? 'VERIFIED' : 'INSTALLED', variant: 'cli + project skill', scope, version, warnings, nextSteps };\n },\n\n async plan(ctx, report): Promise<PlanResult> {\n const protectPaths = ['CLAUDE.md', '.claude/CLAUDE.md', '.claude/settings.json', '.claude/settings.local.json', 'AGENTS.md'];\n const blockers: string[] = [];\n const notes: string[] = [];\n const steps: PlanStep[] = [];\n if (report.status === 'VERIFIED' && !ctx.setup.repair) return { steps, blockers, protectPaths, notes: ['already verified'] };\n\n const loc = await locate(ctx, true);\n const exeRef: { path: string | null } = { path: loc.path };\n const scope = ctx.setup.scope;\n\n if (!loc.path) {\n const uv = await findTool(ctx.runner, 'uv', ctx.signal);\n const pipx = await findTool(ctx.runner, 'pipx', ctx.signal);\n const py = await findPython(ctx.runner, ctx.platform, ctx.signal);\n const pyOk = !!py && compareVersions(py.version, MIN_PYTHON) >= 0;\n const choice: { command: string[]; how: string } | null = uv\n ? { command: [uv.command, 'tool', 'install', 'graphifyy'], how: 'uv tool install' }\n : pipx\n ? { command: [pipx.command, 'install', 'graphifyy'], how: 'pipx install' }\n : pyOk && py\n ? { command: [py.command, ...py.args, '-m', 'pip', 'install', '--user', 'graphifyy'], how: 'pip install --user' }\n : null;\n if (choice && !uv && !pipx) notes.push('uv and pipx were not found; falling back to the documented `pip install --user` (no administrator rights needed).');\n if (!choice) {\n if (py && !pyOk) blockers.push(`Python ${py.version} is too old; graphify needs Python 3.10 or newer.`);\n blockers.push(\n 'No installer found. Graphify is a Python tool: install ONE of these, then re-run setup (nothing else is required):',\n ctx.platform === 'win32'\n ? ' - uv: winget install --id astral-sh.uv (or https://docs.astral.sh/uv/getting-started/installation/)'\n : ' - uv: https://docs.astral.sh/uv/getting-started/installation/',\n ' - Python 3.10+ from https://www.python.org/downloads/ (the Microsoft Store \"python\" shortcut on Windows is not a real install)',\n ' - pipx: https://pipx.pypa.io/stable/installation/',\n );\n return { steps, blockers, protectPaths, notes };\n }\n const { command, how } = choice;\n const [cmd, ...args] = command;\n steps.push({\n id: 'graphify:tool',\n description: `Install the graphifyy package for your user account (${how}; no administrator rights, nothing system-wide)`,\n command: command.map((c, i) => (i === 0 ? path.basename(c, path.extname(c)) : c)).join(' '),\n level: 'machine',\n async run(): Promise<StepOutcome> {\n const r = await ctx.runner.run({ command: cmd!, args, cwd: ctx.root, timeoutMs: 600_000, signal: ctx.signal });\n if (r.aborted) return { ok: false, detail: 'cancelled' };\n if (r.timedOut) return { ok: false, detail: 'timed out after 10 minutes (network?)' };\n if (!r.ok) {\n const text = `${r.stderr}\\n${r.stdout}`;\n if (/externally[- ]managed/i.test(text)) return { ok: false, detail: 'pip refuses to install into this Python (externally-managed environment). Install pipx or uv instead, then re-run.' };\n if (/permission denied|access is denied|EACCES|WinError 5/i.test(text)) return { ok: false, detail: 'permission denied while installing. Run the command yourself from a terminal with the right permissions.' };\n const tail = text.split(/\\r?\\n/).map((l) => l.trim()).filter(Boolean).slice(-3).join(' ');\n return { ok: false, detail: (tail || r.error || `exit ${r.code}`).slice(0, 400) };\n }\n const found = await locate(ctx, true);\n if (!found.path) return { ok: false, detail: 'graphifyy was installed but the graphify executable could not be located. Check the Scripts/bin folder of your Python tool.' };\n exeRef.path = found.path;\n return { ok: true, detail: found.onPath ? 'installed' : 'installed (not on PATH in this terminal)' };\n },\n });\n }\n\n steps.push({\n id: 'graphify:project-skill',\n description:\n scope === 'user'\n ? 'Install the Graphify skill for your user account (graphify install)'\n : 'Install the Graphify project skill (graphify install --project): adds .claude/skills/graphify/, appends a graphify section to CLAUDE.md and registers Claude Code hooks in .claude/settings.json (existing content is preserved and verified)',\n command: scope === 'user' ? 'graphify install' : 'graphify install --project',\n level: scope === 'user' ? 'user-config' : 'project',\n async run(): Promise<StepOutcome> {\n if (!exeRef.path) return { ok: false, detail: 'the graphify executable is not available' };\n const r = await ctx.runner.run({ command: exeRef.path, args: scope === 'user' ? ['install'] : ['install', '--project'], cwd: ctx.root, timeoutMs: 120_000, signal: ctx.signal });\n if (r.aborted) return { ok: false, detail: 'cancelled' };\n if (r.timedOut) return { ok: false, detail: 'timed out after 120 s' };\n if (!r.ok) {\n const tail = `${r.stderr}\\n${r.stdout}`.split(/\\r?\\n/).map((l) => l.trim()).filter(Boolean).slice(-3).join(' ');\n return { ok: false, detail: (tail || r.error || `exit ${r.code}`).slice(0, 400) };\n }\n return { ok: true, detail: 'project skill installed', restartRequired: true, managedPaths: scope === 'user' ? [] : ['.claude/skills/graphify'] };\n },\n });\n steps.push({\n id: 'graphify:notice',\n description: 'Record attribution in .devworkspace/THIRD_PARTY_NOTICES.md',\n level: 'project',\n async run(): Promise<StepOutcome> {\n await writeThirdPartyNotice(ctx.root, GRAPHIFY_NOTICE);\n return { ok: true };\n },\n });\n return { steps, blockers, protectPaths, notes };\n },\n};\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IntegrationAdapter } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* OmniRoute (model routing) is OPTIONAL and outside DevWorkspace's execution path. It is only observed, never installed:
|
|
4
|
+
* nothing here requires API keys, model configuration or network access.
|
|
5
|
+
*/
|
|
6
|
+
export declare const omniroute: IntegrationAdapter;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OmniRoute (model routing) is OPTIONAL and outside DevWorkspace's execution path. It is only observed, never installed:
|
|
3
|
+
* nothing here requires API keys, model configuration or network access.
|
|
4
|
+
*/
|
|
5
|
+
export const omniroute = {
|
|
6
|
+
id: 'omniroute',
|
|
7
|
+
name: 'OmniRoute',
|
|
8
|
+
required: false,
|
|
9
|
+
docsUrl: 'https://github.com/diegosouzapw/OmniRoute',
|
|
10
|
+
usage: 'Not used by DevWorkspace.',
|
|
11
|
+
async inspect(ctx, deep) {
|
|
12
|
+
const exe = await ctx.runner.which('omniroute');
|
|
13
|
+
return {
|
|
14
|
+
id: 'omniroute',
|
|
15
|
+
name: 'OmniRoute',
|
|
16
|
+
required: false,
|
|
17
|
+
status: exe ? 'INSTALLED' : 'NOT_INSTALLED',
|
|
18
|
+
variant: exe ? 'executable on PATH (unused)' : null,
|
|
19
|
+
scope: null,
|
|
20
|
+
version: null,
|
|
21
|
+
checks: [{ name: 'omniroute executable', ok: !!exe, detail: exe ? 'found; DevWorkspace does not use it' : 'not found (optional)' }],
|
|
22
|
+
capabilities: {},
|
|
23
|
+
warnings: [],
|
|
24
|
+
nextSteps: [],
|
|
25
|
+
deep,
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
async plan() {
|
|
29
|
+
return { steps: [], blockers: [], protectPaths: [], notes: ['OmniRoute is optional and is never installed by DevWorkspace.'] };
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=omniroute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"omniroute.js","sourceRoot":"","sources":["../../../src/integrations/adapters/omniroute.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAuB;IAC3C,EAAE,EAAE,WAAW;IACf,IAAI,EAAE,WAAW;IACjB,QAAQ,EAAE,KAAK;IACf,OAAO,EAAE,2CAA2C;IACpD,KAAK,EAAE,2BAA2B;IAClC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI;QACrB,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAChD,OAAO;YACL,EAAE,EAAE,WAAW;YACf,IAAI,EAAE,WAAW;YACjB,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,eAAe;YAC3C,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,IAAI;YACnD,KAAK,EAAE,IAAI;YACX,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,qCAAqC,CAAC,CAAC,CAAC,sBAAsB,EAAE,CAAC;YACnI,YAAY,EAAE,EAAE;YAChB,QAAQ,EAAE,EAAE;YACZ,SAAS,EAAE,EAAE;YACb,IAAI;SACL,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI;QACR,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,+DAA+D,CAAC,EAAE,CAAC;IACjI,CAAC;CACF,CAAC","sourcesContent":["import type { IntegrationAdapter, IntegrationReport } from '../types.js';\n\n/**\n * OmniRoute (model routing) is OPTIONAL and outside DevWorkspace's execution path. It is only observed, never installed:\n * nothing here requires API keys, model configuration or network access.\n */\nexport const omniroute: IntegrationAdapter = {\n id: 'omniroute',\n name: 'OmniRoute',\n required: false,\n docsUrl: 'https://github.com/diegosouzapw/OmniRoute',\n usage: 'Not used by DevWorkspace.',\n async inspect(ctx, deep): Promise<IntegrationReport> {\n const exe = await ctx.runner.which('omniroute');\n return {\n id: 'omniroute',\n name: 'OmniRoute',\n required: false,\n status: exe ? 'INSTALLED' : 'NOT_INSTALLED',\n variant: exe ? 'executable on PATH (unused)' : null,\n scope: null,\n version: null,\n checks: [{ name: 'omniroute executable', ok: !!exe, detail: exe ? 'found; DevWorkspace does not use it' : 'not found (optional)' }],\n capabilities: {},\n warnings: [],\n nextSteps: [],\n deep,\n };\n },\n async plan() {\n return { steps: [], blockers: [], protectPaths: [], notes: ['OmniRoute is optional and is never installed by DevWorkspace.'] };\n },\n};\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type NativeSpec } from './claudePlugin.js';
|
|
2
|
+
import { type NoticeSource } from '../notices.js';
|
|
3
|
+
import type { AdapterContext, Check, IntegrationAdapter, IntegrationId } from '../types.js';
|
|
4
|
+
export interface PluginSkillConfig {
|
|
5
|
+
id: IntegrationId;
|
|
6
|
+
name: string;
|
|
7
|
+
usage: string;
|
|
8
|
+
docsUrl: string;
|
|
9
|
+
native: NativeSpec;
|
|
10
|
+
/** `owner/repo` for the portable `npx skills add` path. */
|
|
11
|
+
skillsRepo: string;
|
|
12
|
+
/** Skill directories whose presence means the portable skills are installed. */
|
|
13
|
+
coreSkills: string[];
|
|
14
|
+
notice: NoticeSource;
|
|
15
|
+
/** Capabilities the native plugin provides that portable skills cannot. */
|
|
16
|
+
nativeOnly: string[];
|
|
17
|
+
/** True only when the plugin's own manifest registers lifecycle hooks (verified from its plugin.json). */
|
|
18
|
+
nativeHooks: boolean;
|
|
19
|
+
/** Extra check for the native plugin (e.g. hooks need node). */
|
|
20
|
+
extraNativeChecks?(ctx: AdapterContext): Promise<{
|
|
21
|
+
checks: Check[];
|
|
22
|
+
warnings: string[];
|
|
23
|
+
degraded: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
/** Shared supplementary directory expected beside skills (agent-skills ships references/). */
|
|
26
|
+
referencesDir?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function createPluginSkillAdapter(cfg: PluginSkillConfig): IntegrationAdapter;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { compareVersions, findClaudeCli } from '../exec.js';
|
|
4
|
+
import { findPlugin, listPlugins, marketplaceRegistered, MIN_PLUGIN_JSON, nativeSteps, pluginId, readDeclared } from './claudePlugin.js';
|
|
5
|
+
import { writeThirdPartyNotice } from '../notices.js';
|
|
6
|
+
import { pathExists } from '../../utils/fs.js';
|
|
7
|
+
const SKILLS_CLI = 'skills';
|
|
8
|
+
async function skillDirs(base) {
|
|
9
|
+
try {
|
|
10
|
+
return (await fs.readdir(path.join(base, '.claude', 'skills'), { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function fileExists(p) {
|
|
17
|
+
return pathExists(p);
|
|
18
|
+
}
|
|
19
|
+
export function createPluginSkillAdapter(cfg) {
|
|
20
|
+
const id = pluginId(cfg.native);
|
|
21
|
+
async function observe(ctx, deep) {
|
|
22
|
+
const declared = await readDeclared(ctx, cfg.native);
|
|
23
|
+
let claude = null;
|
|
24
|
+
let nativeInstalled = declared.enabled !== null;
|
|
25
|
+
let nativeScope = declared.enabled;
|
|
26
|
+
let nativeError = null;
|
|
27
|
+
let registered = declared.marketplace;
|
|
28
|
+
let version = null;
|
|
29
|
+
let listError = null;
|
|
30
|
+
if (deep) {
|
|
31
|
+
claude = await findClaudeCli(ctx.runner, ctx.home, ctx.signal);
|
|
32
|
+
if (claude && compareVersions(claude.version, MIN_PLUGIN_JSON) >= 0) {
|
|
33
|
+
const l = await listPlugins(ctx, claude);
|
|
34
|
+
if (l.ok) {
|
|
35
|
+
const p = findPlugin(l.plugins, id, ctx.root);
|
|
36
|
+
nativeInstalled = !!p && p.enabled;
|
|
37
|
+
nativeScope = p ? (p.scope === 'user' ? 'user' : 'project') : null;
|
|
38
|
+
version = p?.version ?? null;
|
|
39
|
+
if (p && (p.errors?.length ?? 0) > 0)
|
|
40
|
+
nativeError = p.errors.join('; ').slice(0, 300);
|
|
41
|
+
if (p && !p.enabled)
|
|
42
|
+
nativeError = 'the plugin is installed but disabled';
|
|
43
|
+
if (!p) {
|
|
44
|
+
const reg = await marketplaceRegistered(ctx, claude, cfg.native.marketplaceName);
|
|
45
|
+
registered = reg ?? registered;
|
|
46
|
+
}
|
|
47
|
+
else
|
|
48
|
+
registered = true;
|
|
49
|
+
}
|
|
50
|
+
else
|
|
51
|
+
listError = l.error ?? 'could not list plugins';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const projectSkills = await skillDirs(ctx.root);
|
|
55
|
+
const userSkills = await skillDirs(ctx.home);
|
|
56
|
+
const inProject = cfg.coreSkills.filter((s) => projectSkills.includes(s));
|
|
57
|
+
const inUser = cfg.coreSkills.filter((s) => userSkills.includes(s));
|
|
58
|
+
const portablePresent = inProject.length >= inUser.length ? inProject : inUser;
|
|
59
|
+
const portableScope = portablePresent.length === 0 ? null : inProject.length >= inUser.length ? 'project' : 'user';
|
|
60
|
+
let refs = true;
|
|
61
|
+
if (cfg.referencesDir && portablePresent.length > 0) {
|
|
62
|
+
const base = portableScope === 'user' ? ctx.home : ctx.root;
|
|
63
|
+
refs = (await fileExists(path.join(base, '.claude', 'skills', cfg.referencesDir))) || (await fileExists(path.join(base, '.claude', cfg.referencesDir))) || (await fileExists(path.join(base, cfg.referencesDir)));
|
|
64
|
+
}
|
|
65
|
+
return { claude, nativeInstalled, nativeScope, nativeError, registered, version, listError, portablePresent, portableScope, refs };
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
id: cfg.id,
|
|
69
|
+
name: cfg.name,
|
|
70
|
+
required: true,
|
|
71
|
+
usage: cfg.usage,
|
|
72
|
+
docsUrl: cfg.docsUrl,
|
|
73
|
+
async inspect(ctx, deep) {
|
|
74
|
+
const o = await observe(ctx, deep);
|
|
75
|
+
const checks = [];
|
|
76
|
+
const warnings = [];
|
|
77
|
+
const nextSteps = [];
|
|
78
|
+
const caps = { skills: false, commands: false, hooks: false };
|
|
79
|
+
const base = { id: cfg.id, name: cfg.name, required: true, deep };
|
|
80
|
+
if (deep)
|
|
81
|
+
checks.push({ name: 'Claude Code CLI', ok: !!o.claude, detail: o.claude ? `v${o.claude.version}` : 'not found (PATH or VS Code extension)' });
|
|
82
|
+
if (o.listError)
|
|
83
|
+
warnings.push(`Could not read the plugin list: ${o.listError}`);
|
|
84
|
+
if (o.nativeInstalled) {
|
|
85
|
+
checks.push({ name: 'native plugin', ok: !o.nativeError, detail: o.nativeError ?? `${id}${o.nativeScope ? ` (${o.nativeScope} scope)` : ''}` });
|
|
86
|
+
caps.skills = true;
|
|
87
|
+
caps.commands = true;
|
|
88
|
+
let degraded = !!o.nativeError;
|
|
89
|
+
if (o.nativeError) {
|
|
90
|
+
warnings.push(`${cfg.name} plugin problem: ${o.nativeError}`);
|
|
91
|
+
nextSteps.push(`claude plugin list (inspect errors), then: claude plugin enable ${id}`);
|
|
92
|
+
}
|
|
93
|
+
const extra = cfg.extraNativeChecks ? await cfg.extraNativeChecks(ctx) : { checks: [], warnings: [], degraded: false };
|
|
94
|
+
checks.push(...extra.checks);
|
|
95
|
+
warnings.push(...extra.warnings);
|
|
96
|
+
caps.hooks = cfg.nativeHooks && !extra.degraded;
|
|
97
|
+
degraded = degraded || extra.degraded;
|
|
98
|
+
if (!deep)
|
|
99
|
+
warnings.push('Not yet verified against `claude plugin list`; run `devworkspace doctor` or `devworkspace integrations status`.');
|
|
100
|
+
return { ...base, status: degraded ? 'DEGRADED' : deep ? 'VERIFIED' : 'INSTALLED', variant: 'native plugin', scope: o.nativeScope, version: o.version, checks, capabilities: caps, warnings, nextSteps };
|
|
101
|
+
}
|
|
102
|
+
if (o.portablePresent.length > 0) {
|
|
103
|
+
const complete = o.portablePresent.length === cfg.coreSkills.length;
|
|
104
|
+
caps.skills = complete;
|
|
105
|
+
checks.push({ name: 'portable skills', ok: complete, detail: `${o.portablePresent.length}/${cfg.coreSkills.length} core skills found (${o.portableScope} scope)` });
|
|
106
|
+
if (cfg.referencesDir)
|
|
107
|
+
checks.push({ name: 'supplementary references', ok: o.refs, detail: o.refs ? 'found' : `${cfg.referencesDir}/ not installed` });
|
|
108
|
+
if (!complete) {
|
|
109
|
+
const missing = cfg.coreSkills.filter((s) => !o.portablePresent.includes(s));
|
|
110
|
+
warnings.push(`Partial installation: missing skills ${missing.join(', ')}.`);
|
|
111
|
+
nextSteps.push('devworkspace integrations repair --yes');
|
|
112
|
+
}
|
|
113
|
+
if (!o.refs)
|
|
114
|
+
warnings.push(`The shared ${cfg.referencesDir}/ checklists are not installed (known limitation of the skills CLI), so skills that point to them have broken references.`);
|
|
115
|
+
warnings.push(`Portable skills only: ${cfg.nativeOnly.join(', ')} from the native plugin are not available.`);
|
|
116
|
+
nextSteps.push(`For the full plugin: claude plugin marketplace add ${cfg.native.marketplaceUrl} --scope project, then claude plugin install ${id} --scope project`);
|
|
117
|
+
return { ...base, status: 'DEGRADED', variant: 'portable skills', scope: o.portableScope, version: null, checks, capabilities: caps, warnings, nextSteps };
|
|
118
|
+
}
|
|
119
|
+
checks.push({ name: 'marketplace', ok: o.registered, detail: o.registered ? `${cfg.native.marketplaceName} is registered but the plugin is not installed` : 'not registered' });
|
|
120
|
+
nextSteps.push('devworkspace setup --integrations --yes');
|
|
121
|
+
return { ...base, status: 'NOT_INSTALLED', variant: null, scope: null, version: null, checks, capabilities: caps, warnings, nextSteps };
|
|
122
|
+
},
|
|
123
|
+
async plan(ctx, report) {
|
|
124
|
+
const o = await observe(ctx, true);
|
|
125
|
+
const blockers = [];
|
|
126
|
+
const notes = [];
|
|
127
|
+
const protectPaths = ['.claude/settings.json', '.claude/settings.local.json', 'CLAUDE.md', '.claude/CLAUDE.md', 'AGENTS.md'];
|
|
128
|
+
const steps = [];
|
|
129
|
+
const scope = ctx.setup.scope;
|
|
130
|
+
if (report.status === 'VERIFIED' && !ctx.setup.repair)
|
|
131
|
+
return { steps, blockers, protectPaths, notes: ['already verified'] };
|
|
132
|
+
if (o.nativeInstalled && !o.nativeError)
|
|
133
|
+
return { steps, blockers, protectPaths, notes: ['native plugin already installed'] };
|
|
134
|
+
if (o.claude && compareVersions(o.claude.version, MIN_PLUGIN_JSON) >= 0 && !o.listError) {
|
|
135
|
+
// marketplace registered but plugin missing -> only the install step
|
|
136
|
+
steps.push(...nativeSteps(ctx, o.claude, cfg.native, scope, { registered: o.registered }));
|
|
137
|
+
if (o.nativeError && o.nativeScope) {
|
|
138
|
+
notes.push(`The plugin is present but unhealthy (${o.nativeError}); re-running install to repair.`);
|
|
139
|
+
}
|
|
140
|
+
return { steps, blockers, protectPaths, notes };
|
|
141
|
+
}
|
|
142
|
+
if (o.claude)
|
|
143
|
+
notes.push(`Claude Code ${o.claude.version} is older than ${MIN_PLUGIN_JSON}, which the plugin commands' --json output needs; using portable skills.`);
|
|
144
|
+
else
|
|
145
|
+
notes.push('The Claude Code CLI was not found on PATH or in the VS Code extension; using portable skills instead of the native plugin.');
|
|
146
|
+
if (o.listError)
|
|
147
|
+
notes.push(`\`claude plugin list\` failed (${o.listError}); using portable skills.`);
|
|
148
|
+
// ---- portable skills via the documented Skills CLI ----
|
|
149
|
+
if (o.portablePresent.length === cfg.coreSkills.length && o.refs)
|
|
150
|
+
return { steps, blockers, protectPaths, notes: [...notes, 'portable skills already installed'] };
|
|
151
|
+
const managed = new Set(ctx.prior?.managedPaths ?? []);
|
|
152
|
+
const baseDir = scope === 'user' ? ctx.home : ctx.root;
|
|
153
|
+
const existing = await skillDirs(baseDir);
|
|
154
|
+
const conflicts = cfg.coreSkills.filter((s) => existing.includes(s) && !managed.has(`.claude/skills/${s}`));
|
|
155
|
+
if (conflicts.length > 0 && o.portablePresent.length < cfg.coreSkills.length) {
|
|
156
|
+
blockers.push(`Skill folder(s) ${conflicts.join(', ')} already exist in .claude/skills and were not installed by DevWorkspace, so they are treated as yours and will not be overwritten. Rename or remove them, or install the native plugin instead.`);
|
|
157
|
+
return { steps, blockers, protectPaths, notes };
|
|
158
|
+
}
|
|
159
|
+
const args = ['--yes', SKILLS_CLI, 'add', cfg.skillsRepo, '--agent', 'claude-code', '--skill', '*', '--copy', '-y', '--json', ...(scope === 'user' ? ['-g'] : [])];
|
|
160
|
+
steps.push({
|
|
161
|
+
id: `${cfg.id}:skills-cli`,
|
|
162
|
+
description: `Install ${cfg.name} skills from ${cfg.skillsRepo} with the Skills CLI (${scope} scope, copied files, telemetry disabled)`,
|
|
163
|
+
command: `npx ${args.join(' ')}`,
|
|
164
|
+
level: 'third-party-code',
|
|
165
|
+
async run() {
|
|
166
|
+
const before = new Set(await skillDirs(baseDir));
|
|
167
|
+
const r = await ctx.runner.runNpx(args, { cwd: ctx.root, timeoutMs: 300_000, signal: ctx.signal, env: { DISABLE_TELEMETRY: '1', DO_NOT_TRACK: '1' } });
|
|
168
|
+
if (r.aborted)
|
|
169
|
+
return { ok: false, detail: 'cancelled' };
|
|
170
|
+
if (r.timedOut)
|
|
171
|
+
return { ok: false, detail: 'timed out after 300 s (network?)' };
|
|
172
|
+
if (!r.ok) {
|
|
173
|
+
const tail = `${r.stderr}\n${r.stdout}`.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).slice(-3).join(' ');
|
|
174
|
+
return { ok: false, detail: (tail || r.error || `exit ${r.code}`).slice(0, 400) };
|
|
175
|
+
}
|
|
176
|
+
const after = await skillDirs(baseDir);
|
|
177
|
+
const created = after.filter((s) => !before.has(s));
|
|
178
|
+
ctx.report({ id: cfg.id, step: `${cfg.id}:skills-cli`, state: 'info', detail: `${created.length} skill folder(s) added` });
|
|
179
|
+
return { ok: true, detail: `${created.length} skill folder(s) added`, managedPaths: created.map((s) => `.claude/skills/${s}`) };
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
steps.push({
|
|
183
|
+
id: `${cfg.id}:notice`,
|
|
184
|
+
description: 'Record third-party license/attribution in .devworkspace/THIRD_PARTY_NOTICES.md',
|
|
185
|
+
level: 'project',
|
|
186
|
+
async run() {
|
|
187
|
+
await writeThirdPartyNotice(ctx.root, cfg.notice);
|
|
188
|
+
return { ok: true };
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
return { steps, blockers, protectPaths, notes };
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=pluginSkill.js.map
|