devworkspace 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +15 -2
  3. package/dist/agents/claude/content.js +3 -3
  4. package/dist/agents/claude/content.js.map +1 -1
  5. package/dist/cli/args.d.ts +19 -0
  6. package/dist/cli/args.js +6 -0
  7. package/dist/cli/args.js.map +1 -1
  8. package/dist/cli/help.js +40 -5
  9. package/dist/cli/help.js.map +1 -1
  10. package/dist/cli/render.d.ts +7 -0
  11. package/dist/cli/render.js +104 -17
  12. package/dist/cli/render.js.map +1 -1
  13. package/dist/cli/run.d.ts +6 -0
  14. package/dist/cli/run.js +65 -3
  15. package/dist/cli/run.js.map +1 -1
  16. package/dist/config/schemas.d.ts +7 -1
  17. package/dist/config/schemas.js +10 -0
  18. package/dist/config/schemas.js.map +1 -1
  19. package/dist/core/workspace.js +2 -1
  20. package/dist/core/workspace.js.map +1 -1
  21. package/dist/discovery/commands.d.ts +34 -3
  22. package/dist/discovery/commands.js +95 -33
  23. package/dist/discovery/commands.js.map +1 -1
  24. package/dist/discovery/detect.d.ts +17 -0
  25. package/dist/discovery/detect.js +101 -42
  26. package/dist/discovery/detect.js.map +1 -1
  27. package/dist/index.d.ts +3 -1
  28. package/dist/index.js +2 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/integrations/adapters/agent-skills.d.ts +8 -0
  31. package/dist/integrations/adapters/agent-skills.js +25 -0
  32. package/dist/integrations/adapters/agent-skills.js.map +1 -0
  33. package/dist/integrations/adapters/claudePlugin.d.ts +41 -0
  34. package/dist/integrations/adapters/claudePlugin.js +129 -0
  35. package/dist/integrations/adapters/claudePlugin.js.map +1 -0
  36. package/dist/integrations/adapters/graphify.d.ts +2 -0
  37. package/dist/integrations/adapters/graphify.js +277 -0
  38. package/dist/integrations/adapters/graphify.js.map +1 -0
  39. package/dist/integrations/adapters/omniroute.d.ts +6 -0
  40. package/dist/integrations/adapters/omniroute.js +32 -0
  41. package/dist/integrations/adapters/omniroute.js.map +1 -0
  42. package/dist/integrations/adapters/pluginSkill.d.ts +28 -0
  43. package/dist/integrations/adapters/pluginSkill.js +195 -0
  44. package/dist/integrations/adapters/pluginSkill.js.map +1 -0
  45. package/dist/integrations/adapters/ponytail.d.ts +8 -0
  46. package/dist/integrations/adapters/ponytail.js +33 -0
  47. package/dist/integrations/adapters/ponytail.js.map +1 -0
  48. package/dist/integrations/exec.d.ts +26 -0
  49. package/dist/integrations/exec.js +167 -0
  50. package/dist/integrations/exec.js.map +1 -0
  51. package/dist/integrations/index.d.ts +5 -6
  52. package/dist/integrations/index.js +3 -29
  53. package/dist/integrations/index.js.map +1 -1
  54. package/dist/integrations/manager.d.ts +62 -0
  55. package/dist/integrations/manager.js +261 -0
  56. package/dist/integrations/manager.js.map +1 -0
  57. package/dist/integrations/notices.d.ts +13 -0
  58. package/dist/integrations/notices.js +68 -0
  59. package/dist/integrations/notices.js.map +1 -0
  60. package/dist/integrations/persistentPath.d.ts +20 -0
  61. package/dist/integrations/persistentPath.js +47 -0
  62. package/dist/integrations/persistentPath.js.map +1 -0
  63. package/dist/integrations/protect.d.ts +20 -0
  64. package/dist/integrations/protect.js +131 -0
  65. package/dist/integrations/protect.js.map +1 -0
  66. package/dist/integrations/state.d.ts +10 -0
  67. package/dist/integrations/state.js +81 -0
  68. package/dist/integrations/state.js.map +1 -0
  69. package/dist/integrations/types.d.ts +139 -16
  70. package/dist/integrations/types.js +13 -1
  71. package/dist/integrations/types.js.map +1 -1
  72. package/dist/prompts/onboarding.d.ts +3 -3
  73. package/dist/prompts/onboarding.js +9 -8
  74. package/dist/prompts/onboarding.js.map +1 -1
  75. package/dist/services/analysis.d.ts +15 -3
  76. package/dist/services/analysis.js +21 -23
  77. package/dist/services/analysis.js.map +1 -1
  78. package/dist/services/common.d.ts +8 -5
  79. package/dist/services/common.js +2 -5
  80. package/dist/services/common.js.map +1 -1
  81. package/dist/services/doctor.d.ts +10 -0
  82. package/dist/services/doctor.js +87 -19
  83. package/dist/services/doctor.js.map +1 -1
  84. package/dist/services/init.d.ts +9 -2
  85. package/dist/services/init.js +10 -7
  86. package/dist/services/init.js.map +1 -1
  87. package/dist/services/integrations.d.ts +26 -0
  88. package/dist/services/integrations.js +35 -0
  89. package/dist/services/integrations.js.map +1 -0
  90. package/dist/services/refresh.js +1 -1
  91. package/dist/services/refresh.js.map +1 -1
  92. package/dist/services/status.d.ts +4 -2
  93. package/dist/services/status.js +4 -4
  94. package/dist/services/status.js.map +1 -1
  95. package/dist/utils/lock.d.ts +2 -0
  96. package/dist/utils/lock.js +2 -2
  97. package/dist/utils/lock.js.map +1 -1
  98. package/dist/utils/proc.d.ts +8 -1
  99. package/dist/utils/proc.js +31 -8
  100. package/dist/utils/proc.js.map +1 -1
  101. package/docs/architecture.md +1 -1
  102. package/docs/claude.md +4 -0
  103. package/docs/getting-started.md +2 -2
  104. package/docs/integrations.md +98 -33
  105. package/docs/security.md +5 -1
  106. package/docs/testing.md +6 -0
  107. package/docs/troubleshooting.md +11 -1
  108. package/package.json +1 -1
  109. package/dist/integrations/agent-skills.d.ts +0 -10
  110. package/dist/integrations/agent-skills.js +0 -45
  111. package/dist/integrations/agent-skills.js.map +0 -1
  112. package/dist/integrations/graphify.d.ts +0 -7
  113. package/dist/integrations/graphify.js +0 -54
  114. package/dist/integrations/graphify.js.map +0 -1
  115. package/dist/integrations/omniroute.d.ts +0 -7
  116. package/dist/integrations/omniroute.js +0 -24
  117. package/dist/integrations/omniroute.js.map +0 -1
  118. package/dist/integrations/ponytail.d.ts +0 -10
  119. package/dist/integrations/ponytail.js +0 -58
  120. package/dist/integrations/ponytail.js.map +0 -1
  121. package/dist/integrations/probe.d.ts +0 -6
  122. package/dist/integrations/probe.js +0 -41
  123. package/dist/integrations/probe.js.map +0 -1
@@ -0,0 +1,129 @@
1
+ import path from 'node:path';
2
+ import { promises as fs } from 'node:fs';
3
+ import { compareVersions, parseLastJsonLine } from '../exec.js';
4
+ import { isInside } from '../../utils/paths.js';
5
+ /** Minimum Claude Code versions for the flags we rely on (from the plugin CLI reference). */
6
+ export const MIN_PLUGIN_JSON = '2.1.268';
7
+ export const MIN_MARKETPLACE_JSON = '2.1.287';
8
+ export const pluginId = (s) => `${s.pluginName}@${s.marketplaceName}`;
9
+ export async function listPlugins(ctx, claude) {
10
+ const r = await ctx.runner.run({ command: claude.command, args: ['plugin', 'list', '--json'], cwd: ctx.root, timeoutMs: 30_000, signal: ctx.signal });
11
+ if (!r.ok)
12
+ return { ok: false, plugins: [], error: (r.stderr || r.error || r.stdout).trim().slice(0, 300) || `exit ${r.code}` };
13
+ try {
14
+ const parsed = JSON.parse(r.stdout.trim().split(/\r?\n/).filter((l) => l.trim().startsWith('[')).pop() ?? r.stdout);
15
+ return { ok: true, plugins: Array.isArray(parsed) ? parsed : [] };
16
+ }
17
+ catch {
18
+ return { ok: false, plugins: [], error: 'could not parse `claude plugin list --json` output' };
19
+ }
20
+ }
21
+ export async function marketplaceRegistered(ctx, claude, name) {
22
+ const r = await ctx.runner.run({ command: claude.command, args: ['plugin', 'marketplace', 'list'], cwd: ctx.root, timeoutMs: 30_000, signal: ctx.signal });
23
+ if (!r.ok)
24
+ return null;
25
+ return new RegExp(`(^|[^A-Za-z0-9._-])${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}($|[^A-Za-z0-9._-])`, 'm').test(r.stdout);
26
+ }
27
+ export function samePath(a, b) {
28
+ if (!a)
29
+ return false;
30
+ const norm = (p) => path.resolve(p).replace(/[\\/]+$/, '');
31
+ const x = norm(a);
32
+ const y = norm(b);
33
+ return process.platform === 'win32' ? x.toLowerCase() === y.toLowerCase() : x === y;
34
+ }
35
+ /** Match the plugin for this project: user-scope installs apply everywhere; project/local installs must belong to this root. */
36
+ export function findPlugin(plugins, id, root) {
37
+ return plugins.find((p) => p.id === id && (p.scope === 'user' || p.scope === 'managed' || samePath(p.projectPath, root) || (p.projectPath && isInside(root, p.projectPath))));
38
+ }
39
+ /** Cheap check of settings files (no process spawn): which plugins/marketplaces are declared. */
40
+ export async function readDeclared(ctx, spec) {
41
+ const files = [
42
+ [path.join(ctx.root, '.claude', 'settings.json'), 'project'],
43
+ [path.join(ctx.root, '.claude', 'settings.local.json'), 'project'],
44
+ [path.join(ctx.home, '.claude', 'settings.json'), 'user'],
45
+ ];
46
+ let marketplace = false;
47
+ let enabled = null;
48
+ for (const [file, scope] of files) {
49
+ try {
50
+ const j = JSON.parse(await fs.readFile(file, 'utf8'));
51
+ if (j.extraKnownMarketplaces && spec.marketplaceName in j.extraKnownMarketplaces)
52
+ marketplace = true;
53
+ if (j.enabledPlugins && j.enabledPlugins[pluginId(spec)] === true && enabled === null)
54
+ enabled = scope;
55
+ }
56
+ catch {
57
+ /* missing or unreadable */
58
+ }
59
+ }
60
+ return { marketplace, enabled };
61
+ }
62
+ export const SSH_FIX = 'git config --global url."https://github.com/".insteadOf git@github.com:';
63
+ /** Claude Code clones plugin sources over SSH by default; without a GitHub SSH key that fails with these messages. */
64
+ export function sshHint(text) {
65
+ return /host key verification failed|permission denied \(publickey\)|could not read from remote repository/i.test(text)
66
+ ? `Claude Code cloned over SSH and you have no usable GitHub SSH key. The fix documented by Agent Skills changes your global Git config, so run it yourself, then re-run setup: ${SSH_FIX}`
67
+ : undefined;
68
+ }
69
+ function describeFailure(json, stderr, stdout) {
70
+ const msg = typeof json?.message === 'string' ? json.message : (stderr || stdout).trim().split(/\r?\n/).filter(Boolean).slice(-2).join(' ');
71
+ return msg.slice(0, 400) || 'the command failed without output';
72
+ }
73
+ /** Steps that register the marketplace (if needed) and install the plugin at the given scope. */
74
+ export function nativeSteps(ctx, claude, spec, scope, opts) {
75
+ const steps = [];
76
+ const level = scope === 'user' ? 'user-config' : 'project';
77
+ const useMpJson = compareVersions(claude.version, MIN_MARKETPLACE_JSON) >= 0;
78
+ const useJson = compareVersions(claude.version, MIN_PLUGIN_JSON) >= 0;
79
+ const exe = path.basename(claude.command, path.extname(claude.command));
80
+ if (!opts.registered) {
81
+ const args = ['plugin', 'marketplace', 'add', spec.marketplaceUrl, '--scope', scope, ...(useMpJson ? ['--json'] : [])];
82
+ steps.push({
83
+ id: `${spec.marketplaceName}:marketplace`,
84
+ description: `Register the ${spec.marketplaceName} marketplace (${scope} scope)`,
85
+ command: `${exe} ${args.join(' ')}`,
86
+ level,
87
+ async run() {
88
+ const r = await ctx.runner.run({ command: claude.command, args, cwd: ctx.root, timeoutMs: 180_000, signal: ctx.signal });
89
+ const json = parseLastJsonLine(r.stdout);
90
+ if (r.aborted)
91
+ return { ok: false, detail: 'cancelled' };
92
+ if (r.timedOut)
93
+ return { ok: false, detail: 'timed out after 180 s (network?)' };
94
+ if (r.ok && json?.outcome !== 'failed')
95
+ return { ok: true, detail: typeof json?.message === 'string' ? json.message : 'registered' };
96
+ if (/already (on disk|added|exists)/i.test(r.stdout + r.stderr))
97
+ return { ok: true, detail: 'already registered' };
98
+ return { ok: false, detail: describeFailure(json, r.stderr, r.stdout), hint: sshHint(`${r.stdout}\n${r.stderr}`) };
99
+ },
100
+ });
101
+ }
102
+ const installArgs = ['plugin', 'install', pluginId(spec), '--scope', scope, ...(useJson ? ['--json'] : [])];
103
+ steps.push({
104
+ id: `${pluginId(spec)}:install`,
105
+ description: `Install the ${spec.pluginName} plugin (${scope} scope)`,
106
+ command: `${exe} ${installArgs.join(' ')}`,
107
+ level,
108
+ async run() {
109
+ // Never pass -y: that would accept a marketplace-declared command without the user seeing it.
110
+ const r = await ctx.runner.run({ command: claude.command, args: installArgs, cwd: ctx.root, timeoutMs: 180_000, signal: ctx.signal });
111
+ const json = parseLastJsonLine(r.stdout);
112
+ if (r.aborted)
113
+ return { ok: false, detail: 'cancelled' };
114
+ if (r.timedOut)
115
+ return { ok: false, detail: 'timed out after 180 s (network?)' };
116
+ const text = `${r.stdout}\n${r.stderr}`;
117
+ if (json && (json.shownCommand || json.failureCode === 'command_not_accepted') || /only displayed|Run this command now/i.test(text)) {
118
+ return { ok: false, requiresApproval: true, detail: 'Claude Code needs you to approve a command shown by the marketplace; run the command above yourself in a terminal.' };
119
+ }
120
+ if (/already installed/i.test(text) && r.ok)
121
+ return { ok: true, detail: 'already installed' };
122
+ if (r.ok && json?.outcome !== 'failed')
123
+ return { ok: true, detail: 'installed', restartRequired: true };
124
+ return { ok: false, detail: describeFailure(json, r.stderr, r.stdout), hint: sshHint(`${r.stdout}\n${r.stderr}`) };
125
+ },
126
+ });
127
+ return steps;
128
+ }
129
+ //# sourceMappingURL=claudePlugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claudePlugin.js","sourceRoot":"","sources":["../../../src/integrations/adapters/claudePlugin.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport { promises as fs } from 'node:fs';\nimport { compareVersions, parseLastJsonLine, type Tool } from '../exec.js';\nimport { isInside } from '../../utils/paths.js';\nimport type { AdapterContext, InstallScope, PlanStep, StepOutcome } from '../types.js';\n\n/** Minimum Claude Code versions for the flags we rely on (from the plugin CLI reference). */\nexport const MIN_PLUGIN_JSON = '2.1.268';\nexport const MIN_MARKETPLACE_JSON = '2.1.287';\n\nexport interface InstalledPlugin {\n id: string;\n version?: string;\n scope: string;\n enabled: boolean;\n projectPath?: string;\n errors?: string[];\n}\n\nexport interface NativeSpec {\n marketplaceName: string;\n /** Always an https git URL: `owner/repo` shorthand makes Claude Code try SSH, which fails without GitHub keys. */\n marketplaceUrl: string;\n pluginName: string;\n}\n\nexport const pluginId = (s: NativeSpec): string => `${s.pluginName}@${s.marketplaceName}`;\n\nexport async function listPlugins(ctx: AdapterContext, claude: Tool): Promise<{ ok: boolean; plugins: InstalledPlugin[]; error?: string }> {\n const r = await ctx.runner.run({ command: claude.command, args: ['plugin', 'list', '--json'], cwd: ctx.root, timeoutMs: 30_000, signal: ctx.signal });\n if (!r.ok) return { ok: false, plugins: [], error: (r.stderr || r.error || r.stdout).trim().slice(0, 300) || `exit ${r.code}` };\n try {\n const parsed = JSON.parse(r.stdout.trim().split(/\\r?\\n/).filter((l) => l.trim().startsWith('[')).pop() ?? r.stdout) as unknown;\n return { ok: true, plugins: Array.isArray(parsed) ? (parsed as InstalledPlugin[]) : [] };\n } catch {\n return { ok: false, plugins: [], error: 'could not parse `claude plugin list --json` output' };\n }\n}\n\nexport async function marketplaceRegistered(ctx: AdapterContext, claude: Tool, name: string): Promise<boolean | null> {\n const r = await ctx.runner.run({ command: claude.command, args: ['plugin', 'marketplace', 'list'], cwd: ctx.root, timeoutMs: 30_000, signal: ctx.signal });\n if (!r.ok) return null;\n return new RegExp(`(^|[^A-Za-z0-9._-])${name.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}($|[^A-Za-z0-9._-])`, 'm').test(r.stdout);\n}\n\nexport function samePath(a: string | undefined, b: string): boolean {\n if (!a) return false;\n const norm = (p: string): string => path.resolve(p).replace(/[\\\\/]+$/, '');\n const x = norm(a);\n const y = norm(b);\n return process.platform === 'win32' ? x.toLowerCase() === y.toLowerCase() : x === y;\n}\n\n/** Match the plugin for this project: user-scope installs apply everywhere; project/local installs must belong to this root. */\nexport function findPlugin(plugins: InstalledPlugin[], id: string, root: string): InstalledPlugin | undefined {\n return plugins.find((p) => p.id === id && (p.scope === 'user' || p.scope === 'managed' || samePath(p.projectPath, root) || (p.projectPath && isInside(root, p.projectPath))));\n}\n\n/** Cheap check of settings files (no process spawn): which plugins/marketplaces are declared. */\nexport async function readDeclared(ctx: AdapterContext, spec: NativeSpec): Promise<{ marketplace: boolean; enabled: InstallScope | null }> {\n const files: [string, InstallScope][] = [\n [path.join(ctx.root, '.claude', 'settings.json'), 'project'],\n [path.join(ctx.root, '.claude', 'settings.local.json'), 'project'],\n [path.join(ctx.home, '.claude', 'settings.json'), 'user'],\n ];\n let marketplace = false;\n let enabled: InstallScope | null = null;\n for (const [file, scope] of files) {\n try {\n const j = JSON.parse(await fs.readFile(file, 'utf8')) as { extraKnownMarketplaces?: Record<string, unknown>; enabledPlugins?: Record<string, unknown> };\n if (j.extraKnownMarketplaces && spec.marketplaceName in j.extraKnownMarketplaces) marketplace = true;\n if (j.enabledPlugins && j.enabledPlugins[pluginId(spec)] === true && enabled === null) enabled = scope;\n } catch {\n /* missing or unreadable */\n }\n }\n return { marketplace, enabled };\n}\n\nexport const SSH_FIX = 'git config --global url.\"https://github.com/\".insteadOf git@github.com:';\n\n/** Claude Code clones plugin sources over SSH by default; without a GitHub SSH key that fails with these messages. */\nexport function sshHint(text: string): string | undefined {\n return /host key verification failed|permission denied \\(publickey\\)|could not read from remote repository/i.test(text)\n ? `Claude Code cloned over SSH and you have no usable GitHub SSH key. The fix documented by Agent Skills changes your global Git config, so run it yourself, then re-run setup: ${SSH_FIX}`\n : undefined;\n}\n\nfunction describeFailure(json: Record<string, unknown> | null, stderr: string, stdout: string): string {\n const msg = typeof json?.message === 'string' ? json.message : (stderr || stdout).trim().split(/\\r?\\n/).filter(Boolean).slice(-2).join(' ');\n return msg.slice(0, 400) || 'the command failed without output';\n}\n\n/** Steps that register the marketplace (if needed) and install the plugin at the given scope. */\nexport function nativeSteps(ctx: AdapterContext, claude: Tool, spec: NativeSpec, scope: InstallScope, opts: { registered: boolean }): PlanStep[] {\n const steps: PlanStep[] = [];\n const level = scope === 'user' ? 'user-config' : 'project';\n const useMpJson = compareVersions(claude.version, MIN_MARKETPLACE_JSON) >= 0;\n const useJson = compareVersions(claude.version, MIN_PLUGIN_JSON) >= 0;\n const exe = path.basename(claude.command, path.extname(claude.command));\n\n if (!opts.registered) {\n const args = ['plugin', 'marketplace', 'add', spec.marketplaceUrl, '--scope', scope, ...(useMpJson ? ['--json'] : [])];\n steps.push({\n id: `${spec.marketplaceName}:marketplace`,\n description: `Register the ${spec.marketplaceName} marketplace (${scope} scope)`,\n command: `${exe} ${args.join(' ')}`,\n level,\n async run(): Promise<StepOutcome> {\n const r = await ctx.runner.run({ command: claude.command, args, cwd: ctx.root, timeoutMs: 180_000, signal: ctx.signal });\n const json = parseLastJsonLine(r.stdout);\n if (r.aborted) return { ok: false, detail: 'cancelled' };\n if (r.timedOut) return { ok: false, detail: 'timed out after 180 s (network?)' };\n if (r.ok && json?.outcome !== 'failed') return { ok: true, detail: typeof json?.message === 'string' ? json.message : 'registered' };\n if (/already (on disk|added|exists)/i.test(r.stdout + r.stderr)) return { ok: true, detail: 'already registered' };\n return { ok: false, detail: describeFailure(json, r.stderr, r.stdout), hint: sshHint(`${r.stdout}\\n${r.stderr}`) };\n },\n });\n }\n\n const installArgs = ['plugin', 'install', pluginId(spec), '--scope', scope, ...(useJson ? ['--json'] : [])];\n steps.push({\n id: `${pluginId(spec)}:install`,\n description: `Install the ${spec.pluginName} plugin (${scope} scope)`,\n command: `${exe} ${installArgs.join(' ')}`,\n level,\n async run(): Promise<StepOutcome> {\n // Never pass -y: that would accept a marketplace-declared command without the user seeing it.\n const r = await ctx.runner.run({ command: claude.command, args: installArgs, cwd: ctx.root, timeoutMs: 180_000, signal: ctx.signal });\n const json = parseLastJsonLine(r.stdout);\n if (r.aborted) return { ok: false, detail: 'cancelled' };\n if (r.timedOut) return { ok: false, detail: 'timed out after 180 s (network?)' };\n const text = `${r.stdout}\\n${r.stderr}`;\n if (json && (json.shownCommand || json.failureCode === 'command_not_accepted') || /only displayed|Run this command now/i.test(text)) {\n return { ok: false, requiresApproval: true, detail: 'Claude Code needs you to approve a command shown by the marketplace; run the command above yourself in a terminal.' };\n }\n if (/already installed/i.test(text) && r.ok) return { ok: true, detail: 'already installed' };\n if (r.ok && json?.outcome !== 'failed') return { ok: true, detail: 'installed', restartRequired: true };\n return { ok: false, detail: describeFailure(json, r.stderr, r.stdout), hint: sshHint(`${r.stdout}\\n${r.stderr}`) };\n },\n });\n return steps;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import type { IntegrationAdapter } from '../types.js';
2
+ export declare const graphify: IntegrationAdapter;
@@ -0,0 +1,277 @@
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
+ import { persistentPathHas } from '../persistentPath.js';
7
+ /**
8
+ * Graphify. Verified against the published package (graphifyy 0.9.82) and its CLI help on 2026-10-09:
9
+ * - PyPI package `graphifyy`, executable `graphify`; README documents pip and pipx (pipx recommended on Windows)
10
+ * - `graphify --version` prints "graphify <version>"
11
+ * - `graphify install --project` writes .claude/skills/graphify/, appends a graphify section to CLAUDE.md,
12
+ * creates .claude/CLAUDE.md and registers PreToolUse hooks (`graphify hook-guard ...`) in .claude/settings.json.
13
+ * It merges and is idempotent, but it does change files the user owns, so every run is snapshotted and verified.
14
+ * uv is not mentioned in the README; `uv tool install` is used because it is the equivalent of pipx for console tools.
15
+ */
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' : 'graphify');
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
+ /**
73
+ * Why is graphify not on this process's PATH? On Windows the registry tells us whether the folder is already on the
74
+ * persistent PATH (then a restart is the fix and adding it again would be wrong advice) or genuinely missing from it.
75
+ * Elsewhere, or when the registry cannot be read, the persistent PATH is unknown and the general advice is kept.
76
+ * Only called for deep inspections; quick checks never spawn a process.
77
+ */
78
+ async function diagnoseMissingPath(ctx, dir) {
79
+ const persistent = await ctx.persistentPath();
80
+ const where = persistent ? persistentPathHas(persistent, dir) : null;
81
+ if (persistent && where) {
82
+ return {
83
+ staleSession: true,
84
+ explanation: `graphify is on your ${where} PATH (${dir}), but this terminal or editor started before that was set, so processes launched from it (including Claude Code hooks) still cannot find it.`,
85
+ nextSteps: ['Close and reopen your terminal, or restart VS Code (and Claude Code), so it inherits the updated PATH. No change to PATH is needed. Then run: devworkspace doctor'],
86
+ };
87
+ }
88
+ if (persistent) {
89
+ return {
90
+ staleSession: false,
91
+ explanation: `graphify is not on your PATH: ${dir} is missing from this terminal's PATH and from your persistent user and machine PATH.`,
92
+ nextSteps: pathAdvice(ctx, dir),
93
+ };
94
+ }
95
+ return {
96
+ staleSession: false,
97
+ explanation: 'graphify is not on this terminal\'s PATH.',
98
+ nextSteps: [...pathAdvice(ctx, dir), 'If you already added it, restart the terminal or editor so it picks up the change.'],
99
+ };
100
+ }
101
+ export const graphify = {
102
+ id: 'graphify',
103
+ name: 'Graphify',
104
+ required: true,
105
+ docsUrl: 'https://github.com/Graphify-Labs/graphify',
106
+ 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>") ' +
107
+ '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).',
108
+ async inspect(ctx, deep) {
109
+ const loc = await locate(ctx, deep);
110
+ const projectSkill = await exists(path.join(ctx.root, '.claude', 'skills', 'graphify', 'SKILL.md'));
111
+ const userSkill = !projectSkill && (await exists(path.join(ctx.home, '.claude', 'skills', 'graphify', 'SKILL.md')));
112
+ const settings = (await readText(path.join(ctx.root, '.claude', 'settings.json'))) ?? '';
113
+ const hooks = /graphify\s+hook-guard/.test(settings);
114
+ const graph = await exists(path.join(ctx.root, 'graphify-out', 'graph.json'));
115
+ const skillVersion = ((await readText(path.join(ctx.root, '.claude', 'skills', 'graphify', '.graphify_version'))) ?? '').trim() || null;
116
+ let version = null;
117
+ let runnable = null;
118
+ if (deep && loc.path) {
119
+ const r = await ctx.runner.run({ command: loc.path, args: ['--version'], timeoutMs: 15_000, signal: ctx.signal });
120
+ const m = /graphify\s+(\S+)/i.exec(r.stdout);
121
+ runnable = r.ok && !!m;
122
+ version = m?.[1] ?? null;
123
+ }
124
+ const checks = [
125
+ { name: 'graphify executable', ok: !!loc.path, detail: loc.path ? (loc.onPath ? 'on PATH' : 'found, but not on PATH') : 'not found' },
126
+ ];
127
+ if (deep && loc.path)
128
+ checks.push({ name: 'graphify --version', ok: !!runnable, detail: version ? `graphify ${version}` : 'did not run' });
129
+ checks.push({ name: 'Claude skill', ok: projectSkill || userSkill, detail: projectSkill ? 'project (.claude/skills/graphify)' : userSkill ? 'user (~/.claude/skills/graphify)' : 'missing' });
130
+ if (projectSkill && version && skillVersion)
131
+ checks.push({ name: 'skill matches CLI version', ok: skillVersion === version, detail: `skill ${skillVersion}, CLI ${version}` });
132
+ const capabilities = { cli: !!loc.path, skill: projectSkill || userSkill, hooks, graphBuilt: graph };
133
+ const warnings = [];
134
+ const nextSteps = [];
135
+ if (!graph)
136
+ warnings.push('No graph built yet. In Claude Code run `/graphify .` (graph building uses the model, so DevWorkspace does not do it).');
137
+ const base = { id: 'graphify', name: 'Graphify', required: true, deep, capabilities, checks };
138
+ const scope = projectSkill ? 'project' : userSkill ? 'user' : null;
139
+ if (!loc.path && !projectSkill && !userSkill) {
140
+ nextSteps.push('devworkspace setup --integrations --yes');
141
+ return { ...base, status: 'NOT_INSTALLED', variant: null, scope: null, version: null, warnings, nextSteps };
142
+ }
143
+ if (!loc.path) {
144
+ warnings.push('The Claude skill/hooks exist but the graphify command is missing, so they cannot work.');
145
+ nextSteps.push('devworkspace integrations repair --yes');
146
+ return { ...base, status: 'DEGRADED', variant: 'project skill without CLI', scope, version: null, warnings, nextSteps };
147
+ }
148
+ if (deep && runnable === false) {
149
+ warnings.push('The graphify executable was found but `graphify --version` failed.');
150
+ nextSteps.push('devworkspace integrations repair --yes');
151
+ return { ...base, status: 'DEGRADED', variant: 'cli (not runnable)', scope, version, warnings, nextSteps };
152
+ }
153
+ if (!projectSkill && !userSkill) {
154
+ warnings.push('The graphify CLI is installed but its Claude skill is not set up for this project.');
155
+ nextSteps.push('devworkspace setup --integrations --yes (runs `graphify install --project`)');
156
+ return { ...base, status: 'DEGRADED', variant: 'cli without project skill', scope: null, version, warnings, nextSteps };
157
+ }
158
+ if (version && skillVersion && compareVersions(version.replace(/[^0-9.]/g, '') || '0.0.0', skillVersion.replace(/[^0-9.]/g, '') || '0.0.0') !== 0) {
159
+ warnings.push(`The project skill is from graphify ${skillVersion} but the CLI is ${version}.`);
160
+ nextSteps.push('devworkspace integrations repair --yes');
161
+ return { ...base, status: 'DEGRADED', variant: 'cli + stale project skill', scope, version, warnings, nextSteps };
162
+ }
163
+ if (!loc.onPath) {
164
+ const dir = path.dirname(loc.path);
165
+ // Only a deep inspection may ask the registry; a quick check keeps the general advice.
166
+ const diag = deep
167
+ ? await diagnoseMissingPath(ctx, dir)
168
+ : { staleSession: false, explanation: 'graphify is not on this terminal\'s PATH.', nextSteps: pathAdvice(ctx, dir) };
169
+ if (hooks) {
170
+ warnings.push(`Claude Code hooks call \`graphify\` by name, so they fail until it is found. ${diag.explanation}`);
171
+ nextSteps.push(...diag.nextSteps);
172
+ return diag.staleSession
173
+ ? { ...base, status: 'RESTART_REQUIRED', variant: 'cli + project skill (restart terminal)', scope, version, warnings, nextSteps }
174
+ : { ...base, status: 'DEGRADED', variant: 'cli + project skill (PATH missing)', scope, version, warnings, nextSteps };
175
+ }
176
+ warnings.push(diag.explanation);
177
+ nextSteps.push(...diag.nextSteps);
178
+ }
179
+ return { ...base, status: deep ? 'VERIFIED' : 'INSTALLED', variant: 'cli + project skill', scope, version, warnings, nextSteps };
180
+ },
181
+ async plan(ctx, report) {
182
+ const protectPaths = ['CLAUDE.md', '.claude/CLAUDE.md', '.claude/settings.json', '.claude/settings.local.json', 'AGENTS.md'];
183
+ const blockers = [];
184
+ const notes = [];
185
+ const steps = [];
186
+ if (report.status === 'VERIFIED' && !ctx.setup.repair)
187
+ return { steps, blockers, protectPaths, notes: ['already verified'] };
188
+ const loc = await locate(ctx, true);
189
+ const exeRef = { path: loc.path };
190
+ const scope = ctx.setup.scope;
191
+ if (!loc.path) {
192
+ const uv = await findTool(ctx.runner, 'uv', ctx.signal);
193
+ const pipx = await findTool(ctx.runner, 'pipx', ctx.signal);
194
+ const py = await findPython(ctx.runner, ctx.platform, ctx.signal);
195
+ const pyOk = !!py && compareVersions(py.version, MIN_PYTHON) >= 0;
196
+ const choice = uv
197
+ ? { command: [uv.command, 'tool', 'install', 'graphifyy'], how: 'uv tool install' }
198
+ : pipx
199
+ ? { command: [pipx.command, 'install', 'graphifyy'], how: 'pipx install' }
200
+ : pyOk && py
201
+ ? { command: [py.command, ...py.args, '-m', 'pip', 'install', '--user', 'graphifyy'], how: 'pip install --user' }
202
+ : null;
203
+ if (choice && !uv && !pipx)
204
+ notes.push('uv and pipx were not found; falling back to the documented `pip install --user` (no administrator rights needed).');
205
+ if (!choice) {
206
+ if (py && !pyOk)
207
+ blockers.push(`Python ${py.version} is too old; graphify needs Python 3.10 or newer.`);
208
+ 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'
209
+ ? ' - uv: winget install --id astral-sh.uv (or https://docs.astral.sh/uv/getting-started/installation/)'
210
+ : ' - 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/');
211
+ return { steps, blockers, protectPaths, notes };
212
+ }
213
+ const { command, how } = choice;
214
+ const [cmd, ...args] = command;
215
+ steps.push({
216
+ id: 'graphify:tool',
217
+ description: `Install the graphifyy package for your user account (${how}; no administrator rights, nothing system-wide)`,
218
+ command: command.map((c, i) => (i === 0 ? path.basename(c, path.extname(c)) : c)).join(' '),
219
+ level: 'machine',
220
+ async run() {
221
+ const r = await ctx.runner.run({ command: cmd, args, cwd: ctx.root, timeoutMs: 600_000, signal: ctx.signal });
222
+ if (r.aborted)
223
+ return { ok: false, detail: 'cancelled' };
224
+ if (r.timedOut)
225
+ return { ok: false, detail: 'timed out after 10 minutes (network?)' };
226
+ if (!r.ok) {
227
+ const text = `${r.stderr}\n${r.stdout}`;
228
+ if (/externally[- ]managed/i.test(text))
229
+ return { ok: false, detail: 'pip refuses to install into this Python (externally-managed environment). Install pipx or uv instead, then re-run.' };
230
+ if (/permission denied|access is denied|EACCES|WinError 5/i.test(text))
231
+ return { ok: false, detail: 'permission denied while installing. Run the command yourself from a terminal with the right permissions.' };
232
+ const tail = text.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).slice(-3).join(' ');
233
+ return { ok: false, detail: (tail || r.error || `exit ${r.code}`).slice(0, 400) };
234
+ }
235
+ const found = await locate(ctx, true);
236
+ if (!found.path)
237
+ return { ok: false, detail: 'graphifyy was installed but the graphify executable could not be located. Check the Scripts/bin folder of your Python tool.' };
238
+ exeRef.path = found.path;
239
+ return { ok: true, detail: found.onPath ? 'installed' : 'installed (not on PATH in this terminal)' };
240
+ },
241
+ });
242
+ }
243
+ steps.push({
244
+ id: 'graphify:project-skill',
245
+ description: scope === 'user'
246
+ ? 'Install the Graphify skill for your user account (graphify install)'
247
+ : '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)',
248
+ command: scope === 'user' ? 'graphify install' : 'graphify install --project',
249
+ level: scope === 'user' ? 'user-config' : 'project',
250
+ async run() {
251
+ if (!exeRef.path)
252
+ return { ok: false, detail: 'the graphify executable is not available' };
253
+ const r = await ctx.runner.run({ command: exeRef.path, args: scope === 'user' ? ['install'] : ['install', '--project'], cwd: ctx.root, timeoutMs: 120_000, signal: ctx.signal });
254
+ if (r.aborted)
255
+ return { ok: false, detail: 'cancelled' };
256
+ if (r.timedOut)
257
+ return { ok: false, detail: 'timed out after 120 s' };
258
+ if (!r.ok) {
259
+ const tail = `${r.stderr}\n${r.stdout}`.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).slice(-3).join(' ');
260
+ return { ok: false, detail: (tail || r.error || `exit ${r.code}`).slice(0, 400) };
261
+ }
262
+ return { ok: true, detail: 'project skill installed', restartRequired: true, managedPaths: scope === 'user' ? [] : ['.claude/skills/graphify'] };
263
+ },
264
+ });
265
+ steps.push({
266
+ id: 'graphify:notice',
267
+ description: 'Record attribution in .devworkspace/THIRD_PARTY_NOTICES.md',
268
+ level: 'project',
269
+ async run() {
270
+ await writeThirdPartyNotice(ctx.root, GRAPHIFY_NOTICE);
271
+ return { ok: true };
272
+ },
273
+ });
274
+ return { steps, blockers, protectPaths, notes };
275
+ },
276
+ };
277
+ //# 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';\nimport { persistentPathHas } from '../persistentPath.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 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' : 'graphify');\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\ninterface PathDiagnosis {\n /** The folder is on the PATH new processes get, but this process started before that was set. */\n staleSession: boolean;\n /** Short, accurate description of the situation for the user. */\n explanation: string;\n nextSteps: string[];\n}\n\n/**\n * Why is graphify not on this process's PATH? On Windows the registry tells us whether the folder is already on the\n * persistent PATH (then a restart is the fix and adding it again would be wrong advice) or genuinely missing from it.\n * Elsewhere, or when the registry cannot be read, the persistent PATH is unknown and the general advice is kept.\n * Only called for deep inspections; quick checks never spawn a process.\n */\nasync function diagnoseMissingPath(ctx: AdapterContext, dir: string): Promise<PathDiagnosis> {\n const persistent = await ctx.persistentPath();\n const where = persistent ? persistentPathHas(persistent, dir) : null;\n if (persistent && where) {\n return {\n staleSession: true,\n explanation: `graphify is on your ${where} PATH (${dir}), but this terminal or editor started before that was set, so processes launched from it (including Claude Code hooks) still cannot find it.`,\n nextSteps: ['Close and reopen your terminal, or restart VS Code (and Claude Code), so it inherits the updated PATH. No change to PATH is needed. Then run: devworkspace doctor'],\n };\n }\n if (persistent) {\n return {\n staleSession: false,\n explanation: `graphify is not on your PATH: ${dir} is missing from this terminal's PATH and from your persistent user and machine PATH.`,\n nextSteps: pathAdvice(ctx, dir),\n };\n }\n return {\n staleSession: false,\n explanation: 'graphify is not on this terminal\\'s PATH.',\n nextSteps: [...pathAdvice(ctx, dir), 'If you already added it, restart the terminal or editor so it picks up the change.'],\n };\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 (!loc.onPath) {\n const dir = path.dirname(loc.path);\n // Only a deep inspection may ask the registry; a quick check keeps the general advice.\n const diag: PathDiagnosis = deep\n ? await diagnoseMissingPath(ctx, dir)\n : { staleSession: false, explanation: 'graphify is not on this terminal\\'s PATH.', nextSteps: pathAdvice(ctx, dir) };\n if (hooks) {\n warnings.push(`Claude Code hooks call \\`graphify\\` by name, so they fail until it is found. ${diag.explanation}`);\n nextSteps.push(...diag.nextSteps);\n return diag.staleSession\n ? { ...base, status: 'RESTART_REQUIRED', variant: 'cli + project skill (restart terminal)', scope, version, warnings, nextSteps }\n : { ...base, status: 'DEGRADED', variant: 'cli + project skill (PATH missing)', scope, version, warnings, nextSteps };\n }\n warnings.push(diag.explanation);\n nextSteps.push(...diag.nextSteps);\n }\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;