@kuznai/inception-engine 0.22.0 → 0.24.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 +50 -9
- package/dist/src/config/agents.js +39 -0
- package/dist/src/core/adapters/execution-config.d.ts +14 -0
- package/dist/src/core/adapters/execution-config.js +60 -0
- package/dist/src/core/adapters/index.d.ts +4 -3
- package/dist/src/core/adapters/index.js +7 -5
- package/dist/src/core/adapters/rules.js +50 -19
- package/dist/src/core/capabilities.d.ts +1 -1
- package/dist/src/core/capabilities.js +18 -2
- package/dist/src/core/deploy.js +66 -2
- package/dist/src/core/init.js +69 -13
- package/dist/src/core/ownership.d.ts +1 -0
- package/dist/src/core/ownership.js +34 -4
- package/dist/src/core/preflight.js +28 -0
- package/dist/src/core/revert.js +86 -18
- package/dist/src/core/validation.d.ts +1 -1
- package/dist/src/core/validation.js +56 -3
- package/dist/src/schemas/manifest.d.ts +31 -0
- package/dist/src/schemas/manifest.js +39 -3
- package/dist/src/types.d.ts +6 -2
- package/dist/test/helpers/path.d.ts +2 -2
- package/dist/test/helpers/path.js +3 -3
- package/dist/test/os/posix/deploy.test.js +395 -0
- package/dist/test/os/posix/revert.test.js +188 -0
- package/dist/test/os/windows/deploy.test.d.ts +1 -0
- package/dist/test/os/windows/deploy.test.js +238 -0
- package/dist/test/unit/adapters.test.js +402 -2
- package/dist/test/unit/capabilities.test.js +44 -1
- package/dist/test/unit/cli.test.js +162 -0
- package/dist/test/unit/deploy.test.js +88 -647
- package/dist/test/unit/formatters.test.d.ts +1 -0
- package/dist/test/unit/formatters.test.js +74 -0
- package/dist/test/unit/init-fixture.test.js +85 -1
- package/dist/test/unit/manifest.test.js +72 -0
- package/dist/test/unit/ownership.test.js +61 -4
- package/dist/test/unit/preflight.test.js +145 -0
- package/dist/test/unit/resolve.test.js +67 -1
- package/dist/test/unit/revert.test.js +81 -182
- package/package.json +1 -1
- package/dist/test/os/windows/agentRules-integration.test.js +0 -245
- package/dist/test/os/windows/revert-integration.test.js +0 -72
- /package/dist/test/os/{windows/agentRules-integration.test.d.ts → posix/deploy.test.d.ts} +0 -0
- /package/dist/test/os/{windows/revert-integration.test.d.ts → posix/revert.test.d.ts} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { chmod, mkdir, readFile, rm,
|
|
2
|
+
import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { describe, it } from "node:test";
|
|
5
5
|
import { lookupDeployment, registerDeployment, } from "../../src/core/ownership.js";
|
|
@@ -29,6 +29,41 @@ describe("planRevert", () => {
|
|
|
29
29
|
const actions = planRevert(testManifest, ["codex"], "/home/test");
|
|
30
30
|
assert.equal(actions.length, 0);
|
|
31
31
|
});
|
|
32
|
+
it("includes hook and execution-config reverts for detected agents", () => {
|
|
33
|
+
const manifest = {
|
|
34
|
+
...testManifest,
|
|
35
|
+
hooks: [
|
|
36
|
+
{
|
|
37
|
+
name: "pre-exec-check",
|
|
38
|
+
agents: ["claude-code"],
|
|
39
|
+
config: {
|
|
40
|
+
hooks: {
|
|
41
|
+
PreToolUse: [
|
|
42
|
+
{
|
|
43
|
+
matcher: "Bash",
|
|
44
|
+
hooks: [{ type: "command", command: "scripts/check.sh" }],
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
executionConfigs: [
|
|
52
|
+
{
|
|
53
|
+
name: "gemini-safety",
|
|
54
|
+
agents: ["gemini-cli"],
|
|
55
|
+
config: { safeMode: true },
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
const actions = planRevert(manifest, ["claude-code", "gemini-cli"], "/home/test");
|
|
60
|
+
assert.ok(actions.some((action) => action.kind === "config-patch" &&
|
|
61
|
+
action.agent === "claude-code" &&
|
|
62
|
+
action.skill === "pre-exec-check"), "expected hook revert action for Claude Code");
|
|
63
|
+
assert.ok(actions.some((action) => action.kind === "config-patch" &&
|
|
64
|
+
action.agent === "gemini-cli" &&
|
|
65
|
+
action.skill === "gemini-safety"), "expected execution config revert action for Gemini CLI");
|
|
66
|
+
});
|
|
32
67
|
});
|
|
33
68
|
describe("planRevertAll", () => {
|
|
34
69
|
it("creates revert actions for all agents in manifest regardless of detection", () => {
|
|
@@ -87,193 +122,59 @@ describe("planRevertAll", () => {
|
|
|
87
122
|
assert.match(targets.get("antigravity") ?? "", /\.gemini[\\/]antigravity[\\/]skills[\\/]test-skill$/);
|
|
88
123
|
assert.match(targets.get("opencode") ?? "", /opencode[\\/]skills[\\/]test-skill$/);
|
|
89
124
|
});
|
|
125
|
+
it("includes hook and execution-config reverts for all manifest agents", () => {
|
|
126
|
+
const manifest = {
|
|
127
|
+
...testManifest,
|
|
128
|
+
hooks: [
|
|
129
|
+
{
|
|
130
|
+
name: "pre-exec-check",
|
|
131
|
+
agents: ["claude-code"],
|
|
132
|
+
config: {
|
|
133
|
+
hooks: {
|
|
134
|
+
Stop: [{ hooks: [{ type: "command", command: "notify.sh" }] }],
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
executionConfigs: [
|
|
140
|
+
{
|
|
141
|
+
name: "gemini-safety",
|
|
142
|
+
agents: ["gemini-cli"],
|
|
143
|
+
config: { safeMode: true },
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
};
|
|
147
|
+
const actions = planRevertAll(manifest, "/home/test");
|
|
148
|
+
assert.ok(actions.some((action) => action.kind === "config-patch" &&
|
|
149
|
+
action.agent === "claude-code" &&
|
|
150
|
+
action.skill === "pre-exec-check"), "expected planRevertAll to include Claude hook revert action");
|
|
151
|
+
assert.ok(actions.some((action) => action.kind === "config-patch" &&
|
|
152
|
+
action.agent === "gemini-cli" &&
|
|
153
|
+
action.skill === "gemini-safety"), "expected planRevertAll to include Gemini execution config revert action");
|
|
154
|
+
});
|
|
90
155
|
});
|
|
91
|
-
describe("executeRevert
|
|
92
|
-
it("
|
|
156
|
+
describe("executeRevert reserved state protections", () => {
|
|
157
|
+
it("refuses to modify inception-engine state paths from manifest-managed actions", async () => {
|
|
93
158
|
const home = await makeTmpDir();
|
|
94
|
-
const sourceDir = await makeTmpDir();
|
|
95
159
|
try {
|
|
96
|
-
const
|
|
97
|
-
const target = actions[0]?.target;
|
|
98
|
-
// Create symlink and register in registry
|
|
160
|
+
const target = path.join(home, ".inception-engine", "registry.json");
|
|
99
161
|
await mkdir(path.dirname(target), { recursive: true });
|
|
100
|
-
await
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const { succeeded, skipped } = await executeRevert(actions, false, false, home);
|
|
110
|
-
assert.equal(succeeded, 1);
|
|
111
|
-
assert.equal(skipped, 0);
|
|
112
|
-
assert.ok(!(await exists(target)));
|
|
113
|
-
// Registry entry should be removed
|
|
114
|
-
const entry = await lookupDeployment(home, target);
|
|
115
|
-
assert.equal(entry, null);
|
|
116
|
-
}
|
|
117
|
-
finally {
|
|
118
|
-
await rm(home, { recursive: true, force: true });
|
|
119
|
-
await rm(sourceDir, { recursive: true });
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
it("removes a copied directory registered in the deployment registry", async () => {
|
|
123
|
-
const home = await makeTmpDir();
|
|
124
|
-
try {
|
|
125
|
-
const actions = planRevert(testManifest, ["claude-code"], home);
|
|
126
|
-
const target = actions[0]?.target;
|
|
127
|
-
await mkdir(target, { recursive: true });
|
|
128
|
-
await writeFile(path.join(target, "SKILL.md"), "test");
|
|
129
|
-
await registerDeployment(home, target, {
|
|
130
|
-
kind: "skill-dir",
|
|
131
|
-
source: "/original/source",
|
|
132
|
-
skill: "test-skill",
|
|
133
|
-
agent: "claude-code",
|
|
134
|
-
method: "copy",
|
|
135
|
-
});
|
|
136
|
-
const { succeeded, skipped } = await executeRevert(actions, false, false, home);
|
|
137
|
-
assert.equal(succeeded, 1);
|
|
138
|
-
assert.equal(skipped, 0);
|
|
139
|
-
assert.ok(!(await exists(target)));
|
|
140
|
-
// Registry entry should be removed
|
|
141
|
-
const entry = await lookupDeployment(home, target);
|
|
142
|
-
assert.equal(entry, null);
|
|
143
|
-
}
|
|
144
|
-
finally {
|
|
145
|
-
await rm(home, { recursive: true, force: true });
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
it("skips missing targets", async () => {
|
|
149
|
-
const home = await makeTmpDir();
|
|
150
|
-
try {
|
|
151
|
-
const actions = planRevert(testManifest, ["claude-code"], home);
|
|
152
|
-
const { succeeded, skipped } = await executeRevert(actions, false, false, home);
|
|
153
|
-
assert.equal(succeeded, 0);
|
|
154
|
-
assert.equal(skipped, 1);
|
|
155
|
-
}
|
|
156
|
-
finally {
|
|
157
|
-
await rm(home, { recursive: true });
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
it("records a failure when removal is blocked by permissions", async () => {
|
|
161
|
-
const home = await makeTmpDir();
|
|
162
|
-
const sourceDir = await makeTmpDir();
|
|
163
|
-
const actions = planRevert(testManifest, ["claude-code"], home);
|
|
164
|
-
const target = actions[0]?.target ?? "";
|
|
165
|
-
const targetParent = path.dirname(target);
|
|
166
|
-
try {
|
|
167
|
-
// Create the target directory and register it
|
|
168
|
-
await mkdir(target, { recursive: true });
|
|
169
|
-
await writeFile(path.join(target, "SKILL.md"), "test");
|
|
170
|
-
await registerDeployment(home, target, {
|
|
171
|
-
kind: "skill-dir",
|
|
172
|
-
source: sourceDir,
|
|
173
|
-
skill: "test-skill",
|
|
174
|
-
agent: "claude-code",
|
|
175
|
-
method: "copy",
|
|
176
|
-
});
|
|
177
|
-
// Make the parent directory non-writable so rm() fails
|
|
178
|
-
await chmod(targetParent, 0o555);
|
|
179
|
-
const { succeeded, skipped, failed } = await executeRevert(actions, false, false, home);
|
|
162
|
+
await writeFile(target, "{}");
|
|
163
|
+
const { failed, succeeded } = await executeRevert([
|
|
164
|
+
{
|
|
165
|
+
kind: "file-write",
|
|
166
|
+
skill: "state-target",
|
|
167
|
+
agent: "claude-code",
|
|
168
|
+
target,
|
|
169
|
+
},
|
|
170
|
+
], false, false, home);
|
|
180
171
|
assert.equal(succeeded, 0);
|
|
181
|
-
assert.equal(skipped, 0);
|
|
182
172
|
assert.equal(failed.length, 1);
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
await chmod(targetParent, 0o755);
|
|
186
|
-
await rm(home, { recursive: true, force: true });
|
|
187
|
-
await rm(sourceDir, { recursive: true, force: true });
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
it("does not remove in dry-run mode", async () => {
|
|
191
|
-
const home = await makeTmpDir();
|
|
192
|
-
const sourceDir = await makeTmpDir();
|
|
193
|
-
try {
|
|
194
|
-
const actions = planRevert(testManifest, ["claude-code"], home);
|
|
195
|
-
const target = actions[0]?.target;
|
|
196
|
-
await mkdir(path.dirname(target), { recursive: true });
|
|
197
|
-
await symlink(sourceDir, target, "dir");
|
|
198
|
-
await registerDeployment(home, target, {
|
|
199
|
-
kind: "skill-dir",
|
|
200
|
-
source: sourceDir,
|
|
201
|
-
skill: "test-skill",
|
|
202
|
-
agent: "claude-code",
|
|
203
|
-
method: "symlink",
|
|
204
|
-
});
|
|
205
|
-
const { succeeded } = await executeRevert(actions, true, false, home);
|
|
206
|
-
assert.equal(succeeded, 1);
|
|
207
|
-
assert.ok(await exists(target), "symlink should still exist after dry-run");
|
|
208
|
-
// Registry entry should still exist after dry-run
|
|
209
|
-
const entry = await lookupDeployment(home, target);
|
|
210
|
-
assert.ok(entry, "registry entry should still exist after dry-run");
|
|
211
|
-
}
|
|
212
|
-
finally {
|
|
213
|
-
await rm(home, { recursive: true, force: true });
|
|
214
|
-
await rm(sourceDir, { recursive: true });
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
it("skips target that exists but is not in registry", async () => {
|
|
218
|
-
const home = await makeTmpDir();
|
|
219
|
-
const sourceDir = await makeTmpDir();
|
|
220
|
-
try {
|
|
221
|
-
const actions = planRevert(testManifest, ["claude-code"], home);
|
|
222
|
-
const target = actions[0]?.target;
|
|
223
|
-
// Create symlink but do NOT register — should be treated as unmanaged
|
|
224
|
-
await writeFile(path.join(sourceDir, "SKILL.md"), "---");
|
|
225
|
-
await mkdir(path.dirname(target), { recursive: true });
|
|
226
|
-
await symlink(sourceDir, target, "dir");
|
|
227
|
-
const { succeeded, skipped } = await executeRevert(actions, false, false, home);
|
|
228
|
-
assert.equal(succeeded, 0);
|
|
229
|
-
assert.equal(skipped, 1);
|
|
230
|
-
assert.ok(await exists(target), "symlink should still exist — not managed");
|
|
231
|
-
}
|
|
232
|
-
finally {
|
|
233
|
-
await rm(home, { recursive: true, force: true });
|
|
234
|
-
await rm(sourceDir, { recursive: true });
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
it("skips directory that exists but is not in registry", async () => {
|
|
238
|
-
const home = await makeTmpDir();
|
|
239
|
-
try {
|
|
240
|
-
const actions = planRevert(testManifest, ["claude-code"], home);
|
|
241
|
-
const target = actions[0]?.target;
|
|
242
|
-
await mkdir(target, { recursive: true });
|
|
243
|
-
await writeFile(path.join(target, "SKILL.md"), "user content");
|
|
244
|
-
const { succeeded, skipped } = await executeRevert(actions, false, false, home);
|
|
245
|
-
assert.equal(succeeded, 0);
|
|
246
|
-
assert.equal(skipped, 1);
|
|
247
|
-
assert.ok(await exists(target), "directory should still exist — not in registry");
|
|
248
|
-
}
|
|
249
|
-
finally {
|
|
250
|
-
await rm(home, { recursive: true, force: true });
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
it("skips target whose registry entry has mismatched skill or agent", async () => {
|
|
254
|
-
const home = await makeTmpDir();
|
|
255
|
-
const sourceDir = await makeTmpDir();
|
|
256
|
-
try {
|
|
257
|
-
const actions = planRevert(testManifest, ["claude-code"], home);
|
|
258
|
-
const target = actions[0]?.target;
|
|
259
|
-
// Create symlink and register under a different skill/agent
|
|
260
|
-
await mkdir(path.dirname(target), { recursive: true });
|
|
261
|
-
await symlink(sourceDir, target, "dir");
|
|
262
|
-
await registerDeployment(home, target, {
|
|
263
|
-
kind: "skill-dir",
|
|
264
|
-
source: sourceDir,
|
|
265
|
-
skill: "different-skill",
|
|
266
|
-
agent: "codex",
|
|
267
|
-
method: "symlink",
|
|
268
|
-
});
|
|
269
|
-
const { succeeded, skipped } = await executeRevert(actions, false, false, home);
|
|
270
|
-
assert.equal(succeeded, 0);
|
|
271
|
-
assert.equal(skipped, 1);
|
|
272
|
-
assert.ok(await exists(target), "symlink should still exist — registry entry does not match");
|
|
173
|
+
assert.match(failed[0]?.error ?? "", /Refusing to modify inception-engine state directory/);
|
|
174
|
+
assert.equal(await readFile(target, "utf-8"), "{}");
|
|
273
175
|
}
|
|
274
176
|
finally {
|
|
275
177
|
await rm(home, { recursive: true, force: true });
|
|
276
|
-
await rm(sourceDir, { recursive: true });
|
|
277
178
|
}
|
|
278
179
|
});
|
|
279
180
|
});
|
|
@@ -840,9 +741,7 @@ describe("planRevertAll — mcpServers and agentRules", () => {
|
|
|
840
741
|
assert.ok(agents.includes("codex"));
|
|
841
742
|
});
|
|
842
743
|
});
|
|
843
|
-
describe("executeRevert — mcpServer and agentRule integration", {
|
|
844
|
-
skip: process.platform === "win32",
|
|
845
|
-
}, () => {
|
|
744
|
+
describe("executeRevert — mcpServer and agentRule integration", () => {
|
|
846
745
|
it("reverts a deployed mcpServer config-patch and unregisters it", async () => {
|
|
847
746
|
const home = await makeTmpDir();
|
|
848
747
|
try {
|
package/package.json
CHANGED
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { describe, it } from "node:test";
|
|
5
|
-
import { executeDeploy } from "../../../src/core/deploy.js";
|
|
6
|
-
import { lookupDeployment, registerDeployment, } from "../../../src/core/ownership.js";
|
|
7
|
-
import { executeRevert } from "../../../src/core/revert.js";
|
|
8
|
-
import { exists, makeTmpDir } from "../../helpers/fs.js";
|
|
9
|
-
describe("agentRules repo/workspace file-write (Windows)", {
|
|
10
|
-
skip: process.platform !== "win32",
|
|
11
|
-
}, () => {
|
|
12
|
-
it("deploys agentRules with scope repo and registers it", async () => {
|
|
13
|
-
const home = await makeTmpDir("ie-rules-home");
|
|
14
|
-
const repo = await makeTmpDir("ie-rules-repo");
|
|
15
|
-
const sourceDir = await makeTmpDir("ie-rules-source");
|
|
16
|
-
try {
|
|
17
|
-
const source = path.join(sourceDir, "CLAUDE.md");
|
|
18
|
-
await writeFile(source, "# My Rules\n");
|
|
19
|
-
const target = path.join(repo, "CLAUDE.md");
|
|
20
|
-
const action = {
|
|
21
|
-
kind: "file-write",
|
|
22
|
-
skill: "my-rules",
|
|
23
|
-
agent: "claude-code",
|
|
24
|
-
source,
|
|
25
|
-
target,
|
|
26
|
-
};
|
|
27
|
-
const { succeeded, failed } = await executeDeploy([action], false, false, home);
|
|
28
|
-
assert.equal(succeeded, 1);
|
|
29
|
-
assert.equal(failed.length, 0);
|
|
30
|
-
assert.ok(await exists(target));
|
|
31
|
-
const entry = await lookupDeployment(home, target);
|
|
32
|
-
assert.ok(entry !== null);
|
|
33
|
-
assert.equal(entry?.kind, "file-write");
|
|
34
|
-
}
|
|
35
|
-
finally {
|
|
36
|
-
await rm(home, { recursive: true, force: true });
|
|
37
|
-
await rm(repo, { recursive: true, force: true });
|
|
38
|
-
await rm(sourceDir, { recursive: true, force: true });
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
it("reverts agentRules with scope repo and unregisters it", async () => {
|
|
42
|
-
const home = await makeTmpDir("ie-rules-home");
|
|
43
|
-
const repo = await makeTmpDir("ie-rules-repo");
|
|
44
|
-
try {
|
|
45
|
-
const source = path.join(repo, "source-CLAUDE.md");
|
|
46
|
-
const target = path.join(repo, "CLAUDE.md");
|
|
47
|
-
await writeFile(target, "# My Rules\n");
|
|
48
|
-
await registerDeployment(home, target, {
|
|
49
|
-
kind: "file-write",
|
|
50
|
-
source,
|
|
51
|
-
skill: "my-rules",
|
|
52
|
-
agent: "claude-code",
|
|
53
|
-
});
|
|
54
|
-
const action = {
|
|
55
|
-
kind: "file-write",
|
|
56
|
-
skill: "my-rules",
|
|
57
|
-
agent: "claude-code",
|
|
58
|
-
target,
|
|
59
|
-
};
|
|
60
|
-
const { succeeded, failed } = await executeRevert([action], false, false, home);
|
|
61
|
-
assert.equal(succeeded, 1);
|
|
62
|
-
assert.equal(failed.length, 0);
|
|
63
|
-
assert.ok(!(await exists(target)));
|
|
64
|
-
assert.equal(await lookupDeployment(home, target), null);
|
|
65
|
-
}
|
|
66
|
-
finally {
|
|
67
|
-
await rm(home, { recursive: true, force: true });
|
|
68
|
-
await rm(repo, { recursive: true, force: true });
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
it("deploys agentRules with scope workspace and registers it", async () => {
|
|
72
|
-
const home = await makeTmpDir("ie-rules-home");
|
|
73
|
-
const workspace = await makeTmpDir("ie-rules-workspace");
|
|
74
|
-
const sourceDir = await makeTmpDir("ie-rules-source");
|
|
75
|
-
try {
|
|
76
|
-
const source = path.join(sourceDir, "CLAUDE.md");
|
|
77
|
-
await writeFile(source, "# Workspace Rules\n");
|
|
78
|
-
const target = path.join(workspace, "CLAUDE.md");
|
|
79
|
-
const action = {
|
|
80
|
-
kind: "file-write",
|
|
81
|
-
skill: "my-rules",
|
|
82
|
-
agent: "claude-code",
|
|
83
|
-
source,
|
|
84
|
-
target,
|
|
85
|
-
};
|
|
86
|
-
const { succeeded, failed } = await executeDeploy([action], false, false, home);
|
|
87
|
-
assert.equal(succeeded, 1);
|
|
88
|
-
assert.equal(failed.length, 0);
|
|
89
|
-
assert.ok(await exists(target));
|
|
90
|
-
const entry = await lookupDeployment(home, target);
|
|
91
|
-
assert.ok(entry !== null);
|
|
92
|
-
assert.equal(entry?.kind, "file-write");
|
|
93
|
-
}
|
|
94
|
-
finally {
|
|
95
|
-
await rm(home, { recursive: true, force: true });
|
|
96
|
-
await rm(workspace, { recursive: true, force: true });
|
|
97
|
-
await rm(sourceDir, { recursive: true, force: true });
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
it("reverts agentRules with scope workspace and unregisters it", async () => {
|
|
101
|
-
const home = await makeTmpDir("ie-rules-home");
|
|
102
|
-
const workspace = await makeTmpDir("ie-rules-workspace");
|
|
103
|
-
try {
|
|
104
|
-
const source = path.join(workspace, "source-CLAUDE.md");
|
|
105
|
-
const target = path.join(workspace, "CLAUDE.md");
|
|
106
|
-
await writeFile(target, "# Workspace Rules\n");
|
|
107
|
-
await registerDeployment(home, target, {
|
|
108
|
-
kind: "file-write",
|
|
109
|
-
source,
|
|
110
|
-
skill: "my-rules",
|
|
111
|
-
agent: "claude-code",
|
|
112
|
-
});
|
|
113
|
-
const action = {
|
|
114
|
-
kind: "file-write",
|
|
115
|
-
skill: "my-rules",
|
|
116
|
-
agent: "claude-code",
|
|
117
|
-
target,
|
|
118
|
-
};
|
|
119
|
-
const { succeeded, failed } = await executeRevert([action], false, false, home);
|
|
120
|
-
assert.equal(succeeded, 1);
|
|
121
|
-
assert.equal(failed.length, 0);
|
|
122
|
-
assert.ok(!(await exists(target)));
|
|
123
|
-
assert.equal(await lookupDeployment(home, target), null);
|
|
124
|
-
}
|
|
125
|
-
finally {
|
|
126
|
-
await rm(home, { recursive: true, force: true });
|
|
127
|
-
await rm(workspace, { recursive: true, force: true });
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
describe("Antigravity frontmatter-emit (Windows)", {
|
|
132
|
-
skip: process.platform !== "win32",
|
|
133
|
-
}, () => {
|
|
134
|
-
it("deploys frontmatter-emit for Antigravity and registers it", async () => {
|
|
135
|
-
const home = await makeTmpDir("ie-fm-home");
|
|
136
|
-
const repo = await makeTmpDir("ie-fm-repo");
|
|
137
|
-
try {
|
|
138
|
-
const target = path.join(repo, ".agents", "rules", "my-mcp.md");
|
|
139
|
-
const frontmatter = {
|
|
140
|
-
"mcp-servers": {
|
|
141
|
-
"my-mcp": { command: "npx", args: ["-y", "my-mcp-server"] },
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
const action = {
|
|
145
|
-
kind: "frontmatter-emit",
|
|
146
|
-
skill: "my-mcp",
|
|
147
|
-
agent: "antigravity",
|
|
148
|
-
target,
|
|
149
|
-
frontmatter,
|
|
150
|
-
};
|
|
151
|
-
const { succeeded, failed } = await executeDeploy([action], false, false, home);
|
|
152
|
-
assert.equal(succeeded, 1);
|
|
153
|
-
assert.equal(failed.length, 0);
|
|
154
|
-
assert.ok(await exists(target));
|
|
155
|
-
const content = await readFile(target, "utf-8");
|
|
156
|
-
assert.ok(content.includes("mcp-servers:"));
|
|
157
|
-
const entry = await lookupDeployment(home, target);
|
|
158
|
-
assert.ok(entry !== null);
|
|
159
|
-
assert.equal(entry?.kind, "frontmatter-emit");
|
|
160
|
-
assert.equal(entry.created, true);
|
|
161
|
-
}
|
|
162
|
-
finally {
|
|
163
|
-
await rm(home, { recursive: true, force: true });
|
|
164
|
-
await rm(repo, { recursive: true, force: true });
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
it("reverts frontmatter-emit for Antigravity and removes file when no body", async () => {
|
|
168
|
-
const home = await makeTmpDir("ie-fm-home");
|
|
169
|
-
const repo = await makeTmpDir("ie-fm-repo");
|
|
170
|
-
try {
|
|
171
|
-
const target = path.join(repo, ".agents", "rules", "my-mcp.md");
|
|
172
|
-
const frontmatter = {
|
|
173
|
-
"mcp-servers": {
|
|
174
|
-
"my-mcp": { command: "npx", args: ["-y", "my-mcp-server"] },
|
|
175
|
-
},
|
|
176
|
-
};
|
|
177
|
-
// Deploy first so the file and registry entry are created.
|
|
178
|
-
const deployAction = {
|
|
179
|
-
kind: "frontmatter-emit",
|
|
180
|
-
skill: "my-mcp",
|
|
181
|
-
agent: "antigravity",
|
|
182
|
-
target,
|
|
183
|
-
frontmatter,
|
|
184
|
-
};
|
|
185
|
-
await executeDeploy([deployAction], false, false, home);
|
|
186
|
-
const action = {
|
|
187
|
-
kind: "frontmatter-emit",
|
|
188
|
-
skill: "my-mcp",
|
|
189
|
-
agent: "antigravity",
|
|
190
|
-
target,
|
|
191
|
-
};
|
|
192
|
-
const { succeeded, failed } = await executeRevert([action], false, false, home);
|
|
193
|
-
assert.equal(succeeded, 1);
|
|
194
|
-
assert.equal(failed.length, 0);
|
|
195
|
-
assert.ok(!(await exists(target)));
|
|
196
|
-
assert.equal(await lookupDeployment(home, target), null);
|
|
197
|
-
}
|
|
198
|
-
finally {
|
|
199
|
-
await rm(home, { recursive: true, force: true });
|
|
200
|
-
await rm(repo, { recursive: true, force: true });
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
it("reverts frontmatter-emit for Antigravity preserving body when file had prior content", async () => {
|
|
204
|
-
const home = await makeTmpDir("ie-fm-home");
|
|
205
|
-
const repo = await makeTmpDir("ie-fm-repo");
|
|
206
|
-
try {
|
|
207
|
-
const target = path.join(repo, ".agents", "rules", "my-mcp.md");
|
|
208
|
-
await mkdir(path.dirname(target), { recursive: true });
|
|
209
|
-
await writeFile(target, "# My Rules\n");
|
|
210
|
-
const undoPatch = { "mcp-servers": null };
|
|
211
|
-
await registerDeployment(home, target, {
|
|
212
|
-
kind: "frontmatter-emit",
|
|
213
|
-
patch: {
|
|
214
|
-
"mcp-servers": {
|
|
215
|
-
"my-mcp": { command: "npx", args: ["-y", "my-mcp-server"] },
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
undoPatch,
|
|
219
|
-
created: false,
|
|
220
|
-
hadFrontmatter: false,
|
|
221
|
-
skill: "my-mcp",
|
|
222
|
-
agent: "antigravity",
|
|
223
|
-
});
|
|
224
|
-
const action = {
|
|
225
|
-
kind: "frontmatter-emit",
|
|
226
|
-
skill: "my-mcp",
|
|
227
|
-
agent: "antigravity",
|
|
228
|
-
target,
|
|
229
|
-
};
|
|
230
|
-
const { succeeded, failed } = await executeRevert([action], false, false, home);
|
|
231
|
-
assert.equal(succeeded, 1);
|
|
232
|
-
assert.equal(failed.length, 0);
|
|
233
|
-
// File should still exist because it had body content.
|
|
234
|
-
assert.ok(await exists(target));
|
|
235
|
-
const content = await readFile(target, "utf-8");
|
|
236
|
-
assert.ok(!content.includes("mcp-servers:"));
|
|
237
|
-
assert.ok(content.includes("My Rules"));
|
|
238
|
-
assert.equal(await lookupDeployment(home, target), null);
|
|
239
|
-
}
|
|
240
|
-
finally {
|
|
241
|
-
await rm(home, { recursive: true, force: true });
|
|
242
|
-
await rm(repo, { recursive: true, force: true });
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
});
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { describe, it } from "node:test";
|
|
5
|
-
import { lookupDeployment, registerDeployment, } from "../../../src/core/ownership.js";
|
|
6
|
-
import { executeRevert } from "../../../src/core/revert.js";
|
|
7
|
-
import { exists, makeTmpDir } from "../../helpers/fs.js";
|
|
8
|
-
describe("revert integration (Windows)", {
|
|
9
|
-
skip: process.platform !== "win32",
|
|
10
|
-
}, () => {
|
|
11
|
-
it("reverts a deployed mcpServer config-patch and unregisters it", async () => {
|
|
12
|
-
const home = await makeTmpDir("ie-revert-home");
|
|
13
|
-
try {
|
|
14
|
-
const configFile = path.join(home, ".claude.json");
|
|
15
|
-
await writeFile(configFile, JSON.stringify({
|
|
16
|
-
other: "value",
|
|
17
|
-
mcpServers: { "my-mcp": { command: "npx" } },
|
|
18
|
-
}));
|
|
19
|
-
await registerDeployment(home, configFile, {
|
|
20
|
-
kind: "config-patch",
|
|
21
|
-
patch: { mcpServers: { "my-mcp": { command: "npx" } } },
|
|
22
|
-
undoPatch: { mcpServers: null },
|
|
23
|
-
skill: "my-mcp",
|
|
24
|
-
agent: "claude-code",
|
|
25
|
-
});
|
|
26
|
-
const action = {
|
|
27
|
-
kind: "config-patch",
|
|
28
|
-
skill: "my-mcp",
|
|
29
|
-
agent: "claude-code",
|
|
30
|
-
target: configFile,
|
|
31
|
-
};
|
|
32
|
-
const { succeeded, failed } = await executeRevert([action], false, false, home);
|
|
33
|
-
assert.equal(succeeded, 1);
|
|
34
|
-
assert.equal(failed.length, 0);
|
|
35
|
-
assert.deepEqual(JSON.parse(await readFile(configFile, "utf-8")), {
|
|
36
|
-
other: "value",
|
|
37
|
-
});
|
|
38
|
-
assert.equal(await lookupDeployment(home, configFile), null);
|
|
39
|
-
}
|
|
40
|
-
finally {
|
|
41
|
-
await rm(home, { recursive: true, force: true });
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
it("reverts a deployed agentRule file-write and unregisters it", async () => {
|
|
45
|
-
const home = await makeTmpDir("ie-revert-home");
|
|
46
|
-
try {
|
|
47
|
-
const rulesFile = path.join(home, ".claude", "CLAUDE.md");
|
|
48
|
-
await mkdir(path.dirname(rulesFile), { recursive: true });
|
|
49
|
-
await writeFile(rulesFile, "# My Rules\n");
|
|
50
|
-
await registerDeployment(home, rulesFile, {
|
|
51
|
-
kind: "file-write",
|
|
52
|
-
source: "/some/source/CLAUDE.md",
|
|
53
|
-
skill: "my-rules",
|
|
54
|
-
agent: "claude-code",
|
|
55
|
-
});
|
|
56
|
-
const action = {
|
|
57
|
-
kind: "file-write",
|
|
58
|
-
skill: "my-rules",
|
|
59
|
-
agent: "claude-code",
|
|
60
|
-
target: rulesFile,
|
|
61
|
-
};
|
|
62
|
-
const { succeeded, failed } = await executeRevert([action], false, false, home);
|
|
63
|
-
assert.equal(succeeded, 1);
|
|
64
|
-
assert.equal(failed.length, 0);
|
|
65
|
-
assert.ok(!(await exists(rulesFile)));
|
|
66
|
-
assert.equal(await lookupDeployment(home, rulesFile), null);
|
|
67
|
-
}
|
|
68
|
-
finally {
|
|
69
|
-
await rm(home, { recursive: true, force: true });
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
});
|
|
File without changes
|
|
File without changes
|