@kuznai/inception-engine 0.6.2 → 0.8.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 +45 -8
- package/dist/config/agents.js +1 -0
- package/dist/config/manifest.js +6 -3
- package/dist/core/deploy.d.ts +6 -2
- package/dist/core/deploy.js +341 -31
- package/dist/core/detect.d.ts +2 -2
- package/dist/core/detect.js +5 -5
- package/dist/core/ownership.d.ts +19 -7
- package/dist/core/ownership.js +9 -3
- package/dist/core/preflight.d.ts +2 -2
- package/dist/core/preflight.js +27 -5
- package/dist/core/revert.d.ts +2 -1
- package/dist/core/revert.js +229 -37
- package/dist/index.js +46 -13
- package/dist/schemas/manifest.d.ts +68 -2
- package/dist/schemas/manifest.js +61 -21
- package/dist/schemas/registry.d.ts +112 -5
- package/dist/schemas/registry.js +23 -2
- package/dist/types.d.ts +49 -3
- package/package.json +4 -2
package/dist/core/ownership.d.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
|
-
import { type RegistryEntry } from "../schemas/registry.ts";
|
|
1
|
+
import { type ConfigPatchRegistryEntry, type FileWriteRegistryEntry, type RegistryEntry, type SkillDirRegistryEntry } from "../schemas/registry.ts";
|
|
2
2
|
import type { AgentId } from "../types.ts";
|
|
3
3
|
export type { RegistryEntry } from "../schemas/registry.ts";
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
export type VerifyExpected = {
|
|
5
|
+
kind: "skill-dir";
|
|
6
|
+
source: string;
|
|
7
|
+
skill: string;
|
|
8
|
+
agent: AgentId;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "file-write";
|
|
9
11
|
source: string;
|
|
10
12
|
skill: string;
|
|
11
13
|
agent: AgentId;
|
|
12
|
-
}
|
|
14
|
+
} | {
|
|
15
|
+
kind: "config-patch";
|
|
16
|
+
skill: string;
|
|
17
|
+
agent: AgentId;
|
|
18
|
+
};
|
|
19
|
+
export declare function registryPath(home: string): string;
|
|
20
|
+
export type RegisterEntry = Omit<SkillDirRegistryEntry, "deployed"> | Omit<FileWriteRegistryEntry, "deployed"> | Omit<ConfigPatchRegistryEntry, "deployed">;
|
|
21
|
+
export declare function registerDeployment(home: string, targetPath: string, entry: RegisterEntry): Promise<void>;
|
|
22
|
+
export declare function unregisterDeployment(home: string, targetPath: string): Promise<void>;
|
|
23
|
+
export declare function lookupDeployment(home: string, targetPath: string): Promise<RegistryEntry | null>;
|
|
24
|
+
export declare function verifyDeployment(home: string, targetPath: string, expected: VerifyExpected): Promise<RegistryEntry | null>;
|
package/dist/core/ownership.js
CHANGED
|
@@ -59,9 +59,15 @@ export async function verifyDeployment(home, targetPath, expected) {
|
|
|
59
59
|
const entry = await lookupDeployment(home, targetPath);
|
|
60
60
|
if (!entry)
|
|
61
61
|
return null;
|
|
62
|
-
if (entry.
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
if (entry.kind !== expected.kind)
|
|
63
|
+
return null;
|
|
64
|
+
if (entry.skill !== expected.skill)
|
|
65
|
+
return null;
|
|
66
|
+
if (entry.agent !== expected.agent)
|
|
67
|
+
return null;
|
|
68
|
+
if ((expected.kind === "skill-dir" || expected.kind === "file-write") &&
|
|
69
|
+
(entry.kind === "skill-dir" || entry.kind === "file-write") &&
|
|
70
|
+
entry.source !== expected.source) {
|
|
65
71
|
return null;
|
|
66
72
|
}
|
|
67
73
|
return entry;
|
package/dist/core/preflight.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { CliOptions, Manifest } from "../types.ts";
|
|
1
|
+
import type { AgentId, CliOptions, Manifest } from "../types.ts";
|
|
2
2
|
export interface PreflightWarning {
|
|
3
3
|
kind: "policy" | "config-authority" | "info";
|
|
4
4
|
message: string;
|
|
5
5
|
}
|
|
6
|
-
export declare function runPreflight(_options: CliOptions, _manifest: Manifest, _home: string): Promise<PreflightWarning[]>;
|
|
6
|
+
export declare function runPreflight(_options: CliOptions, _manifest: Manifest, _home: string, detectedAgents: AgentId[]): Promise<PreflightWarning[]>;
|
package/dist/core/preflight.js
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { AGENT_REGISTRY_BY_ID } from "../config/agents.js";
|
|
2
|
+
export async function runPreflight(_options, _manifest, _home, detectedAgents) {
|
|
3
|
+
const warnings = [];
|
|
4
|
+
for (const agentId of detectedAgents) {
|
|
5
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
6
|
+
if (!agent)
|
|
7
|
+
continue;
|
|
8
|
+
if (agent.provenance.skills === "implementation-only") {
|
|
9
|
+
warnings.push({
|
|
10
|
+
kind: "config-authority",
|
|
11
|
+
message: `Agent "${agentId}" skill support is implementation-only: paths are derived from source inspection, not published documentation.`,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
else if (agent.provenance.skills === "provisional") {
|
|
15
|
+
warnings.push({
|
|
16
|
+
kind: "config-authority",
|
|
17
|
+
message: `Agent "${agentId}" skill support is provisional: behavior has not been independently verified.`,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
if (agent.policyNote) {
|
|
21
|
+
warnings.push({
|
|
22
|
+
kind: "policy",
|
|
23
|
+
message: `Agent "${agentId}": ${agent.policyNote}`,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return warnings;
|
|
6
28
|
}
|
package/dist/core/revert.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentId, Manifest, RevertAction } from "../types.ts";
|
|
1
|
+
import type { AgentId, Manifest, PlannedChange, RevertAction } from "../types.ts";
|
|
2
2
|
export declare function planRevert(manifest: Manifest, detectedAgents: AgentId[], home: string): RevertAction[];
|
|
3
3
|
export declare function planRevertAll(manifest: Manifest, home: string): RevertAction[];
|
|
4
4
|
export declare function executeRevert(actions: RevertAction[], dryRun: boolean, verbose: boolean, home: string): Promise<{
|
|
@@ -8,4 +8,5 @@ export declare function executeRevert(actions: RevertAction[], dryRun: boolean,
|
|
|
8
8
|
action: RevertAction;
|
|
9
9
|
error: string;
|
|
10
10
|
}>;
|
|
11
|
+
planned: PlannedChange[];
|
|
11
12
|
}>;
|
package/dist/core/revert.js
CHANGED
|
@@ -1,46 +1,126 @@
|
|
|
1
|
-
import { lstat, rm, unlink } from "node:fs/promises";
|
|
1
|
+
import { lstat, readFile, rm, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
2
3
|
import { AGENT_REGISTRY_BY_ID } from "../config/agents.js";
|
|
3
4
|
import { logger } from "../logger.js";
|
|
4
5
|
import { lookupDeployment, unregisterDeployment } from "./ownership.js";
|
|
5
6
|
import { resolveAgentSkillPath } from "./resolve.js";
|
|
6
|
-
|
|
7
|
+
function resolveTargetTemplate(template, home) {
|
|
8
|
+
const appdata = process.env.APPDATA ?? path.join(home, "AppData", "Roaming");
|
|
9
|
+
const xdgRaw = process.env.XDG_CONFIG_HOME;
|
|
10
|
+
const xdgConfig = xdgRaw && path.isAbsolute(xdgRaw) ? xdgRaw : path.join(home, ".config");
|
|
11
|
+
return template
|
|
12
|
+
.replace("{home}", home)
|
|
13
|
+
.replace("{appdata}", appdata)
|
|
14
|
+
.replace("{xdg_config}", xdgConfig);
|
|
15
|
+
}
|
|
16
|
+
function buildSkillDirReverts(manifest, home, agentFilter) {
|
|
7
17
|
const actions = [];
|
|
8
18
|
for (const skill of manifest.skills) {
|
|
9
19
|
for (const agentId of skill.agents) {
|
|
10
|
-
if (!
|
|
20
|
+
if (agentFilter && !agentFilter.includes(agentId))
|
|
11
21
|
continue;
|
|
12
22
|
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
13
23
|
if (!agent)
|
|
14
24
|
continue;
|
|
15
|
-
const target = resolveAgentSkillPath(agent, skill.name, home);
|
|
16
25
|
actions.push({
|
|
17
26
|
kind: "skill-dir",
|
|
18
27
|
skill: skill.name,
|
|
19
28
|
agent: agentId,
|
|
20
|
-
target,
|
|
29
|
+
target: resolveAgentSkillPath(agent, skill.name, home),
|
|
21
30
|
});
|
|
22
31
|
}
|
|
23
32
|
}
|
|
24
33
|
return actions;
|
|
25
34
|
}
|
|
26
|
-
|
|
35
|
+
function buildFileWriteReverts(manifest, home, agentFilter) {
|
|
27
36
|
const actions = [];
|
|
28
|
-
for (const
|
|
29
|
-
for (const agentId of
|
|
37
|
+
for (const fileEntry of manifest.files ?? []) {
|
|
38
|
+
for (const agentId of fileEntry.agents) {
|
|
39
|
+
if (agentFilter && !agentFilter.includes(agentId))
|
|
40
|
+
continue;
|
|
30
41
|
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
31
42
|
if (!agent)
|
|
32
43
|
continue;
|
|
33
|
-
const target = resolveAgentSkillPath(agent, skill.name, home);
|
|
34
44
|
actions.push({
|
|
35
|
-
kind: "
|
|
36
|
-
skill:
|
|
45
|
+
kind: "file-write",
|
|
46
|
+
skill: fileEntry.name,
|
|
47
|
+
agent: agentId,
|
|
48
|
+
target: resolveTargetTemplate(fileEntry.target, home),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return actions;
|
|
53
|
+
}
|
|
54
|
+
function buildConfigPatchReverts(manifest, home, agentFilter) {
|
|
55
|
+
const actions = [];
|
|
56
|
+
for (const configEntry of manifest.configs ?? []) {
|
|
57
|
+
for (const agentId of configEntry.agents) {
|
|
58
|
+
if (agentFilter && !agentFilter.includes(agentId))
|
|
59
|
+
continue;
|
|
60
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
61
|
+
if (!agent)
|
|
62
|
+
continue;
|
|
63
|
+
actions.push({
|
|
64
|
+
kind: "config-patch",
|
|
65
|
+
skill: configEntry.name,
|
|
37
66
|
agent: agentId,
|
|
38
|
-
target,
|
|
67
|
+
target: resolveTargetTemplate(configEntry.target, home),
|
|
39
68
|
});
|
|
40
69
|
}
|
|
41
70
|
}
|
|
42
71
|
return actions;
|
|
43
72
|
}
|
|
73
|
+
export function planRevert(manifest, detectedAgents, home) {
|
|
74
|
+
return [
|
|
75
|
+
...buildSkillDirReverts(manifest, home, detectedAgents),
|
|
76
|
+
...buildFileWriteReverts(manifest, home, detectedAgents),
|
|
77
|
+
...buildConfigPatchReverts(manifest, home, detectedAgents),
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
export function planRevertAll(manifest, home) {
|
|
81
|
+
return [
|
|
82
|
+
...buildSkillDirReverts(manifest, home, null),
|
|
83
|
+
...buildFileWriteReverts(manifest, home, null),
|
|
84
|
+
...buildConfigPatchReverts(manifest, home, null),
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
function recordOutcome(result, action, counts, failed) {
|
|
88
|
+
if (result.outcome === "fail") {
|
|
89
|
+
failed.push({ action, error: result.error });
|
|
90
|
+
}
|
|
91
|
+
else if (result.outcome === "skip") {
|
|
92
|
+
counts.skipped++;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
counts.succeeded++;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function readJsonConfig(filePath) {
|
|
99
|
+
const rawContent = await readFile(filePath, "utf-8");
|
|
100
|
+
let parsed;
|
|
101
|
+
try {
|
|
102
|
+
parsed = JSON.parse(rawContent);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
throw new Error(`Config file is not valid JSON: ${filePath}`);
|
|
106
|
+
}
|
|
107
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
108
|
+
throw new Error(`Config file is not a JSON object: ${filePath}`);
|
|
109
|
+
}
|
|
110
|
+
return parsed;
|
|
111
|
+
}
|
|
112
|
+
function applyUndoPatch(current, undoPatch) {
|
|
113
|
+
const restored = { ...current };
|
|
114
|
+
for (const [key, originalValue] of Object.entries(undoPatch)) {
|
|
115
|
+
if (originalValue === null) {
|
|
116
|
+
delete restored[key];
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
restored[key] = originalValue;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return restored;
|
|
123
|
+
}
|
|
44
124
|
function lstatOutcome(err) {
|
|
45
125
|
if (err.code === "ENOENT") {
|
|
46
126
|
return { outcome: "skip" };
|
|
@@ -52,34 +132,33 @@ export async function executeRevert(actions, dryRun, verbose, home) {
|
|
|
52
132
|
let succeeded = 0;
|
|
53
133
|
let skipped = 0;
|
|
54
134
|
const failed = [];
|
|
135
|
+
const planned = [];
|
|
136
|
+
const counts = { succeeded, skipped };
|
|
55
137
|
for (const action of actions) {
|
|
138
|
+
let result;
|
|
56
139
|
switch (action.kind) {
|
|
57
|
-
case "skill-dir":
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
else if (result.outcome === "skip") {
|
|
63
|
-
skipped++;
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
succeeded++;
|
|
67
|
-
}
|
|
140
|
+
case "skill-dir":
|
|
141
|
+
result = await executeRevertAction(action, dryRun, verbose, home, planned);
|
|
142
|
+
break;
|
|
143
|
+
case "file-write":
|
|
144
|
+
result = await revertFileWrite(action, dryRun, verbose, home, planned);
|
|
68
145
|
break;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
146
|
+
case "config-patch":
|
|
147
|
+
result = await revertConfigPatch(action, dryRun, verbose, home, planned);
|
|
148
|
+
break;
|
|
149
|
+
default:
|
|
150
|
+
throw new Error(`Unhandled revert action kind: ${action}`);
|
|
74
151
|
}
|
|
152
|
+
recordOutcome(result, action, counts, failed);
|
|
75
153
|
}
|
|
76
|
-
|
|
154
|
+
succeeded = counts.succeeded;
|
|
155
|
+
skipped = counts.skipped;
|
|
156
|
+
return { succeeded, skipped, failed, planned };
|
|
77
157
|
}
|
|
78
|
-
async function executeRevertAction(action, dryRun, verbose, home) {
|
|
158
|
+
async function executeRevertAction(action, dryRun, verbose, home, planned) {
|
|
79
159
|
const label = `${action.skill} -> ${action.agent}`;
|
|
80
|
-
let stat;
|
|
81
160
|
try {
|
|
82
|
-
|
|
161
|
+
await lstat(action.target);
|
|
83
162
|
}
|
|
84
163
|
catch (err) {
|
|
85
164
|
const result = lstatOutcome(err);
|
|
@@ -96,14 +175,20 @@ async function executeRevertAction(action, dryRun, verbose, home) {
|
|
|
96
175
|
return { outcome: "skip" };
|
|
97
176
|
}
|
|
98
177
|
if (dryRun) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
178
|
+
planned.push({
|
|
179
|
+
verb: "remove",
|
|
180
|
+
kind: "skill-dir",
|
|
181
|
+
skill: action.skill,
|
|
182
|
+
agent: action.agent,
|
|
183
|
+
target: action.target,
|
|
184
|
+
});
|
|
103
185
|
return { outcome: "ok" };
|
|
104
186
|
}
|
|
105
187
|
try {
|
|
106
|
-
|
|
188
|
+
// Re-stat immediately before deletion to minimise the window between the
|
|
189
|
+
// type-check and the removal syscall.
|
|
190
|
+
const currentStat = await lstat(action.target);
|
|
191
|
+
if (currentStat.isSymbolicLink()) {
|
|
107
192
|
await unlink(action.target);
|
|
108
193
|
}
|
|
109
194
|
else {
|
|
@@ -116,6 +201,113 @@ async function executeRevertAction(action, dryRun, verbose, home) {
|
|
|
116
201
|
}
|
|
117
202
|
return { outcome: "ok" };
|
|
118
203
|
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
if (err.code === "ENOENT") {
|
|
206
|
+
// Target disappeared between ownership check and removal — treat as skip.
|
|
207
|
+
logger.skip(label, "(disappeared before removal, skipping)");
|
|
208
|
+
return { outcome: "skip" };
|
|
209
|
+
}
|
|
210
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
211
|
+
logger.fail(label, msg);
|
|
212
|
+
return { outcome: "fail", error: msg };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async function revertFileWrite(action, dryRun, verbose, home, planned) {
|
|
216
|
+
const label = `${action.skill} -> ${action.agent}`;
|
|
217
|
+
try {
|
|
218
|
+
await lstat(action.target);
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
const result = lstatOutcome(err);
|
|
222
|
+
if (result.outcome === "skip") {
|
|
223
|
+
logger.skip(label, "(not found, skipping)");
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
logger.fail(label, result.error);
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
const entry = await lookupDeployment(home, action.target);
|
|
230
|
+
if (!entry || entry.skill !== action.skill || entry.agent !== action.agent) {
|
|
231
|
+
logger.warn(label, `skipping: ${action.target} is not in the deployment registry — not managed by inception-engine`);
|
|
232
|
+
return { outcome: "skip" };
|
|
233
|
+
}
|
|
234
|
+
if (dryRun) {
|
|
235
|
+
planned.push({
|
|
236
|
+
verb: "remove",
|
|
237
|
+
kind: "file-write",
|
|
238
|
+
skill: action.skill,
|
|
239
|
+
agent: action.agent,
|
|
240
|
+
target: action.target,
|
|
241
|
+
});
|
|
242
|
+
return { outcome: "ok" };
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
// Re-stat immediately before deletion to minimise the type-check to
|
|
246
|
+
// removal window; handle the case where the file has since disappeared.
|
|
247
|
+
await lstat(action.target);
|
|
248
|
+
await unlink(action.target);
|
|
249
|
+
await unregisterDeployment(home, action.target);
|
|
250
|
+
logger.ok(label);
|
|
251
|
+
if (verbose) {
|
|
252
|
+
logger.detail(`removed: ${action.target}`);
|
|
253
|
+
}
|
|
254
|
+
return { outcome: "ok" };
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
if (err.code === "ENOENT") {
|
|
258
|
+
logger.skip(label, "(disappeared before removal, skipping)");
|
|
259
|
+
return { outcome: "skip" };
|
|
260
|
+
}
|
|
261
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
262
|
+
logger.fail(label, msg);
|
|
263
|
+
return { outcome: "fail", error: msg };
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async function revertConfigPatch(action, dryRun, verbose, home, planned) {
|
|
267
|
+
const label = `${action.skill} -> ${action.agent}`;
|
|
268
|
+
try {
|
|
269
|
+
await lstat(action.target);
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
const result = lstatOutcome(err);
|
|
273
|
+
if (result.outcome === "skip") {
|
|
274
|
+
logger.skip(label, "(not found, skipping)");
|
|
275
|
+
return result;
|
|
276
|
+
}
|
|
277
|
+
logger.fail(label, result.error);
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
const entry = await lookupDeployment(home, action.target);
|
|
281
|
+
if (!entry ||
|
|
282
|
+
entry.kind !== "config-patch" ||
|
|
283
|
+
entry.skill !== action.skill ||
|
|
284
|
+
entry.agent !== action.agent) {
|
|
285
|
+
logger.warn(label, `skipping: ${action.target} is not in the deployment registry — not managed by inception-engine`);
|
|
286
|
+
return { outcome: "skip" };
|
|
287
|
+
}
|
|
288
|
+
const configPatchEntry = entry;
|
|
289
|
+
if (dryRun) {
|
|
290
|
+
planned.push({
|
|
291
|
+
verb: "unapply-patch",
|
|
292
|
+
kind: "config-patch",
|
|
293
|
+
skill: action.skill,
|
|
294
|
+
agent: action.agent,
|
|
295
|
+
target: action.target,
|
|
296
|
+
patch: configPatchEntry.undoPatch,
|
|
297
|
+
});
|
|
298
|
+
return { outcome: "ok" };
|
|
299
|
+
}
|
|
300
|
+
try {
|
|
301
|
+
const current = await readJsonConfig(action.target);
|
|
302
|
+
const restored = applyUndoPatch(current, configPatchEntry.undoPatch);
|
|
303
|
+
await writeFile(action.target, `${JSON.stringify(restored, null, 2)}\n`, "utf-8");
|
|
304
|
+
await unregisterDeployment(home, action.target);
|
|
305
|
+
logger.ok(label);
|
|
306
|
+
if (verbose) {
|
|
307
|
+
logger.detail(`unapplied patch from: ${action.target}`);
|
|
308
|
+
}
|
|
309
|
+
return { outcome: "ok" };
|
|
310
|
+
}
|
|
119
311
|
catch (err) {
|
|
120
312
|
const msg = err instanceof Error ? err.message : String(err);
|
|
121
313
|
logger.fail(label, msg);
|
package/dist/index.js
CHANGED
|
@@ -5,8 +5,8 @@ import { AGENT_REGISTRY } from "./config/agents.js";
|
|
|
5
5
|
import { loadManifest } from "./config/manifest.js";
|
|
6
6
|
import { executeDeploy, planDeploy } from "./core/deploy.js";
|
|
7
7
|
import { detectInstalledAgents } from "./core/detect.js";
|
|
8
|
-
import { resolveHome } from "./core/resolve.js";
|
|
9
8
|
import { runPreflight } from "./core/preflight.js";
|
|
9
|
+
import { resolveHome } from "./core/resolve.js";
|
|
10
10
|
import { executeRevert, planRevert, planRevertAll } from "./core/revert.js";
|
|
11
11
|
import { UserError } from "./errors.js";
|
|
12
12
|
import { dryRunPrefix, logger } from "./logger.js";
|
|
@@ -106,15 +106,26 @@ async function main() {
|
|
|
106
106
|
}
|
|
107
107
|
const manifest = await loadManifest(options.directory);
|
|
108
108
|
const home = resolveHome();
|
|
109
|
-
const preflightWarnings = await runPreflight(options, manifest, home);
|
|
110
|
-
for (const w of preflightWarnings) {
|
|
111
|
-
logger.warn("preflight", w.message);
|
|
112
|
-
}
|
|
113
109
|
if (options.command === "deploy") {
|
|
114
110
|
return runDeploy(options, manifest, home);
|
|
115
111
|
}
|
|
116
112
|
return runRevert(options, manifest, home);
|
|
117
113
|
}
|
|
114
|
+
function renderDryRunPlan(planned) {
|
|
115
|
+
for (const change of planned) {
|
|
116
|
+
logger.plan(`[${change.agent}] ${change.verb} ${change.skill}`);
|
|
117
|
+
if (change.source !== undefined) {
|
|
118
|
+
logger.detail(`source: ${change.source}`);
|
|
119
|
+
}
|
|
120
|
+
logger.detail(`target: ${change.target}`);
|
|
121
|
+
if (change.verb === "patch-config" && change.patch !== undefined) {
|
|
122
|
+
logger.detail(`patch: ${JSON.stringify(change.patch)}`);
|
|
123
|
+
}
|
|
124
|
+
else if (change.verb === "unapply-patch" && change.patch !== undefined) {
|
|
125
|
+
logger.detail(`undo: ${JSON.stringify(change.patch)}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
118
129
|
async function runDeploy(options, manifest, home) {
|
|
119
130
|
let detectedAgents;
|
|
120
131
|
if (options.agents) {
|
|
@@ -134,19 +145,34 @@ async function runDeploy(options, manifest, home) {
|
|
|
134
145
|
logger.info(`Detected agents: ${detectedAgents.join(", ")}`);
|
|
135
146
|
}
|
|
136
147
|
}
|
|
137
|
-
const
|
|
148
|
+
const preflightWarnings = await runPreflight(options, manifest, home, detectedAgents);
|
|
149
|
+
for (const w of preflightWarnings) {
|
|
150
|
+
const label = w.kind === "policy" ? "policy" : "preflight";
|
|
151
|
+
logger.warn(label, w.message);
|
|
152
|
+
}
|
|
153
|
+
const { actions, warnings: planWarnings } = await planDeploy(manifest, options.directory, detectedAgents, home);
|
|
154
|
+
for (const w of planWarnings) {
|
|
155
|
+
logger.warn("plan", w.message);
|
|
156
|
+
}
|
|
138
157
|
if (actions.length === 0) {
|
|
139
158
|
logger.info("No skills to deploy for detected agents.");
|
|
140
159
|
return 0;
|
|
141
160
|
}
|
|
142
|
-
logger.info(`${dryRunPrefix(options.dryRun)}Deploying ${actions.length}
|
|
143
|
-
const { succeeded, failed } = await executeDeploy(actions, options.dryRun, options.verbose, home);
|
|
161
|
+
logger.info(`${dryRunPrefix(options.dryRun)}Deploying ${actions.length} action(s):`);
|
|
162
|
+
const { succeeded, failed, planned } = await executeDeploy(actions, options.dryRun, options.verbose, home);
|
|
163
|
+
if (options.dryRun) {
|
|
164
|
+
logger.info("");
|
|
165
|
+
renderDryRunPlan(planned);
|
|
166
|
+
logger.info("");
|
|
167
|
+
logger.info(`${planned.length} action(s) would be applied (dry-run)`);
|
|
168
|
+
return 0;
|
|
169
|
+
}
|
|
144
170
|
logger.info("");
|
|
145
171
|
if (failed.length > 0) {
|
|
146
172
|
logger.info(`${succeeded} succeeded, ${failed.length} failed`);
|
|
147
173
|
return 1;
|
|
148
174
|
}
|
|
149
|
-
logger.info(`${succeeded}
|
|
175
|
+
logger.info(`${succeeded} action(s) deployed`);
|
|
150
176
|
return 0;
|
|
151
177
|
}
|
|
152
178
|
async function runRevert(options, manifest, home) {
|
|
@@ -157,21 +183,28 @@ async function runRevert(options, manifest, home) {
|
|
|
157
183
|
logger.info("No skills to revert.");
|
|
158
184
|
return 0;
|
|
159
185
|
}
|
|
160
|
-
logger.info(`${dryRunPrefix(options.dryRun)}Reverting ${actions.length}
|
|
161
|
-
const { succeeded, skipped, failed } = await executeRevert(actions, options.dryRun, options.verbose, home);
|
|
186
|
+
logger.info(`${dryRunPrefix(options.dryRun)}Reverting ${actions.length} action(s):`);
|
|
187
|
+
const { succeeded, skipped, failed, planned } = await executeRevert(actions, options.dryRun, options.verbose, home);
|
|
188
|
+
if (options.dryRun) {
|
|
189
|
+
logger.info("");
|
|
190
|
+
renderDryRunPlan(planned);
|
|
191
|
+
logger.info("");
|
|
192
|
+
logger.info(`${planned.length} action(s) would be removed (dry-run)`);
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
162
195
|
logger.info("");
|
|
163
196
|
if (failed.length > 0) {
|
|
164
197
|
const parts = [`${succeeded} removed`];
|
|
165
198
|
if (skipped > 0)
|
|
166
199
|
parts.push(`${skipped} skipped`);
|
|
167
200
|
parts.push(`${failed.length} failed`);
|
|
168
|
-
logger.info(
|
|
201
|
+
logger.info(parts.join(", "));
|
|
169
202
|
return 1;
|
|
170
203
|
}
|
|
171
204
|
const parts = [`${succeeded} removed`];
|
|
172
205
|
if (skipped > 0)
|
|
173
206
|
parts.push(`${skipped} skipped`);
|
|
174
|
-
logger.info(
|
|
207
|
+
logger.info(parts.join(", "));
|
|
175
208
|
return 0;
|
|
176
209
|
}
|
|
177
210
|
const USER_ERROR_EXIT = {
|
|
@@ -22,6 +22,38 @@ export declare const SkillEntrySchema: z.ZodObject<{
|
|
|
22
22
|
"github-copilot": "github-copilot";
|
|
23
23
|
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
24
24
|
}, z.core.$strip>;
|
|
25
|
+
export declare const FileEntrySchema: z.ZodObject<{
|
|
26
|
+
name: z.ZodString;
|
|
27
|
+
path: z.ZodString;
|
|
28
|
+
target: z.ZodString;
|
|
29
|
+
agents: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
30
|
+
"claude-code": "claude-code";
|
|
31
|
+
codex: "codex";
|
|
32
|
+
"gemini-cli": "gemini-cli";
|
|
33
|
+
antigravity: "antigravity";
|
|
34
|
+
opencode: "opencode";
|
|
35
|
+
"github-copilot": "github-copilot";
|
|
36
|
+
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
37
|
+
}, z.core.$strip>;
|
|
38
|
+
export declare const ConfigEntrySchema: z.ZodObject<{
|
|
39
|
+
name: z.ZodString;
|
|
40
|
+
target: z.ZodString;
|
|
41
|
+
patch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
42
|
+
agents: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
43
|
+
"claude-code": "claude-code";
|
|
44
|
+
codex: "codex";
|
|
45
|
+
"gemini-cli": "gemini-cli";
|
|
46
|
+
antigravity: "antigravity";
|
|
47
|
+
opencode: "opencode";
|
|
48
|
+
"github-copilot": "github-copilot";
|
|
49
|
+
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
50
|
+
}, z.core.$strip>;
|
|
51
|
+
export declare const McpServerEntrySchema: z.ZodObject<{
|
|
52
|
+
name: z.ZodString;
|
|
53
|
+
}, z.core.$loose>;
|
|
54
|
+
export declare const AgentRuleEntrySchema: z.ZodObject<{
|
|
55
|
+
name: z.ZodString;
|
|
56
|
+
}, z.core.$loose>;
|
|
25
57
|
export declare const ManifestSchema: z.ZodObject<{
|
|
26
58
|
skills: z.ZodArray<z.ZodObject<{
|
|
27
59
|
name: z.ZodString;
|
|
@@ -35,10 +67,44 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
35
67
|
"github-copilot": "github-copilot";
|
|
36
68
|
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
37
69
|
}, z.core.$strip>>;
|
|
38
|
-
|
|
39
|
-
|
|
70
|
+
files: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
71
|
+
name: z.ZodString;
|
|
72
|
+
path: z.ZodString;
|
|
73
|
+
target: z.ZodString;
|
|
74
|
+
agents: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
75
|
+
"claude-code": "claude-code";
|
|
76
|
+
codex: "codex";
|
|
77
|
+
"gemini-cli": "gemini-cli";
|
|
78
|
+
antigravity: "antigravity";
|
|
79
|
+
opencode: "opencode";
|
|
80
|
+
"github-copilot": "github-copilot";
|
|
81
|
+
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
82
|
+
}, z.core.$strip>>>;
|
|
83
|
+
configs: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
84
|
+
name: z.ZodString;
|
|
85
|
+
target: z.ZodString;
|
|
86
|
+
patch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
87
|
+
agents: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
88
|
+
"claude-code": "claude-code";
|
|
89
|
+
codex: "codex";
|
|
90
|
+
"gemini-cli": "gemini-cli";
|
|
91
|
+
antigravity: "antigravity";
|
|
92
|
+
opencode: "opencode";
|
|
93
|
+
"github-copilot": "github-copilot";
|
|
94
|
+
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
95
|
+
}, z.core.$strip>>>;
|
|
96
|
+
mcpServers: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
97
|
+
name: z.ZodString;
|
|
98
|
+
}, z.core.$loose>>>;
|
|
99
|
+
agentRules: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
100
|
+
name: z.ZodString;
|
|
101
|
+
}, z.core.$loose>>>;
|
|
40
102
|
}, z.core.$strip>;
|
|
41
103
|
export type SkillEntry = z.infer<typeof SkillEntrySchema>;
|
|
104
|
+
export type FileEntry = z.infer<typeof FileEntrySchema>;
|
|
105
|
+
export type ConfigEntry = z.infer<typeof ConfigEntrySchema>;
|
|
106
|
+
export type McpServerEntry = z.infer<typeof McpServerEntrySchema>;
|
|
107
|
+
export type AgentRuleEntry = z.infer<typeof AgentRuleEntrySchema>;
|
|
42
108
|
export type Manifest = z.infer<typeof ManifestSchema>;
|
|
43
109
|
export declare const AgentListSchema: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string[], string>>, z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
44
110
|
"claude-code": "claude-code";
|