@roarpeng/graphflow 1.0.9 → 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/README.md +9 -1
- package/dist/agents/insight.js +1 -1
- package/dist/agents/insight.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +1 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/embedding-factory.d.ts.map +1 -1
- package/dist/config/embedding-factory.js +23 -12
- package/dist/config/embedding-factory.js.map +1 -1
- package/dist/config/loader.d.ts +11 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +103 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/core/agent-assignment.d.ts +39 -0
- package/dist/core/agent-assignment.d.ts.map +1 -0
- package/dist/core/agent-assignment.js +73 -0
- package/dist/core/agent-assignment.js.map +1 -0
- package/dist/core/dag-checkpoint.d.ts +83 -0
- package/dist/core/dag-checkpoint.d.ts.map +1 -0
- package/dist/core/dag-checkpoint.js +164 -0
- package/dist/core/dag-checkpoint.js.map +1 -0
- package/dist/core/dag-engine.d.ts +9 -0
- package/dist/core/dag-engine.d.ts.map +1 -1
- package/dist/core/dag-engine.js +42 -0
- package/dist/core/dag-engine.js.map +1 -1
- package/dist/core/orchestrator-context.d.ts +10 -0
- package/dist/core/orchestrator-context.d.ts.map +1 -0
- package/dist/core/orchestrator-context.js +112 -0
- package/dist/core/orchestrator-context.js.map +1 -0
- package/dist/core/orchestrator-episode.d.ts +13 -0
- package/dist/core/orchestrator-episode.d.ts.map +1 -0
- package/dist/core/orchestrator-episode.js +128 -0
- package/dist/core/orchestrator-episode.js.map +1 -0
- package/dist/core/orchestrator-route.d.ts +20 -0
- package/dist/core/orchestrator-route.d.ts.map +1 -0
- package/dist/core/orchestrator-route.js +51 -0
- package/dist/core/orchestrator-route.js.map +1 -0
- package/dist/core/orchestrator.d.ts +2 -38
- package/dist/core/orchestrator.d.ts.map +1 -1
- package/dist/core/orchestrator.js +97 -286
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/triage.d.ts +21 -0
- package/dist/core/triage.d.ts.map +1 -1
- package/dist/core/triage.js +25 -6
- package/dist/core/triage.js.map +1 -1
- package/dist/core/types.d.ts +53 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/graph/context-slicer-types.d.ts +62 -0
- package/dist/graph/context-slicer-types.d.ts.map +1 -0
- package/dist/graph/context-slicer-types.js +13 -0
- package/dist/graph/context-slicer-types.js.map +1 -0
- package/dist/graph/context-slicer-utils.d.ts +24 -0
- package/dist/graph/context-slicer-utils.d.ts.map +1 -0
- package/dist/graph/context-slicer-utils.js +118 -0
- package/dist/graph/context-slicer-utils.js.map +1 -0
- package/dist/graph/context-slicer.d.ts +6 -61
- package/dist/graph/context-slicer.d.ts.map +1 -1
- package/dist/graph/context-slicer.js +73 -170
- package/dist/graph/context-slicer.js.map +1 -1
- package/dist/graph/file-indexer-cache.d.ts +34 -0
- package/dist/graph/file-indexer-cache.d.ts.map +1 -0
- package/dist/graph/file-indexer-cache.js +105 -0
- package/dist/graph/file-indexer-cache.js.map +1 -0
- package/dist/graph/file-indexer-edges.d.ts +55 -0
- package/dist/graph/file-indexer-edges.d.ts.map +1 -0
- package/dist/graph/file-indexer-edges.js +258 -0
- package/dist/graph/file-indexer-edges.js.map +1 -0
- package/dist/graph/file-indexer-nodes.d.ts +43 -0
- package/dist/graph/file-indexer-nodes.d.ts.map +1 -0
- package/dist/graph/file-indexer-nodes.js +137 -0
- package/dist/graph/file-indexer-nodes.js.map +1 -0
- package/dist/graph/file-indexer-walker.d.ts +35 -0
- package/dist/graph/file-indexer-walker.d.ts.map +1 -0
- package/dist/graph/file-indexer-walker.js +80 -0
- package/dist/graph/file-indexer-walker.js.map +1 -0
- package/dist/graph/file-indexer.d.ts +13 -39
- package/dist/graph/file-indexer.d.ts.map +1 -1
- package/dist/graph/file-indexer.js +167 -600
- package/dist/graph/file-indexer.js.map +1 -1
- package/dist/graph/graphify-file-client.d.ts.map +1 -1
- package/dist/graph/graphify-file-client.js +16 -6
- package/dist/graph/graphify-file-client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/agent-mcp-installer.d.ts +25 -1
- package/dist/integrations/agent-mcp-installer.d.ts.map +1 -1
- package/dist/integrations/agent-mcp-installer.js +189 -9
- package/dist/integrations/agent-mcp-installer.js.map +1 -1
- package/dist/integrations/skill-installer.d.ts +50 -0
- package/dist/integrations/skill-installer.d.ts.map +1 -0
- package/dist/integrations/skill-installer.js +373 -0
- package/dist/integrations/skill-installer.js.map +1 -0
- package/dist/learning/embeddings.d.ts +11 -0
- package/dist/learning/embeddings.d.ts.map +1 -1
- package/dist/learning/embeddings.js +24 -0
- package/dist/learning/embeddings.js.map +1 -1
- package/dist/learning/episodic-memory.d.ts +3 -2
- package/dist/learning/episodic-memory.d.ts.map +1 -1
- package/dist/learning/episodic-memory.js +105 -10
- package/dist/learning/episodic-memory.js.map +1 -1
- package/dist/learning/local-embedding.d.ts.map +1 -1
- package/dist/learning/local-embedding.js +16 -0
- package/dist/learning/local-embedding.js.map +1 -1
- package/dist/learning/seed-skills.d.ts +21 -0
- package/dist/learning/seed-skills.d.ts.map +1 -0
- package/dist/learning/seed-skills.js +113 -0
- package/dist/learning/seed-skills.js.map +1 -0
- package/dist/learning/skill-evolution.d.ts +1 -1
- package/dist/learning/skill-evolution.d.ts.map +1 -1
- package/dist/learning/skill-evolution.js +1 -1
- package/dist/learning/skill-evolution.js.map +1 -1
- package/dist/learning/skill-package.d.ts +55 -0
- package/dist/learning/skill-package.d.ts.map +1 -0
- package/dist/learning/skill-package.js +115 -0
- package/dist/learning/skill-package.js.map +1 -0
- package/dist/learning/triage-telemetry.d.ts +43 -0
- package/dist/learning/triage-telemetry.d.ts.map +1 -0
- package/dist/learning/triage-telemetry.js +114 -0
- package/dist/learning/triage-telemetry.js.map +1 -0
- package/dist/surfaces/cli/index.js +65 -0
- package/dist/surfaces/cli/index.js.map +1 -1
- package/dist/surfaces/cli/init.d.ts +17 -1
- package/dist/surfaces/cli/init.d.ts.map +1 -1
- package/dist/surfaces/cli/init.js +254 -25
- package/dist/surfaces/cli/init.js.map +1 -1
- package/dist/surfaces/cli/output.d.ts.map +1 -1
- package/dist/surfaces/cli/output.js +5 -0
- package/dist/surfaces/cli/output.js.map +1 -1
- package/dist/surfaces/cli/runtime/graph.d.ts +23 -0
- package/dist/surfaces/cli/runtime/graph.d.ts.map +1 -1
- package/dist/surfaces/cli/runtime/graph.js +40 -0
- package/dist/surfaces/cli/runtime/graph.js.map +1 -1
- package/dist/surfaces/cli/runtime/routing.d.ts.map +1 -1
- package/dist/surfaces/cli/runtime/routing.js +8 -1
- package/dist/surfaces/cli/runtime/routing.js.map +1 -1
- package/dist/surfaces/cli/runtime.d.ts +1 -1
- package/dist/surfaces/cli/runtime.d.ts.map +1 -1
- package/dist/surfaces/cli/runtime.js +4 -1
- package/dist/surfaces/cli/runtime.js.map +1 -1
- package/dist/surfaces/cursor-rules/graphflow.mdc +131 -0
- package/dist/surfaces/mcp/tool-definitions.js +20 -20
- package/dist/surfaces/mcp/tool-definitions.js.map +1 -1
- package/package.json +4 -3
- package/scripts/safe-postinstall.cjs +323 -5
- package/src/surfaces/cursor-rules/graphflow.mdc +131 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roarpeng/graphflow",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"wasm",
|
|
10
10
|
"scripts/safe-postinstall.cjs",
|
|
11
11
|
"src/surfaces/trae-skill",
|
|
12
|
+
"src/surfaces/cursor-rules",
|
|
12
13
|
"README.md",
|
|
13
14
|
"LICENSE",
|
|
14
15
|
"CHANGELOG.md",
|
|
@@ -71,7 +72,7 @@
|
|
|
71
72
|
"@types/node": "^25.9.1",
|
|
72
73
|
"@typescript-eslint/eslint-plugin": "^8.60.0",
|
|
73
74
|
"@typescript-eslint/parser": "^8.60.0",
|
|
74
|
-
"@vitest/coverage-v8": "^
|
|
75
|
+
"@vitest/coverage-v8": "^3.2.6",
|
|
75
76
|
"eslint": "^10.4.0",
|
|
76
77
|
"husky": "^9.1.7",
|
|
77
78
|
"lint-staged": "^16.4.0",
|
|
@@ -79,7 +80,7 @@
|
|
|
79
80
|
"tree-sitter-wasms": "0.1.11",
|
|
80
81
|
"tsx": "^4.22.3",
|
|
81
82
|
"typescript": "^6.0.3",
|
|
82
|
-
"vitest": "^
|
|
83
|
+
"vitest": "^3.2.6"
|
|
83
84
|
},
|
|
84
85
|
"dependencies": {
|
|
85
86
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
@@ -1,11 +1,56 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const { existsSync, mkdirSync, copyFileSync, readFileSync } = require("node:fs");
|
|
2
|
+
const { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, unlinkSync } = require("node:fs");
|
|
3
3
|
const { join } = require("node:path");
|
|
4
4
|
const { homedir } = require("node:os");
|
|
5
5
|
const { spawnSync } = require("node:child_process");
|
|
6
6
|
|
|
7
7
|
const isWindows = process.platform === "win32";
|
|
8
8
|
|
|
9
|
+
// ─── 版本比较与自动更新 ───
|
|
10
|
+
|
|
11
|
+
/** 版本标记文件路径:记录上次安装的 GraphFlow 版本号 */
|
|
12
|
+
const VERSION_FILE = join(homedir(), ".graphflow-install-version");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 读取上次安装时记录的版本号
|
|
16
|
+
* @returns {string | null} 上次版本号,不存在则返回 null
|
|
17
|
+
*/
|
|
18
|
+
function readPreviousVersion() {
|
|
19
|
+
try {
|
|
20
|
+
if (existsSync(VERSION_FILE)) {
|
|
21
|
+
return readFileSync(VERSION_FILE, "utf8").trim();
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
// 忽略读取失败
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 将当前版本号写入标记文件
|
|
31
|
+
* @param {string} version 当前版本号
|
|
32
|
+
*/
|
|
33
|
+
function writeCurrentVersion(version) {
|
|
34
|
+
try {
|
|
35
|
+
writeFileSync(VERSION_FILE, version, "utf8");
|
|
36
|
+
} catch {
|
|
37
|
+
// 忽略写入失败
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 比较版本号,判断是否需要强制更新 Skill/Rules/MCP 文件
|
|
43
|
+
* @param {string} currentVersion 当前安装的版本号
|
|
44
|
+
* @returns {boolean} 是否需要强制更新
|
|
45
|
+
*/
|
|
46
|
+
function needsForceUpdate(currentVersion) {
|
|
47
|
+
const previousVersion = readPreviousVersion();
|
|
48
|
+
if (!previousVersion) {
|
|
49
|
+
return true; // 首次安装或无版本记录,强制更新
|
|
50
|
+
}
|
|
51
|
+
return previousVersion !== currentVersion;
|
|
52
|
+
}
|
|
53
|
+
|
|
9
54
|
function resolveHomePaths() {
|
|
10
55
|
const home = homedir();
|
|
11
56
|
const appData = process.env.APPDATA || (isWindows ? join(home, "AppData", "Roaming") : "");
|
|
@@ -147,6 +192,231 @@ function isGlobalInstall() {
|
|
|
147
192
|
return false;
|
|
148
193
|
}
|
|
149
194
|
|
|
195
|
+
/**
|
|
196
|
+
* 获取 Cursor Rules 源文件路径(graphflow.mdc)
|
|
197
|
+
* 优先从 dist 目录查找,回退到 src 目录。
|
|
198
|
+
* @returns {string | null} 源文件路径,不存在则返回 null
|
|
199
|
+
*/
|
|
200
|
+
function getCursorRulesSourceFile() {
|
|
201
|
+
const candidates = [
|
|
202
|
+
join(__dirname, "..", "dist", "surfaces", "cursor-rules", "graphflow.mdc"),
|
|
203
|
+
join(__dirname, "..", "src", "surfaces", "cursor-rules", "graphflow.mdc"),
|
|
204
|
+
];
|
|
205
|
+
for (const filePath of candidates) {
|
|
206
|
+
if (existsSync(filePath)) {
|
|
207
|
+
return filePath;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 获取 CLAUDE.md 源文件路径
|
|
215
|
+
* 优先从包根目录查找。
|
|
216
|
+
* @returns {string | null} 源文件路径,不存在则返回 null
|
|
217
|
+
*/
|
|
218
|
+
function getClaudeMdSourceFile() {
|
|
219
|
+
const candidates = [
|
|
220
|
+
join(__dirname, "..", "CLAUDE.md"),
|
|
221
|
+
];
|
|
222
|
+
for (const filePath of candidates) {
|
|
223
|
+
if (existsSync(filePath)) {
|
|
224
|
+
return filePath;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 获取 Cursor rules 目录列表。
|
|
232
|
+
* Cursor 的 rules 目录为 ~/.cursor/rules/(跨平台一致),
|
|
233
|
+
* 也有用户使用 %APPDATA%/Cursor/User/rules/。
|
|
234
|
+
* @returns {Array<{name: string, rulesDir: string}>} Cursor rules 目录列表
|
|
235
|
+
*/
|
|
236
|
+
function getCursorRulesDirs() {
|
|
237
|
+
const { home, appData } = resolveHomePaths();
|
|
238
|
+
const dirs = [];
|
|
239
|
+
|
|
240
|
+
// ~/.cursor/rules — Cursor 的标准 rules 目录
|
|
241
|
+
if (existsSync(join(home, ".cursor"))) {
|
|
242
|
+
dirs.push({
|
|
243
|
+
name: "Cursor (~/.cursor)",
|
|
244
|
+
rulesDir: join(home, ".cursor", "rules"),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// %APPDATA%/Cursor/User/rules — Windows 上的备选目录
|
|
249
|
+
if (isWindows && existsSync(join(appData, "Cursor", "User"))) {
|
|
250
|
+
dirs.push({
|
|
251
|
+
name: "Cursor (AppData)",
|
|
252
|
+
rulesDir: join(appData, "Cursor", "User", "rules"),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return dirs;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* 通用文件安装逻辑:检测 marker → 创建目标目录 → 复制文件 → 已存在且内容相同则跳过
|
|
261
|
+
* @param {string} sourceFile 源文件路径
|
|
262
|
+
* @param {string} destDir 目标目录
|
|
263
|
+
* @param {string} destFileName 目标文件名
|
|
264
|
+
* @returns {{status: string, reason?: string}} 安装结果
|
|
265
|
+
*/
|
|
266
|
+
function installFileToDir(sourceFile, destDir, destFileName) {
|
|
267
|
+
if (!existsSync(sourceFile)) {
|
|
268
|
+
return { status: "skipped", reason: "源文件不存在" };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const destFile = join(destDir, destFileName);
|
|
272
|
+
const existed = existsSync(destFile);
|
|
273
|
+
|
|
274
|
+
if (existed) {
|
|
275
|
+
const existingContent = readFileSync(destFile, "utf8");
|
|
276
|
+
const newContent = readFileSync(sourceFile, "utf8");
|
|
277
|
+
if (existingContent === newContent) {
|
|
278
|
+
return { status: "skipped", reason: "已是最新" };
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
mkdirSync(destDir, { recursive: true });
|
|
283
|
+
copyFileSync(sourceFile, destFile);
|
|
284
|
+
|
|
285
|
+
return { status: existed ? "updated" : "created" };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* 检测并安装 Cursor Rules(graphflow.mdc)到 Cursor rules 目录
|
|
290
|
+
* @returns {{status: string, reason?: string} | null} 安装结果,无需安装则返回 null
|
|
291
|
+
*/
|
|
292
|
+
function installCursorRules() {
|
|
293
|
+
const sourceFile = getCursorRulesSourceFile();
|
|
294
|
+
if (!sourceFile) {
|
|
295
|
+
return { status: "skipped", reason: "Cursor Rules 源文件不存在" };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const dirs = getCursorRulesDirs();
|
|
299
|
+
if (dirs.length === 0) {
|
|
300
|
+
return null; // 未检测到 Cursor,跳过
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// 只需要安装到第一个有效目录
|
|
304
|
+
const target = dirs[0];
|
|
305
|
+
return installFileToDir(sourceFile, target.rulesDir, "graphflow.mdc");
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* 检测并安装 CLAUDE.md 到 ~/.claude/CLAUDE.md(Claude Code 约定)
|
|
310
|
+
* @returns {{status: string, reason?: string} | null} 安装结果,无需安装则返回 null
|
|
311
|
+
*/
|
|
312
|
+
function installClaudeMd() {
|
|
313
|
+
const sourceFile = getClaudeMdSourceFile();
|
|
314
|
+
if (!sourceFile) {
|
|
315
|
+
return { status: "skipped", reason: "CLAUDE.md 源文件不存在" };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const { home } = resolveHomePaths();
|
|
319
|
+
const claudeDir = join(home, ".claude");
|
|
320
|
+
|
|
321
|
+
// 检测 ~/.claude 是否存在(Claude Code 的 marker 目录)
|
|
322
|
+
if (!existsSync(claudeDir)) {
|
|
323
|
+
return null; // 未检测到 Claude Code,跳过
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return installFileToDir(sourceFile, claudeDir, "CLAUDE.md");
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* 注入 workspace 级 MCP 配置到已存在的 workspace MCP 文件。
|
|
331
|
+
* 仅在检测到 .cursor/mcp.json 或 .vscode/mcp.json 时生效(更安全,只在当前项目内)。
|
|
332
|
+
* @param {string} workspaceRoot 当前项目根目录
|
|
333
|
+
* @returns {Array<{configPath: string, status: string}>} 注入结果列表
|
|
334
|
+
*/
|
|
335
|
+
function installWorkspaceLevelMcp(workspaceRoot) {
|
|
336
|
+
const results = [];
|
|
337
|
+
const configFiles = [
|
|
338
|
+
{ path: join(workspaceRoot, ".cursor", "mcp.json"), name: "Cursor", key: "mcpServers" },
|
|
339
|
+
{ path: join(workspaceRoot, ".vscode", "mcp.json"), name: "VS Code", key: "servers" },
|
|
340
|
+
];
|
|
341
|
+
|
|
342
|
+
const mcpEntry = {
|
|
343
|
+
command: "npx",
|
|
344
|
+
args: ["-y", "--package=@roarpeng/graphflow", "graphflow-mcp"],
|
|
345
|
+
env: { GRAPHFLOW_MCP_STDIO: "1", GRAPHFLOW_LOG_JSON: "1" },
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
for (const config of configFiles) {
|
|
349
|
+
if (!existsSync(config.path)) {
|
|
350
|
+
continue; // 该 workspace 级配置不存在,跳过
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
let json = {};
|
|
355
|
+
try {
|
|
356
|
+
json = JSON.parse(readFileSync(config.path, "utf8"));
|
|
357
|
+
} catch {
|
|
358
|
+
// 配置文件为空或损坏,初始化为空对象
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const servers = json[config.key] || {};
|
|
362
|
+
if (servers.graphflow) {
|
|
363
|
+
results.push({ configPath: config.path, status: "skipped", name: config.name, reason: "already configured" });
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
servers.graphflow = mcpEntry;
|
|
368
|
+
json[config.key] = servers;
|
|
369
|
+
writeFileSync(config.path, JSON.stringify(json, null, 2) + "\n", "utf8");
|
|
370
|
+
results.push({ configPath: config.path, status: "created", name: config.name });
|
|
371
|
+
console.log(`[GraphFlow] Workspace 级 MCP 已注入到 ${config.path} (${config.name})`);
|
|
372
|
+
} catch (err) {
|
|
373
|
+
results.push({ configPath: config.path, status: "error", name: config.name, reason: err.message });
|
|
374
|
+
console.log(`[GraphFlow] Workspace 级 MCP 注入失败 ${config.path}: ${err.message}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return results;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* 在 workspace 级创建 .graphflow/skills/ 目录并复制 Skill 文件。
|
|
383
|
+
* 仅在检测到 .cursor/mcp.json 或 .vscode/mcp.json 时生效。
|
|
384
|
+
* @param {string} workspaceRoot 当前项目根目录
|
|
385
|
+
*/
|
|
386
|
+
function installWorkspaceLevelSkills(workspaceRoot) {
|
|
387
|
+
const configFiles = [
|
|
388
|
+
join(workspaceRoot, ".cursor", "mcp.json"),
|
|
389
|
+
join(workspaceRoot, ".vscode", "mcp.json"),
|
|
390
|
+
];
|
|
391
|
+
const hasWorkspaceConfig = configFiles.some((p) => existsSync(p));
|
|
392
|
+
|
|
393
|
+
if (!hasWorkspaceConfig) {
|
|
394
|
+
return; // 无 workspace 级 MCP 配置,跳过 Skill 安装
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const skillSourceDir = getSkillSourceDir();
|
|
398
|
+
if (!skillSourceDir) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// 在 .graphflow/skills/graphflow/ 下创建 workspace 级 Skill
|
|
403
|
+
const skillDestDir = join(workspaceRoot, ".graphflow", "skills", "graphflow");
|
|
404
|
+
const result = installSkill(skillDestDir, skillSourceDir);
|
|
405
|
+
if (result.status !== "skipped") {
|
|
406
|
+
console.log(`[GraphFlow] Workspace 级 Skill: ${result.status}${result.reason ? ` (${result.reason})` : ""} -> ${skillDestDir}`);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// 同时安装 Cursor Rules 到 workspace 级 .cursor/rules/
|
|
410
|
+
const cursorRulesSource = getCursorRulesSourceFile();
|
|
411
|
+
if (cursorRulesSource && existsSync(join(workspaceRoot, ".cursor"))) {
|
|
412
|
+
const cursorRulesDir = join(workspaceRoot, ".cursor", "rules");
|
|
413
|
+
const rulesResult = installFileToDir(cursorRulesSource, cursorRulesDir, "graphflow.mdc");
|
|
414
|
+
if (rulesResult.status !== "skipped") {
|
|
415
|
+
console.log(`[GraphFlow] Workspace 级 Cursor Rules: ${rulesResult.status}${rulesResult.reason ? ` (${rulesResult.reason})` : ""}`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
150
420
|
function main() {
|
|
151
421
|
if (process.env.GRAPHFLOW_SKIP_POSTINSTALL === "1" || process.env.CI === "true") {
|
|
152
422
|
process.exit(0);
|
|
@@ -156,13 +426,44 @@ function main() {
|
|
|
156
426
|
const explicitlyEnabled = process.env.GRAPHFLOW_ENABLE_POSTINSTALL === "1";
|
|
157
427
|
|
|
158
428
|
if (!globalInstall && !explicitlyEnabled) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
429
|
+
// 本地安装:检测 workspace 级 MCP 配置,若有则自动注入 workspace 级 MCP + Skill
|
|
430
|
+
const workspaceRoot = process.cwd();
|
|
431
|
+
const workspaceMcpResults = installWorkspaceLevelMcp(workspaceRoot);
|
|
432
|
+
const hasWorkspaceConfig = workspaceMcpResults.some((r) => r.status === "created" || r.status === "skipped");
|
|
433
|
+
|
|
434
|
+
if (hasWorkspaceConfig) {
|
|
435
|
+
console.log("[GraphFlow] 检测到 workspace 级 MCP 配置,已自动注入 workspace 级 GraphFlow MCP。");
|
|
436
|
+
installWorkspaceLevelSkills(workspaceRoot);
|
|
437
|
+
console.log("[GraphFlow] 如需完整安装(用户级 MCP + Trae Skill),运行:");
|
|
438
|
+
console.log("[GraphFlow] npx @roarpeng/graphflow install");
|
|
439
|
+
} else {
|
|
440
|
+
console.log("[GraphFlow] Local install detected. Auto-setup skipped to avoid modifying your global config.");
|
|
441
|
+
console.log("[GraphFlow] To install GraphFlow MCP + Skill, run:");
|
|
442
|
+
console.log("[GraphFlow] npx @roarpeng/graphflow install");
|
|
443
|
+
}
|
|
162
444
|
process.exit(0);
|
|
163
445
|
}
|
|
164
446
|
|
|
447
|
+
// 读取当前版本号,用于版本比较和强制更新
|
|
448
|
+
let currentVersion = "unknown";
|
|
449
|
+
try {
|
|
450
|
+
const pkg = require("../package.json");
|
|
451
|
+
currentVersion = pkg.version || "unknown";
|
|
452
|
+
} catch {
|
|
453
|
+
// npm 包中 package.json 在上层目录;如果读取失败则跳过版本比较
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const forceUpdate = needsForceUpdate(currentVersion);
|
|
457
|
+
const previousVersion = readPreviousVersion();
|
|
458
|
+
|
|
165
459
|
console.log(`[GraphFlow] Running post-install setup (${globalInstall ? "global install" : "explicitly enabled"})...`);
|
|
460
|
+
console.log(`[GraphFlow] 版本: ${currentVersion}`);
|
|
461
|
+
|
|
462
|
+
if (forceUpdate && previousVersion) {
|
|
463
|
+
console.log(`[GraphFlow] 检测到版本变化 (${previousVersion} → ${currentVersion}),将强制更新所有 Skill/Rules/MCP 文件`);
|
|
464
|
+
} else if (forceUpdate && !previousVersion) {
|
|
465
|
+
console.log("[GraphFlow] 首次安装,将安装所有 Skill/Rules/MCP 文件");
|
|
466
|
+
}
|
|
166
467
|
|
|
167
468
|
const skillSourceDir = getSkillSourceDir();
|
|
168
469
|
const traeDirs = getTraeUserDirs();
|
|
@@ -185,6 +486,23 @@ function main() {
|
|
|
185
486
|
console.log(`[GraphFlow] MCP install skipped: ${mcpResult.reason}`);
|
|
186
487
|
}
|
|
187
488
|
|
|
489
|
+
// ─── Cursor Rules 安装 ───
|
|
490
|
+
const cursorRulesResult = installCursorRules();
|
|
491
|
+
if (cursorRulesResult) {
|
|
492
|
+
console.log(`[GraphFlow] Cursor Rules: ${cursorRulesResult.status}${cursorRulesResult.reason ? ` (${cursorRulesResult.reason})` : ""}`);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// ─── Claude Code 约定安装 ───
|
|
496
|
+
const claudeMdResult = installClaudeMd();
|
|
497
|
+
if (claudeMdResult) {
|
|
498
|
+
console.log(`[GraphFlow] Claude Code 约定: ${claudeMdResult.status}${claudeMdResult.reason ? ` (${claudeMdResult.reason})` : ""}`);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// 写入当前版本号标记文件
|
|
502
|
+
if (currentVersion !== "unknown") {
|
|
503
|
+
writeCurrentVersion(currentVersion);
|
|
504
|
+
}
|
|
505
|
+
|
|
188
506
|
console.log("[GraphFlow] Post-install complete.");
|
|
189
507
|
}
|
|
190
508
|
|
|
@@ -192,4 +510,4 @@ if (require.main === module) {
|
|
|
192
510
|
main();
|
|
193
511
|
}
|
|
194
512
|
|
|
195
|
-
module.exports = { installSkill, detectTrae, getTraeUserDirs, getSkillSourceDir };
|
|
513
|
+
module.exports = { installSkill, detectTrae, getTraeUserDirs, getSkillSourceDir, readPreviousVersion, writeCurrentVersion, needsForceUpdate, VERSION_FILE };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use GraphFlow first for token-efficient repo context, planning, and orchestration.
|
|
3
|
+
globs:
|
|
4
|
+
- "**/*"
|
|
5
|
+
alwaysApply: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# GraphFlow Token-First Rule
|
|
9
|
+
|
|
10
|
+
GraphFlow is a graph-based context and planning service backed by a persistent MCP server. It turns codebases into queryable knowledge graphs, delivering token-efficient compressed context, task planning, and orchestration.
|
|
11
|
+
|
|
12
|
+
Before broad code exploration, implementation, debugging, review, planning, or architecture questions:
|
|
13
|
+
|
|
14
|
+
1. Call `graphflow_preview_context` with the user's task/query.
|
|
15
|
+
2. Use the returned `summary`, `anchors`, `refillPreview`, and `tokenBudget` as the first context source.
|
|
16
|
+
3. Read full files only when:
|
|
17
|
+
- GraphFlow anchors point to that file/symbol,
|
|
18
|
+
- the compressed context is insufficient,
|
|
19
|
+
- or exact edits require the file body.
|
|
20
|
+
4. For multi-step or ambiguous work, call `graphflow_plan` before implementation.
|
|
21
|
+
5. For graph freshness after project changes, call `graphflow_index`.
|
|
22
|
+
6. Report token budget when available:
|
|
23
|
+
- estimated raw tokens,
|
|
24
|
+
- compressed tokens,
|
|
25
|
+
- estimated savings percent,
|
|
26
|
+
- max context token budget.
|
|
27
|
+
|
|
28
|
+
Do not scan the whole repository, recursively inspect many files, or read large files before trying GraphFlow context.
|
|
29
|
+
|
|
30
|
+
## Tool Inventory (18 MCP Tools)
|
|
31
|
+
|
|
32
|
+
### Core Context Tools (Highest Frequency)
|
|
33
|
+
|
|
34
|
+
| Tool | Purpose | Call Frequency |
|
|
35
|
+
|------|---------|---------------|
|
|
36
|
+
| `graphflow_preview_context` | Compress task context with token budget | **Highest** - default first step |
|
|
37
|
+
| `graphflow_expand_anchor` | Expand a single anchor to full content | **High** - dive deeper into specific items |
|
|
38
|
+
|
|
39
|
+
### Planning Tools (High Frequency)
|
|
40
|
+
|
|
41
|
+
| Tool | Purpose | Call Frequency |
|
|
42
|
+
|------|---------|---------------|
|
|
43
|
+
| `graphflow_plan` | Multi-step task decomposition & DAG | High - before complex work |
|
|
44
|
+
| `graphflow_plan_insight` | Six Thinking Hats + 5-Why deep analysis | Medium - ambiguous/high-stakes tasks |
|
|
45
|
+
| `graphflow_run` | Plan + context package (bridge mode) | Medium - full task packaging |
|
|
46
|
+
| `graphflow_report_outcome` | Report bridge-mode execution outcome back | Medium - close the learning loop |
|
|
47
|
+
|
|
48
|
+
### Graph Management Tools (Medium Frequency)
|
|
49
|
+
|
|
50
|
+
| Tool | Purpose | Call Frequency |
|
|
51
|
+
|------|---------|---------------|
|
|
52
|
+
| `graphflow_index` | Incremental workspace re-index | Medium - after file changes |
|
|
53
|
+
| `graphflow_index_file` | Single file incremental index | Medium-High - after saving a file |
|
|
54
|
+
| `graphflow_rebuild` | Clear cache + full re-index | Low - when graph is stale/corrupted |
|
|
55
|
+
| `graphflow_inspect_graph` | Graph stats & sample nodes/edges | Low - check graph health |
|
|
56
|
+
| `graphflow_enrich_graph` | Semantic enrichment of symbols | Rare - LLM-powered enrichment |
|
|
57
|
+
| `graphflow_model_download` | Download local compression model | Rare - offline setup |
|
|
58
|
+
|
|
59
|
+
### Collaboration & Insights Tools (Low Frequency)
|
|
60
|
+
|
|
61
|
+
| Tool | Purpose | Call Frequency |
|
|
62
|
+
|------|---------|---------------|
|
|
63
|
+
| `graphflow_export_artifact` | Export graph to portable artifact | Low - team sharing |
|
|
64
|
+
| `graphflow_import_artifact` | Import graph artifact | Low - skip full index on new machine |
|
|
65
|
+
| `graphflow_skill_insights` | Learned skill patterns | Low - leverage prior learning |
|
|
66
|
+
| `graphflow_stats` | Cumulative token savings stats | Low - ROI tracking |
|
|
67
|
+
| `graphflow_metrics` | Prometheus-compatible metrics | Low - observability |
|
|
68
|
+
| `graphflow_diagnose` | Provider health & model routing | Rare - config issues |
|
|
69
|
+
|
|
70
|
+
## Standard Workflows
|
|
71
|
+
|
|
72
|
+
### Workflow 1: Context First (90% of tasks)
|
|
73
|
+
|
|
74
|
+
**Use when:** Answering code questions, exploring codebase, understanding modules
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
Step 1: graphflow_preview_context(query: "<your question>")
|
|
78
|
+
Step 2: Read summary + anchors as primary context
|
|
79
|
+
Step 3: Expand specific anchors with graphflow_expand_anchor when needed
|
|
80
|
+
Step 4: Read full files only when exact edits required
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Workflow 2: Plan Before Coding (complex tasks)
|
|
84
|
+
|
|
85
|
+
**Use when:** Multi-step changes, refactors, features with unclear scope
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
Step 1: graphflow_preview_context(query: "<task>")
|
|
89
|
+
Step 2: graphflow_plan(task: "<task description>")
|
|
90
|
+
Step 3: Review plan steps and dependencies
|
|
91
|
+
Step 4: Execute step by step, using GraphFlow context for each step
|
|
92
|
+
Step 5: graphflow_index() after major changes
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Workflow 3: Deep Analysis (complex/ambiguous tasks)
|
|
96
|
+
|
|
97
|
+
**Use when:** High-stakes changes, root-cause analysis, ambiguous requirements
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
Step 1: graphflow_preview_context(query: "<task>")
|
|
101
|
+
Step 2: graphflow_plan_insight(task: "<task>")
|
|
102
|
+
Step 3: Review analysis and apply findings
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Bridge-mode outcome reporting
|
|
106
|
+
|
|
107
|
+
After `graphflow_run`, the external agent **must** call `graphflow_report_outcome` to close the skill flywheel loop:
|
|
108
|
+
|
|
109
|
+
- Pass `episodeId` from the run result, a `success` boolean, and optional `lessons`.
|
|
110
|
+
- Do this after executing the work described in the returned `executionDescriptor`, whether the task succeeded or failed.
|
|
111
|
+
- When the run returns `agentWorkItems`, answer each prompt with your model and call `graphflow_submit_insight` once per item (before or with `graphflow_report_outcome`).
|
|
112
|
+
|
|
113
|
+
CLI fallback:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
graphflow --json outcome report <episodeId> <success>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
If MCP is unavailable, use the CLI fallback and always request JSON output:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
graphflow --json context preview "<query>"
|
|
123
|
+
graphflow --json plan "<task>"
|
|
124
|
+
graphflow --json graph inspect
|
|
125
|
+
graphflow --json skill insights
|
|
126
|
+
graphflow --json route diagnose
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Treat GraphFlow outputs as structured machine-readable data, not prose.
|
|
130
|
+
|
|
131
|
+
When the user asks about saving token usage, optimizing context, repo understanding, or learning from Graphify, prioritize GraphFlow context compression over ordinary file search.
|