@kuznai/inception-engine 0.20.0 → 0.22.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 +44 -4
- package/dist/src/config/agents.js +70 -0
- package/dist/src/core/adapters/hooks.d.ts +8 -0
- package/dist/src/core/adapters/hooks.js +86 -0
- package/dist/src/core/adapters/index.d.ts +4 -3
- package/dist/src/core/adapters/index.js +19 -22
- package/dist/src/core/adapters/mcp.js +15 -1
- package/dist/src/core/capabilities.d.ts +1 -1
- package/dist/src/core/capabilities.js +17 -2
- package/dist/src/core/deploy.js +18 -19
- package/dist/src/core/detect.js +2 -7
- package/dist/src/core/init.js +20 -11
- package/dist/src/core/preflight.js +35 -19
- package/dist/src/core/revert.js +1 -1
- package/dist/src/core/validation.d.ts +1 -0
- package/dist/src/core/validation.js +15 -0
- package/dist/src/errors.d.ts +3 -1
- package/dist/src/errors.js +2 -2
- package/dist/src/schemas/manifest.d.ts +27 -0
- package/dist/src/schemas/manifest.js +11 -1
- package/dist/src/types.d.ts +8 -2
- 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/unit/adapters.test.js +86 -2
- package/dist/test/unit/deploy.test.js +5 -3
- 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/preflight.test.js +18 -0
- package/dist/test/unit/revert.test.js +8 -0
- package/package.json +1 -1
package/dist/src/core/deploy.js
CHANGED
|
@@ -4,7 +4,9 @@ import path from "node:path";
|
|
|
4
4
|
import { AGENT_REGISTRY, AGENT_REGISTRY_BY_ID } from "../config/agents.js";
|
|
5
5
|
import { UserError } from "../errors.js";
|
|
6
6
|
import { logger } from "../logger.js";
|
|
7
|
+
import * as frontmatterAdapter from "./adapters/frontmatter.js";
|
|
7
8
|
import { compileAdapterActions } from "./adapters/index.js";
|
|
9
|
+
import { applyTomlMcpPatch } from "./adapters/toml.js";
|
|
8
10
|
import { planCapabilityForDeploy } from "./capabilities.js";
|
|
9
11
|
import { applyMergePatch, computeUndoPatch, isPlainObject, } from "./merge-patch.js";
|
|
10
12
|
import { lookupDeployment, registerDeployment, verifyDeployment, } from "./ownership.js";
|
|
@@ -105,7 +107,7 @@ function checkPairAgentRuleAmbiguities(manifest, primary, rider) {
|
|
|
105
107
|
JSON.stringify(primarySupport.path) === JSON.stringify(riderSupport.path)) {
|
|
106
108
|
warnings.push({
|
|
107
109
|
kind: "ambiguity",
|
|
108
|
-
message: `Both "${primary}" and "${rider}" are listed in agentRules entry "${entry.name}". Both target the same surface
|
|
110
|
+
message: `Both "${primary}" and "${rider}" are listed in agentRules entry "${entry.name}". Both target the same surface - listing both is redundant but harmless; deduplication ensures only one write action is emitted.`,
|
|
109
111
|
});
|
|
110
112
|
}
|
|
111
113
|
}
|
|
@@ -122,7 +124,7 @@ function checkPairMcpAmbiguities(manifest, primary, rider) {
|
|
|
122
124
|
continue;
|
|
123
125
|
warnings.push({
|
|
124
126
|
kind: "ambiguity",
|
|
125
|
-
message: `Both "${primary}" and "${rider}" are listed in mcpServers entry "${entry.name}". "${primary}" writes to a shared MCP surface
|
|
127
|
+
message: `Both "${primary}" and "${rider}" are listed in mcpServers entry "${entry.name}". "${primary}" writes to a shared MCP surface - verify that deploying to both does not produce conflicting MCP server behavior.`,
|
|
126
128
|
});
|
|
127
129
|
}
|
|
128
130
|
return warnings;
|
|
@@ -134,7 +136,7 @@ function checkPairAgentDefinitionAmbiguities(manifest, primary, rider) {
|
|
|
134
136
|
continue;
|
|
135
137
|
warnings.push({
|
|
136
138
|
kind: "ambiguity",
|
|
137
|
-
message: `Both "${primary}" and "${rider}" are listed in agentDefinitions entry "${entry.name}". They write to distinct surfaces
|
|
139
|
+
message: `Both "${primary}" and "${rider}" are listed in agentDefinitions entry "${entry.name}". They write to distinct surfaces - verify that this behavioral divergence is intended.`,
|
|
138
140
|
});
|
|
139
141
|
}
|
|
140
142
|
return warnings;
|
|
@@ -162,7 +164,7 @@ function checkAntigravityPathCollisions(manifest) {
|
|
|
162
164
|
if (defNames.has(entry.name)) {
|
|
163
165
|
warnings.push({
|
|
164
166
|
kind: "collision",
|
|
165
|
-
message: `agentDefinitions entry "${entry.name}" and mcpServers entry "${entry.name}" for agent "antigravity" both resolve to {repo}/.agents/rules/${entry.name}.md
|
|
167
|
+
message: `agentDefinitions entry "${entry.name}" and mcpServers entry "${entry.name}" for agent "antigravity" both resolve to {repo}/.agents/rules/${entry.name}.md - one will silently overwrite the other; use different names or remove one entry`,
|
|
166
168
|
});
|
|
167
169
|
}
|
|
168
170
|
}
|
|
@@ -212,17 +214,15 @@ async function planSkillDirActions(manifest, sourceDir, resolvedSourceDir, realR
|
|
|
212
214
|
});
|
|
213
215
|
}
|
|
214
216
|
}
|
|
215
|
-
|
|
216
|
-
await planSkillEntry(skill);
|
|
217
|
-
}
|
|
217
|
+
await Promise.all(manifest.skills.map(planSkillEntry));
|
|
218
218
|
return { actions, warnings };
|
|
219
219
|
}
|
|
220
220
|
async function planFileWriteActions(manifest, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace) {
|
|
221
221
|
const actions = [];
|
|
222
|
-
|
|
222
|
+
await Promise.all((manifest.files ?? []).map(async (fileEntry) => {
|
|
223
223
|
const targetAgents = fileEntry.agents.filter((agentId) => detectedAgents.includes(agentId));
|
|
224
224
|
if (targetAgents.length === 0)
|
|
225
|
-
|
|
225
|
+
return;
|
|
226
226
|
const source = path.resolve(sourceDir, fileEntry.path);
|
|
227
227
|
await validateSourcePath(source, fileEntry.path, resolvedSourceDir, realRoot);
|
|
228
228
|
await validateSourceFile(source, fileEntry.path);
|
|
@@ -236,10 +236,10 @@ async function planFileWriteActions(manifest, sourceDir, resolvedSourceDir, real
|
|
|
236
236
|
agent: agentId,
|
|
237
237
|
source,
|
|
238
238
|
target: resolveTargetTemplate(fileEntry.target, home, repo, workspace),
|
|
239
|
-
confidence:
|
|
239
|
+
confidence: "implementation-only",
|
|
240
240
|
});
|
|
241
241
|
}
|
|
242
|
-
}
|
|
242
|
+
}));
|
|
243
243
|
return actions;
|
|
244
244
|
}
|
|
245
245
|
function planConfigPatchActions(manifest, detectedAgents, home, repo, workspace) {
|
|
@@ -280,7 +280,7 @@ export async function planDeploy(manifest, sourceDir, detectedAgents, home, repo
|
|
|
280
280
|
...(await planFileWriteActions(manifest, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repoDir, workspace)),
|
|
281
281
|
...planConfigPatchActions(manifest, detectedAgents, home, repoDir, workspace),
|
|
282
282
|
];
|
|
283
|
-
const adapterResult = await compileAdapterActions(manifest.mcpServers, manifest.agentRules, manifest.permissions ?? [], sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repoDir, manifest.agentDefinitions ?? [], workspace);
|
|
283
|
+
const adapterResult = await compileAdapterActions(manifest.mcpServers, manifest.agentRules, manifest.permissions ?? [], sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repoDir, manifest.agentDefinitions ?? [], workspace, manifest.hooks ?? []);
|
|
284
284
|
actions.push(...adapterResult.actions);
|
|
285
285
|
const warnings = [
|
|
286
286
|
...skillPlan.warnings,
|
|
@@ -370,7 +370,7 @@ async function backupManagedFileWriteTarget(action, home, deps) {
|
|
|
370
370
|
agent: action.agent,
|
|
371
371
|
}, deps.registry);
|
|
372
372
|
if (!isOwned) {
|
|
373
|
-
throw new Error(`Target "${action.target}" exists but is not managed by inception-engine
|
|
373
|
+
throw new Error(`Target "${action.target}" exists but is not managed by inception-engine - refusing to overwrite`);
|
|
374
374
|
}
|
|
375
375
|
const backupPath = `${action.target}.inception-backup`;
|
|
376
376
|
await (deps.fileOps ?? defaultDeployFileOps).rm(backupPath, {
|
|
@@ -528,7 +528,7 @@ async function deployConfigPatch(action, dryRun, verbose, home, planned, deps) {
|
|
|
528
528
|
if (existingEntry &&
|
|
529
529
|
(existingEntry.skill !== action.skill ||
|
|
530
530
|
existingEntry.agent !== action.agent)) {
|
|
531
|
-
throw new Error(`Config "${action.target}" is already patched by skill "${existingEntry.skill}" for agent "${existingEntry.agent}"
|
|
531
|
+
throw new Error(`Config "${action.target}" is already patched by skill "${existingEntry.skill}" for agent "${existingEntry.agent}" - refusing to double-patch`);
|
|
532
532
|
}
|
|
533
533
|
const original = await readJsonConfigFile(action.target);
|
|
534
534
|
const undoPatch = computeUndoPatch(original, patch);
|
|
@@ -576,7 +576,7 @@ async function deployTomlPatch(action, dryRun, verbose, home, planned, deps) {
|
|
|
576
576
|
try {
|
|
577
577
|
const { previousValue } = await (deps.registry
|
|
578
578
|
? Promise.resolve({ previousValue: null })
|
|
579
|
-
:
|
|
579
|
+
: applyTomlMcpPatch(action.target, action.skill, action.config));
|
|
580
580
|
// Note: To truly support custom deps here we'd need to refactor toml adapter to accept deps.
|
|
581
581
|
// For now we assume standard adapter for TOML.
|
|
582
582
|
await registerDeployment(home, action.target, {
|
|
@@ -622,9 +622,8 @@ async function deployFrontmatterEmit(action, dryRun, verbose, home, planned, dep
|
|
|
622
622
|
(existingEntry.kind !== "frontmatter-emit" ||
|
|
623
623
|
existingEntry.skill !== action.skill ||
|
|
624
624
|
existingEntry.agent !== action.agent)) {
|
|
625
|
-
throw new Error(`Frontmatter target "${action.target}" is already patched by skill "${existingEntry.skill}" for agent "${existingEntry.agent}"
|
|
625
|
+
throw new Error(`Frontmatter target "${action.target}" is already patched by skill "${existingEntry.skill}" for agent "${existingEntry.agent}" - refusing to double-patch`);
|
|
626
626
|
}
|
|
627
|
-
const frontmatterAdapter = await import("./adapters/frontmatter.js");
|
|
628
627
|
const existing = await frontmatterAdapter.readFrontmatterDocumentFile(action.target);
|
|
629
628
|
const undoPatch = computeUndoPatch(existing.attributes, action.frontmatter);
|
|
630
629
|
const patchedFrontmatter = applyMergePatch(existing.attributes, action.frontmatter);
|
|
@@ -712,7 +711,7 @@ async function assertTargetAbsent(targetPath) {
|
|
|
712
711
|
catch (err) {
|
|
713
712
|
if (err instanceof Error && err.message.startsWith("Target path appeared"))
|
|
714
713
|
throw err;
|
|
715
|
-
// ENOENT is expected
|
|
714
|
+
// ENOENT is expected - target should not exist after backup
|
|
716
715
|
}
|
|
717
716
|
}
|
|
718
717
|
async function createDeployTarget(action, home, deps) {
|
|
@@ -775,7 +774,7 @@ async function backupExisting(targetPath, verbose, home, expected, deps) {
|
|
|
775
774
|
skill: expected.skill,
|
|
776
775
|
agent: expected.agent,
|
|
777
776
|
}, deps.registry))) {
|
|
778
|
-
throw new Error(`Target "${targetPath}" exists but is not managed by inception-engine
|
|
777
|
+
throw new Error(`Target "${targetPath}" exists but is not managed by inception-engine - refusing to overwrite`);
|
|
779
778
|
}
|
|
780
779
|
const backupPath = `${targetPath}.inception-backup`;
|
|
781
780
|
if (verbose) {
|
package/dist/src/core/detect.js
CHANGED
|
@@ -8,13 +8,8 @@ const defaultExecFn = async (cmd, args) => {
|
|
|
8
8
|
await execFileAsync(cmd, args);
|
|
9
9
|
};
|
|
10
10
|
export async function detectInstalledAgents(home) {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
if (await isAgentInstalled(agent, home)) {
|
|
14
|
-
detected.push(agent.id);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return detected;
|
|
11
|
+
const results = await Promise.all(AGENT_REGISTRY.map((agent) => isAgentInstalled(agent, home)));
|
|
12
|
+
return AGENT_REGISTRY.filter((_, i) => results[i]).map((a) => a.id);
|
|
18
13
|
}
|
|
19
14
|
async function isAgentInstalled(agent, home) {
|
|
20
15
|
const detectPath = resolveAgentDetectPath(agent, home);
|
package/dist/src/core/init.js
CHANGED
|
@@ -269,16 +269,24 @@ function agentsForDefinitionSubdir(subdir) {
|
|
|
269
269
|
*/
|
|
270
270
|
async function isSkippedMcpFile(subdir, absPath, relPath) {
|
|
271
271
|
const hasMcpSurfaceHere = AGENT_REGISTRY.some((agent) => {
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
272
|
+
const supports = [
|
|
273
|
+
agent.mcpSupport,
|
|
274
|
+
agent.mcpRepoSupport,
|
|
275
|
+
agent.mcpWorkspaceSupport,
|
|
276
|
+
];
|
|
277
|
+
for (const support of supports) {
|
|
278
|
+
if (support?.status !== "supported")
|
|
279
|
+
continue;
|
|
280
|
+
const tmpl = support.path.posix;
|
|
281
|
+
const repoIdx = tmpl.indexOf("{repo}");
|
|
282
|
+
const nameIdx = tmpl.findIndex((s) => s.includes("{name}"));
|
|
283
|
+
if (repoIdx === -1 || nameIdx === -1 || nameIdx <= repoIdx)
|
|
284
|
+
continue;
|
|
285
|
+
const prefix = tmpl.slice(repoIdx + 1, nameIdx).join("/");
|
|
286
|
+
if (prefix === subdir)
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
return false;
|
|
282
290
|
});
|
|
283
291
|
if (!hasMcpSurfaceHere)
|
|
284
292
|
return false;
|
|
@@ -521,7 +529,8 @@ function logPathAgentEntries(label, entries) {
|
|
|
521
529
|
return;
|
|
522
530
|
logger.detail(`${label}:`);
|
|
523
531
|
for (const e of entries) {
|
|
524
|
-
|
|
532
|
+
const entryAgents = e.agents ?? [];
|
|
533
|
+
logger.detail(` ${e.name} → ${e.path} [${entryAgents.join(", ")}]`);
|
|
525
534
|
}
|
|
526
535
|
}
|
|
527
536
|
function logVerboseManifest(skills, agentRules, mcpServers, files, configs, agentDefinitions) {
|
|
@@ -155,12 +155,22 @@ async function detectInstructionBudgetRisk(detectedAgents, manifest, sourceDir)
|
|
|
155
155
|
// will produce the proper UserError during action compilation.
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
|
+
const entries = [];
|
|
158
159
|
for (const entry of manifest.agentRules ?? []) {
|
|
159
|
-
|
|
160
|
+
entries.push({
|
|
161
|
+
path: entry.path,
|
|
162
|
+
label: "agentRules",
|
|
163
|
+
agents: entry.agents,
|
|
164
|
+
});
|
|
160
165
|
}
|
|
161
166
|
for (const entry of manifest.agentDefinitions ?? []) {
|
|
162
|
-
|
|
167
|
+
entries.push({
|
|
168
|
+
path: entry.path,
|
|
169
|
+
label: "agentDefinitions",
|
|
170
|
+
agents: entry.agents,
|
|
171
|
+
});
|
|
163
172
|
}
|
|
173
|
+
await Promise.all(entries.map((e) => checkEntry(e.path, e.label, e.agents)));
|
|
164
174
|
return warnings;
|
|
165
175
|
}
|
|
166
176
|
function pushCapabilityWarning(acc, kind, message) {
|
|
@@ -169,6 +179,21 @@ function pushCapabilityWarning(acc, kind, message) {
|
|
|
169
179
|
acc.seen.add(`${kind}:${message}`);
|
|
170
180
|
acc.warnings.push({ kind, message });
|
|
171
181
|
}
|
|
182
|
+
function collectPlannedSurfaceWarnings(acc, agentId, capability) {
|
|
183
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
184
|
+
if (!agent?.unsupportedSurfaces ||
|
|
185
|
+
(capability !== "mcpServers" && capability !== "permissions")) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
for (const surface of agent.unsupportedSurfaces) {
|
|
189
|
+
if (surface.status === "planned") {
|
|
190
|
+
pushCapabilityWarning(acc, "info", `Agent "${agentId}": ${surface.reason ?? surface.plannedSurface}`);
|
|
191
|
+
}
|
|
192
|
+
else if (surface.status === "unsupported") {
|
|
193
|
+
pushCapabilityWarning(acc, "config-authority", `Agent "${agentId}": ${surface.reason ?? surface.schemaLabel}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
172
197
|
function collectCapabilityWarningsForAgent(acc, agentId, capability, entryName, targetAgents, scope) {
|
|
173
198
|
const plan = planCapabilityForDeploy({
|
|
174
199
|
agentId,
|
|
@@ -181,24 +206,13 @@ function collectCapabilityWarningsForAgent(acc, agentId, capability, entryName,
|
|
|
181
206
|
pushCapabilityWarning(acc, "info", plan.warning.message);
|
|
182
207
|
return;
|
|
183
208
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (surface.status === "planned") {
|
|
189
|
-
pushCapabilityWarning(acc, "info", `Agent "${agentId}": ${surface.reason ?? surface.plannedSurface}`);
|
|
190
|
-
}
|
|
191
|
-
else if (surface.status === "unsupported") {
|
|
192
|
-
pushCapabilityWarning(acc, "config-authority", `Agent "${agentId}": ${surface.reason ?? surface.schemaLabel}`);
|
|
193
|
-
}
|
|
209
|
+
if (capability !== "skills") {
|
|
210
|
+
const confidence = describeCapabilityConfidence(agentId, capability, scope);
|
|
211
|
+
if (confidence.message) {
|
|
212
|
+
pushCapabilityWarning(acc, "config-authority", confidence.message);
|
|
194
213
|
}
|
|
195
214
|
}
|
|
196
|
-
|
|
197
|
-
return;
|
|
198
|
-
const confidence = describeCapabilityConfidence(agentId, capability, scope);
|
|
199
|
-
if (confidence.message) {
|
|
200
|
-
pushCapabilityWarning(acc, "config-authority", confidence.message);
|
|
201
|
-
}
|
|
215
|
+
collectPlannedSurfaceWarnings(acc, agentId, capability);
|
|
202
216
|
}
|
|
203
217
|
function collectCapabilityWarningsForTargets(acc, targetAgents, capability, entryName, scope) {
|
|
204
218
|
for (const agentId of targetAgents) {
|
|
@@ -252,7 +266,9 @@ async function collectAgentWarnings(agentId, manifest, home) {
|
|
|
252
266
|
});
|
|
253
267
|
}
|
|
254
268
|
else if (agent.policyNote &&
|
|
255
|
-
!agent.policyNote
|
|
269
|
+
!agent.policyNote
|
|
270
|
+
.toLowerCase()
|
|
271
|
+
.includes("organization policies may override")) {
|
|
256
272
|
warnings.push({
|
|
257
273
|
kind: "policy",
|
|
258
274
|
message: `Agent "${agentId}": ${agent.policyNote}`,
|
package/dist/src/core/revert.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { lstat, readFile, rm, unlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import { AGENT_REGISTRY_BY_ID } from "../config/agents.js";
|
|
3
3
|
import { logger } from "../logger.js";
|
|
4
|
+
import * as frontmatterAdapter from "./adapters/frontmatter.js";
|
|
4
5
|
import { compileAgentDefinitionReverts, compileAgentRuleReverts, compileMcpServerReverts, compilePermissionsReverts, } from "./adapters/index.js";
|
|
5
6
|
import { revertTomlMcpPatch } from "./adapters/toml.js";
|
|
6
7
|
import { applyUndoPatch } from "./merge-patch.js";
|
|
@@ -177,7 +178,6 @@ function planFrontmatterRevert(action, frontmatterEntry, planned) {
|
|
|
177
178
|
return { outcome: "ok" };
|
|
178
179
|
}
|
|
179
180
|
async function applyFrontmatterRevert(action, frontmatterEntry) {
|
|
180
|
-
const frontmatterAdapter = await import("./adapters/frontmatter.js");
|
|
181
181
|
const current = await frontmatterAdapter.readFrontmatterDocumentFile(action.target);
|
|
182
182
|
const restoredFrontmatter = applyUndoPatch(current.attributes, frontmatterEntry.undoPatch ?? {});
|
|
183
183
|
const shouldDeleteFile = frontmatterEntry.created === true &&
|
|
@@ -4,6 +4,7 @@ export declare function validateSourcePath(source: string, skillPath: string, re
|
|
|
4
4
|
export declare function validateSourceFile(sourcePath: string, manifestPath: string): Promise<void>;
|
|
5
5
|
export declare function validateMcpServerConfigShape(config: Record<string, unknown>, entryName: string, agentId: string): void;
|
|
6
6
|
export declare function validatePermissionsConfigShape(config: Record<string, unknown>, entryName: string, agentId: string): void;
|
|
7
|
+
export declare function validateHookConfigShape(_config: Record<string, unknown>, _entryName: string, _agentId: string): void;
|
|
7
8
|
export declare function validateAgentRuleMarkdownPath(manifestPath: string, agentId: string): void;
|
|
8
9
|
export declare function validateSkillDefinitionFile(sourcePath: string, manifestPath: string): Promise<{
|
|
9
10
|
attributes: Record<string, unknown>;
|
|
@@ -163,6 +163,10 @@ export function validatePermissionsConfigShape(config, entryName, agentId) {
|
|
|
163
163
|
validateOpenCodePermissions(config, entryName);
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
+
export function validateHookConfigShape(_config, _entryName, _agentId) {
|
|
167
|
+
// Placeholder for agent-specific hook validation logic.
|
|
168
|
+
// Currently allows any record as a hook payload.
|
|
169
|
+
}
|
|
166
170
|
export function validateAgentRuleMarkdownPath(manifestPath, agentId) {
|
|
167
171
|
const extension = path.extname(manifestPath).toLowerCase();
|
|
168
172
|
if (extension !== ".md" && extension !== ".markdown") {
|
|
@@ -218,6 +222,17 @@ function validateGithubCopilotRequirements(attributes, manifestPath) {
|
|
|
218
222
|
if (!(hasTools || hasInstructions)) {
|
|
219
223
|
throw new UserError("DEPLOY_FAILED", `Instruction file "${manifestPath}" for agent "github-copilot" must define "tools" or "instructions" in frontmatter`);
|
|
220
224
|
}
|
|
225
|
+
if (hasTools) {
|
|
226
|
+
const tools = attributes.tools;
|
|
227
|
+
if (!Array.isArray(tools)) {
|
|
228
|
+
throw new UserError("DEPLOY_FAILED", `Instruction file "${manifestPath}" for agent "github-copilot" has a "tools" field that must be an array`);
|
|
229
|
+
}
|
|
230
|
+
for (const tool of tools) {
|
|
231
|
+
if (typeof tool !== "string") {
|
|
232
|
+
throw new UserError("DEPLOY_FAILED", `Instruction file "${manifestPath}" for agent "github-copilot" has a "tools" entry that must be a string`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
221
236
|
}
|
|
222
237
|
function validateAntigravityRequirements(attributes, manifestPath) {
|
|
223
238
|
const mcpServers = attributes["mcp-servers"] ?? attributes.mcpServers;
|
package/dist/src/errors.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export type ErrorCode = "INVALID_ARGS" | "MANIFEST_INVALID" | "DEPLOY_FAILED" | "RESOLVE_FAILED";
|
|
2
2
|
export declare class UserError extends Error {
|
|
3
3
|
readonly code: ErrorCode;
|
|
4
|
-
constructor(code: ErrorCode, message: string
|
|
4
|
+
constructor(code: ErrorCode, message: string, options?: {
|
|
5
|
+
cause?: unknown;
|
|
6
|
+
});
|
|
5
7
|
}
|
package/dist/src/errors.js
CHANGED
|
@@ -63,6 +63,7 @@ export declare const McpServerEntrySchema: z.ZodObject<{
|
|
|
63
63
|
global: "global";
|
|
64
64
|
repo: "repo";
|
|
65
65
|
workspace: "workspace";
|
|
66
|
+
devcontainer: "devcontainer";
|
|
66
67
|
}>>;
|
|
67
68
|
}, z.core.$strip>;
|
|
68
69
|
export declare const AgentRuleEntrySchema: z.ZodObject<{
|
|
@@ -111,6 +112,18 @@ export declare const AgentDefinitionEntrySchema: z.ZodObject<{
|
|
|
111
112
|
workspace: "workspace";
|
|
112
113
|
}>>;
|
|
113
114
|
}, z.core.$strip>;
|
|
115
|
+
export declare const HookEntrySchema: z.ZodObject<{
|
|
116
|
+
name: z.ZodString;
|
|
117
|
+
agents: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
118
|
+
"claude-code": "claude-code";
|
|
119
|
+
codex: "codex";
|
|
120
|
+
"gemini-cli": "gemini-cli";
|
|
121
|
+
antigravity: "antigravity";
|
|
122
|
+
opencode: "opencode";
|
|
123
|
+
"github-copilot": "github-copilot";
|
|
124
|
+
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
125
|
+
config: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
126
|
+
}, z.core.$strip>;
|
|
114
127
|
export declare const ManifestSchema: z.ZodObject<{
|
|
115
128
|
skills: z.ZodArray<z.ZodObject<{
|
|
116
129
|
name: z.ZodString;
|
|
@@ -165,6 +178,7 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
165
178
|
global: "global";
|
|
166
179
|
repo: "repo";
|
|
167
180
|
workspace: "workspace";
|
|
181
|
+
devcontainer: "devcontainer";
|
|
168
182
|
}>>;
|
|
169
183
|
}, z.core.$strip>>>;
|
|
170
184
|
agentRules: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
@@ -213,6 +227,18 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
213
227
|
workspace: "workspace";
|
|
214
228
|
}>>;
|
|
215
229
|
}, z.core.$strip>>>;
|
|
230
|
+
hooks: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
231
|
+
name: z.ZodString;
|
|
232
|
+
agents: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
233
|
+
"claude-code": "claude-code";
|
|
234
|
+
codex: "codex";
|
|
235
|
+
"gemini-cli": "gemini-cli";
|
|
236
|
+
antigravity: "antigravity";
|
|
237
|
+
opencode: "opencode";
|
|
238
|
+
"github-copilot": "github-copilot";
|
|
239
|
+
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
240
|
+
config: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
241
|
+
}, z.core.$strip>>>;
|
|
216
242
|
}, z.core.$strip>;
|
|
217
243
|
export type SkillEntry = z.infer<typeof SkillEntrySchema>;
|
|
218
244
|
export type FileEntry = z.infer<typeof FileEntrySchema>;
|
|
@@ -221,6 +247,7 @@ export type McpServerEntry = z.infer<typeof McpServerEntrySchema>;
|
|
|
221
247
|
export type AgentRuleEntry = z.infer<typeof AgentRuleEntrySchema>;
|
|
222
248
|
export type PermissionsEntry = z.infer<typeof PermissionsEntrySchema>;
|
|
223
249
|
export type AgentDefinitionEntry = z.infer<typeof AgentDefinitionEntrySchema>;
|
|
250
|
+
export type HookEntry = z.infer<typeof HookEntrySchema>;
|
|
224
251
|
export type Manifest = z.infer<typeof ManifestSchema>;
|
|
225
252
|
export declare const AgentListSchema: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string[], string>>, z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
226
253
|
"claude-code": "claude-code";
|
|
@@ -88,7 +88,9 @@ export const McpServerEntrySchema = z.object({
|
|
|
88
88
|
// config files for agents that support them (e.g. GitHub Copilot's
|
|
89
89
|
// .vscode/mcp.json). For agents without scope-specific surfaces the
|
|
90
90
|
// adapter falls back to the global surface or emits a warning.
|
|
91
|
-
scope: z
|
|
91
|
+
scope: z
|
|
92
|
+
.enum(["global", "repo", "workspace", "devcontainer"])
|
|
93
|
+
.default("global"),
|
|
92
94
|
});
|
|
93
95
|
export const AgentRuleEntrySchema = z.object({
|
|
94
96
|
name: nameField,
|
|
@@ -124,6 +126,13 @@ export const AgentDefinitionEntrySchema = z.object({
|
|
|
124
126
|
// within the deployed repository (default).
|
|
125
127
|
scope: z.enum(["global", "repo", "workspace"]).default("repo"),
|
|
126
128
|
});
|
|
129
|
+
export const HookEntrySchema = z.object({
|
|
130
|
+
name: nameField,
|
|
131
|
+
agents: agentsField,
|
|
132
|
+
// Raw hook config payload validated per agent by the hooks adapter.
|
|
133
|
+
// Supports lifecycle-binding hooks (e.g. pre-exec, post-exec) for various agents.
|
|
134
|
+
config: z.record(z.string(), z.unknown()),
|
|
135
|
+
});
|
|
127
136
|
export const ManifestSchema = z.object({
|
|
128
137
|
skills: z.array(SkillEntrySchema).superRefine((skills, ctx) => {
|
|
129
138
|
const seen = new Set();
|
|
@@ -144,6 +153,7 @@ export const ManifestSchema = z.object({
|
|
|
144
153
|
agentRules: z.array(AgentRuleEntrySchema).default([]),
|
|
145
154
|
permissions: z.array(PermissionsEntrySchema).default([]),
|
|
146
155
|
agentDefinitions: z.array(AgentDefinitionEntrySchema).default([]),
|
|
156
|
+
hooks: z.array(HookEntrySchema).optional(),
|
|
147
157
|
});
|
|
148
158
|
// Parses the --agents CLI flag: comma-separated agent IDs → AgentId[]
|
|
149
159
|
export const AgentListSchema = z
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { AgentId } from "./schemas/manifest.ts";
|
|
2
|
-
export type { AgentDefinitionEntry, AgentId, ConfigEntry, FileEntry, Manifest, SkillEntry, } from "./schemas/manifest.ts";
|
|
2
|
+
export type { AgentDefinitionEntry, AgentId, ConfigEntry, FileEntry, HookEntry, Manifest, SkillEntry, } from "./schemas/manifest.ts";
|
|
3
3
|
export interface AgentPaths {
|
|
4
4
|
posix: string[];
|
|
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
|
+
export type CapabilityKind = "skills" | "mcpServers" | "agentRules" | "permissions" | "hooks" | "agentDefinitions";
|
|
9
9
|
export interface SupportedAgentSurface {
|
|
10
10
|
status: "supported";
|
|
11
11
|
/**
|
|
@@ -67,6 +67,7 @@ export interface AgentProvenance {
|
|
|
67
67
|
mcpConfig?: Confidence;
|
|
68
68
|
agentRules?: Confidence;
|
|
69
69
|
permissions?: Confidence;
|
|
70
|
+
hooks?: Confidence;
|
|
70
71
|
agentDefinitions?: Confidence;
|
|
71
72
|
}
|
|
72
73
|
export interface AgentConfig {
|
|
@@ -97,11 +98,16 @@ export interface AgentConfig {
|
|
|
97
98
|
agentRulesRepoSupport?: AgentSurfaceSupport;
|
|
98
99
|
agentRulesWorkspaceSupport?: AgentSurfaceSupport;
|
|
99
100
|
permissionsSupport?: AgentSurfaceSupport;
|
|
101
|
+
hooksSupport?: AgentSurfaceSupport;
|
|
102
|
+
hooksRepoSupport?: AgentSurfaceSupport;
|
|
103
|
+
hooksWorkspaceSupport?: AgentSurfaceSupport;
|
|
100
104
|
agentDefinitionsSupport?: AgentSurfaceSupport;
|
|
101
105
|
agentDefinitionsRepoSupport?: AgentSurfaceSupport;
|
|
102
106
|
agentDefinitionsWorkspaceSupport?: AgentSurfaceSupport;
|
|
103
107
|
agentDefinitionsTomlSupport?: AgentSurfaceSupport;
|
|
104
108
|
agentDefinitionsTomlRepoSupport?: AgentSurfaceSupport;
|
|
109
|
+
mcpDevcontainerSupport?: AgentSurfaceSupport;
|
|
110
|
+
mcpAgentFrontmatterSupport?: AgentSurfaceSupport;
|
|
105
111
|
policyNote?: string;
|
|
106
112
|
/**
|
|
107
113
|
* A list of documented surfaces that are currently not safe to support directly.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|