@kuznai/inception-engine 0.19.0 → 0.21.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 -22
- package/dist/{config → src/config}/agents.js +113 -14
- package/dist/{core → src/core}/adapters/agent-definitions.d.ts +0 -12
- package/dist/src/core/adapters/agent-definitions.js +162 -0
- package/dist/{core → src/core}/adapters/frontmatter.d.ts +10 -0
- package/dist/{core → src/core}/adapters/frontmatter.js +23 -6
- package/dist/src/core/adapters/index.js +25 -0
- package/dist/{core → src/core}/adapters/mcp.js +17 -20
- package/dist/{core → src/core}/adapters/permissions.js +16 -20
- package/dist/{core → src/core}/adapters/rules.js +26 -50
- package/dist/src/core/capabilities.d.ts +47 -0
- package/dist/src/core/capabilities.js +280 -0
- package/dist/{core → src/core}/deploy.js +82 -54
- package/dist/{core → src/core}/detect.js +2 -7
- package/dist/{core → src/core}/init.js +20 -25
- package/dist/src/core/merge-patch.d.ts +4 -0
- package/dist/src/core/merge-patch.js +46 -0
- package/dist/{core → src/core}/ownership.js +13 -0
- package/dist/src/core/preflight.js +296 -0
- package/dist/{core → src/core}/revert.js +86 -26
- package/dist/{core → src/core}/validation.js +31 -0
- package/dist/{errors.d.ts → src/errors.d.ts} +3 -1
- package/dist/{errors.js → src/errors.js} +2 -2
- package/dist/{schemas → src/schemas}/manifest.d.ts +20 -0
- package/dist/{schemas → src/schemas}/manifest.js +11 -0
- package/dist/{schemas → src/schemas}/registry.d.ts +36 -0
- package/dist/{schemas → src/schemas}/registry.js +12 -4
- package/dist/{types.d.ts → src/types.d.ts} +15 -0
- package/dist/test/helpers/fs.d.ts +2 -0
- package/dist/test/helpers/fs.js +17 -0
- package/dist/test/helpers/path.d.ts +10 -0
- package/dist/test/helpers/path.js +15 -0
- package/dist/test/helpers/skill-dir-scenarios.d.ts +7 -0
- package/dist/test/helpers/skill-dir-scenarios.js +206 -0
- package/dist/test/helpers/skill-dir.d.ts +7 -0
- package/dist/test/helpers/skill-dir.js +46 -0
- package/dist/test/os/cross-platform/cross-platform.test.d.ts +1 -0
- package/dist/test/os/cross-platform/cross-platform.test.js +328 -0
- package/dist/test/os/posix/skill-dir.test.d.ts +1 -0
- package/dist/test/os/posix/skill-dir.test.js +19 -0
- package/dist/test/os/windows/agentRules-integration.test.d.ts +1 -0
- package/dist/test/os/windows/agentRules-integration.test.js +245 -0
- package/dist/test/os/windows/revert-integration.test.d.ts +1 -0
- package/dist/test/os/windows/revert-integration.test.js +72 -0
- package/dist/test/os/windows/skill-dir.test.d.ts +1 -0
- package/dist/test/os/windows/skill-dir.test.js +19 -0
- package/dist/test/unit/adapters.test.d.ts +1 -0
- package/dist/test/unit/adapters.test.js +1017 -0
- package/dist/test/unit/capabilities.test.d.ts +1 -0
- package/dist/test/unit/capabilities.test.js +71 -0
- package/dist/test/unit/cli.test.d.ts +1 -0
- package/dist/test/unit/cli.test.js +834 -0
- package/dist/test/unit/deploy.test.d.ts +1 -0
- package/dist/test/unit/deploy.test.js +2481 -0
- package/dist/test/unit/detect.test.d.ts +1 -0
- package/dist/test/unit/detect.test.js +192 -0
- package/dist/test/unit/enterprise.test.d.ts +1 -0
- package/dist/test/unit/enterprise.test.js +106 -0
- package/dist/test/unit/frontmatter.test.d.ts +1 -0
- package/dist/test/unit/frontmatter.test.js +102 -0
- package/dist/test/unit/gemini-north-star.test.d.ts +1 -0
- package/dist/test/unit/gemini-north-star.test.js +243 -0
- package/dist/test/unit/init-fixture.test.d.ts +1 -0
- package/dist/test/unit/init-fixture.test.js +153 -0
- package/dist/test/unit/manifest.test.d.ts +1 -0
- package/dist/test/unit/manifest.test.js +737 -0
- package/dist/test/unit/migration-golden.test.d.ts +1 -0
- package/dist/test/unit/migration-golden.test.js +197 -0
- package/dist/test/unit/ownership.test.d.ts +1 -0
- package/dist/test/unit/ownership.test.js +587 -0
- package/dist/test/unit/preflight.test.d.ts +1 -0
- package/dist/test/unit/preflight.test.js +521 -0
- package/dist/test/unit/resolve.test.d.ts +1 -0
- package/dist/test/unit/resolve.test.js +119 -0
- package/dist/test/unit/revert.test.d.ts +1 -0
- package/dist/test/unit/revert.test.js +1004 -0
- package/dist/test/unit/toml.test.d.ts +1 -0
- package/dist/test/unit/toml.test.js +55 -0
- package/package.json +1 -1
- package/dist/core/adapters/agent-definitions.js +0 -91
- package/dist/core/adapters/index.js +0 -30
- package/dist/core/preflight.js +0 -168
- /package/dist/{config → src/config}/agents.d.ts +0 -0
- /package/dist/{config → src/config}/manifest.d.ts +0 -0
- /package/dist/{config → src/config}/manifest.js +0 -0
- /package/dist/{core → src/core}/adapters/index.d.ts +0 -0
- /package/dist/{core → src/core}/adapters/mcp.d.ts +0 -0
- /package/dist/{core → src/core}/adapters/permissions.d.ts +0 -0
- /package/dist/{core → src/core}/adapters/rules.d.ts +0 -0
- /package/dist/{core → src/core}/adapters/toml.d.ts +0 -0
- /package/dist/{core → src/core}/adapters/toml.js +0 -0
- /package/dist/{core → src/core}/deploy.d.ts +0 -0
- /package/dist/{core → src/core}/detect.d.ts +0 -0
- /package/dist/{core → src/core}/init.d.ts +0 -0
- /package/dist/{core → src/core}/ownership.d.ts +0 -0
- /package/dist/{core → src/core}/preflight.d.ts +0 -0
- /package/dist/{core → src/core}/resolve.d.ts +0 -0
- /package/dist/{core → src/core}/resolve.js +0 -0
- /package/dist/{core → src/core}/revert.d.ts +0 -0
- /package/dist/{core → src/core}/runtime-paths.d.ts +0 -0
- /package/dist/{core → src/core}/runtime-paths.js +0 -0
- /package/dist/{core → src/core}/validation.d.ts +0 -0
- /package/dist/{formatters.d.ts → src/formatters.d.ts} +0 -0
- /package/dist/{formatters.js → src/formatters.js} +0 -0
- /package/dist/{index.d.ts → src/index.d.ts} +0 -0
- /package/dist/{index.js → src/index.js} +0 -0
- /package/dist/{logger.d.ts → src/logger.d.ts} +0 -0
- /package/dist/{logger.js → src/logger.js} +0 -0
- /package/dist/{schemas → src/schemas}/errors.d.ts +0 -0
- /package/dist/{schemas → src/schemas}/errors.js +0 -0
- /package/dist/{types.js → src/types.js} +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
declare const SkillDirRegistryEntrySchema: z.ZodObject<{
|
|
3
|
+
surfaceId: z.ZodOptional<z.ZodString>;
|
|
4
|
+
migratedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
3
5
|
kind: z.ZodLiteral<"skill-dir">;
|
|
4
6
|
source: z.ZodString;
|
|
5
7
|
skill: z.ZodString;
|
|
@@ -18,6 +20,8 @@ declare const SkillDirRegistryEntrySchema: z.ZodObject<{
|
|
|
18
20
|
deployed: z.ZodString;
|
|
19
21
|
}, z.core.$strip>;
|
|
20
22
|
declare const FileWriteRegistryEntrySchema: z.ZodObject<{
|
|
23
|
+
surfaceId: z.ZodOptional<z.ZodString>;
|
|
24
|
+
migratedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
21
25
|
kind: z.ZodLiteral<"file-write">;
|
|
22
26
|
source: z.ZodString;
|
|
23
27
|
skill: z.ZodString;
|
|
@@ -32,6 +36,8 @@ declare const FileWriteRegistryEntrySchema: z.ZodObject<{
|
|
|
32
36
|
deployed: z.ZodString;
|
|
33
37
|
}, z.core.$strip>;
|
|
34
38
|
declare const ConfigPatchRegistryEntrySchema: z.ZodObject<{
|
|
39
|
+
surfaceId: z.ZodOptional<z.ZodString>;
|
|
40
|
+
migratedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
35
41
|
kind: z.ZodLiteral<"config-patch">;
|
|
36
42
|
patch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
37
43
|
undoPatch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -47,7 +53,13 @@ declare const ConfigPatchRegistryEntrySchema: z.ZodObject<{
|
|
|
47
53
|
deployed: z.ZodString;
|
|
48
54
|
}, z.core.$strip>;
|
|
49
55
|
declare const FrontmatterEmitRegistryEntrySchema: z.ZodObject<{
|
|
56
|
+
surfaceId: z.ZodOptional<z.ZodString>;
|
|
57
|
+
migratedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
50
58
|
kind: z.ZodLiteral<"frontmatter-emit">;
|
|
59
|
+
patch: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
60
|
+
undoPatch: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
61
|
+
created: z.ZodOptional<z.ZodBoolean>;
|
|
62
|
+
hadFrontmatter: z.ZodOptional<z.ZodBoolean>;
|
|
51
63
|
skill: z.ZodString;
|
|
52
64
|
agent: z.ZodEnum<{
|
|
53
65
|
"claude-code": "claude-code";
|
|
@@ -60,6 +72,8 @@ declare const FrontmatterEmitRegistryEntrySchema: z.ZodObject<{
|
|
|
60
72
|
deployed: z.ZodString;
|
|
61
73
|
}, z.core.$strip>;
|
|
62
74
|
export declare const RegistryEntrySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
75
|
+
surfaceId: z.ZodOptional<z.ZodString>;
|
|
76
|
+
migratedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
63
77
|
kind: z.ZodLiteral<"skill-dir">;
|
|
64
78
|
source: z.ZodString;
|
|
65
79
|
skill: z.ZodString;
|
|
@@ -77,6 +91,8 @@ export declare const RegistryEntrySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
77
91
|
}>>;
|
|
78
92
|
deployed: z.ZodString;
|
|
79
93
|
}, z.core.$strip>, z.ZodObject<{
|
|
94
|
+
surfaceId: z.ZodOptional<z.ZodString>;
|
|
95
|
+
migratedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
80
96
|
kind: z.ZodLiteral<"file-write">;
|
|
81
97
|
source: z.ZodString;
|
|
82
98
|
skill: z.ZodString;
|
|
@@ -90,6 +106,8 @@ export declare const RegistryEntrySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
90
106
|
}>;
|
|
91
107
|
deployed: z.ZodString;
|
|
92
108
|
}, z.core.$strip>, z.ZodObject<{
|
|
109
|
+
surfaceId: z.ZodOptional<z.ZodString>;
|
|
110
|
+
migratedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
93
111
|
kind: z.ZodLiteral<"config-patch">;
|
|
94
112
|
patch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
95
113
|
undoPatch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -104,7 +122,13 @@ export declare const RegistryEntrySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
104
122
|
}>;
|
|
105
123
|
deployed: z.ZodString;
|
|
106
124
|
}, z.core.$strip>, z.ZodObject<{
|
|
125
|
+
surfaceId: z.ZodOptional<z.ZodString>;
|
|
126
|
+
migratedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
107
127
|
kind: z.ZodLiteral<"frontmatter-emit">;
|
|
128
|
+
patch: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
129
|
+
undoPatch: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
130
|
+
created: z.ZodOptional<z.ZodBoolean>;
|
|
131
|
+
hadFrontmatter: z.ZodOptional<z.ZodBoolean>;
|
|
108
132
|
skill: z.ZodString;
|
|
109
133
|
agent: z.ZodEnum<{
|
|
110
134
|
"claude-code": "claude-code";
|
|
@@ -119,6 +143,8 @@ export declare const RegistryEntrySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
119
143
|
export declare const RegistrySchema: z.ZodObject<{
|
|
120
144
|
version: z.ZodLiteral<1>;
|
|
121
145
|
deployments: z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
146
|
+
surfaceId: z.ZodOptional<z.ZodString>;
|
|
147
|
+
migratedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
122
148
|
kind: z.ZodLiteral<"skill-dir">;
|
|
123
149
|
source: z.ZodString;
|
|
124
150
|
skill: z.ZodString;
|
|
@@ -136,6 +162,8 @@ export declare const RegistrySchema: z.ZodObject<{
|
|
|
136
162
|
}>>;
|
|
137
163
|
deployed: z.ZodString;
|
|
138
164
|
}, z.core.$strip>, z.ZodObject<{
|
|
165
|
+
surfaceId: z.ZodOptional<z.ZodString>;
|
|
166
|
+
migratedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
139
167
|
kind: z.ZodLiteral<"file-write">;
|
|
140
168
|
source: z.ZodString;
|
|
141
169
|
skill: z.ZodString;
|
|
@@ -149,6 +177,8 @@ export declare const RegistrySchema: z.ZodObject<{
|
|
|
149
177
|
}>;
|
|
150
178
|
deployed: z.ZodString;
|
|
151
179
|
}, z.core.$strip>, z.ZodObject<{
|
|
180
|
+
surfaceId: z.ZodOptional<z.ZodString>;
|
|
181
|
+
migratedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
152
182
|
kind: z.ZodLiteral<"config-patch">;
|
|
153
183
|
patch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
154
184
|
undoPatch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
@@ -163,7 +193,13 @@ export declare const RegistrySchema: z.ZodObject<{
|
|
|
163
193
|
}>;
|
|
164
194
|
deployed: z.ZodString;
|
|
165
195
|
}, z.core.$strip>, z.ZodObject<{
|
|
196
|
+
surfaceId: z.ZodOptional<z.ZodString>;
|
|
197
|
+
migratedFrom: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
166
198
|
kind: z.ZodLiteral<"frontmatter-emit">;
|
|
199
|
+
patch: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
200
|
+
undoPatch: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
201
|
+
created: z.ZodOptional<z.ZodBoolean>;
|
|
202
|
+
hadFrontmatter: z.ZodOptional<z.ZodBoolean>;
|
|
167
203
|
skill: z.ZodString;
|
|
168
204
|
agent: z.ZodEnum<{
|
|
169
205
|
"claude-code": "claude-code";
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { AgentIdSchema } from "./manifest.js";
|
|
3
|
-
const
|
|
3
|
+
const RegistryOwnershipMetadataSchema = z.object({
|
|
4
|
+
surfaceId: z.string().optional(),
|
|
5
|
+
migratedFrom: z.array(z.string()).optional(),
|
|
6
|
+
});
|
|
7
|
+
const SkillDirRegistryEntrySchema = RegistryOwnershipMetadataSchema.extend({
|
|
4
8
|
kind: z.literal("skill-dir"),
|
|
5
9
|
source: z.string(),
|
|
6
10
|
skill: z.string(),
|
|
@@ -8,14 +12,14 @@ const SkillDirRegistryEntrySchema = z.object({
|
|
|
8
12
|
method: z.enum(["symlink", "copy"]).optional(),
|
|
9
13
|
deployed: z.string(),
|
|
10
14
|
});
|
|
11
|
-
const FileWriteRegistryEntrySchema =
|
|
15
|
+
const FileWriteRegistryEntrySchema = RegistryOwnershipMetadataSchema.extend({
|
|
12
16
|
kind: z.literal("file-write"),
|
|
13
17
|
source: z.string(),
|
|
14
18
|
skill: z.string(),
|
|
15
19
|
agent: AgentIdSchema,
|
|
16
20
|
deployed: z.string(),
|
|
17
21
|
});
|
|
18
|
-
const ConfigPatchRegistryEntrySchema =
|
|
22
|
+
const ConfigPatchRegistryEntrySchema = RegistryOwnershipMetadataSchema.extend({
|
|
19
23
|
kind: z.literal("config-patch"),
|
|
20
24
|
patch: z.record(z.string(), z.unknown()),
|
|
21
25
|
undoPatch: z.record(z.string(), z.unknown()),
|
|
@@ -23,8 +27,12 @@ const ConfigPatchRegistryEntrySchema = z.object({
|
|
|
23
27
|
agent: AgentIdSchema,
|
|
24
28
|
deployed: z.string(),
|
|
25
29
|
});
|
|
26
|
-
const FrontmatterEmitRegistryEntrySchema =
|
|
30
|
+
const FrontmatterEmitRegistryEntrySchema = RegistryOwnershipMetadataSchema.extend({
|
|
27
31
|
kind: z.literal("frontmatter-emit"),
|
|
32
|
+
patch: z.record(z.string(), z.unknown()).optional(),
|
|
33
|
+
undoPatch: z.record(z.string(), z.unknown()).optional(),
|
|
34
|
+
created: z.boolean().optional(),
|
|
35
|
+
hadFrontmatter: z.boolean().optional(),
|
|
28
36
|
skill: z.string(),
|
|
29
37
|
agent: AgentIdSchema,
|
|
30
38
|
deployed: z.string(),
|
|
@@ -5,6 +5,7 @@ export interface AgentPaths {
|
|
|
5
5
|
windows: string[];
|
|
6
6
|
}
|
|
7
7
|
export type Confidence = "documented" | "implementation-only" | "provisional";
|
|
8
|
+
export type CapabilityKind = "skills" | "mcpServers" | "agentRules" | "permissions" | "agentDefinitions";
|
|
8
9
|
export interface SupportedAgentSurface {
|
|
9
10
|
status: "supported";
|
|
10
11
|
/**
|
|
@@ -90,12 +91,25 @@ export interface AgentConfig {
|
|
|
90
91
|
detectBinary: string | null;
|
|
91
92
|
provenance: AgentProvenance;
|
|
92
93
|
mcpSupport?: AgentSurfaceSupport;
|
|
94
|
+
mcpRepoSupport?: AgentSurfaceSupport;
|
|
95
|
+
mcpWorkspaceSupport?: AgentSurfaceSupport;
|
|
93
96
|
agentRulesSupport?: AgentSurfaceSupport;
|
|
94
97
|
agentRulesRepoSupport?: AgentSurfaceSupport;
|
|
95
98
|
agentRulesWorkspaceSupport?: AgentSurfaceSupport;
|
|
96
99
|
permissionsSupport?: AgentSurfaceSupport;
|
|
97
100
|
agentDefinitionsSupport?: AgentSurfaceSupport;
|
|
101
|
+
agentDefinitionsRepoSupport?: AgentSurfaceSupport;
|
|
102
|
+
agentDefinitionsWorkspaceSupport?: AgentSurfaceSupport;
|
|
103
|
+
agentDefinitionsTomlSupport?: AgentSurfaceSupport;
|
|
104
|
+
agentDefinitionsTomlRepoSupport?: AgentSurfaceSupport;
|
|
105
|
+
mcpDevcontainerSupport?: AgentSurfaceSupport;
|
|
106
|
+
mcpAgentFrontmatterSupport?: AgentSurfaceSupport;
|
|
98
107
|
policyNote?: string;
|
|
108
|
+
/**
|
|
109
|
+
* A list of documented surfaces that are currently not safe to support directly.
|
|
110
|
+
* Preflight will emit warnings for these if the agent is targeted.
|
|
111
|
+
*/
|
|
112
|
+
unsupportedSurfaces?: AgentSurfaceSupport[];
|
|
99
113
|
/**
|
|
100
114
|
* When true, instruction files deployed to this agent must include valid
|
|
101
115
|
* YAML frontmatter. Drives validateInstructionFileRequirements without
|
|
@@ -127,6 +141,7 @@ export interface FileWriteDeployAction {
|
|
|
127
141
|
source: string;
|
|
128
142
|
target: string;
|
|
129
143
|
confidence?: Confidence;
|
|
144
|
+
migratedFrom?: string[];
|
|
130
145
|
}
|
|
131
146
|
export interface ConfigPatchDeployAction {
|
|
132
147
|
kind: "config-patch";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { access, mkdir } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export async function exists(p) {
|
|
5
|
+
try {
|
|
6
|
+
await access(p);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export async function makeTmpDir(prefix = "ie-test-dir") {
|
|
14
|
+
const dir = path.join(os.tmpdir(), `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
15
|
+
await mkdir(dir, { recursive: true });
|
|
16
|
+
return dir;
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize path separators to forward slashes for cross-platform path assertions.
|
|
3
|
+
* Use this instead of hard-coding POSIX separators in expected strings.
|
|
4
|
+
*/
|
|
5
|
+
export declare function normalizeSlashes(value: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Assert that a path ends with the given POSIX-style suffix, normalizing separators.
|
|
8
|
+
* Use instead of: assert.ok(normalizeSlashes(p).endsWith("some/suffix"))
|
|
9
|
+
*/
|
|
10
|
+
export declare function assertPathEndsWith(actual: string, suffix: string, msg?: string): void;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
/**
|
|
3
|
+
* Normalize path separators to forward slashes for cross-platform path assertions.
|
|
4
|
+
* Use this instead of hard-coding POSIX separators in expected strings.
|
|
5
|
+
*/
|
|
6
|
+
export function normalizeSlashes(value) {
|
|
7
|
+
return value.replaceAll("\\", "/");
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Assert that a path ends with the given POSIX-style suffix, normalizing separators.
|
|
11
|
+
* Use instead of: assert.ok(normalizeSlashes(p).endsWith("some/suffix"))
|
|
12
|
+
*/
|
|
13
|
+
export function assertPathEndsWith(actual, suffix, msg) {
|
|
14
|
+
assert.ok(normalizeSlashes(actual).endsWith(suffix), msg ?? `Expected path "${actual}" to end with "${suffix}"`);
|
|
15
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface SkillDirScenarioOptions {
|
|
2
|
+
method: "symlink" | "copy";
|
|
3
|
+
assertManagedTarget(target: string, source: string, expectedSkillMd: string): Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
export declare function registerSharedSkillDirDeployScenarios(options: SkillDirScenarioOptions): void;
|
|
6
|
+
export declare function registerSharedSkillDirRevertScenarios(options: SkillDirScenarioOptions): void;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,206 @@
|
|
|
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 { it } from "node:test";
|
|
5
|
+
import { executeDeploy, planDeploy } from "../../src/core/deploy.js";
|
|
6
|
+
import { lookupDeployment, registerDeployment, } from "../../src/core/ownership.js";
|
|
7
|
+
import { executeRevert, planRevert } from "../../src/core/revert.js";
|
|
8
|
+
import { exists, makeTmpDir } from "./fs.js";
|
|
9
|
+
import { createFailingRegistryPersistence, createSkillSource, testSkillManifest, } from "./skill-dir.js";
|
|
10
|
+
const FIRST_SKILL_MD = "---\nname: test\ndescription: First skill\n---\n# First";
|
|
11
|
+
const SECOND_SKILL_MD = "---\nname: test\ndescription: Second skill\n---\n# Second";
|
|
12
|
+
export function registerSharedSkillDirDeployScenarios(options) {
|
|
13
|
+
it("first deploy succeeds", async () => {
|
|
14
|
+
const sourceDir = await makeTmpDir("ie-skill-dir-source");
|
|
15
|
+
const home = await makeTmpDir("ie-skill-dir-home");
|
|
16
|
+
try {
|
|
17
|
+
const source = await createSkillSource(sourceDir, "skills/test-skill", FIRST_SKILL_MD);
|
|
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
|
+
await options.assertManagedTarget(actions[0]?.target, source, FIRST_SKILL_MD);
|
|
23
|
+
const entry = await lookupDeployment(home, actions[0]?.target);
|
|
24
|
+
assert.ok(entry);
|
|
25
|
+
assert.equal(entry.kind, "skill-dir");
|
|
26
|
+
assert.equal(entry.method, options.method);
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
30
|
+
await rm(home, { recursive: true, force: true });
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
it("dry-run reports the planned skill-dir change only", async () => {
|
|
34
|
+
const sourceDir = await makeTmpDir("ie-skill-dir-source");
|
|
35
|
+
const home = await makeTmpDir("ie-skill-dir-home");
|
|
36
|
+
try {
|
|
37
|
+
await createSkillSource(sourceDir, "skills/test-skill", FIRST_SKILL_MD);
|
|
38
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
39
|
+
const { succeeded, failed, planned } = await executeDeploy(actions, true, false, home);
|
|
40
|
+
assert.equal(succeeded, 1);
|
|
41
|
+
assert.equal(failed.length, 0);
|
|
42
|
+
assert.equal(planned.length, 1);
|
|
43
|
+
assert.equal(planned[0]?.verb, options.method === "symlink" ? "create-symlink" : "copy-dir");
|
|
44
|
+
assert.ok(!(await exists(actions[0]?.target)));
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
48
|
+
await rm(home, { recursive: true, force: true });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
it("refuses to overwrite an unmanaged target", async () => {
|
|
52
|
+
const sourceDir = await makeTmpDir("ie-skill-dir-source");
|
|
53
|
+
const home = await makeTmpDir("ie-skill-dir-home");
|
|
54
|
+
try {
|
|
55
|
+
await createSkillSource(sourceDir, "skills/test-skill", FIRST_SKILL_MD);
|
|
56
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
57
|
+
const target = actions[0]?.target;
|
|
58
|
+
await mkdir(target, { recursive: true });
|
|
59
|
+
await writeFile(path.join(target, "user.txt"), "user content");
|
|
60
|
+
const { succeeded, failed } = await executeDeploy(actions, false, false, home);
|
|
61
|
+
assert.equal(succeeded, 0);
|
|
62
|
+
assert.equal(failed.length, 1);
|
|
63
|
+
assert.match(failed[0]?.error, /not managed by inception-engine/);
|
|
64
|
+
assert.equal(await readFile(path.join(target, "user.txt"), "utf-8"), "user content");
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
68
|
+
await rm(home, { recursive: true, force: true });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
it("refuses to overwrite a target with a mismatched registry entry", async () => {
|
|
72
|
+
const sourceDir = await makeTmpDir("ie-skill-dir-source");
|
|
73
|
+
const home = await makeTmpDir("ie-skill-dir-home");
|
|
74
|
+
try {
|
|
75
|
+
await createSkillSource(sourceDir, "skills/test-skill", FIRST_SKILL_MD);
|
|
76
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
77
|
+
const target = actions[0]?.target;
|
|
78
|
+
await mkdir(target, { recursive: true });
|
|
79
|
+
await writeFile(path.join(target, "SKILL.md"), "other");
|
|
80
|
+
await registerDeployment(home, target, {
|
|
81
|
+
kind: "skill-dir",
|
|
82
|
+
source: "/other/source",
|
|
83
|
+
skill: "other-skill",
|
|
84
|
+
agent: "codex",
|
|
85
|
+
method: options.method,
|
|
86
|
+
});
|
|
87
|
+
const { succeeded, failed } = await executeDeploy(actions, false, false, home);
|
|
88
|
+
assert.equal(succeeded, 0);
|
|
89
|
+
assert.equal(failed.length, 1);
|
|
90
|
+
assert.match(failed[0]?.error, /not managed by inception-engine/);
|
|
91
|
+
assert.equal(await readFile(path.join(target, "SKILL.md"), "utf-8"), "other");
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
95
|
+
await rm(home, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
it("managed redeploy succeeds", async () => {
|
|
99
|
+
const sourceDir = await makeTmpDir("ie-skill-dir-source");
|
|
100
|
+
const home = await makeTmpDir("ie-skill-dir-home");
|
|
101
|
+
try {
|
|
102
|
+
const source = await createSkillSource(sourceDir, "skills/test-skill", FIRST_SKILL_MD);
|
|
103
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
104
|
+
await executeDeploy(actions, false, false, home);
|
|
105
|
+
const { succeeded, failed } = await executeDeploy(actions, false, false, home);
|
|
106
|
+
assert.equal(succeeded, 1);
|
|
107
|
+
assert.equal(failed.length, 0);
|
|
108
|
+
await options.assertManagedTarget(actions[0]?.target, source, FIRST_SKILL_MD);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
112
|
+
await rm(home, { recursive: true, force: true });
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
it("restores the prior managed target when registry persistence fails", async () => {
|
|
116
|
+
const sourceDir = await makeTmpDir("ie-skill-dir-source");
|
|
117
|
+
const home = await makeTmpDir("ie-skill-dir-home");
|
|
118
|
+
try {
|
|
119
|
+
const firstSource = await createSkillSource(sourceDir, "skills/test-skill", FIRST_SKILL_MD);
|
|
120
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
121
|
+
const firstAction = actions[0];
|
|
122
|
+
await executeDeploy([firstAction], false, false, home);
|
|
123
|
+
await writeFile(path.join(firstSource, "SKILL.md"), SECOND_SKILL_MD);
|
|
124
|
+
const failAction = {
|
|
125
|
+
...firstAction,
|
|
126
|
+
};
|
|
127
|
+
const { succeeded, failed } = await executeDeploy([failAction], false, false, home, {
|
|
128
|
+
registry: createFailingRegistryPersistence(),
|
|
129
|
+
});
|
|
130
|
+
assert.equal(succeeded, 0);
|
|
131
|
+
assert.equal(failed.length, 1);
|
|
132
|
+
assert.match(failed[0]?.error, /simulated registry persistence failure/);
|
|
133
|
+
await options.assertManagedTarget(firstAction.target, firstSource, FIRST_SKILL_MD);
|
|
134
|
+
assert.ok(!(await exists(`${firstAction.target}.inception-backup`)));
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
138
|
+
await rm(home, { recursive: true, force: true });
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
export function registerSharedSkillDirRevertScenarios(options) {
|
|
143
|
+
it("reverts a managed skill-dir target", async () => {
|
|
144
|
+
const sourceDir = await makeTmpDir("ie-skill-dir-source");
|
|
145
|
+
const home = await makeTmpDir("ie-skill-dir-home");
|
|
146
|
+
try {
|
|
147
|
+
await createSkillSource(sourceDir, "skills/test-skill", FIRST_SKILL_MD);
|
|
148
|
+
const { actions } = await planDeploy(testSkillManifest, sourceDir, ["claude-code"], home);
|
|
149
|
+
await executeDeploy(actions, false, false, home);
|
|
150
|
+
const revertActions = planRevert(testSkillManifest, ["claude-code"], home);
|
|
151
|
+
const { succeeded, skipped, failed } = await executeRevert(revertActions, false, false, home);
|
|
152
|
+
assert.equal(succeeded, 1);
|
|
153
|
+
assert.equal(skipped, 0);
|
|
154
|
+
assert.equal(failed.length, 0);
|
|
155
|
+
assert.ok(!(await exists(actions[0]?.target)));
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
159
|
+
await rm(home, { recursive: true, force: true });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
it("skips an unmanaged skill-dir target", async () => {
|
|
163
|
+
const home = await makeTmpDir("ie-skill-dir-home");
|
|
164
|
+
try {
|
|
165
|
+
const revertActions = planRevert(testSkillManifest, ["claude-code"], home);
|
|
166
|
+
const target = revertActions[0]?.target;
|
|
167
|
+
await mkdir(target, { recursive: true });
|
|
168
|
+
await writeFile(path.join(target, "SKILL.md"), FIRST_SKILL_MD);
|
|
169
|
+
const { succeeded, skipped, failed } = await executeRevert(revertActions, false, false, home);
|
|
170
|
+
assert.equal(succeeded, 0);
|
|
171
|
+
assert.equal(skipped, 1);
|
|
172
|
+
assert.equal(failed.length, 0);
|
|
173
|
+
assert.ok(await exists(target));
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
await rm(home, { recursive: true, force: true });
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
it("skips a target whose registry entry belongs to a different skill", async () => {
|
|
180
|
+
const sourceDir = await makeTmpDir("ie-skill-dir-source");
|
|
181
|
+
const home = await makeTmpDir("ie-skill-dir-home");
|
|
182
|
+
try {
|
|
183
|
+
const source = await createSkillSource(sourceDir, "skills/test-skill", FIRST_SKILL_MD);
|
|
184
|
+
const revertActions = planRevert(testSkillManifest, ["claude-code"], home);
|
|
185
|
+
const target = revertActions[0]?.target;
|
|
186
|
+
await mkdir(target, { recursive: true });
|
|
187
|
+
await writeFile(path.join(target, "SKILL.md"), FIRST_SKILL_MD);
|
|
188
|
+
await registerDeployment(home, target, {
|
|
189
|
+
kind: "skill-dir",
|
|
190
|
+
source,
|
|
191
|
+
skill: "different-skill",
|
|
192
|
+
agent: "codex",
|
|
193
|
+
method: options.method,
|
|
194
|
+
});
|
|
195
|
+
const { succeeded, skipped, failed } = await executeRevert(revertActions, false, false, home);
|
|
196
|
+
assert.equal(succeeded, 0);
|
|
197
|
+
assert.equal(skipped, 1);
|
|
198
|
+
assert.equal(failed.length, 0);
|
|
199
|
+
assert.ok(await exists(target));
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
await rm(sourceDir, { recursive: true, force: true });
|
|
203
|
+
await rm(home, { recursive: true, force: true });
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RegistryPersistence } from "../../src/core/ownership.ts";
|
|
2
|
+
import type { Manifest } from "../../src/types.ts";
|
|
3
|
+
export declare function createSkillSource(baseDir: string, skillPath?: string, skillBody?: string): Promise<string>;
|
|
4
|
+
export declare const testSkillManifest: Manifest;
|
|
5
|
+
export declare function createFailingRegistryPersistence(error?: Error): RegistryPersistence;
|
|
6
|
+
export declare function assertSymlinkTarget(target: string, source: string): Promise<void>;
|
|
7
|
+
export declare function assertCopyTarget(target: string, expectedSkillMd: string): Promise<void>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { lstat, mkdir, readFile, readlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { defaultRegistryPersistence } from "../../src/core/ownership.js";
|
|
5
|
+
import { exists } from "./fs.js";
|
|
6
|
+
export async function createSkillSource(baseDir, skillPath = "skills/test-skill", skillBody = "---\nname: test\ndescription: Test skill\n---\n# Test") {
|
|
7
|
+
const fullPath = path.join(baseDir, skillPath);
|
|
8
|
+
await mkdir(fullPath, { recursive: true });
|
|
9
|
+
await writeFile(path.join(fullPath, "SKILL.md"), skillBody);
|
|
10
|
+
return fullPath;
|
|
11
|
+
}
|
|
12
|
+
export const testSkillManifest = {
|
|
13
|
+
skills: [
|
|
14
|
+
{
|
|
15
|
+
name: "test-skill",
|
|
16
|
+
path: "skills/test-skill",
|
|
17
|
+
agents: ["claude-code"],
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
files: [],
|
|
21
|
+
configs: [],
|
|
22
|
+
mcpServers: [],
|
|
23
|
+
agentRules: [],
|
|
24
|
+
permissions: [],
|
|
25
|
+
agentDefinitions: [],
|
|
26
|
+
};
|
|
27
|
+
export function createFailingRegistryPersistence(error = new Error("simulated registry persistence failure")) {
|
|
28
|
+
return {
|
|
29
|
+
load(home) {
|
|
30
|
+
return defaultRegistryPersistence.load(home);
|
|
31
|
+
},
|
|
32
|
+
async save() {
|
|
33
|
+
throw error;
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function assertSymlinkTarget(target, source) {
|
|
38
|
+
assert.ok(await exists(target), "expected target to exist");
|
|
39
|
+
assert.ok((await lstat(target)).isSymbolicLink(), "expected a symlink target");
|
|
40
|
+
assert.equal(await readlink(target), source);
|
|
41
|
+
}
|
|
42
|
+
export async function assertCopyTarget(target, expectedSkillMd) {
|
|
43
|
+
assert.ok(await exists(target), "expected target to exist");
|
|
44
|
+
assert.ok(!(await lstat(target)).isSymbolicLink(), "expected a copied directory");
|
|
45
|
+
assert.equal(await readFile(path.join(target, "SKILL.md"), "utf-8"), expectedSkillMd);
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|