@dreamtree-org/graphify 1.1.3 → 1.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.
- package/README.md +18 -2
- package/dist/{chunk-ZK3TA6FW.js → chunk-K322XB4U.js} +45 -7
- package/dist/{chunk-ZK3TA6FW.js.map → chunk-K322XB4U.js.map} +1 -1
- package/dist/{chunk-NS5HB65S.js → chunk-UBXYMMXJ.js} +9 -4
- package/dist/{chunk-NS5HB65S.js.map → chunk-UBXYMMXJ.js.map} +1 -1
- package/dist/cli/index.cjs +135 -23
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +65 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +67 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -4
- package/dist/index.d.ts +58 -4
- package/dist/index.js +8 -2
- package/dist/mcp/server.cjs +43 -6
- package/dist/mcp/server.cjs.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/package.json +1 -1
- package/src/skill/SKILL.md +9 -4
package/dist/cli/index.cjs
CHANGED
|
@@ -246,22 +246,73 @@ var init_security = __esm({
|
|
|
246
246
|
}
|
|
247
247
|
});
|
|
248
248
|
|
|
249
|
+
// src/store/serialize.ts
|
|
250
|
+
function serializeGraph(graph) {
|
|
251
|
+
return graph.export();
|
|
252
|
+
}
|
|
253
|
+
function deserializeGraph(data) {
|
|
254
|
+
return import_graphology2.default.from(data);
|
|
255
|
+
}
|
|
256
|
+
var import_graphology2;
|
|
257
|
+
var init_serialize = __esm({
|
|
258
|
+
"src/store/serialize.ts"() {
|
|
259
|
+
"use strict";
|
|
260
|
+
init_cjs_shims();
|
|
261
|
+
import_graphology2 = __toESM(require("graphology"), 1);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// src/store/localFile.ts
|
|
266
|
+
var fs2, LocalFileGraphStore;
|
|
267
|
+
var init_localFile = __esm({
|
|
268
|
+
"src/store/localFile.ts"() {
|
|
269
|
+
"use strict";
|
|
270
|
+
init_cjs_shims();
|
|
271
|
+
fs2 = __toESM(require("fs/promises"), 1);
|
|
272
|
+
init_security();
|
|
273
|
+
init_serialize();
|
|
274
|
+
LocalFileGraphStore = class {
|
|
275
|
+
async load(ref) {
|
|
276
|
+
let jsonPath;
|
|
277
|
+
try {
|
|
278
|
+
jsonPath = validateGraphPath("graph.json", ref);
|
|
279
|
+
} catch (error) {
|
|
280
|
+
if (error.message.startsWith("Base directory does not exist")) return null;
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
let raw;
|
|
284
|
+
try {
|
|
285
|
+
raw = await fs2.readFile(jsonPath, "utf-8");
|
|
286
|
+
} catch (error) {
|
|
287
|
+
if (error.code === "ENOENT") return null;
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
return deserializeGraph(JSON.parse(raw));
|
|
291
|
+
}
|
|
292
|
+
async save(ref, graph) {
|
|
293
|
+
await fs2.mkdir(ref, { recursive: true });
|
|
294
|
+
const jsonPath = validateGraphPath("graph.json", ref);
|
|
295
|
+
await fs2.writeFile(jsonPath, JSON.stringify(serializeGraph(graph), null, 2), "utf-8");
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
249
301
|
// src/graphStore.ts
|
|
250
302
|
async function loadGraph(outDir = path2.join(process.cwd(), "graphify-out")) {
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
303
|
+
const graph = await new LocalFileGraphStore().load(outDir);
|
|
304
|
+
if (graph === null) {
|
|
305
|
+
throw new Error(`No graph found in ${outDir} \u2014 run \`graphify <path>\` to build one first.`);
|
|
306
|
+
}
|
|
307
|
+
return graph;
|
|
255
308
|
}
|
|
256
|
-
var
|
|
309
|
+
var path2;
|
|
257
310
|
var init_graphStore = __esm({
|
|
258
311
|
"src/graphStore.ts"() {
|
|
259
312
|
"use strict";
|
|
260
313
|
init_cjs_shims();
|
|
261
|
-
fs3 = __toESM(require("fs/promises"), 1);
|
|
262
314
|
path2 = __toESM(require("path"), 1);
|
|
263
|
-
|
|
264
|
-
init_security();
|
|
315
|
+
init_localFile();
|
|
265
316
|
}
|
|
266
317
|
});
|
|
267
318
|
|
|
@@ -1355,9 +1406,10 @@ function cluster(graph, options = {}) {
|
|
|
1355
1406
|
// src/export.ts
|
|
1356
1407
|
init_cjs_shims();
|
|
1357
1408
|
var import_node_module = require("module");
|
|
1358
|
-
var
|
|
1409
|
+
var fs3 = __toESM(require("fs/promises"), 1);
|
|
1359
1410
|
var path = __toESM(require("path"), 1);
|
|
1360
1411
|
init_security();
|
|
1412
|
+
init_localFile();
|
|
1361
1413
|
var require2 = (0, import_node_module.createRequire)(importMetaUrl);
|
|
1362
1414
|
function resolveVisNetworkAssets() {
|
|
1363
1415
|
const packageJsonPath = require2.resolve("vis-network/package.json");
|
|
@@ -1423,8 +1475,8 @@ community: ${communityLabel}` : ""}`,
|
|
|
1423
1475
|
async function renderHtml(graph) {
|
|
1424
1476
|
const { jsPath, cssPath } = resolveVisNetworkAssets();
|
|
1425
1477
|
const [visJs, visCss] = await Promise.all([
|
|
1426
|
-
|
|
1427
|
-
|
|
1478
|
+
fs3.readFile(jsPath, "utf-8"),
|
|
1479
|
+
fs3.readFile(cssPath, "utf-8")
|
|
1428
1480
|
]);
|
|
1429
1481
|
const { nodes, edges } = buildVisNodesAndEdges(graph);
|
|
1430
1482
|
const dataJson = safeInlineJson({ nodes, edges });
|
|
@@ -1465,16 +1517,15 @@ ${visJs}
|
|
|
1465
1517
|
}
|
|
1466
1518
|
async function exportGraph(graph, options, report) {
|
|
1467
1519
|
const outDir = path.resolve(options.outDir);
|
|
1468
|
-
await
|
|
1469
|
-
|
|
1470
|
-
await fs2.writeFile(jsonPath, JSON.stringify(graph.toJSON(), null, 2), "utf-8");
|
|
1520
|
+
await fs3.mkdir(outDir, { recursive: true });
|
|
1521
|
+
await new LocalFileGraphStore().save(outDir, graph);
|
|
1471
1522
|
if (options.html !== false) {
|
|
1472
1523
|
const htmlPath = validateGraphPath("graph.html", outDir);
|
|
1473
|
-
await
|
|
1524
|
+
await fs3.writeFile(htmlPath, await renderHtml(graph), "utf-8");
|
|
1474
1525
|
}
|
|
1475
1526
|
if (report !== void 0) {
|
|
1476
1527
|
const reportPath = validateGraphPath("GRAPH_REPORT.md", outDir);
|
|
1477
|
-
await
|
|
1528
|
+
await fs3.writeFile(reportPath, report, "utf-8");
|
|
1478
1529
|
}
|
|
1479
1530
|
const notImplemented = [];
|
|
1480
1531
|
if (options.svg) notImplemented.push("--svg");
|
|
@@ -3912,6 +3963,11 @@ async function runPipeline(root, options = {}) {
|
|
|
3912
3963
|
},
|
|
3913
3964
|
report
|
|
3914
3965
|
);
|
|
3966
|
+
if (options.store) {
|
|
3967
|
+
const storeRef = options.storeRef ?? outDir;
|
|
3968
|
+
progress(`Saving graph to store (${storeRef}) ...`);
|
|
3969
|
+
await options.store.save(storeRef, graph);
|
|
3970
|
+
}
|
|
3915
3971
|
progress("Done.");
|
|
3916
3972
|
return { manifest, graph, analysis, report };
|
|
3917
3973
|
}
|
|
@@ -4778,6 +4834,29 @@ function packageRoot() {
|
|
|
4778
4834
|
const packageJsonPath = require4.resolve("@dreamtree-org/graphify/package.json");
|
|
4779
4835
|
return path15.dirname(packageJsonPath);
|
|
4780
4836
|
}
|
|
4837
|
+
var MCP_SERVER_ENTRY = {
|
|
4838
|
+
command: "npx",
|
|
4839
|
+
args: ["-y", "@dreamtree-org/graphify", "--mcp"]
|
|
4840
|
+
};
|
|
4841
|
+
async function upsertMcpServer(filePath, serversKey = "mcpServers") {
|
|
4842
|
+
let config = {};
|
|
4843
|
+
try {
|
|
4844
|
+
config = JSON.parse(await fs22.readFile(filePath, "utf-8"));
|
|
4845
|
+
} catch (error) {
|
|
4846
|
+
if (error.code !== "ENOENT") {
|
|
4847
|
+
throw new Error(`cannot update ${filePath} \u2014 it exists but is not valid JSON; fix or remove it and re-run.`, {
|
|
4848
|
+
cause: error
|
|
4849
|
+
});
|
|
4850
|
+
}
|
|
4851
|
+
}
|
|
4852
|
+
const servers = config[serversKey] ?? {};
|
|
4853
|
+
servers["graphify"] = MCP_SERVER_ENTRY;
|
|
4854
|
+
config[serversKey] = servers;
|
|
4855
|
+
await fs22.mkdir(path15.dirname(filePath), { recursive: true });
|
|
4856
|
+
await fs22.writeFile(filePath, `${JSON.stringify(config, null, 2)}
|
|
4857
|
+
`, "utf-8");
|
|
4858
|
+
return filePath;
|
|
4859
|
+
}
|
|
4781
4860
|
var MD_MARKER_BEGIN = "<!-- >>> graphify >>> -->";
|
|
4782
4861
|
var MD_MARKER_END = "<!-- <<< graphify <<< -->";
|
|
4783
4862
|
async function writeOwnedFile(filePath, content) {
|
|
@@ -4812,7 +4891,8 @@ var PLATFORMS = {
|
|
|
4812
4891
|
claude: async (cwd, content) => {
|
|
4813
4892
|
const target = path15.join(cwd, ".claude", "skills", "graphify", "SKILL.md");
|
|
4814
4893
|
await writeOwnedFile(target, content);
|
|
4815
|
-
|
|
4894
|
+
const mcpPath = await upsertMcpServer(path15.join(cwd, ".mcp.json"));
|
|
4895
|
+
return { host: "Claude Code", path: target, mcpPath };
|
|
4816
4896
|
},
|
|
4817
4897
|
cursor: async (cwd, content) => {
|
|
4818
4898
|
const target = path15.join(cwd, ".cursor", "rules", "graphify.mdc");
|
|
@@ -4825,7 +4905,8 @@ alwaysApply: false
|
|
|
4825
4905
|
---
|
|
4826
4906
|
${body}`
|
|
4827
4907
|
);
|
|
4828
|
-
|
|
4908
|
+
const mcpPath = await upsertMcpServer(path15.join(cwd, ".cursor", "mcp.json"));
|
|
4909
|
+
return { host: "Cursor", path: target, mcpPath };
|
|
4829
4910
|
},
|
|
4830
4911
|
windsurf: async (cwd, content) => {
|
|
4831
4912
|
const target = path15.join(cwd, ".windsurf", "rules", "graphify.md");
|
|
@@ -4845,10 +4926,32 @@ ${body}`
|
|
|
4845
4926
|
gemini: async (cwd, content) => {
|
|
4846
4927
|
const target = path15.join(cwd, "GEMINI.md");
|
|
4847
4928
|
await upsertMarkedBlock(target, content.replace(/^---[\s\S]*?---\n/, ""));
|
|
4848
|
-
|
|
4929
|
+
const mcpPath = await upsertMcpServer(path15.join(cwd, ".gemini", "settings.json"));
|
|
4930
|
+
return { host: "Gemini CLI", path: target, mcpPath };
|
|
4849
4931
|
}
|
|
4850
4932
|
};
|
|
4851
4933
|
var SUPPORTED_PLATFORMS = Object.keys(PLATFORMS).sort((a, b) => a.localeCompare(b));
|
|
4934
|
+
var PLATFORM_MARKERS = {
|
|
4935
|
+
cursor: [".cursor"],
|
|
4936
|
+
windsurf: [".windsurf"],
|
|
4937
|
+
cline: [".clinerules"],
|
|
4938
|
+
agents: ["AGENTS.md"],
|
|
4939
|
+
gemini: ["GEMINI.md", ".gemini"]
|
|
4940
|
+
};
|
|
4941
|
+
async function detectPlatforms(cwd = process.cwd()) {
|
|
4942
|
+
const platforms = ["claude"];
|
|
4943
|
+
for (const [platform, markers] of Object.entries(PLATFORM_MARKERS)) {
|
|
4944
|
+
for (const marker of markers) {
|
|
4945
|
+
try {
|
|
4946
|
+
await fs22.access(path15.join(cwd, marker));
|
|
4947
|
+
platforms.push(platform);
|
|
4948
|
+
break;
|
|
4949
|
+
} catch {
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
return platforms;
|
|
4954
|
+
}
|
|
4852
4955
|
async function runInstall(platforms = ["claude"], cwd = process.cwd()) {
|
|
4853
4956
|
const sourcePath = path15.join(packageRoot(), "src", "skill", "SKILL.md");
|
|
4854
4957
|
const content = await fs22.readFile(sourcePath, "utf-8");
|
|
@@ -4864,7 +4967,8 @@ async function runInstall(platforms = ["claude"], cwd = process.cwd()) {
|
|
|
4864
4967
|
results.push(await installer(cwd, content));
|
|
4865
4968
|
}
|
|
4866
4969
|
for (const result of results) {
|
|
4867
|
-
|
|
4970
|
+
const mcpNote = result.mcpPath ? ` (MCP server registered in ${result.mcpPath})` : "";
|
|
4971
|
+
console.log(`Installed graphify for ${result.host} -> ${result.path}${mcpNote}`);
|
|
4868
4972
|
}
|
|
4869
4973
|
return results;
|
|
4870
4974
|
}
|
|
@@ -4980,7 +5084,7 @@ async function watchAndRebuild(root, runOnce) {
|
|
|
4980
5084
|
});
|
|
4981
5085
|
}
|
|
4982
5086
|
var program = new import_commander.Command();
|
|
4983
|
-
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) => {
|
|
5087
|
+
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) => {
|
|
4984
5088
|
try {
|
|
4985
5089
|
if (options.mcp) {
|
|
4986
5090
|
const outDir = path17.resolve(targetArg === "." ? process.cwd() : targetArg, "graphify-out");
|
|
@@ -4989,7 +5093,8 @@ program.name("graphify").description("Turn a folder of code/docs/papers into a q
|
|
|
4989
5093
|
return;
|
|
4990
5094
|
}
|
|
4991
5095
|
let root = targetArg;
|
|
4992
|
-
|
|
5096
|
+
const cloned = looksLikeGitUrl(targetArg);
|
|
5097
|
+
if (cloned) {
|
|
4993
5098
|
console.error(`Cloning ${targetArg} ...`);
|
|
4994
5099
|
root = await cloneRepo(targetArg);
|
|
4995
5100
|
}
|
|
@@ -5015,6 +5120,13 @@ program.name("graphify").description("Turn a folder of code/docs/papers into a q
|
|
|
5015
5120
|
onProgress: (m) => console.error(m)
|
|
5016
5121
|
});
|
|
5017
5122
|
await runOnce();
|
|
5123
|
+
if (options.install && !cloned) {
|
|
5124
|
+
try {
|
|
5125
|
+
await runInstall(await detectPlatforms(root), root);
|
|
5126
|
+
} catch (error) {
|
|
5127
|
+
console.error(`graphify: agent skill/MCP install skipped: ${error.message}`);
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
5018
5130
|
if (options.watch) {
|
|
5019
5131
|
console.error(`Watching ${root} for changes (Ctrl+C to stop) ...`);
|
|
5020
5132
|
await watchAndRebuild(root, runOnce);
|
|
@@ -5096,7 +5208,7 @@ program.command("benchmark [questions...]").description("measure token savings o
|
|
|
5096
5208
|
process.exitCode = 1;
|
|
5097
5209
|
}
|
|
5098
5210
|
});
|
|
5099
|
-
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) => {
|
|
5211
|
+
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) => {
|
|
5100
5212
|
try {
|
|
5101
5213
|
await runInstall(options.platform);
|
|
5102
5214
|
} catch (error) {
|