@ghl-ai/aw 0.1.47-beta.5 → 0.1.47-beta.6
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/integrations.mjs +277 -42
- package/package.json +1 -1
package/integrations.mjs
CHANGED
|
@@ -107,7 +107,10 @@ export const INTEGRATIONS = {
|
|
|
107
107
|
type: 'python-cli',
|
|
108
108
|
label: 'Graphify (Knowledge Graph)',
|
|
109
109
|
description: 'Builds a queryable knowledge graph of your codebase + docs',
|
|
110
|
-
|
|
110
|
+
// mcp,pdf,office,svg,sql covers all practical codebase-analysis needs.
|
|
111
|
+
// Excluded: leiden (Python <3.13 only — breaks on newer Python), video (heavy whisper/yt-dlp),
|
|
112
|
+
// openai/gemini/ollama/bedrock (AI inference — users have their own setup), neo4j (specialized).
|
|
113
|
+
pipPackage: 'graphifyy[mcp,pdf,office,svg,sql]',
|
|
111
114
|
cliCommand: 'graphify',
|
|
112
115
|
minPython: { major: 3, minor: 10 },
|
|
113
116
|
postInstall: [
|
|
@@ -116,10 +119,10 @@ export const INTEGRATIONS = {
|
|
|
116
119
|
perProjectInstall: [
|
|
117
120
|
// IDE wiring — only runs if that IDE's config dir exists on this machine.
|
|
118
121
|
// Each command writes the IDE-specific CLAUDE.md/AGENTS.md section + hook.
|
|
119
|
-
{ args: ['claude', 'install'], requiresGit: false, requiresIde: '.claude' },
|
|
120
|
-
{ args: ['codex', 'install'], requiresGit: false, requiresIde: '.codex' },
|
|
121
|
-
{ args: ['cursor', 'install'], requiresGit: false, requiresIde: '.cursor' },
|
|
122
|
-
{ args: ['gemini', 'install'], requiresGit: false, requiresIde: '.gemini' },
|
|
122
|
+
{ args: ['claude', 'install'], requiresGit: false, requiresIde: '.claude', requiresFile: 'graphify-out/graph.json' },
|
|
123
|
+
{ args: ['codex', 'install'], requiresGit: false, requiresIde: '.codex', requiresFile: 'graphify-out/graph.json' },
|
|
124
|
+
{ args: ['cursor', 'install'], requiresGit: false, requiresIde: '.cursor', requiresFile: 'graphify-out/graph.json' },
|
|
125
|
+
{ args: ['gemini', 'install'], requiresGit: false, requiresIde: '.gemini', requiresFile: 'graphify-out/graph.json' },
|
|
123
126
|
// Git hooks — post-commit AST rebuild + post-checkout sync + merge driver
|
|
124
127
|
{ args: ['hook', 'install'], requiresGit: true },
|
|
125
128
|
// If a graph already exists, register it into the global graph immediately.
|
|
@@ -302,12 +305,16 @@ export function installGraphifyGlobalAddHook(homeDir) {
|
|
|
302
305
|
e => e?.description !== MARKER,
|
|
303
306
|
);
|
|
304
307
|
|
|
305
|
-
//
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
|
|
310
|
-
|
|
308
|
+
// On Windows: bash syntax ([ -f ], $(basename), || true) does not work in cmd.exe/PS.
|
|
309
|
+
// graphify.exe is on PATH via ~/.local/bin (placed there by uv tool install).
|
|
310
|
+
// On POSIX: use python3 -m graphify so the module is always resolved from the
|
|
311
|
+
// correct interpreter regardless of which graphify binary is first on PATH.
|
|
312
|
+
let cmd;
|
|
313
|
+
if (IS_WINDOWS) {
|
|
314
|
+
cmd = `powershell -NoProfile -NonInteractive -Command "if (Test-Path 'graphify-out/graph.json') { graphify global add 'graphify-out/graph.json' --as (Split-Path -Leaf (Get-Location)); graphify claude install }"`;
|
|
315
|
+
} else {
|
|
316
|
+
cmd = `[ -f graphify-out/graph.json ] && { python3 -m graphify global add graphify-out/graph.json --as "$(basename "$PWD")" > /dev/null 2>&1; graphify claude install > /dev/null 2>&1; } || true`;
|
|
317
|
+
}
|
|
311
318
|
|
|
312
319
|
settings.hooks.SessionStart.push({
|
|
313
320
|
description: MARKER,
|
|
@@ -318,38 +325,133 @@ export function installGraphifyGlobalAddHook(homeDir) {
|
|
|
318
325
|
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
319
326
|
}
|
|
320
327
|
|
|
321
|
-
// Write
|
|
322
|
-
//
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
// so the JSON args array is always parseable (py.cmd can be `py -3` which has a space
|
|
327
|
-
// and would break if used directly as the command field).
|
|
328
|
-
export function installGraphifyMcpServer(homeDir) {
|
|
329
|
-
const claudeDir = join(homeDir, '.claude');
|
|
330
|
-
if (!existsSync(claudeDir)) return;
|
|
328
|
+
// Write a global sessionStart hook to ~/.cursor/hooks.json so Cursor auto-wires
|
|
329
|
+
// graphify IDE context on session open whenever a built graph exists in the project.
|
|
330
|
+
export function installGraphifyCursorGlobalHook(homeDir) {
|
|
331
|
+
const cursorDir = join(homeDir, '.cursor');
|
|
332
|
+
if (!existsSync(cursorDir)) return;
|
|
331
333
|
|
|
332
|
-
const
|
|
333
|
-
let
|
|
334
|
-
if (existsSync(
|
|
335
|
-
try {
|
|
334
|
+
const hooksPath = join(cursorDir, 'hooks.json');
|
|
335
|
+
let hooks = { version: 1, hooks: {} };
|
|
336
|
+
if (existsSync(hooksPath)) {
|
|
337
|
+
try { hooks = JSON.parse(readFileSync(hooksPath, 'utf8')); } catch { /* corrupt — start fresh */ }
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (!hooks.hooks) hooks.hooks = {};
|
|
341
|
+
if (!Array.isArray(hooks.hooks.sessionStart)) hooks.hooks.sessionStart = [];
|
|
342
|
+
|
|
343
|
+
const MARKER = 'graphify-cursor-install';
|
|
344
|
+
hooks.hooks.sessionStart = hooks.hooks.sessionStart.filter(
|
|
345
|
+
e => e?.description !== MARKER,
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
let cmd;
|
|
349
|
+
if (IS_WINDOWS) {
|
|
350
|
+
cmd = `powershell -NoProfile -NonInteractive -Command "if (Test-Path 'graphify-out/graph.json') { graphify global add 'graphify-out/graph.json' --as (Split-Path -Leaf (Get-Location)); graphify cursor install }"`;
|
|
351
|
+
} else {
|
|
352
|
+
cmd = `[ -f graphify-out/graph.json ] && { graphify global add graphify-out/graph.json --as "$(basename "$PWD")" > /dev/null 2>&1; graphify cursor install > /dev/null 2>&1; } || true`;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
hooks.hooks.sessionStart.push({ description: MARKER, command: cmd });
|
|
356
|
+
writeFileSync(hooksPath, JSON.stringify(hooks, null, 2) + '\n');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Write a global SessionStart hook to ~/.codex/hooks.json so Codex auto-wires
|
|
360
|
+
// graphify IDE context on session open whenever a built graph exists in the project.
|
|
361
|
+
export function installGraphifyCodexGlobalHook(homeDir) {
|
|
362
|
+
const codexDir = join(homeDir, '.codex');
|
|
363
|
+
if (!existsSync(codexDir)) return;
|
|
364
|
+
|
|
365
|
+
const hooksPath = join(codexDir, 'hooks.json');
|
|
366
|
+
let hooks = { hooks: {} };
|
|
367
|
+
if (existsSync(hooksPath)) {
|
|
368
|
+
try { hooks = JSON.parse(readFileSync(hooksPath, 'utf8')); } catch { /* corrupt — start fresh */ }
|
|
336
369
|
}
|
|
337
370
|
|
|
338
|
-
if (!
|
|
371
|
+
if (!hooks.hooks) hooks.hooks = {};
|
|
372
|
+
if (!Array.isArray(hooks.hooks.SessionStart)) hooks.hooks.SessionStart = [];
|
|
339
373
|
|
|
340
|
-
|
|
374
|
+
const MARKER = 'graphify-codex-install';
|
|
375
|
+
hooks.hooks.SessionStart = hooks.hooks.SessionStart.filter(
|
|
376
|
+
e => e?.description !== MARKER,
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
let cmd;
|
|
380
|
+
if (IS_WINDOWS) {
|
|
381
|
+
cmd = `powershell -NoProfile -NonInteractive -Command "if (Test-Path 'graphify-out/graph.json') { graphify global add 'graphify-out/graph.json' --as (Split-Path -Leaf (Get-Location)); graphify codex install }"`;
|
|
382
|
+
} else {
|
|
383
|
+
cmd = `[ -f graphify-out/graph.json ] && { graphify global add graphify-out/graph.json --as "$(basename "$PWD")" > /dev/null 2>&1; graphify codex install > /dev/null 2>&1; } || true`;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
hooks.hooks.SessionStart.push({
|
|
387
|
+
description: MARKER,
|
|
388
|
+
hooks: [{ type: 'command', command: cmd }],
|
|
389
|
+
});
|
|
390
|
+
writeFileSync(hooksPath, JSON.stringify(hooks, null, 2) + '\n');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Build the platform-correct MCP server config object for graphify-global.
|
|
394
|
+
function buildGraphifyMcpConfig(homeDir) {
|
|
341
395
|
const globalGraphPath = join(homeDir, '.graphify', 'global-graph.json');
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
396
|
+
if (IS_WINDOWS) {
|
|
397
|
+
const uvToolPython = join(homeDir, 'AppData', 'Roaming', 'uv', 'tools', 'graphifyy', 'Scripts', 'python.exe');
|
|
398
|
+
const command = existsSync(uvToolPython) ? uvToolPython : 'graphify';
|
|
399
|
+
const args = existsSync(uvToolPython)
|
|
400
|
+
? ['-m', 'graphify.serve', globalGraphPath]
|
|
401
|
+
: ['serve', globalGraphPath];
|
|
402
|
+
return { command, args };
|
|
403
|
+
}
|
|
404
|
+
return { command: 'python3', args: ['-m', 'graphify.serve', globalGraphPath] };
|
|
405
|
+
}
|
|
345
406
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
407
|
+
// Add a stdio MCP server block to a Codex config.toml (idempotent).
|
|
408
|
+
function addToTomlStdioMcp(filePath, serverName, command, args) {
|
|
409
|
+
mkdirSync(join(filePath, '..'), { recursive: true });
|
|
410
|
+
let content = existsSync(filePath) ? readFileSync(filePath, 'utf8') : '';
|
|
411
|
+
// Remove existing block — regex consumes lines that don't START with '[' (handles
|
|
412
|
+
// args = [...] which contains '[' mid-line but not at line start).
|
|
413
|
+
const escapedName = serverName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
414
|
+
const blockRegex = new RegExp(`\\[mcp_servers\\.${escapedName}\\](?:\\n(?!\\[)[^\\n]*)*\\n?`, 'g');
|
|
415
|
+
content = content.replace(blockRegex, '');
|
|
416
|
+
// TOML: single-quote command path to avoid backslash escaping issues on Windows.
|
|
417
|
+
// JSON.stringify for args — JSON array syntax is valid TOML inline array.
|
|
418
|
+
const argsToml = JSON.stringify(args);
|
|
419
|
+
content += `[mcp_servers.${serverName}]\ncommand = '${command}'\nargs = ${argsToml}\n\n`;
|
|
420
|
+
writeFileSync(filePath, content);
|
|
421
|
+
}
|
|
350
422
|
|
|
351
|
-
|
|
352
|
-
|
|
423
|
+
// Write the graphify-global MCP server entry to ~/.claude.json, ~/.cursor/mcp.json,
|
|
424
|
+
// and ~/.codex/config.toml so the global graph is accessible in all three IDEs.
|
|
425
|
+
export function installGraphifyMcpServer(homeDir) {
|
|
426
|
+
const mcpConfig = buildGraphifyMcpConfig(homeDir);
|
|
427
|
+
|
|
428
|
+
// Claude Code: ~/.claude.json (user-level MCP config surfaced in /mcp UI)
|
|
429
|
+
const claudeJsonPath = join(homeDir, '.claude.json');
|
|
430
|
+
try {
|
|
431
|
+
let settings = {};
|
|
432
|
+
if (existsSync(claudeJsonPath)) {
|
|
433
|
+
try { settings = JSON.parse(readFileSync(claudeJsonPath, 'utf8')); } catch { /* corrupt */ }
|
|
434
|
+
}
|
|
435
|
+
if (!settings.mcpServers) settings.mcpServers = {};
|
|
436
|
+
settings.mcpServers['graphify-global'] = mcpConfig;
|
|
437
|
+
writeFileSync(claudeJsonPath, JSON.stringify(settings, null, 2) + '\n');
|
|
438
|
+
} catch { /* best effort */ }
|
|
439
|
+
|
|
440
|
+
// Cursor: ~/.cursor/mcp.json (same JSON mcpServers format, supports stdio)
|
|
441
|
+
const cursorMcpPath = join(homeDir, '.cursor', 'mcp.json');
|
|
442
|
+
if (existsSync(join(homeDir, '.cursor'))) {
|
|
443
|
+
try {
|
|
444
|
+
addToJsonMcp(cursorMcpPath, 'graphify-global', mcpConfig);
|
|
445
|
+
} catch { /* best effort */ }
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Codex: ~/.codex/config.toml (TOML, supports stdio via command + args)
|
|
449
|
+
const codexConfigPath = join(homeDir, '.codex', 'config.toml');
|
|
450
|
+
if (existsSync(join(homeDir, '.codex'))) {
|
|
451
|
+
try {
|
|
452
|
+
addToTomlStdioMcp(codexConfigPath, 'graphify-global', mcpConfig.command, mcpConfig.args);
|
|
453
|
+
} catch { /* best effort */ }
|
|
454
|
+
}
|
|
353
455
|
}
|
|
354
456
|
|
|
355
457
|
// After pip install, the CLI binary may not be on PATH (pip --user puts it in a Scripts
|
|
@@ -434,8 +536,21 @@ async function runPythonCli(integration, key, { silent = false } = {}) {
|
|
|
434
536
|
// The graph itself is built manually via `/graphify .` — graphify's own per-IDE
|
|
435
537
|
// install (step 6 below) writes the CLAUDE.md / AGENTS.md sections for that.
|
|
436
538
|
if (integration.cliCommand === 'graphify') {
|
|
539
|
+
// Bootstrap global-graph.json now if a graph exists in cwd — graphify.serve requires
|
|
540
|
+
// the file to exist on startup, but only creates it on first `global add` call.
|
|
541
|
+
const cwdGraph = join(process.cwd(), 'graphify-out', 'graph.json');
|
|
542
|
+
if (existsSync(cwdGraph) && process.cwd() !== HOME) {
|
|
543
|
+
try {
|
|
544
|
+
await execAsync(
|
|
545
|
+
`${cli} global add graphify-out/graph.json --as ${basename(process.cwd())}`,
|
|
546
|
+
{ cwd: process.cwd(), timeout: 30 * 1000 },
|
|
547
|
+
);
|
|
548
|
+
} catch { /* best effort — non-fatal if already registered */ }
|
|
549
|
+
}
|
|
437
550
|
installGraphifyMcpServer(HOME);
|
|
438
551
|
installGraphifyGlobalAddHook(HOME);
|
|
552
|
+
installGraphifyCursorGlobalHook(HOME);
|
|
553
|
+
installGraphifyCodexGlobalHook(HOME);
|
|
439
554
|
}
|
|
440
555
|
|
|
441
556
|
// 6. Run per-project hooks if cwd looks like a real project (not HOME).
|
|
@@ -762,14 +877,134 @@ export async function removeIntegration(key, { silent = false } = {}) {
|
|
|
762
877
|
|
|
763
878
|
if (!silent) fmt.logSuccess(`${integration.label} removed from MCP servers`);
|
|
764
879
|
} else if (integration.type === 'python-cli') {
|
|
765
|
-
// PYTHON CLI:
|
|
766
|
-
|
|
880
|
+
// PYTHON CLI: remove global hooks + MCP wired by aw, leave pip package installed.
|
|
881
|
+
if (integration.cliCommand === 'graphify') {
|
|
882
|
+
// Remove SessionStart hook from ~/.claude/settings.json
|
|
883
|
+
const settingsPath = join(HOME, '.claude', 'settings.json');
|
|
884
|
+
try {
|
|
885
|
+
if (existsSync(settingsPath)) {
|
|
886
|
+
const s = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
887
|
+
if (Array.isArray(s.hooks?.SessionStart)) {
|
|
888
|
+
s.hooks.SessionStart = s.hooks.SessionStart.filter(
|
|
889
|
+
e => e?.description !== 'graphify-global-add',
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
writeFileSync(settingsPath, JSON.stringify(s, null, 2) + '\n');
|
|
893
|
+
if (!silent) fmt.logStep('Removed graphify SessionStart hook from ~/.claude/settings.json');
|
|
894
|
+
}
|
|
895
|
+
} catch { /* best effort */ }
|
|
896
|
+
|
|
897
|
+
// Remove graphify-global MCP from ~/.claude.json
|
|
898
|
+
const claudeJsonPath = join(HOME, '.claude.json');
|
|
899
|
+
try {
|
|
900
|
+
if (existsSync(claudeJsonPath)) {
|
|
901
|
+
const c = JSON.parse(readFileSync(claudeJsonPath, 'utf8'));
|
|
902
|
+
if (c.mcpServers?.['graphify-global']) {
|
|
903
|
+
delete c.mcpServers['graphify-global'];
|
|
904
|
+
writeFileSync(claudeJsonPath, JSON.stringify(c, null, 2) + '\n');
|
|
905
|
+
if (!silent) fmt.logStep('Removed graphify-global MCP from ~/.claude.json');
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
} catch { /* best effort */ }
|
|
909
|
+
|
|
910
|
+
// Remove graphify-global MCP from ~/.cursor/mcp.json
|
|
911
|
+
const cursorMcpPath = join(HOME, '.cursor', 'mcp.json');
|
|
912
|
+
try {
|
|
913
|
+
if (existsSync(cursorMcpPath)) {
|
|
914
|
+
const c = JSON.parse(readFileSync(cursorMcpPath, 'utf8'));
|
|
915
|
+
if (c.mcpServers?.['graphify-global']) {
|
|
916
|
+
delete c.mcpServers['graphify-global'];
|
|
917
|
+
writeFileSync(cursorMcpPath, JSON.stringify(c, null, 2) + '\n');
|
|
918
|
+
if (!silent) fmt.logStep('Removed graphify-global MCP from ~/.cursor/mcp.json');
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
} catch { /* best effort */ }
|
|
922
|
+
|
|
923
|
+
// Remove graphify-global block from ~/.codex/config.toml
|
|
924
|
+
const codexConfigPath = join(HOME, '.codex', 'config.toml');
|
|
925
|
+
try {
|
|
926
|
+
if (existsSync(codexConfigPath)) {
|
|
927
|
+
let content = readFileSync(codexConfigPath, 'utf8');
|
|
928
|
+
// Regex consumes lines that don't START with '[' — handles args = [...]
|
|
929
|
+
// which contains '[' mid-line but not at line start.
|
|
930
|
+
const blockRegex = /\[mcp_servers\.graphify-global\](?:\n(?!\[)[^\n]*)*\n?/g;
|
|
931
|
+
content = content.replace(blockRegex, '');
|
|
932
|
+
writeFileSync(codexConfigPath, content);
|
|
933
|
+
if (!silent) fmt.logStep('Removed graphify-global MCP from ~/.codex/config.toml');
|
|
934
|
+
}
|
|
935
|
+
} catch { /* best effort */ }
|
|
936
|
+
|
|
937
|
+
// Remove sessionStart hook from ~/.cursor/hooks.json
|
|
938
|
+
const cursorHooksPath = join(HOME, '.cursor', 'hooks.json');
|
|
939
|
+
try {
|
|
940
|
+
if (existsSync(cursorHooksPath)) {
|
|
941
|
+
const h = JSON.parse(readFileSync(cursorHooksPath, 'utf8'));
|
|
942
|
+
if (Array.isArray(h.hooks?.sessionStart)) {
|
|
943
|
+
h.hooks.sessionStart = h.hooks.sessionStart.filter(
|
|
944
|
+
e => e?.description !== 'graphify-cursor-install',
|
|
945
|
+
);
|
|
946
|
+
writeFileSync(cursorHooksPath, JSON.stringify(h, null, 2) + '\n');
|
|
947
|
+
if (!silent) fmt.logStep('Removed graphify sessionStart hook from ~/.cursor/hooks.json');
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
} catch { /* best effort */ }
|
|
951
|
+
|
|
952
|
+
// Remove SessionStart hook from ~/.codex/hooks.json
|
|
953
|
+
const codexHooksPath = join(HOME, '.codex', 'hooks.json');
|
|
954
|
+
try {
|
|
955
|
+
if (existsSync(codexHooksPath)) {
|
|
956
|
+
const h = JSON.parse(readFileSync(codexHooksPath, 'utf8'));
|
|
957
|
+
if (Array.isArray(h.hooks?.SessionStart)) {
|
|
958
|
+
h.hooks.SessionStart = h.hooks.SessionStart.filter(
|
|
959
|
+
e => e?.description !== 'graphify-codex-install',
|
|
960
|
+
);
|
|
961
|
+
writeFileSync(codexHooksPath, JSON.stringify(h, null, 2) + '\n');
|
|
962
|
+
if (!silent) fmt.logStep('Removed graphify SessionStart hook from ~/.codex/hooks.json');
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
} catch { /* best effort */ }
|
|
966
|
+
|
|
967
|
+
// Per-project cleanup: uninstall IDE wiring + git hooks + deregister from global graph.
|
|
968
|
+
// Mirrors perProjectInstall in reverse. Runs best-effort — non-fatal if graphify not on PATH.
|
|
969
|
+
const cwd = process.cwd();
|
|
970
|
+
const isHome = cwd === HOME;
|
|
971
|
+
const hasGit = existsSync(join(cwd, '.git'));
|
|
972
|
+
const hasGraph = existsSync(join(cwd, 'graphify-out', 'graph.json'));
|
|
973
|
+
if (!isHome) {
|
|
974
|
+
const ideUninstalls = [
|
|
975
|
+
{ cmd: 'graphify claude uninstall', requiresIde: '.claude' },
|
|
976
|
+
{ cmd: 'graphify codex uninstall', requiresIde: '.codex' },
|
|
977
|
+
{ cmd: 'graphify cursor uninstall', requiresIde: '.cursor' },
|
|
978
|
+
{ cmd: 'graphify gemini uninstall', requiresIde: '.gemini' },
|
|
979
|
+
];
|
|
980
|
+
for (const step of ideUninstalls) {
|
|
981
|
+
if (!existsSync(join(HOME, step.requiresIde))) continue;
|
|
982
|
+
try {
|
|
983
|
+
await execAsync(step.cmd, { cwd, timeout: 30 * 1000 });
|
|
984
|
+
if (!silent) fmt.logStep(`Ran: ${step.cmd}`);
|
|
985
|
+
} catch { /* best effort */ }
|
|
986
|
+
}
|
|
987
|
+
if (hasGit) {
|
|
988
|
+
try {
|
|
989
|
+
await execAsync('graphify hook uninstall', { cwd, timeout: 30 * 1000 });
|
|
990
|
+
if (!silent) fmt.logStep('Ran: graphify hook uninstall');
|
|
991
|
+
} catch { /* best effort */ }
|
|
992
|
+
}
|
|
993
|
+
if (hasGraph) {
|
|
994
|
+
try {
|
|
995
|
+
await execAsync(`graphify global remove --as ${basename(cwd)}`, { cwd, timeout: 30 * 1000 });
|
|
996
|
+
if (!silent) fmt.logStep(`Deregistered ${basename(cwd)} from global graph`);
|
|
997
|
+
} catch { /* best effort */ }
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
767
1002
|
if (!silent) {
|
|
768
1003
|
fmt.logWarn(
|
|
769
|
-
`
|
|
770
|
-
` To fully remove
|
|
771
|
-
`
|
|
772
|
-
'
|
|
1004
|
+
`The Python package was left installed (use outside of aw is possible).\n` +
|
|
1005
|
+
` To fully remove: \`uv tool uninstall graphifyy\` (or \`pipx uninstall\` / \`pip uninstall graphifyy\`)\n` +
|
|
1006
|
+
` To remove per-project wiring: \`${integration.cliCommand} claude uninstall\` inside each project`,
|
|
1007
|
+
'Note',
|
|
773
1008
|
);
|
|
774
1009
|
}
|
|
775
1010
|
} else if (integration.type === 'universal-installer') {
|