@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
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { chmod, mkdir, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { describe, it } from "node:test";
|
|
5
|
+
import { executeDeploy, planDeploy } from "../../../src/core/deploy.js";
|
|
6
|
+
import { lookupDeployment, registerDeployment, } from "../../../src/core/ownership.js";
|
|
7
|
+
import { UserError } from "../../../src/errors.js";
|
|
8
|
+
import { exists, makeTmpDir } from "../../helpers/fs.js";
|
|
9
|
+
import { createSkillSource, testSkillManifest, } from "../../helpers/skill-dir.js";
|
|
10
|
+
describe("executeDeploy (Windows)", {
|
|
11
|
+
skip: process.platform !== "win32",
|
|
12
|
+
}, () => {
|
|
13
|
+
it("creates copies", async () => {
|
|
14
|
+
const sourceDir = await makeTmpDir();
|
|
15
|
+
const home = await makeTmpDir();
|
|
16
|
+
try {
|
|
17
|
+
await createSkillSource(sourceDir, "skills/test-skill");
|
|
18
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
19
|
+
const { succeeded, failed } = await executeDeploy(actions, false, false, home);
|
|
20
|
+
assert.equal(succeeded, 1);
|
|
21
|
+
assert.equal(failed.length, 0);
|
|
22
|
+
assert.ok(await exists(actions[0]?.target));
|
|
23
|
+
assert.ok(await exists(path.join(actions[0]?.target, "SKILL.md")));
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
27
|
+
await rm(home, { recursive: true, force: true });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
it("registers deployment in registry on copy deploy", async () => {
|
|
31
|
+
const sourceDir = await makeTmpDir();
|
|
32
|
+
const home = await makeTmpDir();
|
|
33
|
+
try {
|
|
34
|
+
const skillSource = await createSkillSource(sourceDir, "skills/test-skill");
|
|
35
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
36
|
+
await executeDeploy(actions, false, false, home);
|
|
37
|
+
const entry = await lookupDeployment(home, actions[0]?.target);
|
|
38
|
+
assert.ok(entry);
|
|
39
|
+
if (entry.kind !== "skill-dir") {
|
|
40
|
+
assert.fail("Expected skill-dir");
|
|
41
|
+
}
|
|
42
|
+
assert.equal(entry.skill, "test-skill");
|
|
43
|
+
assert.equal(entry.agent, "claude-code");
|
|
44
|
+
assert.equal(entry.source, skillSource);
|
|
45
|
+
assert.equal(entry.method, "copy");
|
|
46
|
+
assert.ok(!(await exists(path.join(skillSource, ".inception-totem"))), "no .inception-totem should be written to source directory");
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
50
|
+
await rm(home, { recursive: true, force: true });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
it("does not create copies in dry-run mode", async () => {
|
|
54
|
+
const sourceDir = await makeTmpDir();
|
|
55
|
+
const home = await makeTmpDir();
|
|
56
|
+
try {
|
|
57
|
+
await createSkillSource(sourceDir, "skills/test-skill");
|
|
58
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
59
|
+
const { succeeded, failed } = await executeDeploy(actions, true, false, home);
|
|
60
|
+
assert.equal(succeeded, 1);
|
|
61
|
+
assert.equal(failed.length, 0);
|
|
62
|
+
assert.ok(!(await exists(actions[0]?.target)));
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
66
|
+
await rm(home, { recursive: true, force: true });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
it("overwrites an existing copy with a matching registry entry", async () => {
|
|
70
|
+
const sourceDir = await makeTmpDir();
|
|
71
|
+
const home = await makeTmpDir();
|
|
72
|
+
try {
|
|
73
|
+
await createSkillSource(sourceDir, "skills/test-skill");
|
|
74
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
75
|
+
await executeDeploy(actions, false, false, home);
|
|
76
|
+
const { succeeded, failed } = await executeDeploy(actions, false, false, home);
|
|
77
|
+
assert.equal(succeeded, 1);
|
|
78
|
+
assert.equal(failed.length, 0);
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
82
|
+
await rm(home, { recursive: true, force: true });
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
it("reports missing source during planning", async () => {
|
|
86
|
+
const home = await makeTmpDir();
|
|
87
|
+
try {
|
|
88
|
+
await assert.rejects(planDeploy(testSkillManifest, "/nonexistent/source", ["claude-code"], home), (err) => {
|
|
89
|
+
assert.ok(err instanceof UserError);
|
|
90
|
+
assert.equal(err.code, "DEPLOY_FAILED");
|
|
91
|
+
assert.match(err.message, /source not found/);
|
|
92
|
+
return true;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
await rm(home, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
it("refuses to overwrite an unmanaged target", async () => {
|
|
100
|
+
const sourceDir = await makeTmpDir();
|
|
101
|
+
const home = await makeTmpDir();
|
|
102
|
+
try {
|
|
103
|
+
await createSkillSource(sourceDir, "skills/test-skill");
|
|
104
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
105
|
+
const target = actions[0]?.target;
|
|
106
|
+
await mkdir(target, { recursive: true });
|
|
107
|
+
await writeFile(path.join(target, "something.txt"), "user content");
|
|
108
|
+
const { succeeded, failed } = await executeDeploy(actions, false, false, home);
|
|
109
|
+
assert.equal(succeeded, 0);
|
|
110
|
+
assert.equal(failed.length, 1);
|
|
111
|
+
assert.ok(failed[0]?.error.includes("not managed by inception-engine"));
|
|
112
|
+
assert.ok(await exists(path.join(target, "something.txt")));
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
116
|
+
await rm(home, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
it("refuses to overwrite a target with a mismatched registry entry", async () => {
|
|
120
|
+
const sourceDir = await makeTmpDir();
|
|
121
|
+
const home = await makeTmpDir();
|
|
122
|
+
try {
|
|
123
|
+
await createSkillSource(sourceDir, "skills/test-skill");
|
|
124
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
125
|
+
const target = actions[0]?.target;
|
|
126
|
+
await mkdir(target, { recursive: true });
|
|
127
|
+
await writeFile(path.join(target, "SKILL.md"), "other content");
|
|
128
|
+
await registerDeployment(home, target, {
|
|
129
|
+
kind: "skill-dir",
|
|
130
|
+
source: "/completely/different/source",
|
|
131
|
+
skill: "different-skill",
|
|
132
|
+
agent: "codex",
|
|
133
|
+
method: "copy",
|
|
134
|
+
});
|
|
135
|
+
const { succeeded, failed } = await executeDeploy(actions, false, false, home);
|
|
136
|
+
assert.equal(succeeded, 0);
|
|
137
|
+
assert.equal(failed.length, 1);
|
|
138
|
+
assert.ok(failed[0]?.error.includes("not managed by inception-engine"));
|
|
139
|
+
assert.ok(await exists(path.join(target, "SKILL.md")));
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
143
|
+
await rm(home, { recursive: true, force: true });
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
it("removes the backup after a successful redeploy", async () => {
|
|
147
|
+
const sourceDir = await makeTmpDir();
|
|
148
|
+
const home = await makeTmpDir();
|
|
149
|
+
try {
|
|
150
|
+
await createSkillSource(sourceDir, "skills/test-skill");
|
|
151
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
152
|
+
const target = actions[0]?.target;
|
|
153
|
+
const backupPath = `${target}.inception-backup`;
|
|
154
|
+
await executeDeploy(actions, false, false, home);
|
|
155
|
+
await executeDeploy(actions, false, false, home);
|
|
156
|
+
assert.ok(!(await exists(backupPath)));
|
|
157
|
+
assert.ok(await exists(target));
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
161
|
+
await rm(home, { recursive: true, force: true });
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe("atomic redeploy behavior (Windows)", {
|
|
166
|
+
skip: process.platform !== "win32",
|
|
167
|
+
}, () => {
|
|
168
|
+
it("cleans up stale .inception-backup from a previous failed attempt", async () => {
|
|
169
|
+
const sourceDir = await makeTmpDir();
|
|
170
|
+
const home = await makeTmpDir();
|
|
171
|
+
try {
|
|
172
|
+
await createSkillSource(sourceDir, "skills/test-skill");
|
|
173
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
174
|
+
const target = actions[0]?.target;
|
|
175
|
+
const backupPath = `${target}.inception-backup`;
|
|
176
|
+
await executeDeploy(actions, false, false, home);
|
|
177
|
+
await mkdir(backupPath, { recursive: true });
|
|
178
|
+
await writeFile(path.join(backupPath, "stale.txt"), "leftover");
|
|
179
|
+
const { succeeded, failed } = await executeDeploy(actions, false, false, home);
|
|
180
|
+
assert.equal(succeeded, 1);
|
|
181
|
+
assert.equal(failed.length, 0);
|
|
182
|
+
assert.ok(!(await exists(backupPath)));
|
|
183
|
+
assert.ok(await exists(target));
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
187
|
+
await rm(home, { recursive: true, force: true });
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
it("does not create a backup on first deploy", async () => {
|
|
191
|
+
const sourceDir = await makeTmpDir();
|
|
192
|
+
const home = await makeTmpDir();
|
|
193
|
+
try {
|
|
194
|
+
await createSkillSource(sourceDir, "skills/test-skill");
|
|
195
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
196
|
+
const target = actions[0]?.target;
|
|
197
|
+
const backupPath = `${target}.inception-backup`;
|
|
198
|
+
const { succeeded } = await executeDeploy(actions, false, false, home);
|
|
199
|
+
assert.equal(succeeded, 1);
|
|
200
|
+
assert.ok(await exists(target));
|
|
201
|
+
assert.ok(!(await exists(backupPath)));
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
205
|
+
await rm(home, { recursive: true, force: true });
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
it("restores the backup when deploy fails because the registry is not writable", async () => {
|
|
209
|
+
const sourceDir = await makeTmpDir();
|
|
210
|
+
const home = await makeTmpDir();
|
|
211
|
+
try {
|
|
212
|
+
await createSkillSource(sourceDir, "skills/test-skill");
|
|
213
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
214
|
+
const target = actions[0]?.target;
|
|
215
|
+
const backupPath = `${target}.inception-backup`;
|
|
216
|
+
const { succeeded: firstSucceeded } = await executeDeploy(actions, false, false, home);
|
|
217
|
+
assert.equal(firstSucceeded, 1);
|
|
218
|
+
const registryFile = path.join(home, ".inception-engine", "registry.json");
|
|
219
|
+
await chmod(registryFile, 0o444);
|
|
220
|
+
const { succeeded, failed } = await executeDeploy(actions, false, false, home);
|
|
221
|
+
assert.equal(succeeded, 0);
|
|
222
|
+
assert.equal(failed.length, 1);
|
|
223
|
+
assert.ok(!(await exists(backupPath)));
|
|
224
|
+
assert.ok(await exists(target));
|
|
225
|
+
assert.ok(await exists(path.join(target, "SKILL.md")));
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
try {
|
|
229
|
+
await chmod(path.join(home, ".inception-engine", "registry.json"), 0o666);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// best effort
|
|
233
|
+
}
|
|
234
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
235
|
+
await rm(home, { recursive: true, force: true });
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
});
|