@wwjd/pi-graphify 0.1.0 → 0.3.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.
@@ -0,0 +1,47 @@
1
+ import type { GraphifyBackend, GraphifyCapabilities, GraphifyResult } from "./types.js";
2
+
3
+ export const allCapabilitiesEnabled = (): GraphifyCapabilities => ({
4
+ build: true,
5
+ query: true,
6
+ path: true,
7
+ explain: true,
8
+ affected: true,
9
+ add: true,
10
+ watch: true,
11
+ reflect: true,
12
+ mcp: true,
13
+ status: true,
14
+ version: true,
15
+ directedGraph: true,
16
+ incrementalUpdate: true,
17
+ semanticExtraction: true,
18
+ });
19
+
20
+ export interface MockBackendOptions {
21
+ version?: string;
22
+ capabilities?: GraphifyCapabilities;
23
+ run?: (operation: string, options: unknown) => Promise<GraphifyResult>;
24
+ }
25
+
26
+ export function createMockBackend(options: MockBackendOptions = {}): GraphifyBackend {
27
+ const {
28
+ version = "2.100.0",
29
+ capabilities = allCapabilitiesEnabled(),
30
+ run = async (operation) => ({
31
+ stdout: `ok: ${operation}`,
32
+ stderr: "",
33
+ exitCode: 0,
34
+ durationMs: 1,
35
+ backend: "cli",
36
+ feature: operation,
37
+ }),
38
+ } = options;
39
+
40
+ return {
41
+ type: "cli",
42
+ version,
43
+ capabilities,
44
+ run,
45
+ close: async () => {},
46
+ };
47
+ }
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Backend abstraction types for pi-graphify.
3
+ *
4
+ * All Graphify operations go through the `GraphifyBackend` interface so the
5
+ * rest of the extension is agnostic to whether the CLI or MCP is used.
6
+ */
7
+
8
+ export interface GraphifyCapabilities {
9
+ /** Core graph build. */
10
+ build: boolean;
11
+ /** Query the graph. */
12
+ query: boolean;
13
+ /** Shortest path between nodes. */
14
+ path: boolean;
15
+ /** Explain a node or relationship. */
16
+ explain: boolean;
17
+ /** Affected nodes/files. */
18
+ affected: boolean;
19
+ /** Add content to the graph. */
20
+ add: boolean;
21
+ /** Watch source files for changes. */
22
+ watch: boolean;
23
+ /** Reflect on graph structure. */
24
+ reflect: boolean;
25
+ /** MCP server support. */
26
+ mcp: boolean;
27
+ /** Graph status check. */
28
+ status: boolean;
29
+ /** Version reporting. */
30
+ version: boolean;
31
+ /** Directed graph support. */
32
+ directedGraph: boolean;
33
+ /** Incremental code-only updates. */
34
+ incrementalUpdate: boolean;
35
+ /** Semantic extraction beyond code. */
36
+ semanticExtraction: boolean;
37
+ }
38
+
39
+ /** Normalized result from any Graphify backend. */
40
+ export interface GraphifyResult {
41
+ stdout: string;
42
+ stderr: string;
43
+ exitCode: number;
44
+ durationMs: number;
45
+ backend: "cli" | "mcp";
46
+ feature: string;
47
+ }
48
+
49
+ /** Backend-agnostic interface for all Graphify operations. */
50
+ export interface GraphifyBackend {
51
+ readonly type: "cli" | "mcp";
52
+ readonly version: string | null;
53
+ readonly capabilities: GraphifyCapabilities;
54
+
55
+ /**
56
+ * Run a Graphify operation.
57
+ *
58
+ * @param operation - The operation name, e.g. `"build"`, `"query"`, `"status"`.
59
+ * @param options - Operation-specific options.
60
+ */
61
+ run(operation: string, options: unknown): Promise<GraphifyResult>;
62
+
63
+ /** Optional cleanup hook called on session shutdown. */
64
+ close?(): Promise<void>;
65
+ }
66
+
67
+ /** Options for the `status` operation. */
68
+ export interface StatusOptions {
69
+ cwd: string;
70
+ }
71
+
72
+ /** Options for the `version` operation. */
73
+ export interface VersionOptions {
74
+ cwd?: string;
75
+ }
76
+
77
+ /** Options for the `build` operation. */
78
+ export interface BuildOptions {
79
+ cwd: string;
80
+ codeOnly?: boolean;
81
+ update?: boolean;
82
+ directed?: boolean;
83
+ }
84
+
85
+ /** Options for the `query` operation. */
86
+ export interface QueryOptions {
87
+ cwd: string;
88
+ question: string;
89
+ }
90
+
91
+ /** Options for the `path` operation. */
92
+ export interface PathOptions {
93
+ cwd: string;
94
+ source: string;
95
+ target: string;
96
+ }
97
+
98
+ /** Options for the `explain` operation. */
99
+ export interface ExplainOptions {
100
+ cwd: string;
101
+ node: string;
102
+ }
103
+
104
+ /** Options for the `affected` operation. */
105
+ export interface AffectedOptions {
106
+ cwd: string;
107
+ files: string[];
108
+ }
109
+
110
+ /** Options for the `add` operation. */
111
+ export interface AddOptions {
112
+ cwd: string;
113
+ paths: string[];
114
+ }
115
+
116
+ /** Options for the `watch` operation. */
117
+ export interface WatchOptions {
118
+ cwd: string;
119
+ codeOnly?: boolean;
120
+ }
121
+
122
+ /** Options for the `reflect` operation. */
123
+ export interface ReflectOptions {
124
+ cwd: string;
125
+ question?: string;
126
+ }
127
+
128
+ /** All operation option types unioned together. */
129
+ export type GraphifyOperationOptions =
130
+ | StatusOptions
131
+ | VersionOptions
132
+ | BuildOptions
133
+ | QueryOptions
134
+ | PathOptions
135
+ | ExplainOptions
136
+ | AffectedOptions
137
+ | AddOptions
138
+ | WatchOptions
139
+ | ReflectOptions;
140
+
141
+ export function allCapabilitiesDisabled(): GraphifyCapabilities {
142
+ return {
143
+ build: false,
144
+ query: false,
145
+ path: false,
146
+ explain: false,
147
+ affected: false,
148
+ add: false,
149
+ watch: false,
150
+ reflect: false,
151
+ mcp: false,
152
+ status: false,
153
+ version: false,
154
+ directedGraph: false,
155
+ incrementalUpdate: false,
156
+ semanticExtraction: false,
157
+ };
158
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * GraphifyCoordinator — the single entry point for all Graphify operations.
3
+ *
4
+ * The coordinator loads configuration, selects a backend, detects the installed
5
+ * Graphify version, builds a capability matrix, and routes tool/command
6
+ * requests to the backend. It also normalizes backend results and errors.
7
+ */
8
+
9
+ import { existsSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { createCliBackend } from "./backends/cli.js";
12
+ import {
13
+ type AddOptions,
14
+ type AffectedOptions,
15
+ allCapabilitiesDisabled,
16
+ type BuildOptions,
17
+ type ExplainOptions,
18
+ type GraphifyBackend,
19
+ type GraphifyCapabilities,
20
+ type GraphifyResult,
21
+ type PathOptions,
22
+ type QueryOptions,
23
+ type ReflectOptions,
24
+ type StatusOptions,
25
+ type VersionOptions,
26
+ type WatchOptions,
27
+ } from "./backends/types.js";
28
+ import { type GraphifyConfig, loadConfig } from "./config.js";
29
+ import { codeForOperation, GraphifyError, messageForCliFailure } from "./errors.js";
30
+
31
+ export interface GraphifyStatusResult {
32
+ hasGraph: boolean;
33
+ graphPath?: string;
34
+ backendVersion: string | null;
35
+ }
36
+
37
+ export interface CoordinatorOptions {
38
+ cwd: string;
39
+ config?: GraphifyConfig;
40
+ /** Optional backend, primarily for tests. */
41
+ backend?: GraphifyBackend;
42
+ }
43
+
44
+ export class GraphifyCoordinator {
45
+ readonly config: GraphifyConfig;
46
+ readonly cwd: string;
47
+ private backend: GraphifyBackend | null = null;
48
+ private readonly _warnings: string[] = [];
49
+ private closed = false;
50
+
51
+ constructor(options: CoordinatorOptions) {
52
+ this.cwd = options.cwd;
53
+ this.config = options.config ?? loadConfig();
54
+ this.backend = options.backend ?? null;
55
+ }
56
+
57
+ async initialize(): Promise<void> {
58
+ if (this.closed) {
59
+ throw new GraphifyError("UNKNOWN", "Coordinator has already been closed");
60
+ }
61
+
62
+ try {
63
+ if (!this.backend) {
64
+ // Phase 2 only supports CLI backend. MCP is a Phase 6 addition.
65
+ this.backend = await createCliBackend({
66
+ cwd: this.cwd,
67
+ timeoutMs: this.config.queryTimeoutMs,
68
+ });
69
+ }
70
+ } catch (error) {
71
+ this.backend = null;
72
+ this._warnings.push(
73
+ error instanceof Error ? error.message : "Graphify backend failed to initialize",
74
+ );
75
+ }
76
+ }
77
+
78
+ get version(): string | null {
79
+ return this.backend?.version ?? null;
80
+ }
81
+
82
+ get capabilities(): GraphifyCapabilities {
83
+ return this.backend?.capabilities ?? allCapabilitiesDisabled();
84
+ }
85
+
86
+ get warnings(): readonly string[] {
87
+ return this._warnings;
88
+ }
89
+
90
+ supports(feature: keyof GraphifyCapabilities): boolean {
91
+ return this.capabilities[feature] ?? false;
92
+ }
93
+
94
+ async status(_options: StatusOptions): Promise<GraphifyStatusResult> {
95
+ const graphPath = join(this.cwd, "graphify-out", "graph.json");
96
+ return {
97
+ hasGraph: existsSync(graphPath),
98
+ graphPath,
99
+ backendVersion: this.version,
100
+ };
101
+ }
102
+
103
+ async getVersion(_options?: VersionOptions): Promise<GraphifyResult> {
104
+ if (!this.backend) {
105
+ throw new GraphifyError("MISSING", "Graphify is not installed or not available on PATH.");
106
+ }
107
+ return {
108
+ stdout: this.version ?? "unknown",
109
+ stderr: "",
110
+ exitCode: 0,
111
+ durationMs: 0,
112
+ backend: "cli",
113
+ feature: "version",
114
+ };
115
+ }
116
+
117
+ async build(options: BuildOptions): Promise<GraphifyResult> {
118
+ return this.runWithFeature("build", options);
119
+ }
120
+
121
+ async query(options: QueryOptions): Promise<GraphifyResult> {
122
+ return this.runWithFeature("query", options);
123
+ }
124
+
125
+ async path(options: PathOptions): Promise<GraphifyResult> {
126
+ return this.runWithFeature("path", options);
127
+ }
128
+
129
+ async explain(options: ExplainOptions): Promise<GraphifyResult> {
130
+ return this.runWithFeature("explain", options);
131
+ }
132
+
133
+ async affected(options: AffectedOptions): Promise<GraphifyResult> {
134
+ return this.runWithFeature("affected", options);
135
+ }
136
+
137
+ async add(options: AddOptions): Promise<GraphifyResult> {
138
+ return this.runWithFeature("add", options);
139
+ }
140
+
141
+ async watch(options: WatchOptions): Promise<GraphifyResult> {
142
+ return this.runWithFeature("watch", options);
143
+ }
144
+
145
+ async reflect(options: ReflectOptions): Promise<GraphifyResult> {
146
+ return this.runWithFeature("reflect", options);
147
+ }
148
+
149
+ async close(): Promise<void> {
150
+ this.closed = true;
151
+ if (this.backend?.close) {
152
+ await this.backend.close();
153
+ }
154
+ this.backend = null;
155
+ }
156
+
157
+ private async runWithFeature(
158
+ operation: keyof GraphifyCapabilities,
159
+ options: unknown,
160
+ ): Promise<GraphifyResult> {
161
+ if (!this.backend) {
162
+ throw new GraphifyError("MISSING", `Graphify is not available. Cannot run ${operation}.`);
163
+ }
164
+
165
+ if (!this.supports(operation)) {
166
+ throw new GraphifyError(
167
+ "VERSION",
168
+ `The installed Graphify version does not support "${operation}".`,
169
+ );
170
+ }
171
+
172
+ const result = await this.backend.run(operation, options);
173
+ if (result.exitCode !== 0) {
174
+ throw new GraphifyError(
175
+ codeForOperation(operation),
176
+ messageForCliFailure(operation, result.exitCode, result.stderr),
177
+ { exitCode: result.exitCode, stderr: result.stderr },
178
+ );
179
+ }
180
+ return result;
181
+ }
182
+ }
183
+
184
+ /** Convenience factory. */
185
+ export async function createGraphifyCoordinator(
186
+ cwd: string,
187
+ config?: GraphifyConfig,
188
+ ): Promise<GraphifyCoordinator> {
189
+ const coordinator = new GraphifyCoordinator({ cwd, config });
190
+ await coordinator.initialize();
191
+ return coordinator;
192
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Normalized error handling for pi-graphify.
3
+ */
4
+
5
+ export type GraphifyErrorCode =
6
+ | "MISSING"
7
+ | "VERSION"
8
+ | "BUILD"
9
+ | "QUERY"
10
+ | "PATH"
11
+ | "EXPLAIN"
12
+ | "AFFECTED"
13
+ | "ADD"
14
+ | "WATCH"
15
+ | "REFLECT"
16
+ | "TIMEOUT"
17
+ | "MCP"
18
+ | "UNKNOWN";
19
+
20
+ export class GraphifyError extends Error {
21
+ readonly code: GraphifyErrorCode;
22
+ readonly details: Record<string, unknown>;
23
+ readonly userMessage: string;
24
+
25
+ constructor(code: GraphifyErrorCode, userMessage: string, details: Record<string, unknown> = {}) {
26
+ super(userMessage);
27
+ this.code = code;
28
+ this.userMessage = userMessage;
29
+ this.details = details;
30
+ this.name = "GraphifyError";
31
+ }
32
+ }
33
+
34
+ /** Map a Graphify operation name to an error code. */
35
+ export function codeForOperation(operation: string): GraphifyErrorCode {
36
+ switch (operation) {
37
+ case "build":
38
+ return "BUILD";
39
+ case "query":
40
+ return "QUERY";
41
+ case "path":
42
+ return "PATH";
43
+ case "explain":
44
+ return "EXPLAIN";
45
+ case "affected":
46
+ return "AFFECTED";
47
+ case "add":
48
+ return "ADD";
49
+ case "watch":
50
+ return "WATCH";
51
+ case "reflect":
52
+ return "REFLECT";
53
+ case "mcp":
54
+ return "MCP";
55
+ default:
56
+ return "UNKNOWN";
57
+ }
58
+ }
59
+
60
+ /** Build a user-friendly message from a backend result. */
61
+ export function messageForCliFailure(operation: string, exitCode: number, stderr: string): string {
62
+ const trimmed = stderr.trim();
63
+ if (trimmed) {
64
+ return `graphify ${operation} failed (exit ${exitCode}): ${trimmed}`;
65
+ }
66
+ return `graphify ${operation} failed with exit code ${exitCode}`;
67
+ }
@@ -0,0 +1,15 @@
1
+ import assert from "node:assert";
2
+ import { describe, it } from "node:test";
3
+
4
+ /**
5
+ * Smoke test that verifies the extension entry point module loads and exports a
6
+ * valid factory function. This catches syntax errors, import issues, or jiti
7
+ * problems without requiring the full Pi runtime.
8
+ */
9
+ describe("extension entry point", () => {
10
+ it("exports a default factory function", async () => {
11
+ const module = await import("../../extensions/index.js");
12
+ assert.equal(typeof module.default, "function");
13
+ assert.equal(module.default.length, 1);
14
+ });
15
+ });
package/src/graphify.ts CHANGED
@@ -1,72 +1,23 @@
1
1
  /**
2
- * Graphify CLI helpers
2
+ * High-level Graphify helpers used by the extension and tools.
3
+ *
4
+ * CLI execution has moved to `src/backends/cli.ts`. This module retains:
5
+ * - graph existence checks
6
+ * - system-prompt hint construction
7
+ * - result formatting utilities
3
8
  */
4
9
 
5
- import { execFile } from "node:child_process";
6
10
  import { existsSync } from "node:fs";
7
11
  import { join } from "node:path";
8
- import type { GraphifyConfig } from "./config.js";
9
-
10
- export interface GraphifyResult {
11
- stdout: string;
12
- stderr: string;
13
- exitCode: number;
14
- }
12
+ import type { GraphifyResult } from "./backends/types.js";
15
13
 
16
14
  export interface GraphStatus {
17
15
  hasGraph: boolean;
18
16
  graphPath?: string;
19
- error?: string;
20
- }
21
-
22
- /** Best-effort resolve of the graphify CLI executable name. */
23
- export async function findGraphifyCli(): Promise<string> {
24
- // Pi shells run with the user's PATH, so the bare command is usually enough.
25
- // We try a platform-aware check first; if it fails, we fall back to "graphify".
26
- const checkCmd = process.platform === "win32" ? "where" : "which";
27
- const shell = process.platform === "win32" ? "cmd.exe" : undefined;
28
-
29
- return new Promise((resolve) => {
30
- execFile(checkCmd, ["graphify"], { shell }, (error, stdout) => {
31
- if (error || !stdout.trim()) {
32
- resolve("graphify");
33
- return;
34
- }
35
- // Use the first match returned by where/which.
36
- resolve(stdout.split("\n")[0].trim());
37
- });
38
- });
39
- }
40
-
41
- /** Run a graphify CLI command with a timeout. */
42
- export async function runGraphify(
43
- args: string[],
44
- cwd: string,
45
- timeoutMs: number,
46
- ): Promise<GraphifyResult> {
47
- const cli = await findGraphifyCli();
48
-
49
- return new Promise((resolve) => {
50
- execFile(cli, args, { cwd, timeout: timeoutMs }, (error, stdout, stderr) => {
51
- if (error) {
52
- resolve({
53
- stdout: stdout.trim(),
54
- stderr: stderr.trim() || error.message,
55
- exitCode: typeof error.code === "number" ? error.code : 1,
56
- });
57
- return;
58
- }
59
- resolve({
60
- stdout: stdout.trim(),
61
- stderr: stderr.trim(),
62
- exitCode: 0,
63
- });
64
- });
65
- });
66
17
  }
67
18
 
68
19
  /** Check whether a graph exists for the given project directory. */
69
- export async function graphifyStatus(cwd: string): Promise<GraphStatus> {
20
+ export function graphifyStatus(cwd: string): GraphStatus {
70
21
  const graphPath = join(cwd, "graphify-out", "graph.json");
71
22
 
72
23
  if (!existsSync(graphPath)) {
@@ -82,6 +33,8 @@ export function buildGraphifyHint(graphPath: string): string {
82
33
  "<graphify-context>",
83
34
  `A Graphify knowledge graph is available at ${graphPath}.`,
84
35
  "When answering questions about this codebase, prefer querying the graph via the graphify_status, graphify_query, graphify_path, and graphify_explain tools instead of grepping or reading raw files.",
36
+ "Use graphify_explain to explain a specific function, file, tool, or concept that is a node in the graph.",
37
+ "Use graphify_path to trace how two functions, files, tools, or concepts are connected in the graph.",
85
38
  "Only fall back to raw file reads when the graph lacks the needed detail or when modifying/debugging specific lines.",
86
39
  "</graphify-context>",
87
40
  ].join("\n");
@@ -94,5 +47,3 @@ export function formatResult(result: GraphifyResult): string {
94
47
  }
95
48
  return `graphify exited with code ${result.exitCode}\n${result.stderr || result.stdout}`.trim();
96
49
  }
97
-
98
- export type { GraphifyConfig };
@@ -0,0 +1,37 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { Type } from "typebox";
3
+ import { GraphifyError } from "../errors.js";
4
+ import { formatGraphifyError, formatGraphifyResult } from "./format.js";
5
+ import type { CoordinatorProvider } from "./index.js";
6
+
7
+ export function registerAffectedTool(pi: ExtensionAPI, getCoordinator: CoordinatorProvider): void {
8
+ pi.registerTool({
9
+ name: "graphify_affected",
10
+ label: "Graphify Affected",
11
+ description: "Show the blast radius of changes to the given files or nodes.",
12
+ promptSnippet: "Show what files, nodes, or concepts depend on the given files or nodes",
13
+ promptGuidelines: [
14
+ "Use graphify_affected when the user asks what would break if they change a file or node.",
15
+ "Use graphify_affected to find downstream dependents of a file or concept before refactoring.",
16
+ ],
17
+ parameters: Type.Object({
18
+ cwd: Type.String({ description: "Working directory of the project to analyze." }),
19
+ files: Type.Array(Type.String({ minLength: 1 }), {
20
+ minItems: 1,
21
+ description: "The files or nodes whose downstream dependents should be reported.",
22
+ }),
23
+ }),
24
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
25
+ const coordinator = getCoordinator();
26
+ if (!coordinator) {
27
+ return formatGraphifyError(new GraphifyError("MISSING", "Graphify is not initialized."));
28
+ }
29
+ try {
30
+ const result = await coordinator.affected({ ...params, cwd: ctx.cwd });
31
+ return formatGraphifyResult(result);
32
+ } catch (error) {
33
+ return formatGraphifyError(error);
34
+ }
35
+ },
36
+ });
37
+ }
@@ -0,0 +1,43 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { Type } from "typebox";
3
+ import { GraphifyError } from "../errors.js";
4
+ import { formatGraphifyError, formatGraphifyResult } from "./format.js";
5
+ import type { CoordinatorProvider } from "./index.js";
6
+
7
+ export function registerBuildTool(pi: ExtensionAPI, getCoordinator: CoordinatorProvider): void {
8
+ pi.registerTool({
9
+ name: "graphify_build",
10
+ label: "Graphify Build",
11
+ description:
12
+ "Build or incrementally update the Graphify knowledge graph for the current project.",
13
+ promptSnippet: "Build or update the project's Graphify knowledge graph",
14
+ promptGuidelines: [
15
+ "Use graphify_build when the user wants to create or update the knowledge graph.",
16
+ "Use graphify_build when graphify_status reports that no graph exists.",
17
+ ],
18
+ parameters: Type.Object({
19
+ cwd: Type.String({ description: "Working directory of the project to build the graph for." }),
20
+ codeOnly: Type.Optional(
21
+ Type.Boolean({ description: "Only index code files; skip semantic extraction." }),
22
+ ),
23
+ update: Type.Optional(
24
+ Type.Boolean({ description: "Perform an incremental update instead of a full rebuild." }),
25
+ ),
26
+ directed: Type.Optional(
27
+ Type.Boolean({ description: "Build a directed graph instead of an undirected one." }),
28
+ ),
29
+ }),
30
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
31
+ const coordinator = getCoordinator();
32
+ if (!coordinator) {
33
+ return formatGraphifyError(new GraphifyError("MISSING", "Graphify is not initialized."));
34
+ }
35
+ try {
36
+ const result = await coordinator.build({ ...params, cwd: ctx.cwd });
37
+ return formatGraphifyResult(result);
38
+ } catch (error) {
39
+ return formatGraphifyError(error);
40
+ }
41
+ },
42
+ });
43
+ }
@@ -0,0 +1,38 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { Type } from "typebox";
3
+ import { GraphifyError } from "../errors.js";
4
+ import { formatGraphifyError, formatGraphifyResult } from "./format.js";
5
+ import type { CoordinatorProvider } from "./index.js";
6
+
7
+ export function registerExplainTool(pi: ExtensionAPI, getCoordinator: CoordinatorProvider): void {
8
+ pi.registerTool({
9
+ name: "graphify_explain",
10
+ label: "Graphify Explain",
11
+ description:
12
+ "Explain how a specific node (function, file, tool, or concept) is connected in the Graphify knowledge graph.",
13
+ promptSnippet: "Explain a function, file, tool, or concept from the project's knowledge graph",
14
+ promptGuidelines: [
15
+ "Use graphify_explain when the user asks what a specific function, file, tool, or concept does.",
16
+ "Use graphify_explain instead of reading source code when the subject is a node in the knowledge graph.",
17
+ ],
18
+ parameters: Type.Object({
19
+ cwd: Type.String({ description: "Working directory of the project to explain." }),
20
+ node: Type.String({
21
+ minLength: 1,
22
+ description: "The node or concept to explain in the graph.",
23
+ }),
24
+ }),
25
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
26
+ const coordinator = getCoordinator();
27
+ if (!coordinator) {
28
+ return formatGraphifyError(new GraphifyError("MISSING", "Graphify is not initialized."));
29
+ }
30
+ try {
31
+ const result = await coordinator.explain({ ...params, cwd: ctx.cwd });
32
+ return formatGraphifyResult(result);
33
+ } catch (error) {
34
+ return formatGraphifyError(error);
35
+ }
36
+ },
37
+ });
38
+ }