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
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oHAAoH;AACpH,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAChG,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,kBAAkB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACrJ,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACjG,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC","sourcesContent":["// Programmatic API: the same engine that powers the CLI, for a future VS Code extension, dashboards and automation.\nexport { packageVersion } from './core/version.js';\nexport { DevWorkspaceError, EXIT } from './errors/index.js';\nexport { initWorkspace } from './services/init.js';\nexport { analyzeWorkspace } from './services/analysis.js';\nexport { getStatus } from './services/status.js';\nexport { runDoctor } from './services/doctor.js';\nexport { refreshWorkspace } from './services/refresh.js';\nexport { getContext, addFact, verifyFactByKey, removeFactByKey } from './services/knowledge.js';\nexport { createTask, listTasksService, showTask, setTaskStatus, updateTask, recordVerification, recordTests, attestTask } from './services/tasks.js';\nexport { recordDecision, getDecisions, recordSession, getSessions } from './services/records.js';\nexport { installAgent } from './services/agent.js';\nexport { exportWorkspace, importWorkspace } from './services/portability.js';\nexport { migrateWorkspace } from './services/migrate.js';\nexport { classifyRequest, detectConstraints, WORKFLOWS } from './tasks/routing.js';\nexport { evaluateGate } from './tasks/task.js';\nexport { redact, findSecrets, containsSecret } from './security/redact.js';\nexport { isSensitiveFile } from './security/files.js';\nexport { buildOnboardingPrompt } from './prompts/onboarding.js';\nexport { ONBOARDING_REQUIREMENTS, REFRESH_REQUIREMENTS, validatePrompt } from './prompts/requirements.js';\nexport type { ServiceOptions } from './services/common.js';\nexport type { StatusResult } from './services/status.js';\nexport type { DoctorResult, DoctorCheck } from './services/doctor.js';\nexport type { Task, TaskStatus, TaskType } from './tasks/task.js';\nexport type { Fact, ProjectConfig, CommandEntry } from './config/schemas.js';\nexport type {
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oHAAoH;AACpH,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAChG,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,kBAAkB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACrJ,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACjG,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC","sourcesContent":["// Programmatic API: the same engine that powers the CLI, for a future VS Code extension, dashboards and automation.\nexport { packageVersion } from './core/version.js';\nexport { DevWorkspaceError, EXIT } from './errors/index.js';\nexport { initWorkspace } from './services/init.js';\nexport { analyzeWorkspace } from './services/analysis.js';\nexport { getStatus } from './services/status.js';\nexport { runDoctor } from './services/doctor.js';\nexport { refreshWorkspace } from './services/refresh.js';\nexport { getContext, addFact, verifyFactByKey, removeFactByKey } from './services/knowledge.js';\nexport { createTask, listTasksService, showTask, setTaskStatus, updateTask, recordVerification, recordTests, attestTask } from './services/tasks.js';\nexport { recordDecision, getDecisions, recordSession, getSessions } from './services/records.js';\nexport { installAgent } from './services/agent.js';\nexport { exportWorkspace, importWorkspace } from './services/portability.js';\nexport { migrateWorkspace } from './services/migrate.js';\nexport { resetWorkspace } from './services/reset.js';\nexport { setupIntegrations, integrationsStatus } from './services/integrations.js';\nexport { classifyRequest, detectConstraints, WORKFLOWS } from './tasks/routing.js';\nexport { evaluateGate } from './tasks/task.js';\nexport { redact, findSecrets, containsSecret } from './security/redact.js';\nexport { isSensitiveFile } from './security/files.js';\nexport { buildOnboardingPrompt } from './prompts/onboarding.js';\nexport { ONBOARDING_REQUIREMENTS, REFRESH_REQUIREMENTS, validatePrompt } from './prompts/requirements.js';\nexport type { ServiceOptions } from './services/common.js';\nexport type { StatusResult } from './services/status.js';\nexport type { DoctorResult, DoctorCheck } from './services/doctor.js';\nexport type { Task, TaskStatus, TaskType } from './tasks/task.js';\nexport type { Fact, ProjectConfig, CommandEntry } from './config/schemas.js';\nexport type { IntegrationReport, StatusCode as IntegrationStatusCode } from './integrations/types.js';\nexport type { AgentAdapter, PlannedChange } from './agents/types.js';\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Skills (addyosmani/agent-skills). Verified against the repository on 2026-10-09:
|
|
3
|
+
* - native Claude Code plugin: marketplace `addy-agent-skills`, plugin `agent-skills`
|
|
4
|
+
* - portable: `npx skills add addyosmani/agent-skills` copies skills/<name>/ (25 skills)
|
|
5
|
+
* - known limitation (README): per-skill installs omit the shared references/ checklists
|
|
6
|
+
* Core skills below are a stable subset used only to decide whether a portable install is present.
|
|
7
|
+
*/
|
|
8
|
+
export declare const agentSkills: import("../types.js").IntegrationAdapter;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AGENT_SKILLS_NOTICE } from '../notices.js';
|
|
2
|
+
import { createPluginSkillAdapter } from './pluginSkill.js';
|
|
3
|
+
/**
|
|
4
|
+
* Agent Skills (addyosmani/agent-skills). Verified against the repository on 2026-10-09:
|
|
5
|
+
* - native Claude Code plugin: marketplace `addy-agent-skills`, plugin `agent-skills`
|
|
6
|
+
* - portable: `npx skills add addyosmani/agent-skills` copies skills/<name>/ (25 skills)
|
|
7
|
+
* - known limitation (README): per-skill installs omit the shared references/ checklists
|
|
8
|
+
* Core skills below are a stable subset used only to decide whether a portable install is present.
|
|
9
|
+
*/
|
|
10
|
+
export const agentSkills = createPluginSkillAdapter({
|
|
11
|
+
id: 'agent-skills',
|
|
12
|
+
name: 'Agent Skills',
|
|
13
|
+
docsUrl: 'https://github.com/addyosmani/agent-skills',
|
|
14
|
+
usage: 'Use the Agent Skills workflows for engineering methodology: spec (requirements), plan (task breakdown), build (implementation), ' +
|
|
15
|
+
'test (verification), review (review gates), code-simplify (reduce complexity) and ship (release). Invoke them instead of re-deriving the method from memory. ' +
|
|
16
|
+
'The /spec /plan /build /test /review /code-simplify /ship commands exist only when the native plugin is installed; with portable skills, ask for the skill by name.',
|
|
17
|
+
native: { marketplaceName: 'addy-agent-skills', marketplaceUrl: 'https://github.com/addyosmani/agent-skills.git', pluginName: 'agent-skills' },
|
|
18
|
+
skillsRepo: 'addyosmani/agent-skills',
|
|
19
|
+
coreSkills: ['using-agent-skills', 'spec-driven-development', 'planning-and-task-breakdown', 'test-driven-development', 'code-review-and-quality', 'shipping-and-launch'],
|
|
20
|
+
notice: AGENT_SKILLS_NOTICE,
|
|
21
|
+
nativeOnly: ['slash commands', 'agent personas'],
|
|
22
|
+
nativeHooks: false,
|
|
23
|
+
referencesDir: 'references',
|
|
24
|
+
});
|
|
25
|
+
//# sourceMappingURL=agent-skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-skills.js","sourceRoot":"","sources":["../../../src/integrations/adapters/agent-skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,wBAAwB,CAAC;IAClD,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,cAAc;IACpB,OAAO,EAAE,4CAA4C;IACrD,KAAK,EACH,kIAAkI;QAClI,+JAA+J;QAC/J,qKAAqK;IACvK,MAAM,EAAE,EAAE,eAAe,EAAE,mBAAmB,EAAE,cAAc,EAAE,gDAAgD,EAAE,UAAU,EAAE,cAAc,EAAE;IAC9I,UAAU,EAAE,yBAAyB;IACrC,UAAU,EAAE,CAAC,oBAAoB,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,qBAAqB,CAAC;IACzK,MAAM,EAAE,mBAAmB;IAC3B,UAAU,EAAE,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAChD,WAAW,EAAE,KAAK;IAClB,aAAa,EAAE,YAAY;CAC5B,CAAC,CAAC","sourcesContent":["import { AGENT_SKILLS_NOTICE } from '../notices.js';\nimport { createPluginSkillAdapter } from './pluginSkill.js';\n\n/**\n * Agent Skills (addyosmani/agent-skills). Verified against the repository on 2026-10-09:\n * - native Claude Code plugin: marketplace `addy-agent-skills`, plugin `agent-skills`\n * - portable: `npx skills add addyosmani/agent-skills` copies skills/<name>/ (25 skills)\n * - known limitation (README): per-skill installs omit the shared references/ checklists\n * Core skills below are a stable subset used only to decide whether a portable install is present.\n */\nexport const agentSkills = createPluginSkillAdapter({\n id: 'agent-skills',\n name: 'Agent Skills',\n docsUrl: 'https://github.com/addyosmani/agent-skills',\n usage:\n 'Use the Agent Skills workflows for engineering methodology: spec (requirements), plan (task breakdown), build (implementation), ' +\n 'test (verification), review (review gates), code-simplify (reduce complexity) and ship (release). Invoke them instead of re-deriving the method from memory. ' +\n 'The /spec /plan /build /test /review /code-simplify /ship commands exist only when the native plugin is installed; with portable skills, ask for the skill by name.',\n native: { marketplaceName: 'addy-agent-skills', marketplaceUrl: 'https://github.com/addyosmani/agent-skills.git', pluginName: 'agent-skills' },\n skillsRepo: 'addyosmani/agent-skills',\n coreSkills: ['using-agent-skills', 'spec-driven-development', 'planning-and-task-breakdown', 'test-driven-development', 'code-review-and-quality', 'shipping-and-launch'],\n notice: AGENT_SKILLS_NOTICE,\n nativeOnly: ['slash commands', 'agent personas'],\n nativeHooks: false,\n referencesDir: 'references',\n});\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type Tool } from '../exec.js';
|
|
2
|
+
import type { AdapterContext, InstallScope, PlanStep } from '../types.js';
|
|
3
|
+
/** Minimum Claude Code versions for the flags we rely on (from the plugin CLI reference). */
|
|
4
|
+
export declare const MIN_PLUGIN_JSON = "2.1.268";
|
|
5
|
+
export declare const MIN_MARKETPLACE_JSON = "2.1.287";
|
|
6
|
+
export interface InstalledPlugin {
|
|
7
|
+
id: string;
|
|
8
|
+
version?: string;
|
|
9
|
+
scope: string;
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
projectPath?: string;
|
|
12
|
+
errors?: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface NativeSpec {
|
|
15
|
+
marketplaceName: string;
|
|
16
|
+
/** Always an https git URL: `owner/repo` shorthand makes Claude Code try SSH, which fails without GitHub keys. */
|
|
17
|
+
marketplaceUrl: string;
|
|
18
|
+
pluginName: string;
|
|
19
|
+
}
|
|
20
|
+
export declare const pluginId: (s: NativeSpec) => string;
|
|
21
|
+
export declare function listPlugins(ctx: AdapterContext, claude: Tool): Promise<{
|
|
22
|
+
ok: boolean;
|
|
23
|
+
plugins: InstalledPlugin[];
|
|
24
|
+
error?: string;
|
|
25
|
+
}>;
|
|
26
|
+
export declare function marketplaceRegistered(ctx: AdapterContext, claude: Tool, name: string): Promise<boolean | null>;
|
|
27
|
+
export declare function samePath(a: string | undefined, b: string): boolean;
|
|
28
|
+
/** Match the plugin for this project: user-scope installs apply everywhere; project/local installs must belong to this root. */
|
|
29
|
+
export declare function findPlugin(plugins: InstalledPlugin[], id: string, root: string): InstalledPlugin | undefined;
|
|
30
|
+
/** Cheap check of settings files (no process spawn): which plugins/marketplaces are declared. */
|
|
31
|
+
export declare function readDeclared(ctx: AdapterContext, spec: NativeSpec): Promise<{
|
|
32
|
+
marketplace: boolean;
|
|
33
|
+
enabled: InstallScope | null;
|
|
34
|
+
}>;
|
|
35
|
+
export declare const SSH_FIX = "git config --global url.\"https://github.com/\".insteadOf git@github.com:";
|
|
36
|
+
/** Claude Code clones plugin sources over SSH by default; without a GitHub SSH key that fails with these messages. */
|
|
37
|
+
export declare function sshHint(text: string): string | undefined;
|
|
38
|
+
/** Steps that register the marketplace (if needed) and install the plugin at the given scope. */
|
|
39
|
+
export declare function nativeSteps(ctx: AdapterContext, claude: Tool, spec: NativeSpec, scope: InstallScope, opts: {
|
|
40
|
+
registered: boolean;
|
|
41
|
+
}): PlanStep[];
|
|
@@ -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,239 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { compareVersions, findPython, findTool } from '../exec.js';
|
|
4
|
+
import { GRAPHIFY_NOTICE, writeThirdPartyNotice } from '../notices.js';
|
|
5
|
+
import { pathExists } from '../../utils/fs.js';
|
|
6
|
+
/**
|
|
7
|
+
* Graphify. Verified against the published package (graphifyy 0.9.82) and its CLI help on 2026-10-09:
|
|
8
|
+
* - PyPI package `graphifyy`, executable `graphify`; README documents pip and pipx (pipx recommended on Windows)
|
|
9
|
+
* - `graphify --version` prints "graphify <version>"
|
|
10
|
+
* - `graphify install --project` writes .claude/skills/graphify/, appends a graphify section to CLAUDE.md,
|
|
11
|
+
* creates .claude/CLAUDE.md and registers PreToolUse hooks (`graphify hook-guard ...`) in .claude/settings.json.
|
|
12
|
+
* It merges and is idempotent, but it does change files the user owns, so every run is snapshotted and verified.
|
|
13
|
+
* uv is not mentioned in the README; `uv tool install` is used because it is the equivalent of pipx for console tools.
|
|
14
|
+
*/
|
|
15
|
+
const EXE = process.platform === 'win32' ? 'graphify.exe' : 'graphify';
|
|
16
|
+
const MIN_PYTHON = '3.10.0';
|
|
17
|
+
async function exists(p) {
|
|
18
|
+
return pathExists(p);
|
|
19
|
+
}
|
|
20
|
+
async function locate(ctx, deep) {
|
|
21
|
+
const onPath = await ctx.runner.which('graphify');
|
|
22
|
+
if (onPath)
|
|
23
|
+
return { path: onPath, onPath: true };
|
|
24
|
+
const dirs = [path.join(ctx.home, '.local', 'bin')];
|
|
25
|
+
if (deep)
|
|
26
|
+
dirs.push(...(await toolBinDirs(ctx)));
|
|
27
|
+
for (const d of dirs) {
|
|
28
|
+
const candidate = path.join(d, ctx.platform === 'win32' ? 'graphify.exe' : EXE);
|
|
29
|
+
if (await exists(candidate))
|
|
30
|
+
return { path: candidate, onPath: false };
|
|
31
|
+
}
|
|
32
|
+
return { path: null, onPath: false };
|
|
33
|
+
}
|
|
34
|
+
/** Where uv, pipx and `pip --user` put console scripts, asked from the tools themselves (they differ by OS and version). */
|
|
35
|
+
async function toolBinDirs(ctx) {
|
|
36
|
+
const out = [];
|
|
37
|
+
const uv = await findTool(ctx.runner, 'uv', ctx.signal);
|
|
38
|
+
if (uv) {
|
|
39
|
+
const r = await ctx.runner.run({ command: uv.command, args: ['tool', 'dir', '--bin'], timeoutMs: 10_000, signal: ctx.signal });
|
|
40
|
+
if (r.ok && r.stdout.trim())
|
|
41
|
+
out.push(r.stdout.trim().split(/\r?\n/)[0]);
|
|
42
|
+
}
|
|
43
|
+
const pipx = await findTool(ctx.runner, 'pipx', ctx.signal);
|
|
44
|
+
if (pipx) {
|
|
45
|
+
const r = await ctx.runner.run({ command: pipx.command, args: ['environment', '--value', 'PIPX_BIN_DIR'], timeoutMs: 10_000, signal: ctx.signal });
|
|
46
|
+
if (r.ok && r.stdout.trim())
|
|
47
|
+
out.push(r.stdout.trim().split(/\r?\n/)[0]);
|
|
48
|
+
}
|
|
49
|
+
const py = await findPython(ctx.runner, ctx.platform, ctx.signal);
|
|
50
|
+
if (py) {
|
|
51
|
+
const code = "import sysconfig,os;print(sysconfig.get_path('scripts', os.name + '_user'))";
|
|
52
|
+
const r = await ctx.runner.run({ command: py.command, args: [...py.args, '-c', code], timeoutMs: 10_000, signal: ctx.signal });
|
|
53
|
+
if (r.ok && r.stdout.trim())
|
|
54
|
+
out.push(r.stdout.trim().split(/\r?\n/)[0]);
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
async function readText(p) {
|
|
59
|
+
try {
|
|
60
|
+
return await fs.readFile(p, 'utf8');
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function pathAdvice(ctx, dir) {
|
|
67
|
+
const where = dir ? `Add ${dir} to your PATH` : 'Add the folder containing graphify to your PATH';
|
|
68
|
+
return ctx.platform === 'win32'
|
|
69
|
+
? [`${where} (Settings > System > About > Advanced system settings > Environment Variables), or run \`pipx ensurepath\` / \`uv tool update-shell\`, then open a new terminal.`]
|
|
70
|
+
: [`${where}, or run \`pipx ensurepath\` / \`uv tool update-shell\`, then open a new terminal.`];
|
|
71
|
+
}
|
|
72
|
+
export const graphify = {
|
|
73
|
+
id: 'graphify',
|
|
74
|
+
name: 'Graphify',
|
|
75
|
+
required: true,
|
|
76
|
+
docsUrl: 'https://github.com/Graphify-Labs/graphify',
|
|
77
|
+
usage: 'For "where is X / what depends on Y" questions, query the Graphify graph first (graphify query "<question>", graphify path "<A>" "<B>", graphify explain "<concept>") ' +
|
|
78
|
+
'or use the /graphify skill. The graph is a navigation aid, not a source of truth: confirm in the actual files before relying on it. After editing code, `graphify update .` refreshes it (no LLM cost).',
|
|
79
|
+
async inspect(ctx, deep) {
|
|
80
|
+
const loc = await locate(ctx, deep);
|
|
81
|
+
const projectSkill = await exists(path.join(ctx.root, '.claude', 'skills', 'graphify', 'SKILL.md'));
|
|
82
|
+
const userSkill = !projectSkill && (await exists(path.join(ctx.home, '.claude', 'skills', 'graphify', 'SKILL.md')));
|
|
83
|
+
const settings = (await readText(path.join(ctx.root, '.claude', 'settings.json'))) ?? '';
|
|
84
|
+
const hooks = /graphify\s+hook-guard/.test(settings);
|
|
85
|
+
const graph = await exists(path.join(ctx.root, 'graphify-out', 'graph.json'));
|
|
86
|
+
const skillVersion = ((await readText(path.join(ctx.root, '.claude', 'skills', 'graphify', '.graphify_version'))) ?? '').trim() || null;
|
|
87
|
+
let version = null;
|
|
88
|
+
let runnable = null;
|
|
89
|
+
if (deep && loc.path) {
|
|
90
|
+
const r = await ctx.runner.run({ command: loc.path, args: ['--version'], timeoutMs: 15_000, signal: ctx.signal });
|
|
91
|
+
const m = /graphify\s+(\S+)/i.exec(r.stdout);
|
|
92
|
+
runnable = r.ok && !!m;
|
|
93
|
+
version = m?.[1] ?? null;
|
|
94
|
+
}
|
|
95
|
+
const checks = [
|
|
96
|
+
{ name: 'graphify executable', ok: !!loc.path, detail: loc.path ? (loc.onPath ? 'on PATH' : 'found, but not on PATH') : 'not found' },
|
|
97
|
+
];
|
|
98
|
+
if (deep && loc.path)
|
|
99
|
+
checks.push({ name: 'graphify --version', ok: !!runnable, detail: version ? `graphify ${version}` : 'did not run' });
|
|
100
|
+
checks.push({ name: 'Claude skill', ok: projectSkill || userSkill, detail: projectSkill ? 'project (.claude/skills/graphify)' : userSkill ? 'user (~/.claude/skills/graphify)' : 'missing' });
|
|
101
|
+
if (projectSkill && version && skillVersion)
|
|
102
|
+
checks.push({ name: 'skill matches CLI version', ok: skillVersion === version, detail: `skill ${skillVersion}, CLI ${version}` });
|
|
103
|
+
const capabilities = { cli: !!loc.path, skill: projectSkill || userSkill, hooks, graphBuilt: graph };
|
|
104
|
+
const warnings = [];
|
|
105
|
+
const nextSteps = [];
|
|
106
|
+
if (!graph)
|
|
107
|
+
warnings.push('No graph built yet. In Claude Code run `/graphify .` (graph building uses the model, so DevWorkspace does not do it).');
|
|
108
|
+
const base = { id: 'graphify', name: 'Graphify', required: true, deep, capabilities, checks };
|
|
109
|
+
const scope = projectSkill ? 'project' : userSkill ? 'user' : null;
|
|
110
|
+
if (!loc.path && !projectSkill && !userSkill) {
|
|
111
|
+
nextSteps.push('devworkspace setup --integrations --yes');
|
|
112
|
+
return { ...base, status: 'NOT_INSTALLED', variant: null, scope: null, version: null, warnings, nextSteps };
|
|
113
|
+
}
|
|
114
|
+
if (!loc.path) {
|
|
115
|
+
warnings.push('The Claude skill/hooks exist but the graphify command is missing, so they cannot work.');
|
|
116
|
+
nextSteps.push('devworkspace integrations repair --yes');
|
|
117
|
+
return { ...base, status: 'DEGRADED', variant: 'project skill without CLI', scope, version: null, warnings, nextSteps };
|
|
118
|
+
}
|
|
119
|
+
if (deep && runnable === false) {
|
|
120
|
+
warnings.push('The graphify executable was found but `graphify --version` failed.');
|
|
121
|
+
nextSteps.push('devworkspace integrations repair --yes');
|
|
122
|
+
return { ...base, status: 'DEGRADED', variant: 'cli (not runnable)', scope, version, warnings, nextSteps };
|
|
123
|
+
}
|
|
124
|
+
if (!projectSkill && !userSkill) {
|
|
125
|
+
warnings.push('The graphify CLI is installed but its Claude skill is not set up for this project.');
|
|
126
|
+
nextSteps.push('devworkspace setup --integrations --yes (runs `graphify install --project`)');
|
|
127
|
+
return { ...base, status: 'DEGRADED', variant: 'cli without project skill', scope: null, version, warnings, nextSteps };
|
|
128
|
+
}
|
|
129
|
+
if (version && skillVersion && compareVersions(version.replace(/[^0-9.]/g, '') || '0.0.0', skillVersion.replace(/[^0-9.]/g, '') || '0.0.0') !== 0) {
|
|
130
|
+
warnings.push(`The project skill is from graphify ${skillVersion} but the CLI is ${version}.`);
|
|
131
|
+
nextSteps.push('devworkspace integrations repair --yes');
|
|
132
|
+
return { ...base, status: 'DEGRADED', variant: 'cli + stale project skill', scope, version, warnings, nextSteps };
|
|
133
|
+
}
|
|
134
|
+
if (hooks && !loc.onPath) {
|
|
135
|
+
warnings.push('Claude Code hooks call `graphify` by name, but it is not on PATH in this terminal, so the hooks would fail.');
|
|
136
|
+
nextSteps.push(...pathAdvice(ctx, loc.path ? path.dirname(loc.path) : null));
|
|
137
|
+
return { ...base, status: 'DEGRADED', variant: 'cli + project skill (PATH missing)', scope, version, warnings, nextSteps };
|
|
138
|
+
}
|
|
139
|
+
if (!loc.onPath)
|
|
140
|
+
warnings.push(`graphify is installed but not on PATH. ${pathAdvice(ctx, path.dirname(loc.path))[0]}`);
|
|
141
|
+
return { ...base, status: deep ? 'VERIFIED' : 'INSTALLED', variant: 'cli + project skill', scope, version, warnings, nextSteps };
|
|
142
|
+
},
|
|
143
|
+
async plan(ctx, report) {
|
|
144
|
+
const protectPaths = ['CLAUDE.md', '.claude/CLAUDE.md', '.claude/settings.json', '.claude/settings.local.json', 'AGENTS.md'];
|
|
145
|
+
const blockers = [];
|
|
146
|
+
const notes = [];
|
|
147
|
+
const steps = [];
|
|
148
|
+
if (report.status === 'VERIFIED' && !ctx.setup.repair)
|
|
149
|
+
return { steps, blockers, protectPaths, notes: ['already verified'] };
|
|
150
|
+
const loc = await locate(ctx, true);
|
|
151
|
+
const exeRef = { path: loc.path };
|
|
152
|
+
const scope = ctx.setup.scope;
|
|
153
|
+
if (!loc.path) {
|
|
154
|
+
const uv = await findTool(ctx.runner, 'uv', ctx.signal);
|
|
155
|
+
const pipx = await findTool(ctx.runner, 'pipx', ctx.signal);
|
|
156
|
+
const py = await findPython(ctx.runner, ctx.platform, ctx.signal);
|
|
157
|
+
const pyOk = !!py && compareVersions(py.version, MIN_PYTHON) >= 0;
|
|
158
|
+
const choice = uv
|
|
159
|
+
? { command: [uv.command, 'tool', 'install', 'graphifyy'], how: 'uv tool install' }
|
|
160
|
+
: pipx
|
|
161
|
+
? { command: [pipx.command, 'install', 'graphifyy'], how: 'pipx install' }
|
|
162
|
+
: pyOk && py
|
|
163
|
+
? { command: [py.command, ...py.args, '-m', 'pip', 'install', '--user', 'graphifyy'], how: 'pip install --user' }
|
|
164
|
+
: null;
|
|
165
|
+
if (choice && !uv && !pipx)
|
|
166
|
+
notes.push('uv and pipx were not found; falling back to the documented `pip install --user` (no administrator rights needed).');
|
|
167
|
+
if (!choice) {
|
|
168
|
+
if (py && !pyOk)
|
|
169
|
+
blockers.push(`Python ${py.version} is too old; graphify needs Python 3.10 or newer.`);
|
|
170
|
+
blockers.push('No installer found. Graphify is a Python tool: install ONE of these, then re-run setup (nothing else is required):', ctx.platform === 'win32'
|
|
171
|
+
? ' - uv: winget install --id astral-sh.uv (or https://docs.astral.sh/uv/getting-started/installation/)'
|
|
172
|
+
: ' - uv: https://docs.astral.sh/uv/getting-started/installation/', ' - Python 3.10+ from https://www.python.org/downloads/ (the Microsoft Store "python" shortcut on Windows is not a real install)', ' - pipx: https://pipx.pypa.io/stable/installation/');
|
|
173
|
+
return { steps, blockers, protectPaths, notes };
|
|
174
|
+
}
|
|
175
|
+
const { command, how } = choice;
|
|
176
|
+
const [cmd, ...args] = command;
|
|
177
|
+
steps.push({
|
|
178
|
+
id: 'graphify:tool',
|
|
179
|
+
description: `Install the graphifyy package for your user account (${how}; no administrator rights, nothing system-wide)`,
|
|
180
|
+
command: command.map((c, i) => (i === 0 ? path.basename(c, path.extname(c)) : c)).join(' '),
|
|
181
|
+
level: 'machine',
|
|
182
|
+
async run() {
|
|
183
|
+
const r = await ctx.runner.run({ command: cmd, args, cwd: ctx.root, timeoutMs: 600_000, signal: ctx.signal });
|
|
184
|
+
if (r.aborted)
|
|
185
|
+
return { ok: false, detail: 'cancelled' };
|
|
186
|
+
if (r.timedOut)
|
|
187
|
+
return { ok: false, detail: 'timed out after 10 minutes (network?)' };
|
|
188
|
+
if (!r.ok) {
|
|
189
|
+
const text = `${r.stderr}\n${r.stdout}`;
|
|
190
|
+
if (/externally[- ]managed/i.test(text))
|
|
191
|
+
return { ok: false, detail: 'pip refuses to install into this Python (externally-managed environment). Install pipx or uv instead, then re-run.' };
|
|
192
|
+
if (/permission denied|access is denied|EACCES|WinError 5/i.test(text))
|
|
193
|
+
return { ok: false, detail: 'permission denied while installing. Run the command yourself from a terminal with the right permissions.' };
|
|
194
|
+
const tail = text.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).slice(-3).join(' ');
|
|
195
|
+
return { ok: false, detail: (tail || r.error || `exit ${r.code}`).slice(0, 400) };
|
|
196
|
+
}
|
|
197
|
+
const found = await locate(ctx, true);
|
|
198
|
+
if (!found.path)
|
|
199
|
+
return { ok: false, detail: 'graphifyy was installed but the graphify executable could not be located. Check the Scripts/bin folder of your Python tool.' };
|
|
200
|
+
exeRef.path = found.path;
|
|
201
|
+
return { ok: true, detail: found.onPath ? 'installed' : 'installed (not on PATH in this terminal)' };
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
steps.push({
|
|
206
|
+
id: 'graphify:project-skill',
|
|
207
|
+
description: scope === 'user'
|
|
208
|
+
? 'Install the Graphify skill for your user account (graphify install)'
|
|
209
|
+
: 'Install the Graphify project skill (graphify install --project): adds .claude/skills/graphify/, appends a graphify section to CLAUDE.md and registers Claude Code hooks in .claude/settings.json (existing content is preserved and verified)',
|
|
210
|
+
command: scope === 'user' ? 'graphify install' : 'graphify install --project',
|
|
211
|
+
level: scope === 'user' ? 'user-config' : 'project',
|
|
212
|
+
async run() {
|
|
213
|
+
if (!exeRef.path)
|
|
214
|
+
return { ok: false, detail: 'the graphify executable is not available' };
|
|
215
|
+
const r = await ctx.runner.run({ command: exeRef.path, args: scope === 'user' ? ['install'] : ['install', '--project'], cwd: ctx.root, timeoutMs: 120_000, signal: ctx.signal });
|
|
216
|
+
if (r.aborted)
|
|
217
|
+
return { ok: false, detail: 'cancelled' };
|
|
218
|
+
if (r.timedOut)
|
|
219
|
+
return { ok: false, detail: 'timed out after 120 s' };
|
|
220
|
+
if (!r.ok) {
|
|
221
|
+
const tail = `${r.stderr}\n${r.stdout}`.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).slice(-3).join(' ');
|
|
222
|
+
return { ok: false, detail: (tail || r.error || `exit ${r.code}`).slice(0, 400) };
|
|
223
|
+
}
|
|
224
|
+
return { ok: true, detail: 'project skill installed', restartRequired: true, managedPaths: scope === 'user' ? [] : ['.claude/skills/graphify'] };
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
steps.push({
|
|
228
|
+
id: 'graphify:notice',
|
|
229
|
+
description: 'Record attribution in .devworkspace/THIRD_PARTY_NOTICES.md',
|
|
230
|
+
level: 'project',
|
|
231
|
+
async run() {
|
|
232
|
+
await writeThirdPartyNotice(ctx.root, GRAPHIFY_NOTICE);
|
|
233
|
+
return { ok: true };
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
return { steps, blockers, protectPaths, notes };
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
//# sourceMappingURL=graphify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphify.js","sourceRoot":"","sources":["../../../src/integrations/adapters/graphify.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { compareVersions, findPython, findTool } from '../exec.js';\nimport { GRAPHIFY_NOTICE, writeThirdPartyNotice } from '../notices.js';\nimport type { AdapterContext, Check, IntegrationAdapter, IntegrationReport, PlanResult, PlanStep, StepOutcome } from '../types.js';\nimport { pathExists } from '../../utils/fs.js';\n\n/**\n * Graphify. Verified against the published package (graphifyy 0.9.82) and its CLI help on 2026-10-09:\n * - PyPI package `graphifyy`, executable `graphify`; README documents pip and pipx (pipx recommended on Windows)\n * - `graphify --version` prints \"graphify <version>\"\n * - `graphify install --project` writes .claude/skills/graphify/, appends a graphify section to CLAUDE.md,\n * creates .claude/CLAUDE.md and registers PreToolUse hooks (`graphify hook-guard ...`) in .claude/settings.json.\n * It merges and is idempotent, but it does change files the user owns, so every run is snapshotted and verified.\n * uv is not mentioned in the README; `uv tool install` is used because it is the equivalent of pipx for console tools.\n */\n\nconst EXE = process.platform === 'win32' ? 'graphify.exe' : 'graphify';\nconst MIN_PYTHON = '3.10.0';\n\ninterface Located {\n path: string | null;\n onPath: boolean;\n}\n\nasync function exists(p: string): Promise<boolean> {\n return pathExists(p);\n}\n\nasync function locate(ctx: AdapterContext, deep: boolean): Promise<Located> {\n const onPath = await ctx.runner.which('graphify');\n if (onPath) return { path: onPath, onPath: true };\n const dirs = [path.join(ctx.home, '.local', 'bin')];\n if (deep) dirs.push(...(await toolBinDirs(ctx)));\n for (const d of dirs) {\n const candidate = path.join(d, ctx.platform === 'win32' ? 'graphify.exe' : EXE);\n if (await exists(candidate)) return { path: candidate, onPath: false };\n }\n return { path: null, onPath: false };\n}\n\n/** Where uv, pipx and `pip --user` put console scripts, asked from the tools themselves (they differ by OS and version). */\nasync function toolBinDirs(ctx: AdapterContext): Promise<string[]> {\n const out: string[] = [];\n const uv = await findTool(ctx.runner, 'uv', ctx.signal);\n if (uv) {\n const r = await ctx.runner.run({ command: uv.command, args: ['tool', 'dir', '--bin'], timeoutMs: 10_000, signal: ctx.signal });\n if (r.ok && r.stdout.trim()) out.push(r.stdout.trim().split(/\\r?\\n/)[0]!);\n }\n const pipx = await findTool(ctx.runner, 'pipx', ctx.signal);\n if (pipx) {\n const r = await ctx.runner.run({ command: pipx.command, args: ['environment', '--value', 'PIPX_BIN_DIR'], timeoutMs: 10_000, signal: ctx.signal });\n if (r.ok && r.stdout.trim()) out.push(r.stdout.trim().split(/\\r?\\n/)[0]!);\n }\n const py = await findPython(ctx.runner, ctx.platform, ctx.signal);\n if (py) {\n const code = \"import sysconfig,os;print(sysconfig.get_path('scripts', os.name + '_user'))\";\n const r = await ctx.runner.run({ command: py.command, args: [...py.args, '-c', code], timeoutMs: 10_000, signal: ctx.signal });\n if (r.ok && r.stdout.trim()) out.push(r.stdout.trim().split(/\\r?\\n/)[0]!);\n }\n return out;\n}\n\nasync function readText(p: string): Promise<string | null> {\n try {\n return await fs.readFile(p, 'utf8');\n } catch {\n return null;\n }\n}\n\nfunction pathAdvice(ctx: AdapterContext, dir: string | null): string[] {\n const where = dir ? `Add ${dir} to your PATH` : 'Add the folder containing graphify to your PATH';\n return ctx.platform === 'win32'\n ? [`${where} (Settings > System > About > Advanced system settings > Environment Variables), or run \\`pipx ensurepath\\` / \\`uv tool update-shell\\`, then open a new terminal.`]\n : [`${where}, or run \\`pipx ensurepath\\` / \\`uv tool update-shell\\`, then open a new terminal.`];\n}\n\nexport const graphify: IntegrationAdapter = {\n id: 'graphify',\n name: 'Graphify',\n required: true,\n docsUrl: 'https://github.com/Graphify-Labs/graphify',\n usage:\n 'For \"where is X / what depends on Y\" questions, query the Graphify graph first (graphify query \"<question>\", graphify path \"<A>\" \"<B>\", graphify explain \"<concept>\") ' +\n 'or use the /graphify skill. The graph is a navigation aid, not a source of truth: confirm in the actual files before relying on it. After editing code, `graphify update .` refreshes it (no LLM cost).',\n\n async inspect(ctx, deep): Promise<IntegrationReport> {\n const loc = await locate(ctx, deep);\n const projectSkill = await exists(path.join(ctx.root, '.claude', 'skills', 'graphify', 'SKILL.md'));\n const userSkill = !projectSkill && (await exists(path.join(ctx.home, '.claude', 'skills', 'graphify', 'SKILL.md')));\n const settings = (await readText(path.join(ctx.root, '.claude', 'settings.json'))) ?? '';\n const hooks = /graphify\\s+hook-guard/.test(settings);\n const graph = await exists(path.join(ctx.root, 'graphify-out', 'graph.json'));\n const skillVersion = ((await readText(path.join(ctx.root, '.claude', 'skills', 'graphify', '.graphify_version'))) ?? '').trim() || null;\n\n let version: string | null = null;\n let runnable: boolean | null = null;\n if (deep && loc.path) {\n const r = await ctx.runner.run({ command: loc.path, args: ['--version'], timeoutMs: 15_000, signal: ctx.signal });\n const m = /graphify\\s+(\\S+)/i.exec(r.stdout);\n runnable = r.ok && !!m;\n version = m?.[1] ?? null;\n }\n\n const checks: Check[] = [\n { name: 'graphify executable', ok: !!loc.path, detail: loc.path ? (loc.onPath ? 'on PATH' : 'found, but not on PATH') : 'not found' },\n ];\n if (deep && loc.path) checks.push({ name: 'graphify --version', ok: !!runnable, detail: version ? `graphify ${version}` : 'did not run' });\n checks.push({ name: 'Claude skill', ok: projectSkill || userSkill, detail: projectSkill ? 'project (.claude/skills/graphify)' : userSkill ? 'user (~/.claude/skills/graphify)' : 'missing' });\n if (projectSkill && version && skillVersion) checks.push({ name: 'skill matches CLI version', ok: skillVersion === version, detail: `skill ${skillVersion}, CLI ${version}` });\n const capabilities = { cli: !!loc.path, skill: projectSkill || userSkill, hooks, graphBuilt: graph };\n const warnings: string[] = [];\n const nextSteps: string[] = [];\n if (!graph) warnings.push('No graph built yet. In Claude Code run `/graphify .` (graph building uses the model, so DevWorkspace does not do it).');\n\n const base = { id: 'graphify', name: 'Graphify', required: true, deep, capabilities, checks } as const;\n const scope = projectSkill ? 'project' : userSkill ? 'user' : null;\n\n if (!loc.path && !projectSkill && !userSkill) {\n nextSteps.push('devworkspace setup --integrations --yes');\n return { ...base, status: 'NOT_INSTALLED', variant: null, scope: null, version: null, warnings, nextSteps };\n }\n if (!loc.path) {\n warnings.push('The Claude skill/hooks exist but the graphify command is missing, so they cannot work.');\n nextSteps.push('devworkspace integrations repair --yes');\n return { ...base, status: 'DEGRADED', variant: 'project skill without CLI', scope, version: null, warnings, nextSteps };\n }\n if (deep && runnable === false) {\n warnings.push('The graphify executable was found but `graphify --version` failed.');\n nextSteps.push('devworkspace integrations repair --yes');\n return { ...base, status: 'DEGRADED', variant: 'cli (not runnable)', scope, version, warnings, nextSteps };\n }\n if (!projectSkill && !userSkill) {\n warnings.push('The graphify CLI is installed but its Claude skill is not set up for this project.');\n nextSteps.push('devworkspace setup --integrations --yes (runs `graphify install --project`)');\n return { ...base, status: 'DEGRADED', variant: 'cli without project skill', scope: null, version, warnings, nextSteps };\n }\n if (version && skillVersion && compareVersions(version.replace(/[^0-9.]/g, '') || '0.0.0', skillVersion.replace(/[^0-9.]/g, '') || '0.0.0') !== 0) {\n warnings.push(`The project skill is from graphify ${skillVersion} but the CLI is ${version}.`);\n nextSteps.push('devworkspace integrations repair --yes');\n return { ...base, status: 'DEGRADED', variant: 'cli + stale project skill', scope, version, warnings, nextSteps };\n }\n if (hooks && !loc.onPath) {\n warnings.push('Claude Code hooks call `graphify` by name, but it is not on PATH in this terminal, so the hooks would fail.');\n nextSteps.push(...pathAdvice(ctx, loc.path ? path.dirname(loc.path) : null));\n return { ...base, status: 'DEGRADED', variant: 'cli + project skill (PATH missing)', scope, version, warnings, nextSteps };\n }\n if (!loc.onPath) warnings.push(`graphify is installed but not on PATH. ${pathAdvice(ctx, path.dirname(loc.path))[0]}`);\n return { ...base, status: deep ? 'VERIFIED' : 'INSTALLED', variant: 'cli + project skill', scope, version, warnings, nextSteps };\n },\n\n async plan(ctx, report): Promise<PlanResult> {\n const protectPaths = ['CLAUDE.md', '.claude/CLAUDE.md', '.claude/settings.json', '.claude/settings.local.json', 'AGENTS.md'];\n const blockers: string[] = [];\n const notes: string[] = [];\n const steps: PlanStep[] = [];\n if (report.status === 'VERIFIED' && !ctx.setup.repair) return { steps, blockers, protectPaths, notes: ['already verified'] };\n\n const loc = await locate(ctx, true);\n const exeRef: { path: string | null } = { path: loc.path };\n const scope = ctx.setup.scope;\n\n if (!loc.path) {\n const uv = await findTool(ctx.runner, 'uv', ctx.signal);\n const pipx = await findTool(ctx.runner, 'pipx', ctx.signal);\n const py = await findPython(ctx.runner, ctx.platform, ctx.signal);\n const pyOk = !!py && compareVersions(py.version, MIN_PYTHON) >= 0;\n const choice: { command: string[]; how: string } | null = uv\n ? { command: [uv.command, 'tool', 'install', 'graphifyy'], how: 'uv tool install' }\n : pipx\n ? { command: [pipx.command, 'install', 'graphifyy'], how: 'pipx install' }\n : pyOk && py\n ? { command: [py.command, ...py.args, '-m', 'pip', 'install', '--user', 'graphifyy'], how: 'pip install --user' }\n : null;\n if (choice && !uv && !pipx) notes.push('uv and pipx were not found; falling back to the documented `pip install --user` (no administrator rights needed).');\n if (!choice) {\n if (py && !pyOk) blockers.push(`Python ${py.version} is too old; graphify needs Python 3.10 or newer.`);\n blockers.push(\n 'No installer found. Graphify is a Python tool: install ONE of these, then re-run setup (nothing else is required):',\n ctx.platform === 'win32'\n ? ' - uv: winget install --id astral-sh.uv (or https://docs.astral.sh/uv/getting-started/installation/)'\n : ' - uv: https://docs.astral.sh/uv/getting-started/installation/',\n ' - Python 3.10+ from https://www.python.org/downloads/ (the Microsoft Store \"python\" shortcut on Windows is not a real install)',\n ' - pipx: https://pipx.pypa.io/stable/installation/',\n );\n return { steps, blockers, protectPaths, notes };\n }\n const { command, how } = choice;\n const [cmd, ...args] = command;\n steps.push({\n id: 'graphify:tool',\n description: `Install the graphifyy package for your user account (${how}; no administrator rights, nothing system-wide)`,\n command: command.map((c, i) => (i === 0 ? path.basename(c, path.extname(c)) : c)).join(' '),\n level: 'machine',\n async run(): Promise<StepOutcome> {\n const r = await ctx.runner.run({ command: cmd!, args, cwd: ctx.root, timeoutMs: 600_000, signal: ctx.signal });\n if (r.aborted) return { ok: false, detail: 'cancelled' };\n if (r.timedOut) return { ok: false, detail: 'timed out after 10 minutes (network?)' };\n if (!r.ok) {\n const text = `${r.stderr}\\n${r.stdout}`;\n if (/externally[- ]managed/i.test(text)) return { ok: false, detail: 'pip refuses to install into this Python (externally-managed environment). Install pipx or uv instead, then re-run.' };\n if (/permission denied|access is denied|EACCES|WinError 5/i.test(text)) return { ok: false, detail: 'permission denied while installing. Run the command yourself from a terminal with the right permissions.' };\n const tail = text.split(/\\r?\\n/).map((l) => l.trim()).filter(Boolean).slice(-3).join(' ');\n return { ok: false, detail: (tail || r.error || `exit ${r.code}`).slice(0, 400) };\n }\n const found = await locate(ctx, true);\n if (!found.path) return { ok: false, detail: 'graphifyy was installed but the graphify executable could not be located. Check the Scripts/bin folder of your Python tool.' };\n exeRef.path = found.path;\n return { ok: true, detail: found.onPath ? 'installed' : 'installed (not on PATH in this terminal)' };\n },\n });\n }\n\n steps.push({\n id: 'graphify:project-skill',\n description:\n scope === 'user'\n ? 'Install the Graphify skill for your user account (graphify install)'\n : 'Install the Graphify project skill (graphify install --project): adds .claude/skills/graphify/, appends a graphify section to CLAUDE.md and registers Claude Code hooks in .claude/settings.json (existing content is preserved and verified)',\n command: scope === 'user' ? 'graphify install' : 'graphify install --project',\n level: scope === 'user' ? 'user-config' : 'project',\n async run(): Promise<StepOutcome> {\n if (!exeRef.path) return { ok: false, detail: 'the graphify executable is not available' };\n const r = await ctx.runner.run({ command: exeRef.path, args: scope === 'user' ? ['install'] : ['install', '--project'], cwd: ctx.root, timeoutMs: 120_000, signal: ctx.signal });\n if (r.aborted) return { ok: false, detail: 'cancelled' };\n if (r.timedOut) return { ok: false, detail: 'timed out after 120 s' };\n if (!r.ok) {\n const tail = `${r.stderr}\\n${r.stdout}`.split(/\\r?\\n/).map((l) => l.trim()).filter(Boolean).slice(-3).join(' ');\n return { ok: false, detail: (tail || r.error || `exit ${r.code}`).slice(0, 400) };\n }\n return { ok: true, detail: 'project skill installed', restartRequired: true, managedPaths: scope === 'user' ? [] : ['.claude/skills/graphify'] };\n },\n });\n steps.push({\n id: 'graphify:notice',\n description: 'Record attribution in .devworkspace/THIRD_PARTY_NOTICES.md',\n level: 'project',\n async run(): Promise<StepOutcome> {\n await writeThirdPartyNotice(ctx.root, GRAPHIFY_NOTICE);\n return { ok: true };\n },\n });\n return { steps, blockers, protectPaths, notes };\n },\n};\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { IntegrationAdapter } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* OmniRoute (model routing) is OPTIONAL and outside DevWorkspace's execution path. It is only observed, never installed:
|
|
4
|
+
* nothing here requires API keys, model configuration or network access.
|
|
5
|
+
*/
|
|
6
|
+
export declare const omniroute: IntegrationAdapter;
|