devworkspace 0.1.1 → 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 +20 -0
- package/README.md +15 -2
- package/dist/agents/claude/content.js +3 -3
- package/dist/agents/claude/content.js.map +1 -1
- package/dist/cli/args.d.ts +19 -0
- package/dist/cli/args.js +6 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/help.js +40 -5
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/render.d.ts +7 -0
- package/dist/cli/render.js +96 -13
- package/dist/cli/render.js.map +1 -1
- package/dist/cli/run.d.ts +6 -0
- package/dist/cli/run.js +65 -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/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 +2 -2
- package/docs/integrations.md +98 -33
- package/docs/security.md +5 -1
- package/docs/testing.md +2 -0
- package/docs/troubleshooting.md +8 -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,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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pluginSkill.js","sourceRoot":"","sources":["../../../src/integrations/adapters/pluginSkill.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { compareVersions, findClaudeCli, type Tool } from '../exec.js';\nimport { findPlugin, listPlugins, marketplaceRegistered, MIN_PLUGIN_JSON, nativeSteps, pluginId, readDeclared, type NativeSpec } from './claudePlugin.js';\nimport { writeThirdPartyNotice, type NoticeSource } from '../notices.js';\nimport type { AdapterContext, Check, IntegrationAdapter, IntegrationId, IntegrationReport, PlanResult, PlanStep, StepOutcome } from '../types.js';\nimport { pathExists } from '../../utils/fs.js';\n\nexport interface PluginSkillConfig {\n id: IntegrationId;\n name: string;\n usage: string;\n docsUrl: string;\n native: NativeSpec;\n /** `owner/repo` for the portable `npx skills add` path. */\n skillsRepo: string;\n /** Skill directories whose presence means the portable skills are installed. */\n coreSkills: string[];\n notice: NoticeSource;\n /** Capabilities the native plugin provides that portable skills cannot. */\n nativeOnly: string[];\n /** True only when the plugin's own manifest registers lifecycle hooks (verified from its plugin.json). */\n nativeHooks: boolean;\n /** Extra check for the native plugin (e.g. hooks need node). */\n extraNativeChecks?(ctx: AdapterContext): Promise<{ checks: Check[]; warnings: string[]; degraded: boolean }>;\n /** Shared supplementary directory expected beside skills (agent-skills ships references/). */\n referencesDir?: string;\n}\n\nconst SKILLS_CLI = 'skills';\n\nasync function skillDirs(base: string): Promise<string[]> {\n try {\n return (await fs.readdir(path.join(base, '.claude', 'skills'), { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nasync function fileExists(p: string): Promise<boolean> {\n return pathExists(p);\n}\n\nexport function createPluginSkillAdapter(cfg: PluginSkillConfig): IntegrationAdapter {\n const id = pluginId(cfg.native);\n\n async function observe(ctx: AdapterContext, deep: boolean): Promise<{\n claude: Tool | null;\n nativeInstalled: boolean;\n nativeScope: 'project' | 'user' | null;\n nativeError: string | null;\n registered: boolean;\n version: string | null;\n listError: string | null;\n portablePresent: string[];\n portableScope: 'project' | 'user' | null;\n refs: boolean;\n }> {\n const declared = await readDeclared(ctx, cfg.native);\n let claude: Tool | null = null;\n let nativeInstalled = declared.enabled !== null;\n let nativeScope = declared.enabled;\n let nativeError: string | null = null;\n let registered = declared.marketplace;\n let version: string | null = null;\n let listError: string | null = null;\n if (deep) {\n claude = await findClaudeCli(ctx.runner, ctx.home, ctx.signal);\n if (claude && compareVersions(claude.version, MIN_PLUGIN_JSON) >= 0) {\n const l = await listPlugins(ctx, claude);\n if (l.ok) {\n const p = findPlugin(l.plugins, id, ctx.root);\n nativeInstalled = !!p && p.enabled;\n nativeScope = p ? (p.scope === 'user' ? 'user' : 'project') : null;\n version = p?.version ?? null;\n if (p && (p.errors?.length ?? 0) > 0) nativeError = p.errors!.join('; ').slice(0, 300);\n if (p && !p.enabled) nativeError = 'the plugin is installed but disabled';\n if (!p) {\n const reg = await marketplaceRegistered(ctx, claude, cfg.native.marketplaceName);\n registered = reg ?? registered;\n } else registered = true;\n } else listError = l.error ?? 'could not list plugins';\n }\n }\n const projectSkills = await skillDirs(ctx.root);\n const userSkills = await skillDirs(ctx.home);\n const inProject = cfg.coreSkills.filter((s) => projectSkills.includes(s));\n const inUser = cfg.coreSkills.filter((s) => userSkills.includes(s));\n const portablePresent = inProject.length >= inUser.length ? inProject : inUser;\n const portableScope = portablePresent.length === 0 ? null : inProject.length >= inUser.length ? 'project' : 'user';\n let refs = true;\n if (cfg.referencesDir && portablePresent.length > 0) {\n const base = portableScope === 'user' ? ctx.home : ctx.root;\n 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)));\n }\n return { claude, nativeInstalled, nativeScope, nativeError, registered, version, listError, portablePresent, portableScope, refs };\n }\n\n return {\n id: cfg.id,\n name: cfg.name,\n required: true,\n usage: cfg.usage,\n docsUrl: cfg.docsUrl,\n\n async inspect(ctx, deep): Promise<IntegrationReport> {\n const o = await observe(ctx, deep);\n const checks: Check[] = [];\n const warnings: string[] = [];\n const nextSteps: string[] = [];\n const caps: Record<string, boolean> = { skills: false, commands: false, hooks: false };\n const base = { id: cfg.id, name: cfg.name, required: true, deep } as const;\n if (deep) checks.push({ name: 'Claude Code CLI', ok: !!o.claude, detail: o.claude ? `v${o.claude.version}` : 'not found (PATH or VS Code extension)' });\n if (o.listError) warnings.push(`Could not read the plugin list: ${o.listError}`);\n\n if (o.nativeInstalled) {\n checks.push({ name: 'native plugin', ok: !o.nativeError, detail: o.nativeError ?? `${id}${o.nativeScope ? ` (${o.nativeScope} scope)` : ''}` });\n caps.skills = true;\n caps.commands = true;\n let degraded = !!o.nativeError;\n if (o.nativeError) {\n warnings.push(`${cfg.name} plugin problem: ${o.nativeError}`);\n nextSteps.push(`claude plugin list (inspect errors), then: claude plugin enable ${id}`);\n }\n const extra = cfg.extraNativeChecks ? await cfg.extraNativeChecks(ctx) : { checks: [], warnings: [], degraded: false };\n checks.push(...extra.checks);\n warnings.push(...extra.warnings);\n caps.hooks = cfg.nativeHooks && !extra.degraded;\n degraded = degraded || extra.degraded;\n if (!deep) warnings.push('Not yet verified against `claude plugin list`; run `devworkspace doctor` or `devworkspace integrations status`.');\n return { ...base, status: degraded ? 'DEGRADED' : deep ? 'VERIFIED' : 'INSTALLED', variant: 'native plugin', scope: o.nativeScope, version: o.version, checks, capabilities: caps, warnings, nextSteps };\n }\n\n if (o.portablePresent.length > 0) {\n const complete = o.portablePresent.length === cfg.coreSkills.length;\n caps.skills = complete;\n checks.push({ name: 'portable skills', ok: complete, detail: `${o.portablePresent.length}/${cfg.coreSkills.length} core skills found (${o.portableScope} scope)` });\n if (cfg.referencesDir) checks.push({ name: 'supplementary references', ok: o.refs, detail: o.refs ? 'found' : `${cfg.referencesDir}/ not installed` });\n if (!complete) {\n const missing = cfg.coreSkills.filter((s) => !o.portablePresent.includes(s));\n warnings.push(`Partial installation: missing skills ${missing.join(', ')}.`);\n nextSteps.push('devworkspace integrations repair --yes');\n }\n if (!o.refs) 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.`);\n warnings.push(`Portable skills only: ${cfg.nativeOnly.join(', ')} from the native plugin are not available.`);\n nextSteps.push(`For the full plugin: claude plugin marketplace add ${cfg.native.marketplaceUrl} --scope project, then claude plugin install ${id} --scope project`);\n return { ...base, status: 'DEGRADED', variant: 'portable skills', scope: o.portableScope, version: null, checks, capabilities: caps, warnings, nextSteps };\n }\n\n checks.push({ name: 'marketplace', ok: o.registered, detail: o.registered ? `${cfg.native.marketplaceName} is registered but the plugin is not installed` : 'not registered' });\n nextSteps.push('devworkspace setup --integrations --yes');\n return { ...base, status: 'NOT_INSTALLED', variant: null, scope: null, version: null, checks, capabilities: caps, warnings, nextSteps };\n },\n\n async plan(ctx, report): Promise<PlanResult> {\n const o = await observe(ctx, true);\n const blockers: string[] = [];\n const notes: string[] = [];\n const protectPaths = ['.claude/settings.json', '.claude/settings.local.json', 'CLAUDE.md', '.claude/CLAUDE.md', 'AGENTS.md'];\n const steps: PlanStep[] = [];\n const scope = ctx.setup.scope;\n\n if (report.status === 'VERIFIED' && !ctx.setup.repair) return { steps, blockers, protectPaths, notes: ['already verified'] };\n if (o.nativeInstalled && !o.nativeError) return { steps, blockers, protectPaths, notes: ['native plugin already installed'] };\n\n if (o.claude && compareVersions(o.claude.version, MIN_PLUGIN_JSON) >= 0 && !o.listError) {\n // marketplace registered but plugin missing -> only the install step\n steps.push(...nativeSteps(ctx, o.claude, cfg.native, scope, { registered: o.registered }));\n if (o.nativeError && o.nativeScope) {\n notes.push(`The plugin is present but unhealthy (${o.nativeError}); re-running install to repair.`);\n }\n return { steps, blockers, protectPaths, notes };\n }\n if (o.claude) notes.push(`Claude Code ${o.claude.version} is older than ${MIN_PLUGIN_JSON}, which the plugin commands' --json output needs; using portable skills.`);\n else 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.');\n if (o.listError) notes.push(`\\`claude plugin list\\` failed (${o.listError}); using portable skills.`);\n\n // ---- portable skills via the documented Skills CLI ----\n if (o.portablePresent.length === cfg.coreSkills.length && o.refs) return { steps, blockers, protectPaths, notes: [...notes, 'portable skills already installed'] };\n const managed = new Set(ctx.prior?.managedPaths ?? []);\n const baseDir = scope === 'user' ? ctx.home : ctx.root;\n const existing = await skillDirs(baseDir);\n const conflicts = cfg.coreSkills.filter((s) => existing.includes(s) && !managed.has(`.claude/skills/${s}`));\n if (conflicts.length > 0 && o.portablePresent.length < cfg.coreSkills.length) {\n blockers.push(\n `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.`,\n );\n return { steps, blockers, protectPaths, notes };\n }\n const args = ['--yes', SKILLS_CLI, 'add', cfg.skillsRepo, '--agent', 'claude-code', '--skill', '*', '--copy', '-y', '--json', ...(scope === 'user' ? ['-g'] : [])];\n steps.push({\n id: `${cfg.id}:skills-cli`,\n description: `Install ${cfg.name} skills from ${cfg.skillsRepo} with the Skills CLI (${scope} scope, copied files, telemetry disabled)`,\n command: `npx ${args.join(' ')}`,\n level: 'third-party-code',\n async run(): Promise<StepOutcome> {\n const before = new Set(await skillDirs(baseDir));\n const r = await ctx.runner.runNpx(args, { cwd: ctx.root, timeoutMs: 300_000, signal: ctx.signal, env: { DISABLE_TELEMETRY: '1', DO_NOT_TRACK: '1' } });\n if (r.aborted) return { ok: false, detail: 'cancelled' };\n if (r.timedOut) return { ok: false, detail: 'timed out after 300 s (network?)' };\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 const after = await skillDirs(baseDir);\n const created = after.filter((s) => !before.has(s));\n ctx.report({ id: cfg.id, step: `${cfg.id}:skills-cli`, state: 'info', detail: `${created.length} skill folder(s) added` });\n return { ok: true, detail: `${created.length} skill folder(s) added`, managedPaths: created.map((s) => `.claude/skills/${s}`) };\n },\n });\n steps.push({\n id: `${cfg.id}:notice`,\n description: 'Record third-party license/attribution in .devworkspace/THIRD_PARTY_NOTICES.md',\n level: 'project',\n async run(): Promise<StepOutcome> {\n await writeThirdPartyNotice(ctx.root, cfg.notice);\n return { ok: true };\n },\n });\n return { steps, blockers, protectPaths, notes };\n },\n };\n}\n\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ponytail (DietrichGebert/ponytail). Verified against INSTALL.md and the repository on 2026-10-09:
|
|
3
|
+
* - native plugin: marketplace `ponytail`, plugin `ponytail`; two Node.js lifecycle hooks (node must be on PATH),
|
|
4
|
+
* mode persistence, level switching, statusline, subagent ruleset injection
|
|
5
|
+
* - portable: `npx skills add DietrichGebert/ponytail` installs the skills only; AGENTS.md carries the always-on ruleset
|
|
6
|
+
* The portable path therefore never claims hooks, commands or the always-on ruleset.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ponytail: import("../types.js").IntegrationAdapter;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { PONYTAIL_NOTICE } from '../notices.js';
|
|
2
|
+
import { createPluginSkillAdapter } from './pluginSkill.js';
|
|
3
|
+
/**
|
|
4
|
+
* Ponytail (DietrichGebert/ponytail). Verified against INSTALL.md and the repository on 2026-10-09:
|
|
5
|
+
* - native plugin: marketplace `ponytail`, plugin `ponytail`; two Node.js lifecycle hooks (node must be on PATH),
|
|
6
|
+
* mode persistence, level switching, statusline, subagent ruleset injection
|
|
7
|
+
* - portable: `npx skills add DietrichGebert/ponytail` installs the skills only; AGENTS.md carries the always-on ruleset
|
|
8
|
+
* The portable path therefore never claims hooks, commands or the always-on ruleset.
|
|
9
|
+
*/
|
|
10
|
+
export const ponytail = createPluginSkillAdapter({
|
|
11
|
+
id: 'ponytail',
|
|
12
|
+
name: 'Ponytail',
|
|
13
|
+
docsUrl: 'https://github.com/DietrichGebert/ponytail',
|
|
14
|
+
usage: 'Use ponytail to review the diff of meaningful changes (features, refactors, security work) for unnecessary complexity and debt, and for simplification. ' +
|
|
15
|
+
'Run /ponytail-review for a diff review and /ponytail-audit only when asked for a repository-wide pass (native plugin only). Quality review never replaces running tests.',
|
|
16
|
+
native: { marketplaceName: 'ponytail', marketplaceUrl: 'https://github.com/DietrichGebert/ponytail.git', pluginName: 'ponytail' },
|
|
17
|
+
skillsRepo: 'DietrichGebert/ponytail',
|
|
18
|
+
coreSkills: ['ponytail'],
|
|
19
|
+
notice: PONYTAIL_NOTICE,
|
|
20
|
+
nativeOnly: ['lifecycle hooks', 'slash commands', 'mode persistence', 'subagent injection'],
|
|
21
|
+
nativeHooks: true,
|
|
22
|
+
async extraNativeChecks(ctx) {
|
|
23
|
+
const node = await ctx.runner.which('node');
|
|
24
|
+
return node
|
|
25
|
+
? { checks: [{ name: 'node on PATH (hooks)', ok: true }], warnings: [], degraded: false }
|
|
26
|
+
: {
|
|
27
|
+
checks: [{ name: 'node on PATH (hooks)', ok: false, detail: 'not found' }],
|
|
28
|
+
warnings: ['The Ponytail plugin runs Node.js lifecycle hooks; without `node` on PATH its skills still work but every hook call fails.'],
|
|
29
|
+
degraded: true,
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=ponytail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ponytail.js","sourceRoot":"","sources":["../../../src/integrations/adapters/ponytail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,wBAAwB,CAAC;IAC/C,EAAE,EAAE,UAAU;IACd,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,4CAA4C;IACrD,KAAK,EACH,0JAA0J;QAC1J,0KAA0K;IAC5K,MAAM,EAAE,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,gDAAgD,EAAE,UAAU,EAAE,UAAU,EAAE;IACjI,UAAU,EAAE,yBAAyB;IACrC,UAAU,EAAE,CAAC,UAAU,CAAC;IACxB,MAAM,EAAE,eAAe;IACvB,UAAU,EAAE,CAAC,iBAAiB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,oBAAoB,CAAC;IAC3F,WAAW,EAAE,IAAI;IACjB,KAAK,CAAC,iBAAiB,CAAC,GAAG;QACzB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5C,OAAO,IAAI;YACT,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;YACzF,CAAC,CAAC;gBACE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;gBAC1E,QAAQ,EAAE,CAAC,2HAA2H,CAAC;gBACvI,QAAQ,EAAE,IAAI;aACf,CAAC;IACR,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { PONYTAIL_NOTICE } from '../notices.js';\nimport { createPluginSkillAdapter } from './pluginSkill.js';\n\n/**\n * Ponytail (DietrichGebert/ponytail). Verified against INSTALL.md and the repository on 2026-10-09:\n * - native plugin: marketplace `ponytail`, plugin `ponytail`; two Node.js lifecycle hooks (node must be on PATH),\n * mode persistence, level switching, statusline, subagent ruleset injection\n * - portable: `npx skills add DietrichGebert/ponytail` installs the skills only; AGENTS.md carries the always-on ruleset\n * The portable path therefore never claims hooks, commands or the always-on ruleset.\n */\nexport const ponytail = createPluginSkillAdapter({\n id: 'ponytail',\n name: 'Ponytail',\n docsUrl: 'https://github.com/DietrichGebert/ponytail',\n usage:\n 'Use ponytail to review the diff of meaningful changes (features, refactors, security work) for unnecessary complexity and debt, and for simplification. ' +\n 'Run /ponytail-review for a diff review and /ponytail-audit only when asked for a repository-wide pass (native plugin only). Quality review never replaces running tests.',\n native: { marketplaceName: 'ponytail', marketplaceUrl: 'https://github.com/DietrichGebert/ponytail.git', pluginName: 'ponytail' },\n skillsRepo: 'DietrichGebert/ponytail',\n coreSkills: ['ponytail'],\n notice: PONYTAIL_NOTICE,\n nativeOnly: ['lifecycle hooks', 'slash commands', 'mode persistence', 'subagent injection'],\n nativeHooks: true,\n async extraNativeChecks(ctx) {\n const node = await ctx.runner.which('node');\n return node\n ? { checks: [{ name: 'node on PATH (hooks)', ok: true }], warnings: [], degraded: false }\n : {\n checks: [{ name: 'node on PATH (hooks)', ok: false, detail: 'not found' }],\n warnings: ['The Ponytail plugin runs Node.js lifecycle hooks; without `node` on PATH its skills still work but every hook call fails.'],\n degraded: true,\n };\n },\n});\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Runner } from './types.js';
|
|
2
|
+
export declare function assertSafeShimArgs(args: string[]): void;
|
|
3
|
+
/** Run a .cmd/.bat shim through cmd.exe with validated, quoted arguments (Node refuses to spawn them directly). */
|
|
4
|
+
export declare function shimInvocation(file: string, args: string[], comspec?: string): {
|
|
5
|
+
command: string;
|
|
6
|
+
args: string[];
|
|
7
|
+
};
|
|
8
|
+
/** npm's npx-cli.js next to the running Node, so npx never needs a shell. */
|
|
9
|
+
export declare function resolveNpxCli(execPath?: string): Promise<string | null>;
|
|
10
|
+
export declare function createRunner(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): Runner;
|
|
11
|
+
export declare function parseVersion(text: string): string | null;
|
|
12
|
+
export declare function compareVersions(a: string, b: string): number;
|
|
13
|
+
export interface Tool {
|
|
14
|
+
command: string;
|
|
15
|
+
args: string[];
|
|
16
|
+
version: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Find a working Python 3. Each candidate must actually run: on Windows `python` is often the Microsoft Store shortcut
|
|
20
|
+
* that exists on PATH but only prints "Python was not found", so PATH presence proves nothing.
|
|
21
|
+
*/
|
|
22
|
+
export declare function findPython(runner: Runner, platform: NodeJS.Platform, signal?: AbortSignal): Promise<Tool | null>;
|
|
23
|
+
export declare function findTool(runner: Runner, name: string, signal?: AbortSignal): Promise<Tool | null>;
|
|
24
|
+
export declare function findClaudeCli(runner: Runner, home: string, signal?: AbortSignal): Promise<Tool | null>;
|
|
25
|
+
/** Parse the one JSON object Claude Code prints on the last stdout line of `--json` commands. */
|
|
26
|
+
export declare function parseLastJsonLine(stdout: string): Record<string, unknown> | null;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { DevWorkspaceError } from '../errors/index.js';
|
|
4
|
+
import { isShellShim, run as procRun, which as procWhich } from '../utils/proc.js';
|
|
5
|
+
/** Arguments allowed through cmd.exe. No quotes, %, ^, &, |, <, >, !, ( ) or control characters can reach the shell. */
|
|
6
|
+
const SAFE_SHIM_ARG = /^[A-Za-z0-9_@.:/\\=,+#\- ]*$/;
|
|
7
|
+
export function assertSafeShimArgs(args) {
|
|
8
|
+
for (const a of args) {
|
|
9
|
+
if (!SAFE_SHIM_ARG.test(a)) {
|
|
10
|
+
throw new DevWorkspaceError('SECURITY', `Refusing to pass an unsafe argument to a Windows .cmd shim: ${JSON.stringify(a.slice(0, 40))}`, {
|
|
11
|
+
hint: 'This is a safety check in DevWorkspace; the argument contains characters cmd.exe would interpret.',
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function quoteWin(s) {
|
|
17
|
+
return `"${s}"`;
|
|
18
|
+
}
|
|
19
|
+
/** Run a .cmd/.bat shim through cmd.exe with validated, quoted arguments (Node refuses to spawn them directly). */
|
|
20
|
+
export function shimInvocation(file, args, comspec = process.env.ComSpec || 'cmd.exe') {
|
|
21
|
+
assertSafeShimArgs(args);
|
|
22
|
+
if (/["%^&|<>!\r\n]/.test(file)) {
|
|
23
|
+
throw new DevWorkspaceError('SECURITY', 'Refusing to run an executable whose path contains shell metacharacters.');
|
|
24
|
+
}
|
|
25
|
+
const line = [quoteWin(file), ...args.map(quoteWin)].join(' ');
|
|
26
|
+
return { command: comspec, args: ['/d', '/s', '/c', `"${line}"`] };
|
|
27
|
+
}
|
|
28
|
+
/** npm's npx-cli.js next to the running Node, so npx never needs a shell. */
|
|
29
|
+
export async function resolveNpxCli(execPath = process.execPath) {
|
|
30
|
+
const dir = path.dirname(execPath);
|
|
31
|
+
for (const candidate of [path.join(dir, 'node_modules', 'npm', 'bin', 'npx-cli.js'), path.join(dir, '..', 'lib', 'node_modules', 'npm', 'bin', 'npx-cli.js')]) {
|
|
32
|
+
try {
|
|
33
|
+
await fs.access(candidate);
|
|
34
|
+
return candidate;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
/* next */
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
export function createRunner(env = process.env, platform = process.platform) {
|
|
43
|
+
const base = (spec, command, args, verbatim = false) => procRun(command, args, {
|
|
44
|
+
cwd: spec.cwd,
|
|
45
|
+
env: { ...(env === process.env ? {} : env), ...spec.env },
|
|
46
|
+
timeoutMs: spec.timeoutMs,
|
|
47
|
+
signal: spec.signal,
|
|
48
|
+
windowsVerbatimArguments: verbatim,
|
|
49
|
+
});
|
|
50
|
+
const runner = {
|
|
51
|
+
which: (name) => procWhich(name, env),
|
|
52
|
+
async run(spec) {
|
|
53
|
+
if (platform === 'win32' && isShellShim(spec.command)) {
|
|
54
|
+
const inv = shimInvocation(spec.command, spec.args);
|
|
55
|
+
return base(spec, inv.command, inv.args, true);
|
|
56
|
+
}
|
|
57
|
+
return base(spec, spec.command, spec.args);
|
|
58
|
+
},
|
|
59
|
+
async runNpx(args, spec = {}) {
|
|
60
|
+
const cli = await resolveNpxCli();
|
|
61
|
+
if (cli)
|
|
62
|
+
return base({ ...spec, command: process.execPath, args }, process.execPath, [cli, ...args]);
|
|
63
|
+
const npx = await procWhich('npx', env);
|
|
64
|
+
if (!npx)
|
|
65
|
+
return { ok: false, code: null, stdout: '', stderr: '', timedOut: false, error: 'npx was not found' };
|
|
66
|
+
return runner.run({ ...spec, command: npx, args });
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
return runner;
|
|
70
|
+
}
|
|
71
|
+
// ---------- semver-ish helpers ----------
|
|
72
|
+
export function parseVersion(text) {
|
|
73
|
+
const m = /(\d+)\.(\d+)\.(\d+)/.exec(text);
|
|
74
|
+
return m ? `${m[1]}.${m[2]}.${m[3]}` : null;
|
|
75
|
+
}
|
|
76
|
+
export function compareVersions(a, b) {
|
|
77
|
+
const pa = a.split('.').map(Number);
|
|
78
|
+
const pb = b.split('.').map(Number);
|
|
79
|
+
for (let i = 0; i < 3; i++) {
|
|
80
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
81
|
+
if (d !== 0)
|
|
82
|
+
return d < 0 ? -1 : 1;
|
|
83
|
+
}
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Find a working Python 3. Each candidate must actually run: on Windows `python` is often the Microsoft Store shortcut
|
|
88
|
+
* that exists on PATH but only prints "Python was not found", so PATH presence proves nothing.
|
|
89
|
+
*/
|
|
90
|
+
export async function findPython(runner, platform, signal) {
|
|
91
|
+
const candidates = [
|
|
92
|
+
...(platform === 'win32' ? [{ name: 'py', args: ['-3'] }] : []),
|
|
93
|
+
{ name: 'python3', args: [] },
|
|
94
|
+
{ name: 'python', args: [] },
|
|
95
|
+
];
|
|
96
|
+
for (const c of candidates) {
|
|
97
|
+
const exe = await runner.which(c.name);
|
|
98
|
+
if (!exe)
|
|
99
|
+
continue;
|
|
100
|
+
const r = await runner.run({ command: exe, args: [...c.args, '--version'], timeoutMs: 10_000, signal });
|
|
101
|
+
const m = /Python (3)\.(\d+)\.(\d+)/.exec(`${r.stdout}\n${r.stderr}`);
|
|
102
|
+
if (r.ok && m)
|
|
103
|
+
return { command: exe, args: c.args, version: `${m[1]}.${m[2]}.${m[3]}` };
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
export async function findTool(runner, name, signal) {
|
|
108
|
+
const exe = await runner.which(name);
|
|
109
|
+
if (!exe)
|
|
110
|
+
return null;
|
|
111
|
+
const r = await runner.run({ command: exe, args: ['--version'], timeoutMs: 10_000, signal });
|
|
112
|
+
const v = parseVersion(`${r.stdout}\n${r.stderr}`);
|
|
113
|
+
return r.ok && v ? { command: exe, args: [], version: v } : null;
|
|
114
|
+
}
|
|
115
|
+
export async function findClaudeCli(runner, home, signal) {
|
|
116
|
+
const candidates = [];
|
|
117
|
+
const onPath = await runner.which('claude');
|
|
118
|
+
if (onPath)
|
|
119
|
+
candidates.push(onPath);
|
|
120
|
+
// The VS Code extension ships its own binary, which is often the only Claude Code CLI on the machine.
|
|
121
|
+
for (const ext of ['.vscode', '.vscode-insiders', '.cursor']) {
|
|
122
|
+
const extDir = path.join(home, ext, 'extensions');
|
|
123
|
+
let names;
|
|
124
|
+
try {
|
|
125
|
+
names = (await fs.readdir(extDir)).filter((n) => n.startsWith('anthropic.claude-code-')).sort().reverse();
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
for (const n of names) {
|
|
131
|
+
for (const exe of ['claude.exe', 'claude']) {
|
|
132
|
+
const full = path.join(extDir, n, 'resources', 'native-binary', exe);
|
|
133
|
+
try {
|
|
134
|
+
await fs.access(full);
|
|
135
|
+
candidates.push(full);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
/* not here */
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
for (const exe of candidates) {
|
|
144
|
+
const r = await runner.run({ command: exe, args: ['--version'], timeoutMs: 15_000, signal });
|
|
145
|
+
const v = parseVersion(r.stdout);
|
|
146
|
+
if (r.ok && v)
|
|
147
|
+
return { command: exe, args: [], version: v };
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
/** Parse the one JSON object Claude Code prints on the last stdout line of `--json` commands. */
|
|
152
|
+
export function parseLastJsonLine(stdout) {
|
|
153
|
+
const lines = stdout.trim().split(/\r?\n/);
|
|
154
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
155
|
+
const l = lines[i].trim();
|
|
156
|
+
if (l.startsWith('{') && l.endsWith('}')) {
|
|
157
|
+
try {
|
|
158
|
+
return JSON.parse(l);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/integrations/exec.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { DevWorkspaceError } from '../errors/index.js';\nimport { isShellShim, run as procRun, which as procWhich, type RunResult } from '../utils/proc.js';\nimport type { Runner, RunSpec } from './types.js';\n\n/** Arguments allowed through cmd.exe. No quotes, %, ^, &, |, <, >, !, ( ) or control characters can reach the shell. */\nconst SAFE_SHIM_ARG = /^[A-Za-z0-9_@.:/\\\\=,+#\\- ]*$/;\n\nexport function assertSafeShimArgs(args: string[]): void {\n for (const a of args) {\n if (!SAFE_SHIM_ARG.test(a)) {\n throw new DevWorkspaceError('SECURITY', `Refusing to pass an unsafe argument to a Windows .cmd shim: ${JSON.stringify(a.slice(0, 40))}`, {\n hint: 'This is a safety check in DevWorkspace; the argument contains characters cmd.exe would interpret.',\n });\n }\n }\n}\n\nfunction quoteWin(s: string): string {\n return `\"${s}\"`;\n}\n\n/** Run a .cmd/.bat shim through cmd.exe with validated, quoted arguments (Node refuses to spawn them directly). */\nexport function shimInvocation(file: string, args: string[], comspec = process.env.ComSpec || 'cmd.exe'): { command: string; args: string[] } {\n assertSafeShimArgs(args);\n if (/[\"%^&|<>!\\r\\n]/.test(file)) {\n throw new DevWorkspaceError('SECURITY', 'Refusing to run an executable whose path contains shell metacharacters.');\n }\n const line = [quoteWin(file), ...args.map(quoteWin)].join(' ');\n return { command: comspec, args: ['/d', '/s', '/c', `\"${line}\"`] };\n}\n\n/** npm's npx-cli.js next to the running Node, so npx never needs a shell. */\nexport async function resolveNpxCli(execPath = process.execPath): Promise<string | null> {\n const dir = path.dirname(execPath);\n for (const candidate of [path.join(dir, 'node_modules', 'npm', 'bin', 'npx-cli.js'), path.join(dir, '..', 'lib', 'node_modules', 'npm', 'bin', 'npx-cli.js')]) {\n try {\n await fs.access(candidate);\n return candidate;\n } catch {\n /* next */\n }\n }\n return null;\n}\n\nexport function createRunner(env: NodeJS.ProcessEnv = process.env, platform: NodeJS.Platform = process.platform): Runner {\n const base = (spec: RunSpec, command: string, args: string[], verbatim = false): Promise<RunResult> =>\n procRun(command, args, {\n cwd: spec.cwd,\n env: { ...(env === process.env ? {} : env), ...spec.env },\n timeoutMs: spec.timeoutMs,\n signal: spec.signal,\n windowsVerbatimArguments: verbatim,\n });\n const runner: Runner = {\n which: (name) => procWhich(name, env),\n async run(spec) {\n if (platform === 'win32' && isShellShim(spec.command)) {\n const inv = shimInvocation(spec.command, spec.args);\n return base(spec, inv.command, inv.args, true);\n }\n return base(spec, spec.command, spec.args);\n },\n async runNpx(args, spec = {}) {\n const cli = await resolveNpxCli();\n if (cli) return base({ ...spec, command: process.execPath, args }, process.execPath, [cli, ...args]);\n const npx = await procWhich('npx', env);\n if (!npx) return { ok: false, code: null, stdout: '', stderr: '', timedOut: false, error: 'npx was not found' };\n return runner.run({ ...spec, command: npx, args });\n },\n };\n return runner;\n}\n\n// ---------- semver-ish helpers ----------\n\nexport function parseVersion(text: string): string | null {\n const m = /(\\d+)\\.(\\d+)\\.(\\d+)/.exec(text);\n return m ? `${m[1]}.${m[2]}.${m[3]}` : null;\n}\n\nexport function compareVersions(a: string, b: string): number {\n const pa = a.split('.').map(Number);\n const pb = b.split('.').map(Number);\n for (let i = 0; i < 3; i++) {\n const d = (pa[i] ?? 0) - (pb[i] ?? 0);\n if (d !== 0) return d < 0 ? -1 : 1;\n }\n return 0;\n}\n\n// ---------- tool discovery (always verified by running --version) ----------\n\nexport interface Tool {\n command: string;\n args: string[];\n version: string;\n}\n\n/**\n * Find a working Python 3. Each candidate must actually run: on Windows `python` is often the Microsoft Store shortcut\n * that exists on PATH but only prints \"Python was not found\", so PATH presence proves nothing.\n */\nexport async function findPython(runner: Runner, platform: NodeJS.Platform, signal?: AbortSignal): Promise<Tool | null> {\n const candidates: { name: string; args: string[] }[] = [\n ...(platform === 'win32' ? [{ name: 'py', args: ['-3'] }] : []),\n { name: 'python3', args: [] },\n { name: 'python', args: [] },\n ];\n for (const c of candidates) {\n const exe = await runner.which(c.name);\n if (!exe) continue;\n const r = await runner.run({ command: exe, args: [...c.args, '--version'], timeoutMs: 10_000, signal });\n const m = /Python (3)\\.(\\d+)\\.(\\d+)/.exec(`${r.stdout}\\n${r.stderr}`);\n if (r.ok && m) return { command: exe, args: c.args, version: `${m[1]}.${m[2]}.${m[3]}` };\n }\n return null;\n}\n\nexport async function findTool(runner: Runner, name: string, signal?: AbortSignal): Promise<Tool | null> {\n const exe = await runner.which(name);\n if (!exe) return null;\n const r = await runner.run({ command: exe, args: ['--version'], timeoutMs: 10_000, signal });\n const v = parseVersion(`${r.stdout}\\n${r.stderr}`);\n return r.ok && v ? { command: exe, args: [], version: v } : null;\n}\n\nexport async function findClaudeCli(runner: Runner, home: string, signal?: AbortSignal): Promise<Tool | null> {\n const candidates: string[] = [];\n const onPath = await runner.which('claude');\n if (onPath) candidates.push(onPath);\n // The VS Code extension ships its own binary, which is often the only Claude Code CLI on the machine.\n for (const ext of ['.vscode', '.vscode-insiders', '.cursor']) {\n const extDir = path.join(home, ext, 'extensions');\n let names: string[];\n try {\n names = (await fs.readdir(extDir)).filter((n) => n.startsWith('anthropic.claude-code-')).sort().reverse();\n } catch {\n continue;\n }\n for (const n of names) {\n for (const exe of ['claude.exe', 'claude']) {\n const full = path.join(extDir, n, 'resources', 'native-binary', exe);\n try {\n await fs.access(full);\n candidates.push(full);\n } catch {\n /* not here */\n }\n }\n }\n }\n for (const exe of candidates) {\n const r = await runner.run({ command: exe, args: ['--version'], timeoutMs: 15_000, signal });\n const v = parseVersion(r.stdout);\n if (r.ok && v) return { command: exe, args: [], version: v };\n }\n return null;\n}\n\n/** Parse the one JSON object Claude Code prints on the last stdout line of `--json` commands. */\nexport function parseLastJsonLine(stdout: string): Record<string, unknown> | null {\n const lines = stdout.trim().split(/\\r?\\n/);\n for (let i = lines.length - 1; i >= 0; i--) {\n const l = lines[i]!.trim();\n if (l.startsWith('{') && l.endsWith('}')) {\n try {\n return JSON.parse(l) as Record<string, unknown>;\n } catch {\n return null;\n }\n }\n }\n return null;\n}\n"]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
export type {
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
export declare function detectIntegrations(ctx: IntegrationContext, list?: Integration[]): Promise<IntegrationStatus[]>;
|
|
1
|
+
export { ADAPTERS, acknowledgeRestart, applyPersisted, inspectAll, runSetup, summarize } from './manager.js';
|
|
2
|
+
export type { IntegrationOutcome, ManagerEnv, OutcomeAction, SetupResult, StepResult } from './manager.js';
|
|
3
|
+
export { createRunner } from './exec.js';
|
|
4
|
+
export { INTEGRATION_IDS, REQUIRED_INTEGRATIONS, STATUS_CODES } from './types.js';
|
|
5
|
+
export type { IntegrationId, IntegrationReport, InstallScope, ProgressEvent, Runner, SetupOptions, StatusCode } from './types.js';
|