@dreamtree-org/graphify 1.1.3 → 1.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/dist/cli/index.js CHANGED
@@ -503,6 +503,29 @@ function packageRoot() {
503
503
  const packageJsonPath = require2.resolve("@dreamtree-org/graphify/package.json");
504
504
  return path4.dirname(packageJsonPath);
505
505
  }
506
+ var MCP_SERVER_ENTRY = {
507
+ command: "npx",
508
+ args: ["-y", "@dreamtree-org/graphify", "--mcp"]
509
+ };
510
+ async function upsertMcpServer(filePath, serversKey = "mcpServers") {
511
+ let config = {};
512
+ try {
513
+ config = JSON.parse(await fs6.readFile(filePath, "utf-8"));
514
+ } catch (error) {
515
+ if (error.code !== "ENOENT") {
516
+ throw new Error(`cannot update ${filePath} \u2014 it exists but is not valid JSON; fix or remove it and re-run.`, {
517
+ cause: error
518
+ });
519
+ }
520
+ }
521
+ const servers = config[serversKey] ?? {};
522
+ servers["graphify"] = MCP_SERVER_ENTRY;
523
+ config[serversKey] = servers;
524
+ await fs6.mkdir(path4.dirname(filePath), { recursive: true });
525
+ await fs6.writeFile(filePath, `${JSON.stringify(config, null, 2)}
526
+ `, "utf-8");
527
+ return filePath;
528
+ }
506
529
  var MD_MARKER_BEGIN = "<!-- >>> graphify >>> -->";
507
530
  var MD_MARKER_END = "<!-- <<< graphify <<< -->";
508
531
  async function writeOwnedFile(filePath, content) {
@@ -537,7 +560,8 @@ var PLATFORMS = {
537
560
  claude: async (cwd, content) => {
538
561
  const target = path4.join(cwd, ".claude", "skills", "graphify", "SKILL.md");
539
562
  await writeOwnedFile(target, content);
540
- return { host: "Claude Code", path: target };
563
+ const mcpPath = await upsertMcpServer(path4.join(cwd, ".mcp.json"));
564
+ return { host: "Claude Code", path: target, mcpPath };
541
565
  },
542
566
  cursor: async (cwd, content) => {
543
567
  const target = path4.join(cwd, ".cursor", "rules", "graphify.mdc");
@@ -550,7 +574,8 @@ alwaysApply: false
550
574
  ---
551
575
  ${body}`
552
576
  );
553
- return { host: "Cursor", path: target };
577
+ const mcpPath = await upsertMcpServer(path4.join(cwd, ".cursor", "mcp.json"));
578
+ return { host: "Cursor", path: target, mcpPath };
554
579
  },
555
580
  windsurf: async (cwd, content) => {
556
581
  const target = path4.join(cwd, ".windsurf", "rules", "graphify.md");
@@ -570,10 +595,32 @@ ${body}`
570
595
  gemini: async (cwd, content) => {
571
596
  const target = path4.join(cwd, "GEMINI.md");
572
597
  await upsertMarkedBlock(target, content.replace(/^---[\s\S]*?---\n/, ""));
573
- return { host: "Gemini CLI", path: target };
598
+ const mcpPath = await upsertMcpServer(path4.join(cwd, ".gemini", "settings.json"));
599
+ return { host: "Gemini CLI", path: target, mcpPath };
574
600
  }
575
601
  };
576
602
  var SUPPORTED_PLATFORMS = Object.keys(PLATFORMS).sort((a, b) => a.localeCompare(b));
603
+ var PLATFORM_MARKERS = {
604
+ cursor: [".cursor"],
605
+ windsurf: [".windsurf"],
606
+ cline: [".clinerules"],
607
+ agents: ["AGENTS.md"],
608
+ gemini: ["GEMINI.md", ".gemini"]
609
+ };
610
+ async function detectPlatforms(cwd = process.cwd()) {
611
+ const platforms = ["claude"];
612
+ for (const [platform, markers] of Object.entries(PLATFORM_MARKERS)) {
613
+ for (const marker of markers) {
614
+ try {
615
+ await fs6.access(path4.join(cwd, marker));
616
+ platforms.push(platform);
617
+ break;
618
+ } catch {
619
+ }
620
+ }
621
+ }
622
+ return platforms;
623
+ }
577
624
  async function runInstall(platforms = ["claude"], cwd = process.cwd()) {
578
625
  const sourcePath = path4.join(packageRoot(), "src", "skill", "SKILL.md");
579
626
  const content = await fs6.readFile(sourcePath, "utf-8");
@@ -589,7 +636,8 @@ async function runInstall(platforms = ["claude"], cwd = process.cwd()) {
589
636
  results.push(await installer(cwd, content));
590
637
  }
591
638
  for (const result of results) {
592
- console.log(`Installed graphify for ${result.host} -> ${result.path}`);
639
+ const mcpNote = result.mcpPath ? ` (MCP server registered in ${result.mcpPath})` : "";
640
+ console.log(`Installed graphify for ${result.host} -> ${result.path}${mcpNote}`);
593
641
  }
594
642
  return results;
595
643
  }
@@ -699,7 +747,7 @@ async function watchAndRebuild(root, runOnce) {
699
747
  });
700
748
  }
701
749
  var program = new Command();
702
- program.name("graphify").description("Turn a folder of code/docs/papers into a queryable knowledge graph.").argument("[path]", "path to scan, or a GitHub URL", ".").option("--mode <mode>", "extraction mode (deep for richer INFERRED edges \u2014 reserved, no-op in v1)").option("--update", "incremental rebuild \u2014 reuse cached extractions for unchanged files").option("--watch", "rebuild on file change").option("--cluster-only", "rerun clustering on an existing graph").option("--leiden", "use Leiden instead of Louvain for community detection (v2 \u2014 better community quality)").option("--max-iterations <n>", "Leiden only: cap on outer iterations for very large graphs", Number).option("--no-viz", "skip graph.html").option("--svg", "also export graph.svg (not implemented in v1)").option("--graphml", "also export graph.graphml (not implemented in v1)").option("--neo4j", "generate graphify-out/cypher.txt for Neo4j (not implemented in v1)").option("--mcp", "start MCP stdio server instead of running the pipeline").option("--mysql <dsn>", "also extract a MySQL schema (mysql://user:pass@host:port/db) into the graph").action(async (targetArg, options) => {
750
+ program.name("graphify").description("Turn a folder of code/docs/papers into a queryable knowledge graph.").argument("[path]", "path to scan, or a GitHub URL", ".").option("--mode <mode>", "extraction mode (deep for richer INFERRED edges \u2014 reserved, no-op in v1)").option("--update", "incremental rebuild \u2014 reuse cached extractions for unchanged files").option("--watch", "rebuild on file change").option("--cluster-only", "rerun clustering on an existing graph").option("--leiden", "use Leiden instead of Louvain for community detection (v2 \u2014 better community quality)").option("--max-iterations <n>", "Leiden only: cap on outer iterations for very large graphs", Number).option("--no-viz", "skip graph.html").option("--svg", "also export graph.svg (not implemented in v1)").option("--graphml", "also export graph.graphml (not implemented in v1)").option("--neo4j", "generate graphify-out/cypher.txt for Neo4j (not implemented in v1)").option("--mcp", "start MCP stdio server instead of running the pipeline").option("--no-install", "skip auto-installing the agent skill + MCP config after the build").option("--mysql <dsn>", "also extract a MySQL schema (mysql://user:pass@host:port/db) into the graph").action(async (targetArg, options) => {
703
751
  try {
704
752
  if (options.mcp) {
705
753
  const outDir = path5.resolve(targetArg === "." ? process.cwd() : targetArg, "graphify-out");
@@ -708,7 +756,8 @@ program.name("graphify").description("Turn a folder of code/docs/papers into a q
708
756
  return;
709
757
  }
710
758
  let root = targetArg;
711
- if (looksLikeGitUrl(targetArg)) {
759
+ const cloned = looksLikeGitUrl(targetArg);
760
+ if (cloned) {
712
761
  console.error(`Cloning ${targetArg} ...`);
713
762
  root = await cloneRepo(targetArg);
714
763
  }
@@ -734,6 +783,13 @@ program.name("graphify").description("Turn a folder of code/docs/papers into a q
734
783
  onProgress: (m) => console.error(m)
735
784
  });
736
785
  await runOnce();
786
+ if (options.install && !cloned) {
787
+ try {
788
+ await runInstall(await detectPlatforms(root), root);
789
+ } catch (error) {
790
+ console.error(`graphify: agent skill/MCP install skipped: ${error.message}`);
791
+ }
792
+ }
737
793
  if (options.watch) {
738
794
  console.error(`Watching ${root} for changes (Ctrl+C to stop) ...`);
739
795
  await watchAndRebuild(root, runOnce);
@@ -815,7 +871,7 @@ program.command("benchmark [questions...]").description("measure token savings o
815
871
  process.exitCode = 1;
816
872
  }
817
873
  });
818
- program.command("install").description("install the skill/rules files into local agents (claude, cursor, windsurf, cline, agents, gemini, all)").option("--platform <names...>", "platform(s) to install for", ["claude"]).action(async (options) => {
874
+ program.command("install").description("install the skill/rules files + project MCP config into local agents (claude, cursor, windsurf, cline, agents, gemini, all)").option("--platform <names...>", "platform(s) to install for", ["claude"]).action(async (options) => {
819
875
  try {
820
876
  await runInstall(options.platform);
821
877
  } catch (error) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/commands/affected.ts","../../src/cli/commands/benchmark.ts","../../src/cli/commands/check.ts","../../src/cli/commands/context.ts","../../src/cli/commands/explain.ts","../../src/cli/commands/review.ts","../../src/cli/commands/tests.ts","../../src/cli/commands/global.ts","../../src/cli/commands/hook.ts","../../src/cli/commands/install.ts","../../src/cli/commands/path.ts","../../src/cli/commands/query.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { execFile } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { promisify } from 'node:util';\nimport { watch as watchFiles } from 'chokidar';\nimport { Command } from 'commander';\nimport { analyze } from '../analyze.js';\nimport { cluster } from '../cluster.js';\nimport { exportGraph } from '../export.js';\nimport { loadGraph } from '../graphStore.js';\nimport { runPipeline } from '../pipeline.js';\nimport { renderReport } from '../report.js';\nimport { validateDsn, validateUrl } from '../security.js';\nimport type { ExtractionResult } from '../types.js';\nimport { runAffected } from './commands/affected.js';\nimport { runBenchmark } from './commands/benchmark.js';\nimport { runCheck } from './commands/check.js';\nimport { runContext } from './commands/context.js';\nimport { runExplain } from './commands/explain.js';\nimport { runReview } from './commands/review.js';\nimport { runTests } from './commands/tests.js';\nimport { runGlobalAdd, runGlobalBuild, runGlobalList, runGlobalRemove, runMerge } from './commands/global.js';\nimport { runHookInstall, runHookUninstall } from './commands/hook.js';\nimport { loadResults, renderLessons, saveResult, type ResultOutcome, type ResultType } from '../memory.js';\nimport { runInstall } from './commands/install.js';\nimport { runPath } from './commands/path.js';\nimport { runQuery } from './commands/query.js';\n\nconst execFileAsync = promisify(execFile);\n\ninterface DefaultCommandOptions {\n mode?: string;\n update?: boolean;\n watch?: boolean;\n clusterOnly?: boolean;\n viz: boolean; // commander negatable --no-viz -> options.viz\n svg?: boolean;\n graphml?: boolean;\n neo4j?: boolean;\n mcp?: boolean;\n leiden?: boolean;\n maxIterations?: number;\n mysql?: string;\n}\n\nfunction looksLikeGitUrl(value: string): boolean {\n if (!/^https?:\\/\\//i.test(value)) return false;\n return /github\\.com|gitlab\\.com|bitbucket\\.org/i.test(value) || value.endsWith('.git');\n}\n\n/** Clone `url` into a fresh temp directory using an argv-array child_process call (never shell: true, never a shell string built from input). */\nasync function cloneRepo(url: string): Promise<string> {\n const validated = validateUrl(url);\n const dest = await fs.mkdtemp(path.join(os.tmpdir(), 'graphify-clone-'));\n await execFileAsync('git', ['clone', '--depth', '1', validated, dest]);\n return dest;\n}\n\nfunction exportOptionsFrom(options: DefaultCommandOptions) {\n return { html: options.viz, svg: options.svg, graphml: options.graphml, neo4j: options.neo4j };\n}\n\nfunction clusterOptionsFrom(options: DefaultCommandOptions) {\n return {\n algorithm: options.leiden ? ('leiden' as const) : ('louvain' as const),\n maxIterations: options.maxIterations,\n };\n}\n\nasync function runClusterOnly(root: string, options: DefaultCommandOptions): Promise<void> {\n const outDir = path.join(root, 'graphify-out');\n const graph = await loadGraph(outDir);\n cluster(graph, clusterOptionsFrom(options));\n const analysis = analyze(graph);\n const report = renderReport(graph, analysis);\n await exportGraph(graph, { outDir, ...exportOptionsFrom(options) }, report);\n console.error(`Re-clustered the existing graph (${clusterOptionsFrom(options).algorithm}).`);\n}\n\n/** Watch `root` (ignoring graphify-out/, node_modules/, .git/) and re-run `runOnce` on any change, debounced. Never resolves — the process stays alive until interrupted, matching normal watch-mode CLI behavior. */\nasync function watchAndRebuild(root: string, runOnce: () => Promise<unknown>): Promise<void> {\n const watcher = watchFiles(root, {\n ignored: (filePath: string) => /(^|[/\\\\])(node_modules|\\.git|graphify-out)([/\\\\]|$)/.test(filePath),\n ignoreInitial: true,\n });\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let running = false;\n const schedule = (): void => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n if (running) return;\n running = true;\n console.error('Change detected, rebuilding...');\n runOnce()\n .catch((error: unknown) => console.error(`graphify --watch: rebuild failed: ${(error as Error).message}`))\n .finally(() => {\n running = false;\n });\n }, 300);\n };\n\n watcher.on('add', schedule).on('change', schedule).on('unlink', schedule);\n\n await new Promise<never>(() => {\n /* keep the process alive until the user interrupts it (Ctrl+C) */\n });\n}\n\nconst program = new Command();\n\nprogram\n .name('graphify')\n .description('Turn a folder of code/docs/papers into a queryable knowledge graph.')\n .argument('[path]', 'path to scan, or a GitHub URL', '.')\n .option('--mode <mode>', 'extraction mode (deep for richer INFERRED edges — reserved, no-op in v1)')\n .option('--update', 'incremental rebuild — reuse cached extractions for unchanged files')\n .option('--watch', 'rebuild on file change')\n .option('--cluster-only', 'rerun clustering on an existing graph')\n .option('--leiden', 'use Leiden instead of Louvain for community detection (v2 — better community quality)')\n .option('--max-iterations <n>', 'Leiden only: cap on outer iterations for very large graphs', Number)\n .option('--no-viz', 'skip graph.html')\n .option('--svg', 'also export graph.svg (not implemented in v1)')\n .option('--graphml', 'also export graph.graphml (not implemented in v1)')\n .option('--neo4j', 'generate graphify-out/cypher.txt for Neo4j (not implemented in v1)')\n .option('--mcp', 'start MCP stdio server instead of running the pipeline')\n .option('--mysql <dsn>', 'also extract a MySQL schema (mysql://user:pass@host:port/db) into the graph')\n .action(async (targetArg: string, options: DefaultCommandOptions) => {\n try {\n if (options.mcp) {\n const outDir = path.resolve(targetArg === '.' ? process.cwd() : targetArg, 'graphify-out');\n const { startServer } = await import('../mcp/server.js');\n await startServer(path.join(outDir, 'graph.json'));\n return;\n }\n\n let root = targetArg;\n if (looksLikeGitUrl(targetArg)) {\n console.error(`Cloning ${targetArg} ...`);\n root = await cloneRepo(targetArg);\n }\n root = path.resolve(root);\n\n if (options.mode) {\n console.error(`--mode ${options.mode} is reserved for a future richer-INFERRED-edges pass — currently a no-op.`);\n }\n\n if (options.clusterOnly) {\n await runClusterOnly(root, options);\n return;\n }\n\n let extraExtractions: ExtractionResult[] | undefined;\n if (options.mysql) {\n const { extractMysql } = await import('../extractors/mysql.js');\n console.error(`Extracting MySQL schema from ${validateDsn(options.mysql).safeDisplay} ...`);\n extraExtractions = [await extractMysql(options.mysql)];\n }\n\n const runOnce = () =>\n runPipeline(root, {\n ...exportOptionsFrom(options),\n ...clusterOptionsFrom(options),\n extraExtractions,\n update: options.update,\n onProgress: (m) => console.error(m),\n });\n await runOnce();\n\n if (options.watch) {\n console.error(`Watching ${root} for changes (Ctrl+C to stop) ...`);\n await watchAndRebuild(root, runOnce);\n }\n } catch (error) {\n console.error(`graphify: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('query <question>')\n .description('BFS (default) or DFS traversal answering a question against the existing graph')\n .option('--dfs', 'depth-first traversal (trace a specific path)')\n .option('--budget <tokens>', 'cap answer at N tokens', Number)\n .action(async (question: string, options: { dfs?: boolean; budget?: number }) => {\n try {\n await runQuery(question, options);\n } catch (error) {\n console.error(`graphify query: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('path <from> <to>')\n .description('shortest path between two named nodes')\n .action(async (from: string, to: string) => {\n try {\n await runPath(from, to);\n } catch (error) {\n console.error(`graphify path: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('explain <node>')\n .description('plain-language explanation of a node')\n .action(async (node: string) => {\n try {\n await runExplain(node);\n } catch (error) {\n console.error(`graphify explain: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('affected <node>')\n .description('reverse impact analysis — everything that (transitively) depends on a node')\n .option('--depth <n>', 'how many reverse hops to follow (default 3)', Number)\n .option('--limit <n>', 'cap on affected nodes reported (default 200)', Number)\n .action(async (node: string, options: { depth?: number; limit?: number }) => {\n try {\n await runAffected(node, options);\n } catch (error) {\n console.error(`graphify affected: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('context <task>')\n .description('token-budgeted working-set pack — the actual code for a task, in one call')\n .option('--budget <tokens>', 'approximate token cap for the pack (default 4000)', Number)\n .action(async (task: string, options: { budget?: number }) => {\n try {\n await runContext(task, options);\n } catch (error) {\n console.error(`graphify context: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('tests [node]')\n .description('structural test selection — the minimal test files worth running for a change')\n .option('--changed [rev]', 'select for the working-tree diff (optionally vs a revision) instead of a node')\n .action(async (node: string | undefined, options: { changed?: string | boolean }) => {\n try {\n await runTests(node, options);\n } catch (error) {\n console.error(`graphify tests: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('review <base> [head]')\n .description('structural review between two git revisions: added/removed/rewired symbols with blast radius + tests')\n .action(async (base: string, head?: string) => {\n try {\n await runReview(base, head);\n } catch (error) {\n console.error(`graphify review: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('check')\n .description('check the graph against dependency rules (graphify.rules.json) — exits 1 on violations')\n .option('--rules <file>', 'rules file (default ./graphify.rules.json)')\n .action(async (options: { rules?: string }) => {\n try {\n await runCheck(options);\n } catch (error) {\n console.error(`graphify check: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('benchmark [questions...]')\n .description('measure token savings of graph answers vs reading the files they touch')\n .action(async (questions: string[]) => {\n try {\n await runBenchmark(questions);\n } catch (error) {\n console.error(`graphify benchmark: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('install')\n .description('install the skill/rules files into local agents (claude, cursor, windsurf, cline, agents, gemini, all)')\n .option('--platform <names...>', 'platform(s) to install for', ['claude'])\n .action(async (options: { platform: string[] }) => {\n try {\n await runInstall(options.platform);\n } catch (error) {\n console.error(`graphify install: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nconst globalCommand = program\n .command('global')\n .description('manage the cross-project global graph (registry at ~/.graphify/global.json)');\n\nglobalCommand\n .command('add [path]')\n .description('register a project (must already have graphify-out/graph.json)')\n .action(async (target?: string) => {\n try {\n await runGlobalAdd(target);\n } catch (error) {\n console.error(`graphify global add: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nglobalCommand\n .command('remove <name>')\n .description('unregister a project by name')\n .action(async (name: string) => {\n try {\n await runGlobalRemove(name);\n } catch (error) {\n console.error(`graphify global remove: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nglobalCommand\n .command('list')\n .description('list registered projects')\n .action(async () => {\n try {\n await runGlobalList();\n } catch (error) {\n console.error(`graphify global list: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nglobalCommand\n .command('build')\n .description('merge every registered project into one global graph')\n .option('--out <dir>', 'output directory (default ~/.graphify/global-out)')\n .action(async (options: { out?: string }) => {\n try {\n await runGlobalBuild(options);\n } catch (error) {\n console.error(`graphify global build: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('merge <dirs...>')\n .description('one-shot merge of two or more built project graphs into one')\n .option('--out <dir>', 'output directory (default ./graphify-merged)')\n .action(async (dirs: string[], options: { out?: string }) => {\n try {\n await runMerge(dirs, options);\n } catch (error) {\n console.error(`graphify merge: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('save-result')\n .description('save a Q&A result to graphify-out/memory/ for the graph feedback loop')\n .requiredOption('--question <q>', 'the question that was asked')\n .requiredOption('--answer <a>', 'the answer that was given')\n .option('--nodes <ids...>', 'node ids/labels cited in the answer', [])\n .option('--type <t>', 'query|path|explain|affected', 'query')\n .option('--outcome <o>', 'useful|dead_end|corrected', 'useful')\n .option('--correction <text>', 'what the right answer was (pairs with --outcome corrected)')\n .action(async (options: {\n question: string;\n answer: string;\n nodes: string[];\n type: string;\n outcome: string;\n correction?: string;\n }) => {\n try {\n const memoryDir = path.join(process.cwd(), 'graphify-out', 'memory');\n const file = await saveResult(\n {\n question: options.question,\n answer: options.answer,\n nodes: options.nodes,\n type: options.type as ResultType,\n outcome: options.outcome as ResultOutcome,\n correction: options.correction,\n },\n memoryDir,\n );\n console.log(`Saved -> ${file}`);\n } catch (error) {\n console.error(`graphify save-result: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('reflect')\n .description('aggregate graphify-out/memory/ into a recency-weighted lessons doc')\n .option('--half-life-days <n>', 'a result loses half its weight every N days (default 30)', Number)\n .option('--out <file>', 'output path (default graphify-out/reflections/LESSONS.md)')\n .action(async (options: { halfLifeDays?: number; out?: string }) => {\n try {\n const memoryDir = path.join(process.cwd(), 'graphify-out', 'memory');\n const results = await loadResults(memoryDir);\n const lessons = renderLessons(results, { halfLifeDays: options.halfLifeDays });\n const outPath = path.resolve(options.out ?? path.join(process.cwd(), 'graphify-out', 'reflections', 'LESSONS.md'));\n await fs.mkdir(path.dirname(outPath), { recursive: true });\n await fs.writeFile(outPath, lessons, 'utf-8');\n console.log(`Reflected ${results.length} result(s) -> ${outPath}`);\n } catch (error) {\n console.error(`graphify reflect: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nconst hookCommand = program\n .command('hook')\n .description('manage the git hooks that auto-rebuild the graph on commit/pull');\n\nhookCommand\n .command('install')\n .description('install post-commit and post-merge hooks (preserves existing hook content)')\n .action(async () => {\n try {\n await runHookInstall();\n } catch (error) {\n console.error(`graphify hook install: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nhookCommand\n .command('uninstall')\n .description('remove the graphify block from the git hooks')\n .action(async () => {\n try {\n await runHookUninstall();\n } catch (error) {\n console.error(`graphify hook uninstall: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram.parseAsync(process.argv);\n","import { loadGraph } from '../../graphStore.js';\nimport { affectedBy } from '../../impact.js';\n\nexport interface AffectedOptions {\n depth?: number;\n limit?: number;\n}\n\n/** Reverse impact analysis: everything that (transitively) depends on the named node. */\nexport async function runAffected(nodeName: string, options: AffectedOptions = {}): Promise<void> {\n const graph = await loadGraph();\n const result = affectedBy(graph, nodeName, { maxDepth: options.depth, limit: options.limit });\n\n if (!result) {\n console.log(`No node matched \"${nodeName}\".`);\n return;\n }\n\n console.log(`Impact of changing ${result.target.label} (${result.target.id}):`);\n\n if (result.affected.length === 0) {\n console.log(' Nothing in the graph depends on it — no incoming dependency edges.');\n return;\n }\n\n let currentDepth = 0;\n for (const node of result.affected) {\n if (node.depth !== currentDepth) {\n currentDepth = node.depth;\n console.log('');\n console.log(currentDepth === 1 ? 'Directly affected:' : `Affected at depth ${currentDepth}:`);\n }\n const isPlaceholder =\n node.sourceFile === '<unknown>' || node.sourceFile === '<external>' || node.sourceFile === '';\n const where = isPlaceholder\n ? '(reference only)'\n : `${node.sourceFile}${node.sourceLocation ? `:${node.sourceLocation}` : ''}`;\n console.log(\n ` ${node.label} — ${where} ` +\n `(--${node.via.relation}--> ${node.via.dependsOn}, ${node.via.confidence})`,\n );\n }\n\n console.log('');\n const { EXTRACTED, INFERRED, AMBIGUOUS } = result.byConfidence;\n console.log(\n `Blast radius: ${result.affected.length} node(s) within ${result.maxDepth} hop(s) — ` +\n `${EXTRACTED} certain (EXTRACTED), ${INFERRED} likely (INFERRED), ${AMBIGUOUS} uncertain (AMBIGUOUS).`,\n );\n if (result.truncated) {\n console.log('(List truncated — raise --limit to see more.)');\n }\n}\n","import * as fs from 'node:fs/promises';\nimport type Graph from 'graphology';\nimport { loadGraph } from '../../graphStore.js';\nimport { queryGraph } from '../../query.js';\nimport { isTestFile } from '../../testmap.js';\n\n/**\n * `graphify benchmark` — measure the token savings of answering through the\n * graph instead of reading source files. For each question it compares the\n * serialized size of the graph answer against the size of every source file\n * that answer's nodes live in (the \"just read the relevant files\" baseline\n * an agent without the graph would pay). Chars/4 is the usual token\n * approximation, applied to both sides so the ratio is fair.\n */\n\nexport interface BenchmarkRow {\n question: string;\n graphTokens: number;\n naiveTokens: number;\n /** naive / graph — how many times cheaper the graph answer is. */\n reduction: number;\n filesRead: number;\n filesMissing: number;\n}\n\nexport type FileReader = (path: string) => Promise<string>;\n\nconst tokensOf = (text: string): number => Math.ceil(text.length / 4);\n\nexport async function benchmarkQuestion(\n graph: Graph,\n question: string,\n readFile: FileReader,\n): Promise<BenchmarkRow> {\n const result = queryGraph(graph, question);\n // Compact JSON, matching how queryGraph's own token budget measures itself.\n const graphTokens = tokensOf(JSON.stringify(result));\n\n const sourceFiles = new Set<string>();\n for (const node of result.visited) {\n if (node.sourceFile && !node.sourceFile.startsWith('<') && !node.sourceFile.includes('://')) {\n sourceFiles.add(node.sourceFile);\n }\n }\n\n let naiveTokens = 0;\n let filesRead = 0;\n let filesMissing = 0;\n for (const file of [...sourceFiles].sort((a, b) => a.localeCompare(b))) {\n try {\n naiveTokens += tokensOf(await readFile(file));\n filesRead++;\n } catch {\n filesMissing++;\n }\n }\n\n return {\n question,\n graphTokens,\n naiveTokens,\n reduction: graphTokens > 0 && naiveTokens > 0 ? naiveTokens / graphTokens : 0,\n filesRead,\n filesMissing,\n };\n}\n\n/** Files no agent would ask a \"how does X work\" question about — build config, examples, scripts, docs. */\nconst NON_QUESTION_FILE =\n /(^|\\/)(examples?|scripts?|bench(?:mark)?s?|fixtures?|docs?|\\.github)\\/|\\.(config|conf)\\.[^/]+$|(^|\\/)(rollup|webpack|vite|vitest|jest|babel|tsup|eslint|prettier)\\.[^/]*$/i;\n\n/**\n * Default questions when none are given: the labels of the highest-degree\n * (most load-bearing) SOURCE entities. Tests, build config, and examples\n * are excluded — they score high on degree but are questions no agent\n * would ask, and they drag the measured reduction down unfairly.\n */\nexport function defaultQuestions(graph: Graph, count = 5): string[] {\n const entries = [...graph.nodes()]\n .map((id) => ({\n id,\n degree: graph.degree(id),\n label: (graph.getNodeAttribute(id, 'label') as string | undefined) ?? id,\n file: (graph.getNodeAttribute(id, 'sourceFile') as string | undefined) ?? '',\n }))\n // File nodes score high on degree but make poor questions — prefer entities.\n .filter((e) => e.id.includes('::'))\n .filter((e) => !isTestFile(e.file) && !NON_QUESTION_FILE.test(e.file))\n .sort((a, b) => b.degree - a.degree || a.id.localeCompare(b.id));\n return entries.slice(0, count).map((e) => `how does ${e.label} work`);\n}\n\nexport async function runBenchmark(questions: string[]): Promise<BenchmarkRow[]> {\n const graph = await loadGraph();\n const effective = questions.length > 0 ? questions : defaultQuestions(graph);\n if (effective.length === 0) {\n console.log('The graph has no entity nodes to benchmark against — build it first.');\n return [];\n }\n\n const rows: BenchmarkRow[] = [];\n for (const question of effective) {\n rows.push(await benchmarkQuestion(graph, question, (p) => fs.readFile(p, 'utf-8')));\n }\n\n console.log('Token cost per question — graph answer vs reading the files it touches:\\n');\n for (const row of rows) {\n const reduction = row.reduction > 0 ? `${row.reduction.toFixed(1)}x` : 'n/a';\n console.log(` \"${row.question}\"`);\n console.log(\n ` graph: ~${row.graphTokens} tokens | files: ~${row.naiveTokens} tokens ` +\n `(${row.filesRead} file(s)${row.filesMissing ? `, ${row.filesMissing} unreadable` : ''}) | reduction: ${reduction}`,\n );\n }\n\n const scored = rows.filter((r) => r.reduction > 0);\n if (scored.length > 0) {\n const avg = scored.reduce((s, r) => s + r.reduction, 0) / scored.length;\n console.log(`\\nAverage reduction: ${avg.toFixed(1)}x across ${scored.length} question(s).`);\n } else {\n console.log('\\nNo reduction could be measured — run from the project root so source files are readable.');\n }\n return rows;\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { checkRules, validateRules } from '../../check.js';\nimport { loadGraph } from '../../graphStore.js';\n\nexport interface CheckCommandOptions {\n rules?: string;\n}\n\n/**\n * Check the graph against declared dependency rules. Exits non-zero on\n * violations so CI (and agents verifying their own edits) can gate on it.\n */\nexport async function runCheck(options: CheckCommandOptions = {}): Promise<void> {\n const rulesPath = path.resolve(options.rules ?? 'graphify.rules.json');\n let raw: string;\n try {\n raw = await fs.readFile(rulesPath, 'utf-8');\n } catch {\n throw new Error(\n `No rules file at ${rulesPath} — create graphify.rules.json with ` +\n '{ \"rules\": [{ \"name\": \"...\", \"from\": \"src/a/**\", \"disallow\": [\"src/b/**\"] }] }.',\n );\n }\n const config = validateRules(JSON.parse(raw));\n\n const graph = await loadGraph();\n const violations = checkRules(graph, config);\n\n if (violations.length === 0) {\n console.log(`OK — ${config.rules.length} rule(s), no violations.`);\n return;\n }\n\n console.log(`${violations.length} violation(s):`);\n for (const v of violations) {\n console.log(` [${v.rule}] ${v.fromNode} --${v.relation}--> ${v.toNode}`);\n console.log(` ${v.fromFile} must not depend on ${v.toFile}${v.reason ? ` — ${v.reason}` : ''}`);\n }\n process.exitCode = 1;\n}\n","import * as fs from 'node:fs/promises';\nimport { buildContextPack, renderContextPack } from '../../context.js';\nimport { loadGraph } from '../../graphStore.js';\n\nexport interface ContextCommandOptions {\n budget?: number;\n}\n\n/** Token-budgeted working-set pack: the actual code an agent needs for a task, in one call. */\nexport async function runContext(task: string, options: ContextCommandOptions = {}): Promise<void> {\n const graph = await loadGraph();\n const pack = await buildContextPack(graph, task, (p) => fs.readFile(p, 'utf-8'), {\n tokenBudget: options.budget,\n });\n console.log(renderContextPack(pack));\n}\n","import { loadGraph } from '../../graphStore.js';\nimport { explainNode } from '../../query.js';\n\n/** Plain-language explanation of a single node. */\nexport async function runExplain(nodeName: string): Promise<void> {\n const graph = await loadGraph();\n const result = explainNode(graph, nodeName);\n\n if (!result) {\n console.log(`No node matched \"${nodeName}\".`);\n return;\n }\n\n console.log(result.label);\n if (result.sourceFile === '<unknown>' || result.sourceFile === '<external>' || result.sourceFile === '') {\n // Auto-vivified endpoint (re-export alias / unresolved reference) — it\n // has no source of its own, so \"<unknown>:L0\" would read like a bug.\n console.log(' location: (reference only — no declaration seen; check its outgoing edges for the real definition)');\n } else {\n const location = result.sourceLocation ? `:${result.sourceLocation}` : '';\n console.log(` location: ${result.sourceFile}${location}`);\n }\n if (result.communityLabel) {\n console.log(` community: ${result.communityLabel}`);\n }\n\n console.log('');\n console.log(`Calls / references out (${result.outgoing.length}):`);\n if (result.outgoing.length === 0) {\n console.log(' (none)');\n } else {\n for (const edge of result.outgoing) {\n console.log(` --${edge.relation}--> ${edge.target} (${edge.confidence})`);\n }\n }\n\n console.log('');\n console.log(`Referenced by (${result.incoming.length}):`);\n if (result.incoming.length === 0) {\n console.log(' (none)');\n } else {\n for (const edge of result.incoming) {\n console.log(` ${edge.source} --${edge.relation}--> (${edge.confidence})`);\n }\n }\n}\n","import { renderReview, reviewRevisions } from '../../diff.js';\n\n/** Structural review between two git revisions (head defaults to HEAD). */\nexport async function runReview(base: string, head?: string): Promise<void> {\n const diff = await reviewRevisions(process.cwd(), base, head ?? 'HEAD');\n console.log(renderReview(diff));\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { loadGraph } from '../../graphStore.js';\nimport { testsForChangedFiles, testsForNode } from '../../testmap.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport interface TestsCommandOptions {\n /** Select tests for the working-tree diff (optionally against a rev) instead of a named node. */\n changed?: string | boolean;\n}\n\n/** Structural test selection: the minimal test files worth running for a change. */\nexport async function runTests(nodeQuery: string | undefined, options: TestsCommandOptions = {}): Promise<void> {\n const graph = await loadGraph();\n\n let selection;\n if (options.changed !== undefined && options.changed !== false) {\n const args = ['diff', '--name-only'];\n if (typeof options.changed === 'string') args.push(options.changed);\n const { stdout } = await execFileAsync('git', args);\n const changedFiles = stdout.split('\\n').map((l) => l.trim()).filter(Boolean);\n if (changedFiles.length === 0) {\n console.log('No changed files in the diff.');\n return;\n }\n selection = testsForChangedFiles(graph, changedFiles);\n } else if (nodeQuery) {\n selection = testsForNode(graph, nodeQuery);\n if (!selection) {\n console.log(`No node matched \"${nodeQuery}\".`);\n return;\n }\n } else {\n console.log('Provide a node to select tests for, or --changed for the working-tree diff.');\n return;\n }\n\n if (selection.testFiles.length === 0) {\n console.log(\n `No test files found in the blast radius of ${selection.target} ` +\n `(${selection.affectedNodeCount} affected node(s) checked) — either it's untested or the tests ` +\n 'reach it through a path the graph does not capture.',\n );\n return;\n }\n\n const how =\n selection.precision === 'usage'\n ? 'selected by actual usage (calls/inheritance)'\n : 'selected by import reachability — may over-include files sharing an entry point';\n console.log(`Test files for ${selection.target} (${how}; most direct first):`);\n for (const test of selection.testFiles) {\n const via = test.depth === 0 ? test.via : `depth ${test.depth}, via ${test.via}`;\n console.log(` ${test.file} (${via})`);\n }\n}\n","import * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { analyze } from '../../analyze.js';\nimport { cluster } from '../../cluster.js';\nimport { exportGraph } from '../../export.js';\nimport { loadGraph } from '../../graphStore.js';\nimport { mergeGraphs, type MergeEntry } from '../../merge.js';\nimport { renderReport } from '../../report.js';\n\n/**\n * `graphify global add|remove|list|build` — a registry of projects whose\n * built graphs get merged into one global cross-project graph, plus the\n * one-shot `graphify merge <dir...>` for merging without a registry.\n * The registry lives at ~/.graphify/global.json; the merged output defaults\n * to ~/.graphify/global-out/.\n */\n\nexport interface GlobalRegistry {\n projects: Array<{ name: string; root: string }>;\n}\n\nfunction registryDir(): string {\n return path.join(os.homedir(), '.graphify');\n}\n\nfunction registryPath(): string {\n return path.join(registryDir(), 'global.json');\n}\n\nasync function readRegistry(): Promise<GlobalRegistry> {\n try {\n const raw = await fs.readFile(registryPath(), 'utf-8');\n const parsed: unknown = JSON.parse(raw);\n const projects = (parsed as GlobalRegistry).projects;\n if (!Array.isArray(projects)) return { projects: [] };\n return { projects: projects.filter((p) => typeof p?.name === 'string' && typeof p?.root === 'string') };\n } catch {\n return { projects: [] };\n }\n}\n\nasync function writeRegistry(registry: GlobalRegistry): Promise<void> {\n await fs.mkdir(registryDir(), { recursive: true });\n await fs.writeFile(registryPath(), JSON.stringify(registry, null, 2), 'utf-8');\n}\n\n/** Project name for a root: its package.json \"name\" if present, else the directory basename. */\nasync function projectNameFor(root: string): Promise<string> {\n try {\n const raw = await fs.readFile(path.join(root, 'package.json'), 'utf-8');\n const name = (JSON.parse(raw) as { name?: unknown }).name;\n if (typeof name === 'string' && name.length > 0) return name;\n } catch {\n // no package.json — fall through to the basename\n }\n return path.basename(root);\n}\n\nexport async function runGlobalAdd(target: string = '.'): Promise<void> {\n const root = path.resolve(target);\n const graphJson = path.join(root, 'graphify-out', 'graph.json');\n try {\n await fs.access(graphJson);\n } catch {\n throw new Error(`${root} has no graphify-out/graph.json — run \\`graphify ${target}\\` first, then add it.`);\n }\n\n const name = await projectNameFor(root);\n const registry = await readRegistry();\n\n const existing = registry.projects.find((p) => p.name === name);\n if (existing) {\n if (existing.root === root) {\n console.log(`${name} is already registered (${root}).`);\n return;\n }\n throw new Error(\n `A different project is already registered as \"${name}\" (${existing.root}). ` +\n 'Remove it first with `graphify global remove`.',\n );\n }\n\n registry.projects.push({ name, root });\n registry.projects.sort((a, b) => a.name.localeCompare(b.name));\n await writeRegistry(registry);\n console.log(`Registered ${name} (${root}). ${registry.projects.length} project(s) in the global graph.`);\n}\n\nexport async function runGlobalRemove(name: string): Promise<void> {\n const registry = await readRegistry();\n const before = registry.projects.length;\n registry.projects = registry.projects.filter((p) => p.name !== name);\n if (registry.projects.length === before) {\n throw new Error(`No project named \"${name}\" is registered. See \\`graphify global list\\`.`);\n }\n await writeRegistry(registry);\n console.log(`Removed ${name}. ${registry.projects.length} project(s) remain.`);\n}\n\nexport async function runGlobalList(): Promise<void> {\n const registry = await readRegistry();\n if (registry.projects.length === 0) {\n console.log('No projects registered. Add one with `graphify global add <path>`.');\n return;\n }\n for (const project of registry.projects) {\n console.log(`${project.name} ${project.root}`);\n }\n}\n\n/** Load every entry's graph, skipping (with a warning) the ones that fail — one broken project must not sink the merge. */\nasync function loadEntries(sources: Array<{ name: string; outDir: string }>): Promise<MergeEntry[]> {\n const entries: MergeEntry[] = [];\n for (const source of sources) {\n try {\n entries.push({ name: source.name, graph: await loadGraph(source.outDir) });\n } catch (error) {\n console.warn(`Skipping ${source.name}: ${(error as Error).message}`);\n }\n }\n return entries;\n}\n\nasync function mergeAndExport(entries: MergeEntry[], outDir: string): Promise<void> {\n if (entries.length === 0) {\n throw new Error('Nothing to merge — no project graphs could be loaded.');\n }\n\n const merged = mergeGraphs(entries);\n cluster(merged);\n const analysis = analyze(merged);\n const report = renderReport(merged, analysis);\n await exportGraph(merged, { outDir }, report);\n\n console.log(\n `Merged ${entries.length} project(s) into ${outDir} — ` +\n `${merged.order} node(s), ${merged.size} edge(s).`,\n );\n}\n\nexport async function runGlobalBuild(options: { out?: string } = {}): Promise<void> {\n const registry = await readRegistry();\n if (registry.projects.length === 0) {\n throw new Error('No projects registered. Add some with `graphify global add <path>` first.');\n }\n\n const entries = await loadEntries(\n registry.projects.map((p) => ({ name: p.name, outDir: path.join(p.root, 'graphify-out') })),\n );\n await mergeAndExport(entries, path.resolve(options.out ?? path.join(registryDir(), 'global-out')));\n}\n\n/** One-shot merge of two or more project directories, no registry involved. */\nexport async function runMerge(targets: string[], options: { out?: string } = {}): Promise<void> {\n if (targets.length < 2) {\n throw new Error('Provide at least two project directories to merge.');\n }\n\n const sources: Array<{ name: string; outDir: string }> = [];\n for (const target of targets) {\n const root = path.resolve(target);\n sources.push({ name: await projectNameFor(root), outDir: path.join(root, 'graphify-out') });\n }\n\n const names = sources.map((s) => s.name);\n const duplicate = names.find((name, i) => names.indexOf(name) !== i);\n if (duplicate !== undefined) {\n throw new Error(`Two of the given projects resolve to the same name (\"${duplicate}\") — rename one.`);\n }\n\n const entries = await loadEntries(sources);\n await mergeAndExport(entries, path.resolve(options.out ?? 'graphify-merged'));\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\n/**\n * `graphify hook install` / `graphify hook uninstall` — auto-rebuild the\n * graph after commits and pulls, mirroring the Python original's\n * `hook install`. The hook body lives inside a marked block so we can\n * coexist with (and later cleanly remove ourselves from) hooks the user\n * already has; we never overwrite or delete anything outside our markers.\n */\n\nconst MARKER_BEGIN = '# >>> graphify >>>';\nconst MARKER_END = '# <<< graphify <<<';\n\n/** post-commit: rebuild after each commit. post-merge: rebuild after pull/merge. */\nconst HOOK_NAMES = ['post-commit', 'post-merge'] as const;\n\nconst HOOK_BLOCK = `${MARKER_BEGIN}\n# Rebuild the graphify knowledge graph in the background so the hook never\n# slows down the commit. Errors are silenced — a failed rebuild should never\n# break a git operation. Installed by \\`graphify hook install\\`.\n(graphify . --update --no-viz >/dev/null 2>&1 &)\n${MARKER_END}`;\n\nexport interface HookResult {\n hook: string;\n path: string;\n action: 'created' | 'appended' | 'already-installed' | 'removed' | 'not-installed';\n}\n\nasync function hooksDir(cwd: string): Promise<string> {\n const gitDir = path.join(cwd, '.git');\n let stats;\n try {\n stats = await fs.stat(gitDir);\n } catch {\n throw new Error(`${cwd} is not a git repository (no .git directory) — run this from the repo root.`);\n }\n if (!stats.isDirectory()) {\n // .git can be a file in worktrees/submodules (\"gitdir: <path>\") — follow it.\n const content = (await fs.readFile(gitDir, 'utf-8')).trim();\n const match = /^gitdir:\\s*(.+)$/.exec(content);\n if (!match) throw new Error(`${gitDir} exists but is neither a directory nor a gitdir pointer.`);\n return path.resolve(cwd, match[1] as string, 'hooks');\n }\n return path.join(gitDir, 'hooks');\n}\n\n/** Install the marked graphify block into post-commit and post-merge, preserving any existing hook content. */\nexport async function runHookInstall(cwd: string = process.cwd()): Promise<HookResult[]> {\n const dir = await hooksDir(cwd);\n await fs.mkdir(dir, { recursive: true });\n\n const results: HookResult[] = [];\n for (const hook of HOOK_NAMES) {\n const hookPath = path.join(dir, hook);\n let existing: string | null = null;\n try {\n existing = await fs.readFile(hookPath, 'utf-8');\n } catch {\n // no existing hook — we'll create it\n }\n\n if (existing === null) {\n await fs.writeFile(hookPath, `#!/bin/sh\\n${HOOK_BLOCK}\\n`, { mode: 0o755 });\n results.push({ hook, path: hookPath, action: 'created' });\n } else if (existing.includes(MARKER_BEGIN)) {\n results.push({ hook, path: hookPath, action: 'already-installed' });\n } else {\n const separator = existing.endsWith('\\n') ? '' : '\\n';\n await fs.writeFile(hookPath, `${existing}${separator}${HOOK_BLOCK}\\n`, 'utf-8');\n await fs.chmod(hookPath, 0o755);\n results.push({ hook, path: hookPath, action: 'appended' });\n }\n }\n\n for (const result of results) {\n console.log(`${result.hook}: ${result.action} (${result.path})`);\n }\n console.log('');\n console.log('The graph will rebuild in the background after each commit and pull.');\n console.log('Remove with `graphify hook uninstall`.');\n return results;\n}\n\n/** Remove only the marked graphify block; delete the hook file if nothing else is left in it. */\nexport async function runHookUninstall(cwd: string = process.cwd()): Promise<HookResult[]> {\n const dir = await hooksDir(cwd);\n\n const results: HookResult[] = [];\n for (const hook of HOOK_NAMES) {\n const hookPath = path.join(dir, hook);\n let existing: string | null = null;\n try {\n existing = await fs.readFile(hookPath, 'utf-8');\n } catch {\n // nothing installed\n }\n\n if (existing === null || !existing.includes(MARKER_BEGIN)) {\n results.push({ hook, path: hookPath, action: 'not-installed' });\n continue;\n }\n\n const blockPattern = new RegExp(`\\\\n?${MARKER_BEGIN}[\\\\s\\\\S]*?${MARKER_END}\\\\n?`);\n const remaining = existing.replace(blockPattern, '\\n').trim();\n\n if (remaining === '' || remaining === '#!/bin/sh') {\n await fs.unlink(hookPath);\n } else {\n await fs.writeFile(hookPath, `${remaining}\\n`, 'utf-8');\n }\n results.push({ hook, path: hookPath, action: 'removed' });\n }\n\n for (const result of results) {\n console.log(`${result.hook}: ${result.action}`);\n }\n return results;\n}\n","import { createRequire } from 'node:module';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\nconst require = createRequire(import.meta.url);\n\n/**\n * Resolve this package's own root directory, whether we're running from\n * source (tests/dev), the built dist/ bundle, or installed as a real\n * dependency elsewhere. Uses Node's self-reference resolution (walking up\n * from this module's location to the nearest package.json named\n * \"@dreamtree-org/graphify\") rather than a hardcoded relative path, since\n * the number of directories between this file and the package root\n * differs between src/ and the bundled dist/ output.\n */\nfunction packageRoot(): string {\n const packageJsonPath = require.resolve('@dreamtree-org/graphify/package.json');\n return path.dirname(packageJsonPath);\n}\n\nexport interface InstallResult {\n host: string;\n path: string;\n}\n\n/** Markers for hosts where we append into a shared file (AGENTS.md / GEMINI.md) instead of owning one. */\nconst MD_MARKER_BEGIN = '<!-- >>> graphify >>> -->';\nconst MD_MARKER_END = '<!-- <<< graphify <<< -->';\n\nasync function writeOwnedFile(filePath: string, content: string): Promise<void> {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(filePath, content, 'utf-8');\n}\n\n/** Create `filePath` with the marked block, or replace/append the block in an existing file — user content is never touched. */\nasync function upsertMarkedBlock(filePath: string, content: string): Promise<void> {\n const block = `${MD_MARKER_BEGIN}\\n${content.trim()}\\n${MD_MARKER_END}`;\n let existing: string | null = null;\n try {\n existing = await fs.readFile(filePath, 'utf-8');\n } catch {\n // file doesn't exist yet\n }\n\n if (existing === null) {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(filePath, `${block}\\n`, 'utf-8');\n return;\n }\n if (existing.includes(MD_MARKER_BEGIN)) {\n const pattern = new RegExp(`${MD_MARKER_BEGIN}[\\\\s\\\\S]*?${MD_MARKER_END}`);\n await fs.writeFile(filePath, existing.replace(pattern, block), 'utf-8');\n return;\n }\n const separator = existing.endsWith('\\n') ? '\\n' : '\\n\\n';\n await fs.writeFile(filePath, `${existing}${separator}${block}\\n`, 'utf-8');\n}\n\ntype PlatformInstaller = (cwd: string, skillContent: string) => Promise<InstallResult>;\n\n/**\n * Curated per-host installers. `claude` gets the native skill layout; hosts\n * with a project rules directory (cursor/windsurf/cline) get their own\n * rules file; hosts that read a single shared instructions file\n * (AGENTS.md-based agents like Codex/opencode/Amp, and Gemini CLI's\n * GEMINI.md) get an idempotent marked block appended.\n */\nconst PLATFORMS: Record<string, PlatformInstaller> = {\n claude: async (cwd, content) => {\n const target = path.join(cwd, '.claude', 'skills', 'graphify', 'SKILL.md');\n await writeOwnedFile(target, content);\n return { host: 'Claude Code', path: target };\n },\n cursor: async (cwd, content) => {\n const target = path.join(cwd, '.cursor', 'rules', 'graphify.mdc');\n const body = content.replace(/^---[\\s\\S]*?---\\n/, ''); // strip the skill frontmatter\n await writeOwnedFile(\n target,\n `---\\ndescription: Query the graphify knowledge graph for codebase structure questions\\nalwaysApply: false\\n---\\n${body}`,\n );\n return { host: 'Cursor', path: target };\n },\n windsurf: async (cwd, content) => {\n const target = path.join(cwd, '.windsurf', 'rules', 'graphify.md');\n await writeOwnedFile(target, content.replace(/^---[\\s\\S]*?---\\n/, ''));\n return { host: 'Windsurf', path: target };\n },\n cline: async (cwd, content) => {\n const target = path.join(cwd, '.clinerules', 'graphify.md');\n await writeOwnedFile(target, content.replace(/^---[\\s\\S]*?---\\n/, ''));\n return { host: 'Cline', path: target };\n },\n agents: async (cwd, content) => {\n const target = path.join(cwd, 'AGENTS.md');\n await upsertMarkedBlock(target, content.replace(/^---[\\s\\S]*?---\\n/, ''));\n return { host: 'AGENTS.md agents (Codex, opencode, Amp, ...)', path: target };\n },\n gemini: async (cwd, content) => {\n const target = path.join(cwd, 'GEMINI.md');\n await upsertMarkedBlock(target, content.replace(/^---[\\s\\S]*?---\\n/, ''));\n return { host: 'Gemini CLI', path: target };\n },\n};\n\nexport const SUPPORTED_PLATFORMS = Object.keys(PLATFORMS).sort((a, b) => a.localeCompare(b));\n\n/**\n * Install the graphify skill/rules for the requested platforms (default:\n * claude). `all` installs every supported platform.\n */\nexport async function runInstall(\n platforms: string[] = ['claude'],\n cwd: string = process.cwd(),\n): Promise<InstallResult[]> {\n const sourcePath = path.join(packageRoot(), 'src', 'skill', 'SKILL.md');\n const content = await fs.readFile(sourcePath, 'utf-8');\n\n const requested = platforms.includes('all') ? SUPPORTED_PLATFORMS : platforms;\n const results: InstallResult[] = [];\n for (const platform of requested) {\n const installer = PLATFORMS[platform];\n if (!installer) {\n throw new Error(\n `Unknown platform \"${platform}\" — supported: ${SUPPORTED_PLATFORMS.join(', ')}, or \"all\".`,\n );\n }\n results.push(await installer(cwd, content));\n }\n\n for (const result of results) {\n console.log(`Installed graphify for ${result.host} -> ${result.path}`);\n }\n return results;\n}\n","import { loadGraph } from '../../graphStore.js';\nimport { shortestPath } from '../../query.js';\n\n/** Shortest path between two named nodes. */\nexport async function runPath(fromNode: string, toNode: string): Promise<void> {\n const graph = await loadGraph();\n const result = shortestPath(graph, fromNode, toNode);\n\n if (!result) {\n console.log(`Could not resolve \"${fromNode}\" and/or \"${toNode}\" to a node in the graph.`);\n return;\n }\n\n if (!result.found) {\n console.log(\n `No path found between \"${result.from.label}\" and \"${result.to.label}\" — they may be in ` +\n 'disconnected parts of the graph (see GRAPH_REPORT.md § Open Questions).',\n );\n return;\n }\n\n console.log(`${result.from.label} -> ${result.to.label} (${result.path.length} node(s)):`);\n console.log(` ${result.path.join(' -> ')}`);\n if (result.edges.length > 0) {\n console.log('');\n console.log('Edges along the path:');\n for (const edge of result.edges) {\n console.log(` ${edge.source} --${edge.relation}--> ${edge.target} (${edge.confidence})`);\n }\n }\n}\n","import { loadGraph } from '../../graphStore.js';\nimport { queryGraph } from '../../query.js';\n\nexport interface QueryOptions {\n dfs?: boolean;\n /** Approximate token cap for the printed context (default 2000). */\n budget?: number;\n}\n\n/** BFS (default) or DFS traversal answering a natural-language question against the graph. */\nexport async function runQuery(question: string, options: QueryOptions = {}): Promise<void> {\n const graph = await loadGraph();\n const result = queryGraph(graph, question, { dfs: options.dfs, tokenBudget: options.budget });\n\n if (result.seeds.length === 0) {\n console.log(\n `No nodes matched \"${question}\". Try different keywords, or run \\`graphify explain <node>\\` ` +\n 'if you already know the name.',\n );\n return;\n }\n\n console.log(`Matched ${result.seeds.length} seed node(s):`);\n for (const seed of result.seeds) {\n console.log(` - ${seed.label} (${seed.id})`);\n }\n\n console.log('');\n console.log(`Context — ${result.visited.length} node(s), ${options.dfs ? 'DFS' : 'BFS'} traversal:`);\n for (const node of result.visited) {\n const location = node.sourceLocation ? `:${node.sourceLocation}` : '';\n console.log(` [depth ${node.depth}] ${node.label} — ${node.sourceFile}${location}`);\n }\n\n console.log('');\n console.log(`Edges within this context (${result.edges.length}):`);\n for (const edge of result.edges) {\n console.log(` ${edge.source} --${edge.relation}--> ${edge.target} (${edge.confidence})`);\n }\n}\n"],"mappings":"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","names":["execFile","fs","os","path","promisify","readFile","fs","fs","fs","path","fs","path","fs","path","require","execFileAsync","promisify","execFile"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/commands/affected.ts","../../src/cli/commands/benchmark.ts","../../src/cli/commands/check.ts","../../src/cli/commands/context.ts","../../src/cli/commands/explain.ts","../../src/cli/commands/review.ts","../../src/cli/commands/tests.ts","../../src/cli/commands/global.ts","../../src/cli/commands/hook.ts","../../src/cli/commands/install.ts","../../src/cli/commands/path.ts","../../src/cli/commands/query.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { execFile } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { promisify } from 'node:util';\nimport { watch as watchFiles } from 'chokidar';\nimport { Command } from 'commander';\nimport { analyze } from '../analyze.js';\nimport { cluster } from '../cluster.js';\nimport { exportGraph } from '../export.js';\nimport { loadGraph } from '../graphStore.js';\nimport { runPipeline } from '../pipeline.js';\nimport { renderReport } from '../report.js';\nimport { validateDsn, validateUrl } from '../security.js';\nimport type { ExtractionResult } from '../types.js';\nimport { runAffected } from './commands/affected.js';\nimport { runBenchmark } from './commands/benchmark.js';\nimport { runCheck } from './commands/check.js';\nimport { runContext } from './commands/context.js';\nimport { runExplain } from './commands/explain.js';\nimport { runReview } from './commands/review.js';\nimport { runTests } from './commands/tests.js';\nimport { runGlobalAdd, runGlobalBuild, runGlobalList, runGlobalRemove, runMerge } from './commands/global.js';\nimport { runHookInstall, runHookUninstall } from './commands/hook.js';\nimport { loadResults, renderLessons, saveResult, type ResultOutcome, type ResultType } from '../memory.js';\nimport { detectPlatforms, runInstall } from './commands/install.js';\nimport { runPath } from './commands/path.js';\nimport { runQuery } from './commands/query.js';\n\nconst execFileAsync = promisify(execFile);\n\ninterface DefaultCommandOptions {\n mode?: string;\n update?: boolean;\n watch?: boolean;\n clusterOnly?: boolean;\n viz: boolean; // commander negatable --no-viz -> options.viz\n svg?: boolean;\n graphml?: boolean;\n neo4j?: boolean;\n mcp?: boolean;\n install: boolean; // commander negatable --no-install -> options.install\n leiden?: boolean;\n maxIterations?: number;\n mysql?: string;\n}\n\nfunction looksLikeGitUrl(value: string): boolean {\n if (!/^https?:\\/\\//i.test(value)) return false;\n return /github\\.com|gitlab\\.com|bitbucket\\.org/i.test(value) || value.endsWith('.git');\n}\n\n/** Clone `url` into a fresh temp directory using an argv-array child_process call (never shell: true, never a shell string built from input). */\nasync function cloneRepo(url: string): Promise<string> {\n const validated = validateUrl(url);\n const dest = await fs.mkdtemp(path.join(os.tmpdir(), 'graphify-clone-'));\n await execFileAsync('git', ['clone', '--depth', '1', validated, dest]);\n return dest;\n}\n\nfunction exportOptionsFrom(options: DefaultCommandOptions) {\n return { html: options.viz, svg: options.svg, graphml: options.graphml, neo4j: options.neo4j };\n}\n\nfunction clusterOptionsFrom(options: DefaultCommandOptions) {\n return {\n algorithm: options.leiden ? ('leiden' as const) : ('louvain' as const),\n maxIterations: options.maxIterations,\n };\n}\n\nasync function runClusterOnly(root: string, options: DefaultCommandOptions): Promise<void> {\n const outDir = path.join(root, 'graphify-out');\n const graph = await loadGraph(outDir);\n cluster(graph, clusterOptionsFrom(options));\n const analysis = analyze(graph);\n const report = renderReport(graph, analysis);\n await exportGraph(graph, { outDir, ...exportOptionsFrom(options) }, report);\n console.error(`Re-clustered the existing graph (${clusterOptionsFrom(options).algorithm}).`);\n}\n\n/** Watch `root` (ignoring graphify-out/, node_modules/, .git/) and re-run `runOnce` on any change, debounced. Never resolves — the process stays alive until interrupted, matching normal watch-mode CLI behavior. */\nasync function watchAndRebuild(root: string, runOnce: () => Promise<unknown>): Promise<void> {\n const watcher = watchFiles(root, {\n ignored: (filePath: string) => /(^|[/\\\\])(node_modules|\\.git|graphify-out)([/\\\\]|$)/.test(filePath),\n ignoreInitial: true,\n });\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let running = false;\n const schedule = (): void => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n if (running) return;\n running = true;\n console.error('Change detected, rebuilding...');\n runOnce()\n .catch((error: unknown) => console.error(`graphify --watch: rebuild failed: ${(error as Error).message}`))\n .finally(() => {\n running = false;\n });\n }, 300);\n };\n\n watcher.on('add', schedule).on('change', schedule).on('unlink', schedule);\n\n await new Promise<never>(() => {\n /* keep the process alive until the user interrupts it (Ctrl+C) */\n });\n}\n\nconst program = new Command();\n\nprogram\n .name('graphify')\n .description('Turn a folder of code/docs/papers into a queryable knowledge graph.')\n .argument('[path]', 'path to scan, or a GitHub URL', '.')\n .option('--mode <mode>', 'extraction mode (deep for richer INFERRED edges — reserved, no-op in v1)')\n .option('--update', 'incremental rebuild — reuse cached extractions for unchanged files')\n .option('--watch', 'rebuild on file change')\n .option('--cluster-only', 'rerun clustering on an existing graph')\n .option('--leiden', 'use Leiden instead of Louvain for community detection (v2 — better community quality)')\n .option('--max-iterations <n>', 'Leiden only: cap on outer iterations for very large graphs', Number)\n .option('--no-viz', 'skip graph.html')\n .option('--svg', 'also export graph.svg (not implemented in v1)')\n .option('--graphml', 'also export graph.graphml (not implemented in v1)')\n .option('--neo4j', 'generate graphify-out/cypher.txt for Neo4j (not implemented in v1)')\n .option('--mcp', 'start MCP stdio server instead of running the pipeline')\n .option('--no-install', 'skip auto-installing the agent skill + MCP config after the build')\n .option('--mysql <dsn>', 'also extract a MySQL schema (mysql://user:pass@host:port/db) into the graph')\n .action(async (targetArg: string, options: DefaultCommandOptions) => {\n try {\n if (options.mcp) {\n const outDir = path.resolve(targetArg === '.' ? process.cwd() : targetArg, 'graphify-out');\n const { startServer } = await import('../mcp/server.js');\n await startServer(path.join(outDir, 'graph.json'));\n return;\n }\n\n let root = targetArg;\n const cloned = looksLikeGitUrl(targetArg);\n if (cloned) {\n console.error(`Cloning ${targetArg} ...`);\n root = await cloneRepo(targetArg);\n }\n root = path.resolve(root);\n\n if (options.mode) {\n console.error(`--mode ${options.mode} is reserved for a future richer-INFERRED-edges pass — currently a no-op.`);\n }\n\n if (options.clusterOnly) {\n await runClusterOnly(root, options);\n return;\n }\n\n let extraExtractions: ExtractionResult[] | undefined;\n if (options.mysql) {\n const { extractMysql } = await import('../extractors/mysql.js');\n console.error(`Extracting MySQL schema from ${validateDsn(options.mysql).safeDisplay} ...`);\n extraExtractions = [await extractMysql(options.mysql)];\n }\n\n const runOnce = () =>\n runPipeline(root, {\n ...exportOptionsFrom(options),\n ...clusterOptionsFrom(options),\n extraExtractions,\n update: options.update,\n onProgress: (m) => console.error(m),\n });\n await runOnce();\n\n // Make the graph instantly usable by AI agents: install the skill and\n // register the project-scoped MCP server for whichever hosts this\n // project uses. Skipped for cloned URLs (the clone lives in a temp\n // dir) and with --no-install; a failure here never fails the build.\n if (options.install && !cloned) {\n try {\n await runInstall(await detectPlatforms(root), root);\n } catch (error) {\n console.error(`graphify: agent skill/MCP install skipped: ${(error as Error).message}`);\n }\n }\n\n if (options.watch) {\n console.error(`Watching ${root} for changes (Ctrl+C to stop) ...`);\n await watchAndRebuild(root, runOnce);\n }\n } catch (error) {\n console.error(`graphify: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('query <question>')\n .description('BFS (default) or DFS traversal answering a question against the existing graph')\n .option('--dfs', 'depth-first traversal (trace a specific path)')\n .option('--budget <tokens>', 'cap answer at N tokens', Number)\n .action(async (question: string, options: { dfs?: boolean; budget?: number }) => {\n try {\n await runQuery(question, options);\n } catch (error) {\n console.error(`graphify query: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('path <from> <to>')\n .description('shortest path between two named nodes')\n .action(async (from: string, to: string) => {\n try {\n await runPath(from, to);\n } catch (error) {\n console.error(`graphify path: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('explain <node>')\n .description('plain-language explanation of a node')\n .action(async (node: string) => {\n try {\n await runExplain(node);\n } catch (error) {\n console.error(`graphify explain: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('affected <node>')\n .description('reverse impact analysis — everything that (transitively) depends on a node')\n .option('--depth <n>', 'how many reverse hops to follow (default 3)', Number)\n .option('--limit <n>', 'cap on affected nodes reported (default 200)', Number)\n .action(async (node: string, options: { depth?: number; limit?: number }) => {\n try {\n await runAffected(node, options);\n } catch (error) {\n console.error(`graphify affected: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('context <task>')\n .description('token-budgeted working-set pack — the actual code for a task, in one call')\n .option('--budget <tokens>', 'approximate token cap for the pack (default 4000)', Number)\n .action(async (task: string, options: { budget?: number }) => {\n try {\n await runContext(task, options);\n } catch (error) {\n console.error(`graphify context: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('tests [node]')\n .description('structural test selection — the minimal test files worth running for a change')\n .option('--changed [rev]', 'select for the working-tree diff (optionally vs a revision) instead of a node')\n .action(async (node: string | undefined, options: { changed?: string | boolean }) => {\n try {\n await runTests(node, options);\n } catch (error) {\n console.error(`graphify tests: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('review <base> [head]')\n .description('structural review between two git revisions: added/removed/rewired symbols with blast radius + tests')\n .action(async (base: string, head?: string) => {\n try {\n await runReview(base, head);\n } catch (error) {\n console.error(`graphify review: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('check')\n .description('check the graph against dependency rules (graphify.rules.json) — exits 1 on violations')\n .option('--rules <file>', 'rules file (default ./graphify.rules.json)')\n .action(async (options: { rules?: string }) => {\n try {\n await runCheck(options);\n } catch (error) {\n console.error(`graphify check: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('benchmark [questions...]')\n .description('measure token savings of graph answers vs reading the files they touch')\n .action(async (questions: string[]) => {\n try {\n await runBenchmark(questions);\n } catch (error) {\n console.error(`graphify benchmark: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('install')\n .description('install the skill/rules files + project MCP config into local agents (claude, cursor, windsurf, cline, agents, gemini, all)')\n .option('--platform <names...>', 'platform(s) to install for', ['claude'])\n .action(async (options: { platform: string[] }) => {\n try {\n await runInstall(options.platform);\n } catch (error) {\n console.error(`graphify install: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nconst globalCommand = program\n .command('global')\n .description('manage the cross-project global graph (registry at ~/.graphify/global.json)');\n\nglobalCommand\n .command('add [path]')\n .description('register a project (must already have graphify-out/graph.json)')\n .action(async (target?: string) => {\n try {\n await runGlobalAdd(target);\n } catch (error) {\n console.error(`graphify global add: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nglobalCommand\n .command('remove <name>')\n .description('unregister a project by name')\n .action(async (name: string) => {\n try {\n await runGlobalRemove(name);\n } catch (error) {\n console.error(`graphify global remove: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nglobalCommand\n .command('list')\n .description('list registered projects')\n .action(async () => {\n try {\n await runGlobalList();\n } catch (error) {\n console.error(`graphify global list: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nglobalCommand\n .command('build')\n .description('merge every registered project into one global graph')\n .option('--out <dir>', 'output directory (default ~/.graphify/global-out)')\n .action(async (options: { out?: string }) => {\n try {\n await runGlobalBuild(options);\n } catch (error) {\n console.error(`graphify global build: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('merge <dirs...>')\n .description('one-shot merge of two or more built project graphs into one')\n .option('--out <dir>', 'output directory (default ./graphify-merged)')\n .action(async (dirs: string[], options: { out?: string }) => {\n try {\n await runMerge(dirs, options);\n } catch (error) {\n console.error(`graphify merge: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('save-result')\n .description('save a Q&A result to graphify-out/memory/ for the graph feedback loop')\n .requiredOption('--question <q>', 'the question that was asked')\n .requiredOption('--answer <a>', 'the answer that was given')\n .option('--nodes <ids...>', 'node ids/labels cited in the answer', [])\n .option('--type <t>', 'query|path|explain|affected', 'query')\n .option('--outcome <o>', 'useful|dead_end|corrected', 'useful')\n .option('--correction <text>', 'what the right answer was (pairs with --outcome corrected)')\n .action(async (options: {\n question: string;\n answer: string;\n nodes: string[];\n type: string;\n outcome: string;\n correction?: string;\n }) => {\n try {\n const memoryDir = path.join(process.cwd(), 'graphify-out', 'memory');\n const file = await saveResult(\n {\n question: options.question,\n answer: options.answer,\n nodes: options.nodes,\n type: options.type as ResultType,\n outcome: options.outcome as ResultOutcome,\n correction: options.correction,\n },\n memoryDir,\n );\n console.log(`Saved -> ${file}`);\n } catch (error) {\n console.error(`graphify save-result: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram\n .command('reflect')\n .description('aggregate graphify-out/memory/ into a recency-weighted lessons doc')\n .option('--half-life-days <n>', 'a result loses half its weight every N days (default 30)', Number)\n .option('--out <file>', 'output path (default graphify-out/reflections/LESSONS.md)')\n .action(async (options: { halfLifeDays?: number; out?: string }) => {\n try {\n const memoryDir = path.join(process.cwd(), 'graphify-out', 'memory');\n const results = await loadResults(memoryDir);\n const lessons = renderLessons(results, { halfLifeDays: options.halfLifeDays });\n const outPath = path.resolve(options.out ?? path.join(process.cwd(), 'graphify-out', 'reflections', 'LESSONS.md'));\n await fs.mkdir(path.dirname(outPath), { recursive: true });\n await fs.writeFile(outPath, lessons, 'utf-8');\n console.log(`Reflected ${results.length} result(s) -> ${outPath}`);\n } catch (error) {\n console.error(`graphify reflect: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nconst hookCommand = program\n .command('hook')\n .description('manage the git hooks that auto-rebuild the graph on commit/pull');\n\nhookCommand\n .command('install')\n .description('install post-commit and post-merge hooks (preserves existing hook content)')\n .action(async () => {\n try {\n await runHookInstall();\n } catch (error) {\n console.error(`graphify hook install: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nhookCommand\n .command('uninstall')\n .description('remove the graphify block from the git hooks')\n .action(async () => {\n try {\n await runHookUninstall();\n } catch (error) {\n console.error(`graphify hook uninstall: ${(error as Error).message}`);\n process.exitCode = 1;\n }\n });\n\nprogram.parseAsync(process.argv);\n","import { loadGraph } from '../../graphStore.js';\nimport { affectedBy } from '../../impact.js';\n\nexport interface AffectedOptions {\n depth?: number;\n limit?: number;\n}\n\n/** Reverse impact analysis: everything that (transitively) depends on the named node. */\nexport async function runAffected(nodeName: string, options: AffectedOptions = {}): Promise<void> {\n const graph = await loadGraph();\n const result = affectedBy(graph, nodeName, { maxDepth: options.depth, limit: options.limit });\n\n if (!result) {\n console.log(`No node matched \"${nodeName}\".`);\n return;\n }\n\n console.log(`Impact of changing ${result.target.label} (${result.target.id}):`);\n\n if (result.affected.length === 0) {\n console.log(' Nothing in the graph depends on it — no incoming dependency edges.');\n return;\n }\n\n let currentDepth = 0;\n for (const node of result.affected) {\n if (node.depth !== currentDepth) {\n currentDepth = node.depth;\n console.log('');\n console.log(currentDepth === 1 ? 'Directly affected:' : `Affected at depth ${currentDepth}:`);\n }\n const isPlaceholder =\n node.sourceFile === '<unknown>' || node.sourceFile === '<external>' || node.sourceFile === '';\n const where = isPlaceholder\n ? '(reference only)'\n : `${node.sourceFile}${node.sourceLocation ? `:${node.sourceLocation}` : ''}`;\n console.log(\n ` ${node.label} — ${where} ` +\n `(--${node.via.relation}--> ${node.via.dependsOn}, ${node.via.confidence})`,\n );\n }\n\n console.log('');\n const { EXTRACTED, INFERRED, AMBIGUOUS } = result.byConfidence;\n console.log(\n `Blast radius: ${result.affected.length} node(s) within ${result.maxDepth} hop(s) — ` +\n `${EXTRACTED} certain (EXTRACTED), ${INFERRED} likely (INFERRED), ${AMBIGUOUS} uncertain (AMBIGUOUS).`,\n );\n if (result.truncated) {\n console.log('(List truncated — raise --limit to see more.)');\n }\n}\n","import * as fs from 'node:fs/promises';\nimport type Graph from 'graphology';\nimport { loadGraph } from '../../graphStore.js';\nimport { queryGraph } from '../../query.js';\nimport { isTestFile } from '../../testmap.js';\n\n/**\n * `graphify benchmark` — measure the token savings of answering through the\n * graph instead of reading source files. For each question it compares the\n * serialized size of the graph answer against the size of every source file\n * that answer's nodes live in (the \"just read the relevant files\" baseline\n * an agent without the graph would pay). Chars/4 is the usual token\n * approximation, applied to both sides so the ratio is fair.\n */\n\nexport interface BenchmarkRow {\n question: string;\n graphTokens: number;\n naiveTokens: number;\n /** naive / graph — how many times cheaper the graph answer is. */\n reduction: number;\n filesRead: number;\n filesMissing: number;\n}\n\nexport type FileReader = (path: string) => Promise<string>;\n\nconst tokensOf = (text: string): number => Math.ceil(text.length / 4);\n\nexport async function benchmarkQuestion(\n graph: Graph,\n question: string,\n readFile: FileReader,\n): Promise<BenchmarkRow> {\n const result = queryGraph(graph, question);\n // Compact JSON, matching how queryGraph's own token budget measures itself.\n const graphTokens = tokensOf(JSON.stringify(result));\n\n const sourceFiles = new Set<string>();\n for (const node of result.visited) {\n if (node.sourceFile && !node.sourceFile.startsWith('<') && !node.sourceFile.includes('://')) {\n sourceFiles.add(node.sourceFile);\n }\n }\n\n let naiveTokens = 0;\n let filesRead = 0;\n let filesMissing = 0;\n for (const file of [...sourceFiles].sort((a, b) => a.localeCompare(b))) {\n try {\n naiveTokens += tokensOf(await readFile(file));\n filesRead++;\n } catch {\n filesMissing++;\n }\n }\n\n return {\n question,\n graphTokens,\n naiveTokens,\n reduction: graphTokens > 0 && naiveTokens > 0 ? naiveTokens / graphTokens : 0,\n filesRead,\n filesMissing,\n };\n}\n\n/** Files no agent would ask a \"how does X work\" question about — build config, examples, scripts, docs. */\nconst NON_QUESTION_FILE =\n /(^|\\/)(examples?|scripts?|bench(?:mark)?s?|fixtures?|docs?|\\.github)\\/|\\.(config|conf)\\.[^/]+$|(^|\\/)(rollup|webpack|vite|vitest|jest|babel|tsup|eslint|prettier)\\.[^/]*$/i;\n\n/**\n * Default questions when none are given: the labels of the highest-degree\n * (most load-bearing) SOURCE entities. Tests, build config, and examples\n * are excluded — they score high on degree but are questions no agent\n * would ask, and they drag the measured reduction down unfairly.\n */\nexport function defaultQuestions(graph: Graph, count = 5): string[] {\n const entries = [...graph.nodes()]\n .map((id) => ({\n id,\n degree: graph.degree(id),\n label: (graph.getNodeAttribute(id, 'label') as string | undefined) ?? id,\n file: (graph.getNodeAttribute(id, 'sourceFile') as string | undefined) ?? '',\n }))\n // File nodes score high on degree but make poor questions — prefer entities.\n .filter((e) => e.id.includes('::'))\n .filter((e) => !isTestFile(e.file) && !NON_QUESTION_FILE.test(e.file))\n .sort((a, b) => b.degree - a.degree || a.id.localeCompare(b.id));\n return entries.slice(0, count).map((e) => `how does ${e.label} work`);\n}\n\nexport async function runBenchmark(questions: string[]): Promise<BenchmarkRow[]> {\n const graph = await loadGraph();\n const effective = questions.length > 0 ? questions : defaultQuestions(graph);\n if (effective.length === 0) {\n console.log('The graph has no entity nodes to benchmark against — build it first.');\n return [];\n }\n\n const rows: BenchmarkRow[] = [];\n for (const question of effective) {\n rows.push(await benchmarkQuestion(graph, question, (p) => fs.readFile(p, 'utf-8')));\n }\n\n console.log('Token cost per question — graph answer vs reading the files it touches:\\n');\n for (const row of rows) {\n const reduction = row.reduction > 0 ? `${row.reduction.toFixed(1)}x` : 'n/a';\n console.log(` \"${row.question}\"`);\n console.log(\n ` graph: ~${row.graphTokens} tokens | files: ~${row.naiveTokens} tokens ` +\n `(${row.filesRead} file(s)${row.filesMissing ? `, ${row.filesMissing} unreadable` : ''}) | reduction: ${reduction}`,\n );\n }\n\n const scored = rows.filter((r) => r.reduction > 0);\n if (scored.length > 0) {\n const avg = scored.reduce((s, r) => s + r.reduction, 0) / scored.length;\n console.log(`\\nAverage reduction: ${avg.toFixed(1)}x across ${scored.length} question(s).`);\n } else {\n console.log('\\nNo reduction could be measured — run from the project root so source files are readable.');\n }\n return rows;\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { checkRules, validateRules } from '../../check.js';\nimport { loadGraph } from '../../graphStore.js';\n\nexport interface CheckCommandOptions {\n rules?: string;\n}\n\n/**\n * Check the graph against declared dependency rules. Exits non-zero on\n * violations so CI (and agents verifying their own edits) can gate on it.\n */\nexport async function runCheck(options: CheckCommandOptions = {}): Promise<void> {\n const rulesPath = path.resolve(options.rules ?? 'graphify.rules.json');\n let raw: string;\n try {\n raw = await fs.readFile(rulesPath, 'utf-8');\n } catch {\n throw new Error(\n `No rules file at ${rulesPath} — create graphify.rules.json with ` +\n '{ \"rules\": [{ \"name\": \"...\", \"from\": \"src/a/**\", \"disallow\": [\"src/b/**\"] }] }.',\n );\n }\n const config = validateRules(JSON.parse(raw));\n\n const graph = await loadGraph();\n const violations = checkRules(graph, config);\n\n if (violations.length === 0) {\n console.log(`OK — ${config.rules.length} rule(s), no violations.`);\n return;\n }\n\n console.log(`${violations.length} violation(s):`);\n for (const v of violations) {\n console.log(` [${v.rule}] ${v.fromNode} --${v.relation}--> ${v.toNode}`);\n console.log(` ${v.fromFile} must not depend on ${v.toFile}${v.reason ? ` — ${v.reason}` : ''}`);\n }\n process.exitCode = 1;\n}\n","import * as fs from 'node:fs/promises';\nimport { buildContextPack, renderContextPack } from '../../context.js';\nimport { loadGraph } from '../../graphStore.js';\n\nexport interface ContextCommandOptions {\n budget?: number;\n}\n\n/** Token-budgeted working-set pack: the actual code an agent needs for a task, in one call. */\nexport async function runContext(task: string, options: ContextCommandOptions = {}): Promise<void> {\n const graph = await loadGraph();\n const pack = await buildContextPack(graph, task, (p) => fs.readFile(p, 'utf-8'), {\n tokenBudget: options.budget,\n });\n console.log(renderContextPack(pack));\n}\n","import { loadGraph } from '../../graphStore.js';\nimport { explainNode } from '../../query.js';\n\n/** Plain-language explanation of a single node. */\nexport async function runExplain(nodeName: string): Promise<void> {\n const graph = await loadGraph();\n const result = explainNode(graph, nodeName);\n\n if (!result) {\n console.log(`No node matched \"${nodeName}\".`);\n return;\n }\n\n console.log(result.label);\n if (result.sourceFile === '<unknown>' || result.sourceFile === '<external>' || result.sourceFile === '') {\n // Auto-vivified endpoint (re-export alias / unresolved reference) — it\n // has no source of its own, so \"<unknown>:L0\" would read like a bug.\n console.log(' location: (reference only — no declaration seen; check its outgoing edges for the real definition)');\n } else {\n const location = result.sourceLocation ? `:${result.sourceLocation}` : '';\n console.log(` location: ${result.sourceFile}${location}`);\n }\n if (result.communityLabel) {\n console.log(` community: ${result.communityLabel}`);\n }\n\n console.log('');\n console.log(`Calls / references out (${result.outgoing.length}):`);\n if (result.outgoing.length === 0) {\n console.log(' (none)');\n } else {\n for (const edge of result.outgoing) {\n console.log(` --${edge.relation}--> ${edge.target} (${edge.confidence})`);\n }\n }\n\n console.log('');\n console.log(`Referenced by (${result.incoming.length}):`);\n if (result.incoming.length === 0) {\n console.log(' (none)');\n } else {\n for (const edge of result.incoming) {\n console.log(` ${edge.source} --${edge.relation}--> (${edge.confidence})`);\n }\n }\n}\n","import { renderReview, reviewRevisions } from '../../diff.js';\n\n/** Structural review between two git revisions (head defaults to HEAD). */\nexport async function runReview(base: string, head?: string): Promise<void> {\n const diff = await reviewRevisions(process.cwd(), base, head ?? 'HEAD');\n console.log(renderReview(diff));\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { loadGraph } from '../../graphStore.js';\nimport { testsForChangedFiles, testsForNode } from '../../testmap.js';\n\nconst execFileAsync = promisify(execFile);\n\nexport interface TestsCommandOptions {\n /** Select tests for the working-tree diff (optionally against a rev) instead of a named node. */\n changed?: string | boolean;\n}\n\n/** Structural test selection: the minimal test files worth running for a change. */\nexport async function runTests(nodeQuery: string | undefined, options: TestsCommandOptions = {}): Promise<void> {\n const graph = await loadGraph();\n\n let selection;\n if (options.changed !== undefined && options.changed !== false) {\n const args = ['diff', '--name-only'];\n if (typeof options.changed === 'string') args.push(options.changed);\n const { stdout } = await execFileAsync('git', args);\n const changedFiles = stdout.split('\\n').map((l) => l.trim()).filter(Boolean);\n if (changedFiles.length === 0) {\n console.log('No changed files in the diff.');\n return;\n }\n selection = testsForChangedFiles(graph, changedFiles);\n } else if (nodeQuery) {\n selection = testsForNode(graph, nodeQuery);\n if (!selection) {\n console.log(`No node matched \"${nodeQuery}\".`);\n return;\n }\n } else {\n console.log('Provide a node to select tests for, or --changed for the working-tree diff.');\n return;\n }\n\n if (selection.testFiles.length === 0) {\n console.log(\n `No test files found in the blast radius of ${selection.target} ` +\n `(${selection.affectedNodeCount} affected node(s) checked) — either it's untested or the tests ` +\n 'reach it through a path the graph does not capture.',\n );\n return;\n }\n\n const how =\n selection.precision === 'usage'\n ? 'selected by actual usage (calls/inheritance)'\n : 'selected by import reachability — may over-include files sharing an entry point';\n console.log(`Test files for ${selection.target} (${how}; most direct first):`);\n for (const test of selection.testFiles) {\n const via = test.depth === 0 ? test.via : `depth ${test.depth}, via ${test.via}`;\n console.log(` ${test.file} (${via})`);\n }\n}\n","import * as fs from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { analyze } from '../../analyze.js';\nimport { cluster } from '../../cluster.js';\nimport { exportGraph } from '../../export.js';\nimport { loadGraph } from '../../graphStore.js';\nimport { mergeGraphs, type MergeEntry } from '../../merge.js';\nimport { renderReport } from '../../report.js';\n\n/**\n * `graphify global add|remove|list|build` — a registry of projects whose\n * built graphs get merged into one global cross-project graph, plus the\n * one-shot `graphify merge <dir...>` for merging without a registry.\n * The registry lives at ~/.graphify/global.json; the merged output defaults\n * to ~/.graphify/global-out/.\n */\n\nexport interface GlobalRegistry {\n projects: Array<{ name: string; root: string }>;\n}\n\nfunction registryDir(): string {\n return path.join(os.homedir(), '.graphify');\n}\n\nfunction registryPath(): string {\n return path.join(registryDir(), 'global.json');\n}\n\nasync function readRegistry(): Promise<GlobalRegistry> {\n try {\n const raw = await fs.readFile(registryPath(), 'utf-8');\n const parsed: unknown = JSON.parse(raw);\n const projects = (parsed as GlobalRegistry).projects;\n if (!Array.isArray(projects)) return { projects: [] };\n return { projects: projects.filter((p) => typeof p?.name === 'string' && typeof p?.root === 'string') };\n } catch {\n return { projects: [] };\n }\n}\n\nasync function writeRegistry(registry: GlobalRegistry): Promise<void> {\n await fs.mkdir(registryDir(), { recursive: true });\n await fs.writeFile(registryPath(), JSON.stringify(registry, null, 2), 'utf-8');\n}\n\n/** Project name for a root: its package.json \"name\" if present, else the directory basename. */\nasync function projectNameFor(root: string): Promise<string> {\n try {\n const raw = await fs.readFile(path.join(root, 'package.json'), 'utf-8');\n const name = (JSON.parse(raw) as { name?: unknown }).name;\n if (typeof name === 'string' && name.length > 0) return name;\n } catch {\n // no package.json — fall through to the basename\n }\n return path.basename(root);\n}\n\nexport async function runGlobalAdd(target: string = '.'): Promise<void> {\n const root = path.resolve(target);\n const graphJson = path.join(root, 'graphify-out', 'graph.json');\n try {\n await fs.access(graphJson);\n } catch {\n throw new Error(`${root} has no graphify-out/graph.json — run \\`graphify ${target}\\` first, then add it.`);\n }\n\n const name = await projectNameFor(root);\n const registry = await readRegistry();\n\n const existing = registry.projects.find((p) => p.name === name);\n if (existing) {\n if (existing.root === root) {\n console.log(`${name} is already registered (${root}).`);\n return;\n }\n throw new Error(\n `A different project is already registered as \"${name}\" (${existing.root}). ` +\n 'Remove it first with `graphify global remove`.',\n );\n }\n\n registry.projects.push({ name, root });\n registry.projects.sort((a, b) => a.name.localeCompare(b.name));\n await writeRegistry(registry);\n console.log(`Registered ${name} (${root}). ${registry.projects.length} project(s) in the global graph.`);\n}\n\nexport async function runGlobalRemove(name: string): Promise<void> {\n const registry = await readRegistry();\n const before = registry.projects.length;\n registry.projects = registry.projects.filter((p) => p.name !== name);\n if (registry.projects.length === before) {\n throw new Error(`No project named \"${name}\" is registered. See \\`graphify global list\\`.`);\n }\n await writeRegistry(registry);\n console.log(`Removed ${name}. ${registry.projects.length} project(s) remain.`);\n}\n\nexport async function runGlobalList(): Promise<void> {\n const registry = await readRegistry();\n if (registry.projects.length === 0) {\n console.log('No projects registered. Add one with `graphify global add <path>`.');\n return;\n }\n for (const project of registry.projects) {\n console.log(`${project.name} ${project.root}`);\n }\n}\n\n/** Load every entry's graph, skipping (with a warning) the ones that fail — one broken project must not sink the merge. */\nasync function loadEntries(sources: Array<{ name: string; outDir: string }>): Promise<MergeEntry[]> {\n const entries: MergeEntry[] = [];\n for (const source of sources) {\n try {\n entries.push({ name: source.name, graph: await loadGraph(source.outDir) });\n } catch (error) {\n console.warn(`Skipping ${source.name}: ${(error as Error).message}`);\n }\n }\n return entries;\n}\n\nasync function mergeAndExport(entries: MergeEntry[], outDir: string): Promise<void> {\n if (entries.length === 0) {\n throw new Error('Nothing to merge — no project graphs could be loaded.');\n }\n\n const merged = mergeGraphs(entries);\n cluster(merged);\n const analysis = analyze(merged);\n const report = renderReport(merged, analysis);\n await exportGraph(merged, { outDir }, report);\n\n console.log(\n `Merged ${entries.length} project(s) into ${outDir} — ` +\n `${merged.order} node(s), ${merged.size} edge(s).`,\n );\n}\n\nexport async function runGlobalBuild(options: { out?: string } = {}): Promise<void> {\n const registry = await readRegistry();\n if (registry.projects.length === 0) {\n throw new Error('No projects registered. Add some with `graphify global add <path>` first.');\n }\n\n const entries = await loadEntries(\n registry.projects.map((p) => ({ name: p.name, outDir: path.join(p.root, 'graphify-out') })),\n );\n await mergeAndExport(entries, path.resolve(options.out ?? path.join(registryDir(), 'global-out')));\n}\n\n/** One-shot merge of two or more project directories, no registry involved. */\nexport async function runMerge(targets: string[], options: { out?: string } = {}): Promise<void> {\n if (targets.length < 2) {\n throw new Error('Provide at least two project directories to merge.');\n }\n\n const sources: Array<{ name: string; outDir: string }> = [];\n for (const target of targets) {\n const root = path.resolve(target);\n sources.push({ name: await projectNameFor(root), outDir: path.join(root, 'graphify-out') });\n }\n\n const names = sources.map((s) => s.name);\n const duplicate = names.find((name, i) => names.indexOf(name) !== i);\n if (duplicate !== undefined) {\n throw new Error(`Two of the given projects resolve to the same name (\"${duplicate}\") — rename one.`);\n }\n\n const entries = await loadEntries(sources);\n await mergeAndExport(entries, path.resolve(options.out ?? 'graphify-merged'));\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\n/**\n * `graphify hook install` / `graphify hook uninstall` — auto-rebuild the\n * graph after commits and pulls, mirroring the Python original's\n * `hook install`. The hook body lives inside a marked block so we can\n * coexist with (and later cleanly remove ourselves from) hooks the user\n * already has; we never overwrite or delete anything outside our markers.\n */\n\nconst MARKER_BEGIN = '# >>> graphify >>>';\nconst MARKER_END = '# <<< graphify <<<';\n\n/** post-commit: rebuild after each commit. post-merge: rebuild after pull/merge. */\nconst HOOK_NAMES = ['post-commit', 'post-merge'] as const;\n\nconst HOOK_BLOCK = `${MARKER_BEGIN}\n# Rebuild the graphify knowledge graph in the background so the hook never\n# slows down the commit. Errors are silenced — a failed rebuild should never\n# break a git operation. Installed by \\`graphify hook install\\`.\n(graphify . --update --no-viz >/dev/null 2>&1 &)\n${MARKER_END}`;\n\nexport interface HookResult {\n hook: string;\n path: string;\n action: 'created' | 'appended' | 'already-installed' | 'removed' | 'not-installed';\n}\n\nasync function hooksDir(cwd: string): Promise<string> {\n const gitDir = path.join(cwd, '.git');\n let stats;\n try {\n stats = await fs.stat(gitDir);\n } catch {\n throw new Error(`${cwd} is not a git repository (no .git directory) — run this from the repo root.`);\n }\n if (!stats.isDirectory()) {\n // .git can be a file in worktrees/submodules (\"gitdir: <path>\") — follow it.\n const content = (await fs.readFile(gitDir, 'utf-8')).trim();\n const match = /^gitdir:\\s*(.+)$/.exec(content);\n if (!match) throw new Error(`${gitDir} exists but is neither a directory nor a gitdir pointer.`);\n return path.resolve(cwd, match[1] as string, 'hooks');\n }\n return path.join(gitDir, 'hooks');\n}\n\n/** Install the marked graphify block into post-commit and post-merge, preserving any existing hook content. */\nexport async function runHookInstall(cwd: string = process.cwd()): Promise<HookResult[]> {\n const dir = await hooksDir(cwd);\n await fs.mkdir(dir, { recursive: true });\n\n const results: HookResult[] = [];\n for (const hook of HOOK_NAMES) {\n const hookPath = path.join(dir, hook);\n let existing: string | null = null;\n try {\n existing = await fs.readFile(hookPath, 'utf-8');\n } catch {\n // no existing hook — we'll create it\n }\n\n if (existing === null) {\n await fs.writeFile(hookPath, `#!/bin/sh\\n${HOOK_BLOCK}\\n`, { mode: 0o755 });\n results.push({ hook, path: hookPath, action: 'created' });\n } else if (existing.includes(MARKER_BEGIN)) {\n results.push({ hook, path: hookPath, action: 'already-installed' });\n } else {\n const separator = existing.endsWith('\\n') ? '' : '\\n';\n await fs.writeFile(hookPath, `${existing}${separator}${HOOK_BLOCK}\\n`, 'utf-8');\n await fs.chmod(hookPath, 0o755);\n results.push({ hook, path: hookPath, action: 'appended' });\n }\n }\n\n for (const result of results) {\n console.log(`${result.hook}: ${result.action} (${result.path})`);\n }\n console.log('');\n console.log('The graph will rebuild in the background after each commit and pull.');\n console.log('Remove with `graphify hook uninstall`.');\n return results;\n}\n\n/** Remove only the marked graphify block; delete the hook file if nothing else is left in it. */\nexport async function runHookUninstall(cwd: string = process.cwd()): Promise<HookResult[]> {\n const dir = await hooksDir(cwd);\n\n const results: HookResult[] = [];\n for (const hook of HOOK_NAMES) {\n const hookPath = path.join(dir, hook);\n let existing: string | null = null;\n try {\n existing = await fs.readFile(hookPath, 'utf-8');\n } catch {\n // nothing installed\n }\n\n if (existing === null || !existing.includes(MARKER_BEGIN)) {\n results.push({ hook, path: hookPath, action: 'not-installed' });\n continue;\n }\n\n const blockPattern = new RegExp(`\\\\n?${MARKER_BEGIN}[\\\\s\\\\S]*?${MARKER_END}\\\\n?`);\n const remaining = existing.replace(blockPattern, '\\n').trim();\n\n if (remaining === '' || remaining === '#!/bin/sh') {\n await fs.unlink(hookPath);\n } else {\n await fs.writeFile(hookPath, `${remaining}\\n`, 'utf-8');\n }\n results.push({ hook, path: hookPath, action: 'removed' });\n }\n\n for (const result of results) {\n console.log(`${result.hook}: ${result.action}`);\n }\n return results;\n}\n","import { createRequire } from 'node:module';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\n\nconst require = createRequire(import.meta.url);\n\n/**\n * Resolve this package's own root directory, whether we're running from\n * source (tests/dev), the built dist/ bundle, or installed as a real\n * dependency elsewhere. Uses Node's self-reference resolution (walking up\n * from this module's location to the nearest package.json named\n * \"@dreamtree-org/graphify\") rather than a hardcoded relative path, since\n * the number of directories between this file and the package root\n * differs between src/ and the bundled dist/ output.\n */\nfunction packageRoot(): string {\n const packageJsonPath = require.resolve('@dreamtree-org/graphify/package.json');\n return path.dirname(packageJsonPath);\n}\n\nexport interface InstallResult {\n host: string;\n path: string;\n /** Project-scoped MCP config file this install registered the graphify server in, if the host supports one. */\n mcpPath?: string;\n}\n\n/**\n * The MCP server entry written into project-scoped MCP configs. `npx -y`\n * resolves a locally installed @dreamtree-org/graphify first and falls back\n * to fetching it, so the config works both for the person who ran the\n * install and for teammates who check the file in without installing\n * anything. MCP hosts launch project servers with cwd = project root, so\n * the default graph path (./graphify-out/graph.json) resolves correctly.\n */\nconst MCP_SERVER_ENTRY = {\n command: 'npx',\n args: ['-y', '@dreamtree-org/graphify', '--mcp'],\n};\n\n/**\n * Register the graphify MCP server under `serversKey` in a JSON config\n * file, creating the file if needed and preserving every existing key\n * (other servers, unrelated settings). Idempotent: re-running replaces the\n * graphify entry only. Refuses to touch a file it cannot parse rather than\n * clobbering user config.\n */\nasync function upsertMcpServer(filePath: string, serversKey = 'mcpServers'): Promise<string> {\n let config: Record<string, unknown> = {};\n try {\n config = JSON.parse(await fs.readFile(filePath, 'utf-8')) as Record<string, unknown>;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw new Error(`cannot update ${filePath} — it exists but is not valid JSON; fix or remove it and re-run.`, {\n cause: error,\n });\n }\n }\n\n const servers = (config[serversKey] ?? {}) as Record<string, unknown>;\n servers['graphify'] = MCP_SERVER_ENTRY;\n config[serversKey] = servers;\n\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(filePath, `${JSON.stringify(config, null, 2)}\\n`, 'utf-8');\n return filePath;\n}\n\n/** Markers for hosts where we append into a shared file (AGENTS.md / GEMINI.md) instead of owning one. */\nconst MD_MARKER_BEGIN = '<!-- >>> graphify >>> -->';\nconst MD_MARKER_END = '<!-- <<< graphify <<< -->';\n\nasync function writeOwnedFile(filePath: string, content: string): Promise<void> {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(filePath, content, 'utf-8');\n}\n\n/** Create `filePath` with the marked block, or replace/append the block in an existing file — user content is never touched. */\nasync function upsertMarkedBlock(filePath: string, content: string): Promise<void> {\n const block = `${MD_MARKER_BEGIN}\\n${content.trim()}\\n${MD_MARKER_END}`;\n let existing: string | null = null;\n try {\n existing = await fs.readFile(filePath, 'utf-8');\n } catch {\n // file doesn't exist yet\n }\n\n if (existing === null) {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(filePath, `${block}\\n`, 'utf-8');\n return;\n }\n if (existing.includes(MD_MARKER_BEGIN)) {\n const pattern = new RegExp(`${MD_MARKER_BEGIN}[\\\\s\\\\S]*?${MD_MARKER_END}`);\n await fs.writeFile(filePath, existing.replace(pattern, block), 'utf-8');\n return;\n }\n const separator = existing.endsWith('\\n') ? '\\n' : '\\n\\n';\n await fs.writeFile(filePath, `${existing}${separator}${block}\\n`, 'utf-8');\n}\n\ntype PlatformInstaller = (cwd: string, skillContent: string) => Promise<InstallResult>;\n\n/**\n * Curated per-host installers. `claude` gets the native skill layout; hosts\n * with a project rules directory (cursor/windsurf/cline) get their own\n * rules file; hosts that read a single shared instructions file\n * (AGENTS.md-based agents like Codex/opencode/Amp, and Gemini CLI's\n * GEMINI.md) get an idempotent marked block appended.\n */\nconst PLATFORMS: Record<string, PlatformInstaller> = {\n claude: async (cwd, content) => {\n const target = path.join(cwd, '.claude', 'skills', 'graphify', 'SKILL.md');\n await writeOwnedFile(target, content);\n const mcpPath = await upsertMcpServer(path.join(cwd, '.mcp.json'));\n return { host: 'Claude Code', path: target, mcpPath };\n },\n cursor: async (cwd, content) => {\n const target = path.join(cwd, '.cursor', 'rules', 'graphify.mdc');\n const body = content.replace(/^---[\\s\\S]*?---\\n/, ''); // strip the skill frontmatter\n await writeOwnedFile(\n target,\n `---\\ndescription: Query the graphify knowledge graph for codebase structure questions\\nalwaysApply: false\\n---\\n${body}`,\n );\n const mcpPath = await upsertMcpServer(path.join(cwd, '.cursor', 'mcp.json'));\n return { host: 'Cursor', path: target, mcpPath };\n },\n windsurf: async (cwd, content) => {\n const target = path.join(cwd, '.windsurf', 'rules', 'graphify.md');\n await writeOwnedFile(target, content.replace(/^---[\\s\\S]*?---\\n/, ''));\n return { host: 'Windsurf', path: target };\n },\n cline: async (cwd, content) => {\n const target = path.join(cwd, '.clinerules', 'graphify.md');\n await writeOwnedFile(target, content.replace(/^---[\\s\\S]*?---\\n/, ''));\n return { host: 'Cline', path: target };\n },\n agents: async (cwd, content) => {\n const target = path.join(cwd, 'AGENTS.md');\n await upsertMarkedBlock(target, content.replace(/^---[\\s\\S]*?---\\n/, ''));\n return { host: 'AGENTS.md agents (Codex, opencode, Amp, ...)', path: target };\n },\n gemini: async (cwd, content) => {\n const target = path.join(cwd, 'GEMINI.md');\n await upsertMarkedBlock(target, content.replace(/^---[\\s\\S]*?---\\n/, ''));\n const mcpPath = await upsertMcpServer(path.join(cwd, '.gemini', 'settings.json'));\n return { host: 'Gemini CLI', path: target, mcpPath };\n },\n};\n\nexport const SUPPORTED_PLATFORMS = Object.keys(PLATFORMS).sort((a, b) => a.localeCompare(b));\n\n/** Filesystem markers that indicate a given agent host is in use in a project. */\nconst PLATFORM_MARKERS: Record<string, string[]> = {\n cursor: ['.cursor'],\n windsurf: ['.windsurf'],\n cline: ['.clinerules'],\n agents: ['AGENTS.md'],\n gemini: ['GEMINI.md', '.gemini'],\n};\n\n/**\n * Detect which agent hosts a project uses so the default pipeline run can\n * install for them automatically. Claude Code is always included — it picks\n * up `.claude/skills/` and `.mcp.json` from any project with zero prior\n * setup, so installing it is what makes `npx @dreamtree-org/graphify .`\n * work \"instantly\"; the other hosts are added only when their config\n * dir/file already exists.\n */\nexport async function detectPlatforms(cwd: string = process.cwd()): Promise<string[]> {\n const platforms = ['claude'];\n for (const [platform, markers] of Object.entries(PLATFORM_MARKERS)) {\n for (const marker of markers) {\n try {\n await fs.access(path.join(cwd, marker));\n platforms.push(platform);\n break;\n } catch {\n // marker absent — not this host\n }\n }\n }\n return platforms;\n}\n\n/**\n * Install the graphify skill/rules for the requested platforms (default:\n * claude). `all` installs every supported platform.\n */\nexport async function runInstall(\n platforms: string[] = ['claude'],\n cwd: string = process.cwd(),\n): Promise<InstallResult[]> {\n const sourcePath = path.join(packageRoot(), 'src', 'skill', 'SKILL.md');\n const content = await fs.readFile(sourcePath, 'utf-8');\n\n const requested = platforms.includes('all') ? SUPPORTED_PLATFORMS : platforms;\n const results: InstallResult[] = [];\n for (const platform of requested) {\n const installer = PLATFORMS[platform];\n if (!installer) {\n throw new Error(\n `Unknown platform \"${platform}\" — supported: ${SUPPORTED_PLATFORMS.join(', ')}, or \"all\".`,\n );\n }\n results.push(await installer(cwd, content));\n }\n\n for (const result of results) {\n const mcpNote = result.mcpPath ? ` (MCP server registered in ${result.mcpPath})` : '';\n console.log(`Installed graphify for ${result.host} -> ${result.path}${mcpNote}`);\n }\n return results;\n}\n","import { loadGraph } from '../../graphStore.js';\nimport { shortestPath } from '../../query.js';\n\n/** Shortest path between two named nodes. */\nexport async function runPath(fromNode: string, toNode: string): Promise<void> {\n const graph = await loadGraph();\n const result = shortestPath(graph, fromNode, toNode);\n\n if (!result) {\n console.log(`Could not resolve \"${fromNode}\" and/or \"${toNode}\" to a node in the graph.`);\n return;\n }\n\n if (!result.found) {\n console.log(\n `No path found between \"${result.from.label}\" and \"${result.to.label}\" — they may be in ` +\n 'disconnected parts of the graph (see GRAPH_REPORT.md § Open Questions).',\n );\n return;\n }\n\n console.log(`${result.from.label} -> ${result.to.label} (${result.path.length} node(s)):`);\n console.log(` ${result.path.join(' -> ')}`);\n if (result.edges.length > 0) {\n console.log('');\n console.log('Edges along the path:');\n for (const edge of result.edges) {\n console.log(` ${edge.source} --${edge.relation}--> ${edge.target} (${edge.confidence})`);\n }\n }\n}\n","import { loadGraph } from '../../graphStore.js';\nimport { queryGraph } from '../../query.js';\n\nexport interface QueryOptions {\n dfs?: boolean;\n /** Approximate token cap for the printed context (default 2000). */\n budget?: number;\n}\n\n/** BFS (default) or DFS traversal answering a natural-language question against the graph. */\nexport async function runQuery(question: string, options: QueryOptions = {}): Promise<void> {\n const graph = await loadGraph();\n const result = queryGraph(graph, question, { dfs: options.dfs, tokenBudget: options.budget });\n\n if (result.seeds.length === 0) {\n console.log(\n `No nodes matched \"${question}\". Try different keywords, or run \\`graphify explain <node>\\` ` +\n 'if you already know the name.',\n );\n return;\n }\n\n console.log(`Matched ${result.seeds.length} seed node(s):`);\n for (const seed of result.seeds) {\n console.log(` - ${seed.label} (${seed.id})`);\n }\n\n console.log('');\n console.log(`Context — ${result.visited.length} node(s), ${options.dfs ? 'DFS' : 'BFS'} traversal:`);\n for (const node of result.visited) {\n const location = node.sourceLocation ? `:${node.sourceLocation}` : '';\n console.log(` [depth ${node.depth}] ${node.label} — ${node.sourceFile}${location}`);\n }\n\n console.log('');\n console.log(`Edges within this context (${result.edges.length}):`);\n for (const edge of result.edges) {\n console.log(` ${edge.source} --${edge.relation}--> ${edge.target} (${edge.confidence})`);\n }\n}\n"],"mappings":"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","names":["execFile","fs","os","path","promisify","readFile","fs","fs","fs","path","fs","path","fs","path","require","execFileAsync","promisify","execFile"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreamtree-org/graphify",
3
- "version": "1.1.3",
3
+ "version": "1.2.0",
4
4
  "description": "Turn a folder of code/docs/papers into a queryable, persistent knowledge graph for AI coding assistants.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -59,7 +59,10 @@ graphify benchmark [q...] # measure token savings of graph answers vs re
59
59
  graphify save-result --question Q --answer A --nodes id... --outcome useful|dead_end|corrected
60
60
  # log which nodes answered — feeds the graph feedback loop
61
61
  graphify reflect # aggregate saved results into graphify-out/reflections/LESSONS.md
62
- graphify install [--platform p] # (re-)install this skill (claude|cursor|windsurf|cline|agents|gemini|all)
62
+ graphify install [--platform p] # (re-)install this skill + project MCP config
63
+ # (claude|cursor|windsurf|cline|agents|gemini|all); a plain
64
+ # `graphify .` run already does this for detected hosts
65
+ # (opt out with --no-install)
63
66
 
64
67
  graphify hook install|uninstall # auto-rebuild the graph after each commit/pull
65
68
  graphify global add|remove|list|build # cross-project global graph (registry in ~/.graphify)
@@ -104,6 +107,8 @@ graphify merge <dirA> <dirB> ... # one-shot merge of built project graphs (--ou
104
107
  based and deterministic. You (the agent) are the semantic layer on top of its output.
105
108
  - `graphify` never executes source code and never shells out with a string built from file
106
109
  content — see SECURITY.md if you need to reason about its threat model.
107
- - If an MCP server is preferred over shelling out, run `graphify <path> --mcp` and use its
108
- `query`/`path`/`explain`/`affected` tools instead of the CLI subcommands above — same
109
- underlying logic, tool-call interface instead of stdout.
110
+ - If an MCP server is preferred over shelling out, check the project MCP config first — the
111
+ pipeline run registers a `graphify` stdio server (in `.mcp.json` / `.cursor/mcp.json` /
112
+ `.gemini/settings.json`), so its `context`/`query`/`path`/`explain`/`affected`/`tests` tools
113
+ may already be available. Otherwise run `graphify <path> --mcp` — same underlying logic,
114
+ tool-call interface instead of stdout.