@liuxincuit/pi-codegraph 0.1.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/CONTEXT.md +21 -0
- package/LICENSE +22 -0
- package/README.md +114 -0
- package/extensions/codegraph.ts +281 -0
- package/package.json +46 -0
- package/skills/codegraph/SKILL.md +27 -0
package/CONTEXT.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# pi-codegraph
|
|
2
|
+
|
|
3
|
+
A pi extension that gives the agent access to a CodeGraph index of the current project.
|
|
4
|
+
|
|
5
|
+
## Language
|
|
6
|
+
|
|
7
|
+
**Project**:
|
|
8
|
+
The directory the pi session runs in (`ctx.cwd`). The unit that CodeGraph indexes and the default scope of every query.
|
|
9
|
+
_Avoid_: workspace, repository, repo
|
|
10
|
+
|
|
11
|
+
**Index**:
|
|
12
|
+
The `.codegraph/` directory at the project root, holding the SQLite knowledge graph of the project's symbols, edges, and files. Built by `codegraph init`, updated by `codegraph sync`.
|
|
13
|
+
_Avoid_: database, cache, graph (ambiguous with the data structure)
|
|
14
|
+
|
|
15
|
+
**Explore**:
|
|
16
|
+
The single agent-facing query operation: a natural-language or symbol question answered with the relevant symbols' verbatim source plus the call paths between them.
|
|
17
|
+
_Avoid_: search, query, lookup
|
|
18
|
+
|
|
19
|
+
**Sync**:
|
|
20
|
+
An incremental update of the Index to match the files currently on disk. Cheap when nothing changed.
|
|
21
|
+
_Avoid_: refresh, rebuild (that's a full re-index)
|
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 izhimu
|
|
4
|
+
Copyright (c) 2026 liuxincuit
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# pi-codegraph
|
|
2
|
+
|
|
3
|
+
> 本仓库 fork 自 [izhimu/pi-codegraph](https://github.com/izhimu/pi-codegraph),原作者版权信息见 [LICENSE](LICENSE)。
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://github.com/earendil-works/pi)
|
|
7
|
+
|
|
8
|
+
为 [pi](https://github.com/earendil-works/pi) 提供 [CodeGraph](https://github.com/colbymchenry/codegraph) 支持:智能体将获得一个 `codegraph_explore` 工具——一次查询即可返回相关符号逐字、带行号的源码,以及它们之间的调用路径。
|
|
9
|
+
|
|
10
|
+
## 环境要求
|
|
11
|
+
|
|
12
|
+
`PATH` 中需要存在 `codegraph` CLI:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm i -g @colbymchenry/codegraph
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 安装
|
|
19
|
+
|
|
20
|
+
### npm(推荐)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pi install @liuxincuit/pi-codegraph
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 从 Git 安装
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# 全局安装
|
|
30
|
+
pi install git:github.com/liuxincuit/pi-codegraph
|
|
31
|
+
|
|
32
|
+
# 项目本地安装(通过 .pi/settings.json 与团队共享)
|
|
33
|
+
pi install git:github.com/liuxincuit/pi-codegraph -l
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 从本地路径安装
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pi install /path/to/pi-codegraph
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 快速测试(无需安装)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pi -e ./extensions/codegraph.ts
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 功能一览
|
|
49
|
+
|
|
50
|
+
- **`codegraph_explore` 工具** — 面向智能体的核心代码智能工具。输入符号名或自然语言问题即可查询;可选传 `path` 查询其他已建索引的项目,传 `maxFiles` 限制返回的源码行数。
|
|
51
|
+
- **`/codegraph-init [path]`** — 为项目建立索引(`codegraph init`)。
|
|
52
|
+
- **`/codegraph-sync [path]`** — 手动同步自上次索引以来的改动(`codegraph sync`)。
|
|
53
|
+
- **`/codegraph-status [path]`** — 查看索引状态与统计信息(`codegraph status`)。
|
|
54
|
+
- **`/codegraph-unlock [path]`** — 守护进程崩溃后释放过期的数据库锁(`codegraph unlock`)。
|
|
55
|
+
- **会话开始时自动同步** — 每个会话自动执行一次 `codegraph sync -q`,确保索引反映你最近的编辑。
|
|
56
|
+
|
|
57
|
+
## 使用方法
|
|
58
|
+
|
|
59
|
+
像往常一样提出结构性问题即可——项目建立索引后,智能体会优先调用 `codegraph_explore`:
|
|
60
|
+
|
|
61
|
+
- "会话加载是如何工作的?"
|
|
62
|
+
- "如果我修改 `ExtensionRunner.emit`,会破坏什么?"
|
|
63
|
+
|
|
64
|
+
在新项目中第一次使用 pi 时,请先手动建立一次索引:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
/codegraph-init
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
索引构建始终由你显式触发——智能体自身不会运行 `codegraph init`(见 `docs/adr/0002`)。
|
|
71
|
+
|
|
72
|
+
## 工作原理
|
|
73
|
+
|
|
74
|
+
pi 原生不支持 MCP,因此本扩展通过执行 CLI 来桥接 CodeGraph——`codegraph explore` 产生与 `codegraph_explore` MCP 工具相同的输出(见 `docs/adr/0001`)。每次工具调用仅需一次 `pi.exec`:没有守护进程、没有 JSON-RPC,没有可泄漏或需要恢复的状态。
|
|
75
|
+
|
|
76
|
+
`skills/codegraph/` 下的 `SKILL.md` 会被 pi 的技能系统自动发现,用于指导智能体在何种情况下优先使用该工具而非 grep/read。
|
|
77
|
+
|
|
78
|
+
## 项目结构
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
pi-codegraph/
|
|
82
|
+
├── extensions/
|
|
83
|
+
│ └── codegraph.ts # pi ExtensionAPI 集成
|
|
84
|
+
├── skills/
|
|
85
|
+
│ └── codegraph/
|
|
86
|
+
│ └── SKILL.md # codegraph_explore 的智能体使用指南
|
|
87
|
+
├── docs/adr/ # 设计决策记录
|
|
88
|
+
├── CONTEXT.md # 领域术语表
|
|
89
|
+
├── package.json # pi 包清单
|
|
90
|
+
├── tsconfig.json
|
|
91
|
+
├── LICENSE
|
|
92
|
+
└── README.md
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 开发
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm install
|
|
99
|
+
npm run typecheck
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## 贡献
|
|
103
|
+
|
|
104
|
+
欢迎提交贡献!请:
|
|
105
|
+
|
|
106
|
+
1. Fork 本仓库
|
|
107
|
+
2. 新建功能分支(`git checkout -b feat/amazing-feature`)
|
|
108
|
+
3. 提交改动(`git commit -m 'feat: add amazing feature'`)
|
|
109
|
+
4. 推送到分支(`git push origin feat/amazing-feature`)
|
|
110
|
+
5. 发起 Pull Request
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
[MIT](LICENSE) © [liuxincuit](https://github.com/liuxincuit),fork 自 [izhimu/pi-codegraph](https://github.com/izhimu/pi-codegraph)。
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// pi-codegraph — pi extension
|
|
2
|
+
//
|
|
3
|
+
// CodeGraph support for pi: codegraph_explore tool, /codegraph-init,
|
|
4
|
+
// /codegraph-sync, /codegraph-status, and /codegraph-unlock commands.
|
|
5
|
+
//
|
|
6
|
+
// Bridges to CodeGraph by executing the CLI (see docs/adr/0001). Requires the
|
|
7
|
+
// codegraph CLI on PATH: npm i -g @colbymchenry/codegraph
|
|
8
|
+
// Upstream: https://github.com/colbymchenry/codegraph
|
|
9
|
+
|
|
10
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
11
|
+
import { Type } from "typebox";
|
|
12
|
+
import * as fs from "node:fs/promises";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
|
|
15
|
+
const INSTALL_HINT =
|
|
16
|
+
"codegraph CLI not found on PATH. Install: npm i -g @colbymchenry/codegraph";
|
|
17
|
+
|
|
18
|
+
// Result of `codegraph version` this session — null until first check.
|
|
19
|
+
let cliAvailable: boolean | null = null;
|
|
20
|
+
let notifiedMissing = false;
|
|
21
|
+
|
|
22
|
+
async function execCg(
|
|
23
|
+
pi: ExtensionAPI,
|
|
24
|
+
args: string[],
|
|
25
|
+
options: { signal?: AbortSignal; timeout?: number; cwd?: string } = {},
|
|
26
|
+
) {
|
|
27
|
+
if (process.platform === "win32") {
|
|
28
|
+
return await pi.exec("cmd.exe", ["/d", "/s", "/c", "codegraph", ...args], options);
|
|
29
|
+
}
|
|
30
|
+
return await pi.exec("codegraph", args, options);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function ensureCli(pi: ExtensionAPI): Promise<boolean> {
|
|
34
|
+
if (cliAvailable !== null) return cliAvailable;
|
|
35
|
+
try {
|
|
36
|
+
const result = await execCg(pi, ["version"], { timeout: 10_000 });
|
|
37
|
+
cliAvailable = result.code === 0;
|
|
38
|
+
} catch {
|
|
39
|
+
cliAvailable = false;
|
|
40
|
+
}
|
|
41
|
+
return cliAvailable;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function textResult(text: string) {
|
|
45
|
+
return { content: [{ type: "text" as const, text }], details: undefined };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function outputOf(result: { stdout: string; stderr: string; code: number }): string {
|
|
49
|
+
return (result.stdout + result.stderr).trim() || `exit ${result.code}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function isIndexed(cwd: string): Promise<boolean> {
|
|
53
|
+
try {
|
|
54
|
+
await fs.access(path.join(cwd, ".codegraph", "codegraph.db"));
|
|
55
|
+
return true;
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type StatusState = "index" | "sync" | "init" | boolean | undefined;
|
|
62
|
+
|
|
63
|
+
function updateStatusBar(ctx: ExtensionContext, state: StatusState) {
|
|
64
|
+
if (!ctx.hasUI) return;
|
|
65
|
+
const label = state === true ? "index" : state === false ? undefined : state;
|
|
66
|
+
if (label) {
|
|
67
|
+
const text = ctx.ui.theme?.fg
|
|
68
|
+
? `${ctx.ui.theme.fg("accent", "⬡")} ${label}`
|
|
69
|
+
: `⬡ ${label}`;
|
|
70
|
+
ctx.ui.setStatus("codegraph", text);
|
|
71
|
+
} else {
|
|
72
|
+
ctx.ui.setStatus("codegraph", undefined);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Modeled on upstream's MCP SERVER_INSTRUCTIONS (src/mcp/server-instructions.ts):
|
|
77
|
+
// lead the agent to codegraph_explore BEFORE grep/read, plus anti-patterns and staleness handling.
|
|
78
|
+
const INDEX_HINT = `# CodeGraph — this project is indexed
|
|
79
|
+
|
|
80
|
+
A \`.codegraph/\` index exists here: SQLite knowledge graph of every symbol, edge, and file (30+ languages). ONE \`codegraph_explore\` call returns the relevant symbols' verbatim line-numbered source (treat it as already Read — safe to Edit from) PLUS call paths between them and a blast-radius summary of what depends on them.
|
|
81
|
+
|
|
82
|
+
- For structural questions (how does X work / where is X / who calls Y / what breaks if I change Z), call \`codegraph_explore\` INSTEAD of grep + read — usually ONE call answers the whole question.
|
|
83
|
+
- Call it BEFORE and WHILE writing or editing code: it puts the blast radius in view before you touch a symbol you can name.
|
|
84
|
+
- Flow tracing: name endpoint symbols (e.g. \`mutateElement renderScene\`) to surface the path across dynamic-dispatch hops.
|
|
85
|
+
- Anti-patterns: don't grep or Read first; don't re-verify codegraph output with grep (AST-derived, more accurate than grep); don't reconstruct a flow by hand.
|
|
86
|
+
- "Already sent earlier in this conversation": pointer means content is already in context — do not re-fetch or Read.
|
|
87
|
+
- Staleness: if tool output contains "⚠️ Some files referenced below were edited since the last index sync", read only those flagged files directly.
|
|
88
|
+
- Multi-project / Monorepo: pass \`path\` to query any indexed sub-project directory.
|
|
89
|
+
- If a project has no \`.codegraph/\`, use built-in tools there; indexing is the user's decision — suggest /codegraph-init if it comes up.`;
|
|
90
|
+
|
|
91
|
+
export default function codegraphExtension(pi: ExtensionAPI) {
|
|
92
|
+
// ── codegraph_explore tool ─────────────────────────────────────────────
|
|
93
|
+
pi.registerTool({
|
|
94
|
+
name: "codegraph_explore",
|
|
95
|
+
label: "CodeGraph Explore",
|
|
96
|
+
description:
|
|
97
|
+
"PRIMARY tool for code questions — call it BEFORE grep/read when the project has a .codegraph/ index. " +
|
|
98
|
+
"One query returns the relevant symbols' verbatim line-numbered source plus the call paths between them and a blast-radius summary. " +
|
|
99
|
+
"If the project is not indexed the output says so: continue with built-in tools and suggest the user run /codegraph-init.",
|
|
100
|
+
promptSnippet:
|
|
101
|
+
"codegraph_explore: symbol source + call paths in one shot from the project's CodeGraph index",
|
|
102
|
+
promptGuidelines: [
|
|
103
|
+
"For structural code questions (how does X work, where is X, what breaks if I change X), prefer codegraph_explore over grep when the project has a .codegraph/ index.",
|
|
104
|
+
"Indexing is the user's decision — never run codegraph init yourself; suggest /codegraph-init instead.",
|
|
105
|
+
],
|
|
106
|
+
parameters: Type.Object({
|
|
107
|
+
query: Type.String({
|
|
108
|
+
description: "Symbol names or a natural-language question about the code",
|
|
109
|
+
}),
|
|
110
|
+
path: Type.Optional(
|
|
111
|
+
Type.String({
|
|
112
|
+
description: "Project path to query; defaults to the current working directory",
|
|
113
|
+
}),
|
|
114
|
+
),
|
|
115
|
+
maxFiles: Type.Optional(
|
|
116
|
+
Type.Integer({
|
|
117
|
+
description: "Maximum number of files to include source from",
|
|
118
|
+
}),
|
|
119
|
+
),
|
|
120
|
+
}),
|
|
121
|
+
execute: async (_toolCallId, params, signal, _onUpdate, ctx) => {
|
|
122
|
+
if (!(await ensureCli(pi))) return textResult(INSTALL_HINT);
|
|
123
|
+
const cwd = params.path ?? ctx.cwd;
|
|
124
|
+
const args = ["explore", params.query, "-p", cwd];
|
|
125
|
+
if (typeof params.maxFiles === "number" && params.maxFiles > 0) {
|
|
126
|
+
args.push("--max-files", String(params.maxFiles));
|
|
127
|
+
}
|
|
128
|
+
const result = await execCg(pi, args, {
|
|
129
|
+
signal,
|
|
130
|
+
timeout: 120_000,
|
|
131
|
+
});
|
|
132
|
+
if (result.killed) return textResult("codegraph explore timed out (120s)");
|
|
133
|
+
// Non-zero exits carry upstream's agent-friendly guidance (e.g. the
|
|
134
|
+
// "not initialized" message) — pass it through verbatim.
|
|
135
|
+
if (result.code !== 0) return textResult(outputOf(result));
|
|
136
|
+
return textResult(result.stdout.trim());
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// ── /codegraph-init ────────────────────────────────────────────────────
|
|
141
|
+
pi.registerCommand("codegraph-init", {
|
|
142
|
+
description: "Build the CodeGraph index for the current project (codegraph init)",
|
|
143
|
+
handler: async (args, ctx) => {
|
|
144
|
+
if (!(await ensureCli(pi))) {
|
|
145
|
+
if (ctx.hasUI) ctx.ui.notify(INSTALL_HINT, "error");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const target = args?.trim() || ctx.cwd;
|
|
149
|
+
if (ctx.hasUI) {
|
|
150
|
+
ctx.ui.notify(`Indexing ${target} — can take minutes on a large repo…`, "info");
|
|
151
|
+
}
|
|
152
|
+
updateStatusBar(ctx, "init");
|
|
153
|
+
try {
|
|
154
|
+
const result = await execCg(pi, ["init", target], { timeout: 1_800_000 });
|
|
155
|
+
const out = outputOf(result);
|
|
156
|
+
const success = result.code === 0;
|
|
157
|
+
if (ctx.hasUI) {
|
|
158
|
+
ctx.ui.notify(
|
|
159
|
+
success ? "CodeGraph index built." : `codegraph init failed (exit ${result.code})`,
|
|
160
|
+
success ? "info" : "error",
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
pi.sendMessage(
|
|
164
|
+
{ customType: "codegraph-init", content: out, display: true },
|
|
165
|
+
{ triggerTurn: false },
|
|
166
|
+
);
|
|
167
|
+
} finally {
|
|
168
|
+
updateStatusBar(ctx, await isIndexed(ctx.cwd));
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// ── /codegraph-sync ────────────────────────────────────────────────────
|
|
174
|
+
pi.registerCommand("codegraph-sync", {
|
|
175
|
+
description: "Sync CodeGraph changes since last index (codegraph sync)",
|
|
176
|
+
handler: async (args, ctx) => {
|
|
177
|
+
if (!(await ensureCli(pi))) {
|
|
178
|
+
if (ctx.hasUI) ctx.ui.notify(INSTALL_HINT, "error");
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const target = args?.trim() || ctx.cwd;
|
|
182
|
+
if (ctx.hasUI) {
|
|
183
|
+
ctx.ui.notify(`Syncing CodeGraph for ${target}…`, "info");
|
|
184
|
+
}
|
|
185
|
+
updateStatusBar(ctx, "sync");
|
|
186
|
+
try {
|
|
187
|
+
const result = await execCg(pi, ["sync", target], { timeout: 300_000 });
|
|
188
|
+
const out = outputOf(result);
|
|
189
|
+
if (ctx.hasUI) {
|
|
190
|
+
ctx.ui.notify(out, result.code === 0 ? "info" : "warning");
|
|
191
|
+
}
|
|
192
|
+
pi.sendMessage(
|
|
193
|
+
{ customType: "codegraph-sync", content: out, display: true },
|
|
194
|
+
{ triggerTurn: false },
|
|
195
|
+
);
|
|
196
|
+
} finally {
|
|
197
|
+
updateStatusBar(ctx, await isIndexed(ctx.cwd));
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// ── /codegraph-status ──────────────────────────────────────────────────
|
|
203
|
+
pi.registerCommand("codegraph-status", {
|
|
204
|
+
description: "Show CodeGraph index status and statistics",
|
|
205
|
+
handler: async (args, ctx) => {
|
|
206
|
+
if (!(await ensureCli(pi))) {
|
|
207
|
+
if (ctx.hasUI) ctx.ui.notify(INSTALL_HINT, "error");
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const target = args?.trim() || ctx.cwd;
|
|
211
|
+
const result = await execCg(pi, ["status", target], { timeout: 30_000 });
|
|
212
|
+
const out = outputOf(result);
|
|
213
|
+
if (ctx.hasUI) ctx.ui.notify(out, result.code === 0 ? "info" : "warning");
|
|
214
|
+
updateStatusBar(ctx, await isIndexed(ctx.cwd));
|
|
215
|
+
pi.sendMessage(
|
|
216
|
+
{ customType: "codegraph-status", content: out, display: true },
|
|
217
|
+
{ triggerTurn: false },
|
|
218
|
+
);
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// ── /codegraph-unlock ──────────────────────────────────────────────────
|
|
223
|
+
pi.registerCommand("codegraph-unlock", {
|
|
224
|
+
description: "Release stale CodeGraph database lock (codegraph unlock)",
|
|
225
|
+
handler: async (args, ctx) => {
|
|
226
|
+
if (!(await ensureCli(pi))) {
|
|
227
|
+
if (ctx.hasUI) ctx.ui.notify(INSTALL_HINT, "error");
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const target = args?.trim() || ctx.cwd;
|
|
231
|
+
const result = await execCg(pi, ["unlock", target], { timeout: 10_000 });
|
|
232
|
+
const out = outputOf(result);
|
|
233
|
+
if (ctx.hasUI) ctx.ui.notify(out, result.code === 0 ? "info" : "warning");
|
|
234
|
+
pi.sendMessage(
|
|
235
|
+
{ customType: "codegraph-unlock", content: out, display: true },
|
|
236
|
+
{ triggerTurn: false },
|
|
237
|
+
);
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// ── session_start: CLI check + incremental sync + context hint ────────
|
|
242
|
+
pi.on("session_start", async (event, ctx) => {
|
|
243
|
+
if (!(await ensureCli(pi))) {
|
|
244
|
+
updateStatusBar(ctx, false);
|
|
245
|
+
if (ctx.hasUI && !notifiedMissing) {
|
|
246
|
+
notifiedMissing = true;
|
|
247
|
+
ctx.ui.notify(INSTALL_HINT, "warning");
|
|
248
|
+
}
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const indexed = await isIndexed(ctx.cwd);
|
|
253
|
+
if (indexed) {
|
|
254
|
+
updateStatusBar(ctx, "sync");
|
|
255
|
+
// Incremental sync; near-zero cost when nothing changed.
|
|
256
|
+
void execCg(pi, ["sync", "-q", ctx.cwd], { timeout: 300_000 })
|
|
257
|
+
.catch(() => {})
|
|
258
|
+
.finally(async () => {
|
|
259
|
+
updateStatusBar(ctx, await isIndexed(ctx.cwd));
|
|
260
|
+
});
|
|
261
|
+
} else {
|
|
262
|
+
updateStatusBar(ctx, false);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Inject the agent playbook once per process when the project IS
|
|
266
|
+
// indexed (upstream does this via MCP initialize instructions). Skip
|
|
267
|
+
// "reload": extensions rebind in place and the message would duplicate.
|
|
268
|
+
if (event.reason !== "reload" && indexed) {
|
|
269
|
+
pi.sendMessage(
|
|
270
|
+
{ customType: "codegraph-context", content: INDEX_HINT, display: false },
|
|
271
|
+
{ triggerTurn: false },
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
277
|
+
if (ctx.hasUI) {
|
|
278
|
+
ctx.ui.setStatus("codegraph", undefined);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@liuxincuit/pi-codegraph",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CodeGraph support for pi — symbol source + call paths via the codegraph CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"pi-package",
|
|
8
|
+
"codegraph",
|
|
9
|
+
"code-intelligence",
|
|
10
|
+
"knowledge-graph"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/liuxincuit/pi-codegraph.git"
|
|
15
|
+
},
|
|
16
|
+
"author": "liuxincuit",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
23
|
+
"@types/node": "^22.0.0",
|
|
24
|
+
"typebox": "^1.3.7",
|
|
25
|
+
"typescript": "^5.5.0"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"smoke": "tsc extensions/codegraph.ts --outDir .smoke-build --module esnext --target es2022 --moduleResolution bundler --skipLibCheck --noEmit false && node smoke.mjs"
|
|
30
|
+
},
|
|
31
|
+
"pi": {
|
|
32
|
+
"extensions": [
|
|
33
|
+
"./extensions"
|
|
34
|
+
],
|
|
35
|
+
"skills": [
|
|
36
|
+
"./skills"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"extensions/",
|
|
41
|
+
"skills/",
|
|
42
|
+
"CONTEXT.md",
|
|
43
|
+
"README.md",
|
|
44
|
+
"LICENSE"
|
|
45
|
+
]
|
|
46
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codegraph
|
|
3
|
+
description: Query the project's CodeGraph index (symbol source + call paths) via the codegraph_explore tool. Use when answering structural code questions — how X works, where X is, what a change affects — in a project that has a .codegraph/ index.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CodeGraph
|
|
7
|
+
|
|
8
|
+
The `codegraph_explore` tool answers structural code questions in one shot: the relevant symbols' verbatim line-numbered source, the call paths between them, and a blast-radius summary. It reads the project's CodeGraph index (`.codegraph/`), built and maintained by the `codegraph` CLI.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
|
|
12
|
+
- "How does X work?" / "Where is X?" / "What calls Y?" / "What breaks if I change Z?"
|
|
13
|
+
- Before an edit, to map the symbols you are about to touch and inspect the blast radius.
|
|
14
|
+
- Prefer it over grep/read for structural exploration **when the project has a `.codegraph/` index**.
|
|
15
|
+
|
|
16
|
+
## How to query
|
|
17
|
+
|
|
18
|
+
- `query`: symbol names (`CodeGraph open`, `MCPSession`), endpoint flows (`mutateElement renderScene`), or a natural-language question. Naming a file or symbol returns its current line-numbered source.
|
|
19
|
+
- `path`: optional project path. Defaults to the current working directory. In a monorepo, pass the sub-project directory that contains `.codegraph/`.
|
|
20
|
+
- `maxFiles`: optional integer to limit how many file sources are returned.
|
|
21
|
+
|
|
22
|
+
## Anti-patterns & Guidance
|
|
23
|
+
|
|
24
|
+
- **Trust AST results.** Don't re-verify codegraph output with grep.
|
|
25
|
+
- **Already sent earlier in this conversation.** When this pointer appears, the lines are already in your session context — scroll back instead of re-fetching or reading the file.
|
|
26
|
+
- **Staleness banner.** If output warns `⚠️ Some files referenced below were edited since the last index sync`, read only those specific files directly; other files in the response remain fresh.
|
|
27
|
+
- **No index, no tool.** If the output says the project isn't indexed, stop calling `codegraph_explore` for that project this session and use built-in tools. Indexing is the user's decision — suggest the user run `/codegraph-init` if appropriate.
|