@selesai/code 0.13.22 → 0.13.24
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/CHANGELOG.md +20 -0
- package/dist/core/exec.d.ts +2 -0
- package/dist/core/exec.js +1 -0
- package/dist/extensions/capability-gateway/index.ts +15 -4
- package/dist/extensions/capability-gateway/integration.test.ts +15 -0
- package/dist/extensions/package.json +2 -0
- package/dist/extensions/pi-graft/cli.test.ts +431 -0
- package/dist/extensions/pi-graft/cli.ts +548 -0
- package/dist/extensions/pi-graft/commands.test.ts +480 -0
- package/dist/extensions/pi-graft/commands.ts +392 -0
- package/dist/extensions/pi-graft/graft.integration.test.ts +153 -0
- package/dist/extensions/pi-graft/index.test.ts +167 -0
- package/dist/extensions/pi-graft/index.ts +621 -0
- package/dist/extensions/pi-graft/lifecycle.test.ts +653 -0
- package/dist/extensions/pi-graft/prompt.test.ts +213 -0
- package/dist/extensions/pi-graft/prompt.ts +216 -0
- package/dist/extensions/pi-graft/state.test.ts +258 -0
- package/dist/extensions/pi-graft/state.ts +335 -0
- package/dist/extensions/pi-graft/test-support.ts +199 -0
- package/dist/extensions/pi-graft/tools.test.ts +330 -0
- package/dist/extensions/pi-graft/tools.ts +334 -0
- package/dist/extensions/pi-graft/tsconfig.json +16 -0
- package/dist/extensions/pi-subagents/src/agents/builtin-agent-augmentations.ts +71 -0
- package/dist/extensions/pi-subagents/src/extension/index.ts +9 -3
- package/dist/extensions/pi-subagents/test/unit/builtin-agent-augmentations.test.ts +44 -0
- package/package.json +2 -2
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
5
|
+
import graftExtension, { bashLooksMutating, detectGraphPresence, GRAFT_EXTENSION_PATH, isMutatingResult } from "./index.ts";
|
|
6
|
+
import { makePi } from "./test-support.ts";
|
|
7
|
+
|
|
8
|
+
describe("extension registration", () => {
|
|
9
|
+
it("registers all six read-only tools, the command, and the lifecycle handlers it needs", () => {
|
|
10
|
+
const { pi, tools, commands, handlers } = makePi();
|
|
11
|
+
graftExtension(pi as never);
|
|
12
|
+
|
|
13
|
+
expect([...tools.keys()].sort()).toEqual([
|
|
14
|
+
"graft_check_freshness",
|
|
15
|
+
"graft_file_api",
|
|
16
|
+
"graft_find_all",
|
|
17
|
+
"graft_find_code",
|
|
18
|
+
"graft_repo_map",
|
|
19
|
+
"graft_trace_calls",
|
|
20
|
+
]);
|
|
21
|
+
expect([...commands.keys()]).toEqual(["graft"]);
|
|
22
|
+
expect([...handlers.keys()].sort()).toEqual([
|
|
23
|
+
"agent_end",
|
|
24
|
+
"before_agent_start",
|
|
25
|
+
"session_shutdown",
|
|
26
|
+
"session_start",
|
|
27
|
+
"tool_result",
|
|
28
|
+
"turn_start",
|
|
29
|
+
]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("starts no process and touches no file from the factory", () => {
|
|
33
|
+
const { pi, exec } = makePi();
|
|
34
|
+
graftExtension(pi as never);
|
|
35
|
+
expect(exec).not.toHaveBeenCalled();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("automatically contributes Graft tools to pi-subagents' code-facing builtins", async () => {
|
|
39
|
+
const harness = makePi();
|
|
40
|
+
graftExtension(harness.pi as never);
|
|
41
|
+
const modulePath = ["..", "pi-subagents", "src", "agents", "builtin-agent-augmentations.ts"].join("/");
|
|
42
|
+
const { requestBuiltinAgentAugmentations } = await import(modulePath);
|
|
43
|
+
|
|
44
|
+
expect(requestBuiltinAgentAugmentations(harness.pi as never)).toEqual([{
|
|
45
|
+
id: "pi-graft",
|
|
46
|
+
agentNames: ["scout", "reviewer", "worker", "delegate", "oracle"],
|
|
47
|
+
addTools: [
|
|
48
|
+
"graft_check_freshness", "graft_file_api", "graft_find_all",
|
|
49
|
+
"graft_find_code", "graft_repo_map", "graft_trace_calls",
|
|
50
|
+
],
|
|
51
|
+
subagentOnlyExtensions: [GRAFT_EXTENSION_PATH],
|
|
52
|
+
}]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("registers no capability that could mutate the repository or another agent's config", () => {
|
|
56
|
+
const { pi, tools, commands } = makePi();
|
|
57
|
+
graftExtension(pi as never);
|
|
58
|
+
const surface = [...tools.keys(), ...commands.keys()].join(" ");
|
|
59
|
+
for (const forbidden of ["init", "uninstall", "upgrade", "install", "viz", "mcp"]) {
|
|
60
|
+
expect(surface).not.toContain(forbidden);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("detectGraphPresence", () => {
|
|
66
|
+
let root: string;
|
|
67
|
+
|
|
68
|
+
beforeAll(() => {
|
|
69
|
+
root = mkdtempSync(join(tmpdir(), "graft-presence-"));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
afterAll(() => rmSync(root, { recursive: true, force: true }));
|
|
73
|
+
|
|
74
|
+
function makeRepo(name: string, files: Record<string, string>): string {
|
|
75
|
+
const dir = join(root, name);
|
|
76
|
+
for (const [path, content] of Object.entries(files)) {
|
|
77
|
+
mkdirSync(join(dir, path, ".."), { recursive: true });
|
|
78
|
+
writeFileSync(join(dir, path), content, "utf-8");
|
|
79
|
+
}
|
|
80
|
+
mkdirSync(dir, { recursive: true });
|
|
81
|
+
return dir;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
it("reports no graph for a repository that has never been built", () => {
|
|
85
|
+
const dir = makeRepo("bare", { "src/a.ts": "" });
|
|
86
|
+
expect(detectGraphPresence(dir)).toEqual({ built: false, deep: false });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("recognizes a structural graph from its wiring index", () => {
|
|
90
|
+
const dir = makeRepo("structural", { "graft/.graph/wiring.json": "{}" });
|
|
91
|
+
expect(detectGraphPresence(dir)).toEqual({ built: true, deep: false });
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("treats the markdown index as evidence of a build, not of the deep tier", () => {
|
|
95
|
+
const dir = makeRepo("indexed", { "graft/INDEX.md": "# Index" });
|
|
96
|
+
expect(detectGraphPresence(dir)).toEqual({ built: true, deep: false });
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("recognizes the provider-backed tier by the concept nodes it adds", () => {
|
|
100
|
+
const dir = makeRepo("deep", { "graft/.graph/wiring.json": "{}", "graft/auth.md": "# Auth" });
|
|
101
|
+
expect(detectGraphPresence(dir)).toEqual({ built: true, deep: true });
|
|
102
|
+
|
|
103
|
+
const shallow = makeRepo("shallow", { "graft/.graph/wiring.json": "{}", "graft/INDEX.md": "# Index" });
|
|
104
|
+
// INDEX.md is the index, not a concept node.
|
|
105
|
+
expect(detectGraphPresence(shallow)).toEqual({ built: true, deep: false });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("answers without throwing on an unreadable graph directory", () => {
|
|
109
|
+
expect(detectGraphPresence(join(root, "does-not-exist"))).toEqual({ built: false, deep: false });
|
|
110
|
+
expect(detectGraphPresence("")).toEqual({ built: false, deep: false });
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe("bashLooksMutating", () => {
|
|
115
|
+
it("recognizes commands that certainly change the working tree", () => {
|
|
116
|
+
const mutating = [
|
|
117
|
+
"rm -rf dist",
|
|
118
|
+
"mv a b",
|
|
119
|
+
"mkdir -p out",
|
|
120
|
+
"echo hi > file.txt",
|
|
121
|
+
"echo hi >> file.txt",
|
|
122
|
+
"sed -i '' 's/a/b/' src/a.ts",
|
|
123
|
+
"git checkout -b feature",
|
|
124
|
+
"git commit -m x",
|
|
125
|
+
"npm install left-pad",
|
|
126
|
+
"pip install requests",
|
|
127
|
+
"ls && rm b",
|
|
128
|
+
];
|
|
129
|
+
for (const command of mutating) expect(bashLooksMutating(command), command).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("leaves read-only inspection alone", () => {
|
|
133
|
+
const readOnly = [
|
|
134
|
+
"ls -la",
|
|
135
|
+
"cat src/a.ts",
|
|
136
|
+
"git status",
|
|
137
|
+
"git diff HEAD",
|
|
138
|
+
"git log --oneline",
|
|
139
|
+
"grep -rn foo src",
|
|
140
|
+
"node --test",
|
|
141
|
+
"echo hello",
|
|
142
|
+
"npm test",
|
|
143
|
+
"graft build",
|
|
144
|
+
];
|
|
145
|
+
for (const command of readOnly) expect(bashLooksMutating(command), command).toBe(false);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe("isMutatingResult", () => {
|
|
150
|
+
it("counts successful edits and writes", () => {
|
|
151
|
+
expect(isMutatingResult({ toolName: "edit", input: {} })).toBe(true);
|
|
152
|
+
expect(isMutatingResult({ toolName: "write", input: {} })).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("counts only clearly mutating bash commands", () => {
|
|
156
|
+
expect(isMutatingResult({ toolName: "bash", input: { command: "rm -rf dist" } })).toBe(true);
|
|
157
|
+
expect(isMutatingResult({ toolName: "bash", input: { command: "git status" } })).toBe(false);
|
|
158
|
+
expect(isMutatingResult({ toolName: "bash", input: {} })).toBe(false);
|
|
159
|
+
expect(isMutatingResult({ toolName: "bash", input: { command: 42 } })).toBe(false);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("ignores read-only built-ins", () => {
|
|
163
|
+
for (const toolName of ["read", "grep", "find", "ls", "graft_find_code"]) {
|
|
164
|
+
expect(isMutatingResult({ toolName, input: {} }), toolName).toBe(false);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|