@kuznai/inception-engine 0.18.0 → 0.19.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 +27 -24
- package/dist/config/agents.js +87 -10
- package/dist/core/adapters/agent-definitions.d.ts +2 -2
- package/dist/core/adapters/agent-definitions.js +6 -5
- package/dist/core/adapters/index.d.ts +1 -1
- package/dist/core/adapters/index.js +4 -4
- package/dist/core/adapters/mcp.d.ts +2 -2
- package/dist/core/adapters/mcp.js +4 -4
- package/dist/core/adapters/rules.d.ts +2 -2
- package/dist/core/adapters/rules.js +61 -14
- package/dist/core/deploy.d.ts +1 -1
- package/dist/core/deploy.js +206 -54
- package/dist/core/init.js +127 -47
- package/dist/core/ownership.d.ts +2 -2
- package/dist/core/preflight.d.ts +1 -1
- package/dist/core/preflight.js +95 -25
- package/dist/core/resolve.d.ts +1 -1
- package/dist/core/resolve.js +7 -4
- package/dist/core/revert.js +9 -7
- package/dist/core/runtime-paths.d.ts +2 -1
- package/dist/core/runtime-paths.js +30 -23
- package/dist/core/validation.d.ts +10 -1
- package/dist/core/validation.js +55 -0
- package/dist/formatters.d.ts +5 -0
- package/dist/formatters.js +58 -0
- package/dist/index.js +8 -24
- package/dist/logger.js +22 -21
- package/dist/schemas/manifest.d.ts +2 -0
- package/dist/schemas/manifest.js +8 -5
- package/dist/schemas/registry.d.ts +38 -0
- package/dist/schemas/registry.js +7 -0
- package/dist/types.d.ts +47 -0
- package/package.json +1 -1
package/dist/core/deploy.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { constants } from "node:fs";
|
|
2
2
|
import { access, copyFile, cp, lstat, mkdir, readFile, realpath, rename, rm, symlink, unlink, writeFile, } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { AGENT_REGISTRY_BY_ID } from "../config/agents.js";
|
|
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
7
|
import { compileAdapterActions } from "./adapters/index.js";
|
|
@@ -80,26 +80,118 @@ function detectCollisions(actions) {
|
|
|
80
80
|
}
|
|
81
81
|
return warnings;
|
|
82
82
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Finds all (primary, rider) pairs among detected agents where the rider has
|
|
85
|
+
* at least one surface annotated as `shared-via` the primary and the primary
|
|
86
|
+
* is also detected. Used to drive ambiguity warnings without hardcoding agent
|
|
87
|
+
* IDs.
|
|
88
|
+
*/
|
|
89
|
+
function findSharedSurfacePairs(detectedAgents) {
|
|
90
|
+
const pairs = [];
|
|
91
|
+
for (const agent of AGENT_REGISTRY) {
|
|
92
|
+
if (!detectedAgents.includes(agent.id))
|
|
93
|
+
continue;
|
|
94
|
+
const surfaces = [
|
|
95
|
+
agent.agentRulesSupport,
|
|
96
|
+
agent.agentRulesRepoSupport,
|
|
97
|
+
agent.agentRulesWorkspaceSupport,
|
|
98
|
+
agent.mcpSupport,
|
|
99
|
+
agent.agentDefinitionsSupport,
|
|
100
|
+
];
|
|
101
|
+
for (const surface of surfaces) {
|
|
102
|
+
if (surface?.status === "supported" &&
|
|
103
|
+
surface.surfaceKind?.kind === "shared-via") {
|
|
104
|
+
const via = surface.surfaceKind.via;
|
|
105
|
+
if (detectedAgents.includes(via) &&
|
|
106
|
+
!pairs.some((p) => p.primary === via && p.rider === agent.id)) {
|
|
107
|
+
pairs.push({ primary: via, rider: agent.id });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
88
111
|
}
|
|
89
|
-
|
|
112
|
+
return pairs;
|
|
113
|
+
}
|
|
114
|
+
function resolveAgentRulesSupportForScope(agentId, scope) {
|
|
115
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
116
|
+
if (scope === "repo")
|
|
117
|
+
return agent?.agentRulesRepoSupport ?? agent?.agentRulesSupport;
|
|
118
|
+
if (scope === "workspace") {
|
|
119
|
+
return (agent?.agentRulesWorkspaceSupport ??
|
|
120
|
+
agent?.agentRulesRepoSupport ??
|
|
121
|
+
agent?.agentRulesSupport);
|
|
122
|
+
}
|
|
123
|
+
return agent?.agentRulesSupport;
|
|
124
|
+
}
|
|
125
|
+
function checkPairAgentRuleAmbiguities(manifest, primary, rider) {
|
|
126
|
+
const warnings = [];
|
|
90
127
|
for (const entry of manifest.agentRules ?? []) {
|
|
91
|
-
if (
|
|
128
|
+
if (!(entry.agents.includes(primary) && entry.agents.includes(rider)))
|
|
129
|
+
continue;
|
|
130
|
+
const primarySupport = resolveAgentRulesSupportForScope(primary, entry.scope);
|
|
131
|
+
const riderSupport = resolveAgentRulesSupportForScope(rider, entry.scope);
|
|
132
|
+
if (primarySupport?.status === "supported" &&
|
|
133
|
+
riderSupport?.status === "supported" &&
|
|
134
|
+
JSON.stringify(primarySupport.path) === JSON.stringify(riderSupport.path)) {
|
|
92
135
|
warnings.push({
|
|
93
136
|
kind: "ambiguity",
|
|
94
|
-
message: `Both "
|
|
137
|
+
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.`,
|
|
95
138
|
});
|
|
96
139
|
}
|
|
97
140
|
}
|
|
141
|
+
return warnings;
|
|
142
|
+
}
|
|
143
|
+
function checkPairMcpAmbiguities(manifest, primary, rider) {
|
|
144
|
+
const warnings = [];
|
|
145
|
+
const primaryMcp = AGENT_REGISTRY_BY_ID[primary]?.mcpSupport;
|
|
146
|
+
const riderMcp = AGENT_REGISTRY_BY_ID[rider]?.mcpSupport;
|
|
147
|
+
if (primaryMcp?.status !== "supported" || riderMcp?.status !== "supported")
|
|
148
|
+
return warnings;
|
|
98
149
|
for (const entry of manifest.mcpServers ?? []) {
|
|
99
|
-
if (
|
|
150
|
+
if (!(entry.agents.includes(primary) && entry.agents.includes(rider)))
|
|
151
|
+
continue;
|
|
152
|
+
warnings.push({
|
|
153
|
+
kind: "ambiguity",
|
|
154
|
+
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.`,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return warnings;
|
|
158
|
+
}
|
|
159
|
+
function checkPairAgentDefinitionAmbiguities(manifest, primary, rider) {
|
|
160
|
+
const warnings = [];
|
|
161
|
+
for (const entry of manifest.agentDefinitions ?? []) {
|
|
162
|
+
if (!(entry.agents.includes(primary) && entry.agents.includes(rider)))
|
|
163
|
+
continue;
|
|
164
|
+
warnings.push({
|
|
165
|
+
kind: "ambiguity",
|
|
166
|
+
message: `Both "${primary}" and "${rider}" are listed in agentDefinitions entry "${entry.name}". They write to distinct surfaces — verify that this behavioral divergence is intended.`,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return warnings;
|
|
170
|
+
}
|
|
171
|
+
function detectAmbiguities(detectedAgents, manifest) {
|
|
172
|
+
const pairs = findSharedSurfacePairs(detectedAgents);
|
|
173
|
+
if (pairs.length === 0)
|
|
174
|
+
return [];
|
|
175
|
+
const warnings = [];
|
|
176
|
+
for (const { primary, rider } of pairs) {
|
|
177
|
+
warnings.push(...checkPairAgentRuleAmbiguities(manifest, primary, rider));
|
|
178
|
+
warnings.push(...checkPairMcpAmbiguities(manifest, primary, rider));
|
|
179
|
+
warnings.push(...checkPairAgentDefinitionAmbiguities(manifest, primary, rider));
|
|
180
|
+
}
|
|
181
|
+
return warnings;
|
|
182
|
+
}
|
|
183
|
+
function checkAntigravityPathCollisions(manifest) {
|
|
184
|
+
const warnings = [];
|
|
185
|
+
const defNames = new Set((manifest.agentDefinitions ?? [])
|
|
186
|
+
.filter((e) => e.agents.includes("antigravity"))
|
|
187
|
+
.map((e) => e.name));
|
|
188
|
+
for (const entry of manifest.mcpServers ?? []) {
|
|
189
|
+
if (!entry.agents.includes("antigravity"))
|
|
190
|
+
continue;
|
|
191
|
+
if (defNames.has(entry.name)) {
|
|
100
192
|
warnings.push({
|
|
101
|
-
kind: "
|
|
102
|
-
message: `
|
|
193
|
+
kind: "collision",
|
|
194
|
+
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`,
|
|
103
195
|
});
|
|
104
196
|
}
|
|
105
197
|
}
|
|
@@ -134,7 +226,7 @@ async function planSkillDirActions(manifest, sourceDir, resolvedSourceDir, realR
|
|
|
134
226
|
}
|
|
135
227
|
return actions;
|
|
136
228
|
}
|
|
137
|
-
async function planFileWriteActions(manifest, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home) {
|
|
229
|
+
async function planFileWriteActions(manifest, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace) {
|
|
138
230
|
const actions = [];
|
|
139
231
|
for (const fileEntry of manifest.files ?? []) {
|
|
140
232
|
const targetAgents = fileEntry.agents.filter((agentId) => detectedAgents.includes(agentId));
|
|
@@ -152,14 +244,14 @@ async function planFileWriteActions(manifest, sourceDir, resolvedSourceDir, real
|
|
|
152
244
|
skill: fileEntry.name,
|
|
153
245
|
agent: agentId,
|
|
154
246
|
source,
|
|
155
|
-
target: resolveTargetTemplate(fileEntry.target, home),
|
|
247
|
+
target: resolveTargetTemplate(fileEntry.target, home, repo, workspace),
|
|
156
248
|
confidence: agent.provenance.skills,
|
|
157
249
|
});
|
|
158
250
|
}
|
|
159
251
|
}
|
|
160
252
|
return actions;
|
|
161
253
|
}
|
|
162
|
-
function planConfigPatchActions(manifest, detectedAgents, home) {
|
|
254
|
+
function planConfigPatchActions(manifest, detectedAgents, home, repo, workspace) {
|
|
163
255
|
const actions = [];
|
|
164
256
|
for (const configEntry of manifest.configs ?? []) {
|
|
165
257
|
for (const agentId of configEntry.agents) {
|
|
@@ -172,7 +264,7 @@ function planConfigPatchActions(manifest, detectedAgents, home) {
|
|
|
172
264
|
kind: "config-patch",
|
|
173
265
|
skill: configEntry.name,
|
|
174
266
|
agent: agentId,
|
|
175
|
-
target: resolveTargetTemplate(configEntry.target, home),
|
|
267
|
+
target: resolveTargetTemplate(configEntry.target, home, repo, workspace),
|
|
176
268
|
patch: configEntry.patch,
|
|
177
269
|
confidence: agent.provenance.skills,
|
|
178
270
|
});
|
|
@@ -180,7 +272,7 @@ function planConfigPatchActions(manifest, detectedAgents, home) {
|
|
|
180
272
|
}
|
|
181
273
|
return actions;
|
|
182
274
|
}
|
|
183
|
-
export async function planDeploy(manifest, sourceDir, detectedAgents, home) {
|
|
275
|
+
export async function planDeploy(manifest, sourceDir, detectedAgents, home, repo, workspace) {
|
|
184
276
|
const resolvedSourceDir = path.resolve(sourceDir);
|
|
185
277
|
let realRoot;
|
|
186
278
|
try {
|
|
@@ -189,15 +281,17 @@ export async function planDeploy(manifest, sourceDir, detectedAgents, home) {
|
|
|
189
281
|
catch {
|
|
190
282
|
realRoot = resolvedSourceDir;
|
|
191
283
|
}
|
|
284
|
+
const repoDir = repo ?? realRoot;
|
|
192
285
|
const actions = [
|
|
193
286
|
...(await planSkillDirActions(manifest, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home)),
|
|
194
|
-
...(await planFileWriteActions(manifest, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home)),
|
|
195
|
-
...planConfigPatchActions(manifest, detectedAgents, home),
|
|
287
|
+
...(await planFileWriteActions(manifest, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repoDir, workspace)),
|
|
288
|
+
...planConfigPatchActions(manifest, detectedAgents, home, repoDir, workspace),
|
|
196
289
|
];
|
|
197
|
-
const adapterResult = await compileAdapterActions(manifest.mcpServers, manifest.agentRules, manifest.permissions ?? [], sourceDir, resolvedSourceDir, realRoot, detectedAgents, home,
|
|
290
|
+
const adapterResult = await compileAdapterActions(manifest.mcpServers, manifest.agentRules, manifest.permissions ?? [], sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repoDir, manifest.agentDefinitions ?? [], workspace);
|
|
198
291
|
actions.push(...adapterResult.actions);
|
|
199
292
|
const warnings = [
|
|
200
293
|
...detectAmbiguities(detectedAgents, manifest),
|
|
294
|
+
...checkAntigravityPathCollisions(manifest),
|
|
201
295
|
...detectCollisions(actions),
|
|
202
296
|
...adapterResult.warnings,
|
|
203
297
|
];
|
|
@@ -208,44 +302,32 @@ export async function executeDeploy(actions, dryRun, verbose, home, deps = {}) {
|
|
|
208
302
|
const failed = [];
|
|
209
303
|
const planned = [];
|
|
210
304
|
for (const action of actions) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
else {
|
|
218
|
-
failed.push({ action, error: result.error });
|
|
219
|
-
}
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
case "file-write": {
|
|
223
|
-
const result = await deployFileWrite(action, dryRun, verbose, home, planned, deps);
|
|
224
|
-
if (result.error === null) {
|
|
225
|
-
succeeded++;
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
failed.push({ action, error: result.error });
|
|
229
|
-
}
|
|
230
|
-
break;
|
|
231
|
-
}
|
|
232
|
-
case "config-patch": {
|
|
233
|
-
const result = await deployConfigPatch(action, dryRun, verbose, home, planned, deps);
|
|
234
|
-
if (result.error === null) {
|
|
235
|
-
succeeded++;
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
failed.push({ action, error: result.error });
|
|
239
|
-
}
|
|
240
|
-
break;
|
|
241
|
-
}
|
|
242
|
-
default: {
|
|
243
|
-
throw new Error(`Unhandled deploy action kind: ${action}`);
|
|
244
|
-
}
|
|
305
|
+
const result = await dispatchDeployAction(action, dryRun, verbose, home, planned, deps);
|
|
306
|
+
if (result.error === null) {
|
|
307
|
+
succeeded++;
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
failed.push({ action, error: result.error });
|
|
245
311
|
}
|
|
246
312
|
}
|
|
247
313
|
return { succeeded, failed, planned };
|
|
248
314
|
}
|
|
315
|
+
async function dispatchDeployAction(action, dryRun, verbose, home, planned, deps) {
|
|
316
|
+
switch (action.kind) {
|
|
317
|
+
case "skill-dir":
|
|
318
|
+
return deploySkillDir(action, dryRun, verbose, home, planned, deps);
|
|
319
|
+
case "file-write":
|
|
320
|
+
return deployFileWrite(action, dryRun, verbose, home, planned, deps);
|
|
321
|
+
case "config-patch":
|
|
322
|
+
return deployConfigPatch(action, dryRun, verbose, home, planned, deps);
|
|
323
|
+
case "toml-patch":
|
|
324
|
+
return deployTomlPatch(action, dryRun, verbose, home, planned, deps);
|
|
325
|
+
case "frontmatter-emit":
|
|
326
|
+
return deployFrontmatterEmit(action, dryRun, verbose, home, planned, deps);
|
|
327
|
+
default:
|
|
328
|
+
throw new Error(`Unhandled deploy action kind: ${action.kind}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
249
331
|
const defaultSkillDirOps = {
|
|
250
332
|
async createTarget(action) {
|
|
251
333
|
if (action.method === "symlink") {
|
|
@@ -483,6 +565,76 @@ async function deployConfigPatch(action, dryRun, verbose, home, planned, deps) {
|
|
|
483
565
|
return { error: msg };
|
|
484
566
|
}
|
|
485
567
|
}
|
|
568
|
+
async function deployTomlPatch(action, dryRun, verbose, home, planned, deps) {
|
|
569
|
+
const label = `${action.skill} -> ${action.agent}`;
|
|
570
|
+
if (dryRun) {
|
|
571
|
+
planned.push({
|
|
572
|
+
verb: "patch-toml",
|
|
573
|
+
kind: "toml-patch",
|
|
574
|
+
skill: action.skill,
|
|
575
|
+
agent: action.agent,
|
|
576
|
+
target: action.target,
|
|
577
|
+
patch: action.config,
|
|
578
|
+
});
|
|
579
|
+
return { error: null };
|
|
580
|
+
}
|
|
581
|
+
try {
|
|
582
|
+
const { previousValue } = await (deps.registry
|
|
583
|
+
? Promise.resolve({ previousValue: null })
|
|
584
|
+
: (await import("./adapters/toml.js")).applyTomlMcpPatch(action.target, action.skill, action.config));
|
|
585
|
+
// Note: To truly support custom deps here we'd need to refactor toml adapter to accept deps.
|
|
586
|
+
// For now we assume standard adapter for TOML.
|
|
587
|
+
await registerDeployment(home, action.target, {
|
|
588
|
+
kind: "config-patch",
|
|
589
|
+
patch: action.config,
|
|
590
|
+
undoPatch: { mcpServers: { [action.skill]: previousValue } },
|
|
591
|
+
skill: action.skill,
|
|
592
|
+
agent: action.agent,
|
|
593
|
+
}, deps.registry);
|
|
594
|
+
logger.ok(label);
|
|
595
|
+
if (verbose) {
|
|
596
|
+
logger.detail(`patch-toml: ${action.target}`);
|
|
597
|
+
}
|
|
598
|
+
return { error: null };
|
|
599
|
+
}
|
|
600
|
+
catch (err) {
|
|
601
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
602
|
+
logger.fail(label, msg);
|
|
603
|
+
return { error: msg };
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
async function deployFrontmatterEmit(action, dryRun, verbose, home, planned, deps) {
|
|
607
|
+
const label = `${action.skill} -> ${action.agent}`;
|
|
608
|
+
if (dryRun) {
|
|
609
|
+
planned.push({
|
|
610
|
+
verb: "emit-frontmatter",
|
|
611
|
+
kind: "frontmatter-emit",
|
|
612
|
+
skill: action.skill,
|
|
613
|
+
agent: action.agent,
|
|
614
|
+
target: action.target,
|
|
615
|
+
frontmatter: action.frontmatter,
|
|
616
|
+
});
|
|
617
|
+
return { error: null };
|
|
618
|
+
}
|
|
619
|
+
try {
|
|
620
|
+
await (await import("./adapters/frontmatter.js")).writeFrontmatterFile(action.target, action.frontmatter, { preserveBody: true });
|
|
621
|
+
await registerDeployment(home, action.target, {
|
|
622
|
+
kind: "frontmatter-emit",
|
|
623
|
+
skill: action.skill,
|
|
624
|
+
agent: action.agent,
|
|
625
|
+
}, deps.registry);
|
|
626
|
+
logger.ok(label);
|
|
627
|
+
if (verbose) {
|
|
628
|
+
logger.detail(`emit-frontmatter: ${action.target}`);
|
|
629
|
+
}
|
|
630
|
+
return { error: null };
|
|
631
|
+
}
|
|
632
|
+
catch (err) {
|
|
633
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
634
|
+
logger.fail(label, msg);
|
|
635
|
+
return { error: msg };
|
|
636
|
+
}
|
|
637
|
+
}
|
|
486
638
|
async function validateSkillContract(source, skillPath) {
|
|
487
639
|
let stat;
|
|
488
640
|
try {
|
package/dist/core/init.js
CHANGED
|
@@ -1,42 +1,81 @@
|
|
|
1
1
|
import { access, readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { AGENT_REGISTRY_BY_ID } from "../config/agents.js";
|
|
3
|
+
import { AGENT_REGISTRY, AGENT_REGISTRY_BY_ID } from "../config/agents.js";
|
|
4
4
|
import { dryRunPrefix, logger } from "../logger.js";
|
|
5
5
|
import { AGENT_IDS, AgentDefinitionEntrySchema, ConfigEntrySchema, FileEntrySchema, McpServerEntrySchema, } from "../schemas/manifest.js";
|
|
6
|
+
import { parseFrontmatterDocument } from "./adapters/frontmatter.js";
|
|
6
7
|
const SAFE_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
7
8
|
// Ordered list: first match wins. Catch-all is applied at call site.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
// Derived from the registry: group agents by the filename of their global
|
|
10
|
+
// agentRulesSupport path. Agents with requiresPrimary (e.g. github-copilot)
|
|
11
|
+
// are excluded because they cannot be deployed independently. The
|
|
12
|
+
// copilot-instructions.md convention mapping is appended explicitly since it
|
|
13
|
+
// is a well-known filename convention, not derivable from a path template.
|
|
14
|
+
const AGENT_RULES_FILE_PATTERNS = (() => {
|
|
15
|
+
const filenameToAgents = new Map();
|
|
16
|
+
for (const agent of AGENT_REGISTRY) {
|
|
17
|
+
const support = agent.agentRulesSupport;
|
|
18
|
+
if (support?.status !== "supported")
|
|
19
|
+
continue;
|
|
20
|
+
// Skip riders that require the primary — they cannot be listed independently
|
|
21
|
+
if (support.surfaceKind?.kind === "shared-via" &&
|
|
22
|
+
support.surfaceKind.requiresPrimary)
|
|
23
|
+
continue;
|
|
24
|
+
const filename = support.path.posix[support.path.posix.length - 1]?.toLowerCase();
|
|
25
|
+
if (!filename)
|
|
26
|
+
continue;
|
|
27
|
+
const list = filenameToAgents.get(filename) ?? [];
|
|
28
|
+
list.push(agent.id);
|
|
29
|
+
filenameToAgents.set(filename, list);
|
|
30
|
+
}
|
|
31
|
+
return [
|
|
32
|
+
...Array.from(filenameToAgents.entries()).map(([filename, agents]) => ({
|
|
33
|
+
fileNames: [filename, filename.replace(".md", "-instructions.md")],
|
|
34
|
+
agents,
|
|
35
|
+
})),
|
|
36
|
+
// Convention mapping: copilot-instructions.md → claude-code. Copilot reads
|
|
37
|
+
// CLAUDE.md natively; this filename is a well-known convention that cannot
|
|
38
|
+
// be derived from any agent path template.
|
|
39
|
+
{
|
|
40
|
+
fileNames: ["copilot-instructions.md"],
|
|
41
|
+
agents: ["claude-code"],
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
})();
|
|
23
45
|
// Conventional subdirectory names to scan one level deep for .md files.
|
|
24
|
-
const AGENT_RULES_SUBDIRS = [
|
|
25
|
-
"rules",
|
|
26
|
-
"instructions",
|
|
27
|
-
".github",
|
|
28
|
-
".agents/rules",
|
|
29
|
-
];
|
|
46
|
+
const AGENT_RULES_SUBDIRS = ["rules", "instructions", ".github"];
|
|
30
47
|
// Conventional subdirectories that contain agent definition files.
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
const AGENT_DEFINITION_SUBDIRS =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
// Derived from the registry: for each agent with a supported agentDefinitions
|
|
49
|
+
// surface, extract the directory prefix from its posix path template.
|
|
50
|
+
const AGENT_DEFINITION_SUBDIRS = (() => {
|
|
51
|
+
const subdirs = new Set();
|
|
52
|
+
for (const agent of AGENT_REGISTRY) {
|
|
53
|
+
const support = agent.agentDefinitionsSupport;
|
|
54
|
+
if (support?.status !== "supported")
|
|
55
|
+
continue;
|
|
56
|
+
const tmpl = support.path.posix;
|
|
57
|
+
const repoIdx = tmpl.indexOf("{repo}");
|
|
58
|
+
const nameIdx = tmpl.findIndex((s) => s.includes("{name}"));
|
|
59
|
+
if (repoIdx === -1 || nameIdx === -1 || nameIdx <= repoIdx)
|
|
60
|
+
continue;
|
|
61
|
+
const dirSegs = tmpl.slice(repoIdx + 1, nameIdx);
|
|
62
|
+
if (dirSegs.length > 0)
|
|
63
|
+
subdirs.add(dirSegs.join("/"));
|
|
64
|
+
}
|
|
65
|
+
return Array.from(subdirs);
|
|
66
|
+
})();
|
|
67
|
+
async function hasAntigravityMcpFrontmatter(absPath) {
|
|
68
|
+
let raw;
|
|
69
|
+
try {
|
|
70
|
+
raw = await readFile(absPath, "utf-8");
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const { attributes } = parseFrontmatterDocument(raw);
|
|
76
|
+
return (Object.hasOwn(attributes, "mcp-servers") ||
|
|
77
|
+
Object.hasOwn(attributes, "mcpServers"));
|
|
78
|
+
}
|
|
40
79
|
async function findSkillDirs(baseDir, dir, found) {
|
|
41
80
|
let entries;
|
|
42
81
|
try {
|
|
@@ -196,24 +235,53 @@ function deriveAgentDefinitionName(relPath, fileName) {
|
|
|
196
235
|
}
|
|
197
236
|
/**
|
|
198
237
|
* Maps a known agent-definition subdirectory to the agent IDs that own it.
|
|
199
|
-
*
|
|
200
|
-
*
|
|
238
|
+
* Derived from the registry by matching each agent's agentDefinitionsSupport
|
|
239
|
+
* path prefix. Returns null when the subdir is not recognized.
|
|
201
240
|
*/
|
|
202
241
|
function agentsForDefinitionSubdir(subdir) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
242
|
+
const matching = [];
|
|
243
|
+
for (const agent of AGENT_REGISTRY) {
|
|
244
|
+
const support = agent.agentDefinitionsSupport;
|
|
245
|
+
if (support?.status !== "supported")
|
|
246
|
+
continue;
|
|
247
|
+
const tmpl = support.path.posix;
|
|
248
|
+
const repoIdx = tmpl.indexOf("{repo}");
|
|
249
|
+
const nameIdx = tmpl.findIndex((s) => s.includes("{name}"));
|
|
250
|
+
if (repoIdx === -1 || nameIdx === -1 || nameIdx <= repoIdx)
|
|
251
|
+
continue;
|
|
252
|
+
const agentSubdir = tmpl.slice(repoIdx + 1, nameIdx).join("/");
|
|
253
|
+
if (agentSubdir === subdir)
|
|
254
|
+
matching.push(agent.id);
|
|
216
255
|
}
|
|
256
|
+
return matching.length > 0 ? matching : null;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Returns true when a file in a definition subdir contains MCP-specific
|
|
260
|
+
* frontmatter and should be excluded from agentDefinitions discovery. Detects
|
|
261
|
+
* this by checking whether any registered agent uses the same directory as
|
|
262
|
+
* both its definition surface and its MCP surface (currently: Antigravity's
|
|
263
|
+
* .agents/rules/).
|
|
264
|
+
*/
|
|
265
|
+
async function isSkippedMcpFile(subdir, absPath, relPath) {
|
|
266
|
+
const hasMcpSurfaceHere = AGENT_REGISTRY.some((agent) => {
|
|
267
|
+
const support = agent.mcpSupport;
|
|
268
|
+
if (support?.status !== "supported")
|
|
269
|
+
return false;
|
|
270
|
+
const tmpl = support.path.posix;
|
|
271
|
+
const repoIdx = tmpl.indexOf("{repo}");
|
|
272
|
+
const nameIdx = tmpl.findIndex((s) => s.includes("{name}"));
|
|
273
|
+
if (repoIdx === -1 || nameIdx === -1 || nameIdx <= repoIdx)
|
|
274
|
+
return false;
|
|
275
|
+
const prefix = tmpl.slice(repoIdx + 1, nameIdx).join("/");
|
|
276
|
+
return prefix === subdir;
|
|
277
|
+
});
|
|
278
|
+
if (!hasMcpSurfaceHere)
|
|
279
|
+
return false;
|
|
280
|
+
const isMcp = await hasAntigravityMcpFrontmatter(absPath);
|
|
281
|
+
if (isMcp) {
|
|
282
|
+
logger.warn("init", `Skipping "${relPath}" as agentDefinitions: frontmatter contains "mcp-servers" — file is an MCP surface, not an agent definition`);
|
|
283
|
+
}
|
|
284
|
+
return isMcp;
|
|
217
285
|
}
|
|
218
286
|
async function scanDefinitionSubdir(subdir, baseDir, seen, skillDirRelPaths, agentRulesRelPaths, candidates) {
|
|
219
287
|
const suggestedAgents = agentsForDefinitionSubdir(subdir) ?? [];
|
|
@@ -237,6 +305,8 @@ async function scanDefinitionSubdir(subdir, baseDir, seen, skillDirRelPaths, age
|
|
|
237
305
|
isInsideSkillDir(relPath, skillDirRelPaths) ||
|
|
238
306
|
agentRulesRelPaths.has(relPath))
|
|
239
307
|
continue;
|
|
308
|
+
if (await isSkippedMcpFile(subdir, absPath, relPath))
|
|
309
|
+
continue;
|
|
240
310
|
const name = deriveAgentDefinitionName(relPath, entry.name);
|
|
241
311
|
if (name === null)
|
|
242
312
|
continue;
|
|
@@ -478,7 +548,17 @@ function logVerboseManifest(skills, agentRules, mcpServers, files, configs, agen
|
|
|
478
548
|
export async function runInit(options) {
|
|
479
549
|
const { directory, dryRun, force, verbose } = options;
|
|
480
550
|
const agents = options.agents ?? [...AGENT_IDS];
|
|
481
|
-
const agentRulesCapableAgents = agents.filter((id) =>
|
|
551
|
+
const agentRulesCapableAgents = agents.filter((id) => {
|
|
552
|
+
const support = AGENT_REGISTRY_BY_ID[id].agentRulesSupport;
|
|
553
|
+
if (!support || support.status === "unsupported")
|
|
554
|
+
return false;
|
|
555
|
+
// Exclude riders that require the primary — they cannot operate independently
|
|
556
|
+
if (support.status === "supported" &&
|
|
557
|
+
support.surfaceKind?.kind === "shared-via" &&
|
|
558
|
+
support.surfaceKind.requiresPrimary)
|
|
559
|
+
return false;
|
|
560
|
+
return true;
|
|
561
|
+
});
|
|
482
562
|
const manifestPath = path.join(directory, "inception.json");
|
|
483
563
|
if (!dryRun && (await manifestExists(manifestPath)) && !force) {
|
|
484
564
|
logger.error(`Error: ${manifestPath} already exists. Use --force to overwrite.`);
|
package/dist/core/ownership.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ConfigPatchRegistryEntry, type FileWriteRegistryEntry, type Registry, type RegistryEntry, type SkillDirRegistryEntry } from "../schemas/registry.ts";
|
|
1
|
+
import { type ConfigPatchRegistryEntry, type FileWriteRegistryEntry, type FrontmatterEmitRegistryEntry, type Registry, 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
4
|
export interface RegistryPersistence {
|
|
@@ -22,7 +22,7 @@ export type VerifyExpected = {
|
|
|
22
22
|
};
|
|
23
23
|
export declare function registryPath(home: string): string;
|
|
24
24
|
export declare const defaultRegistryPersistence: RegistryPersistence;
|
|
25
|
-
export type RegisterEntry = Omit<SkillDirRegistryEntry, "deployed"> | Omit<FileWriteRegistryEntry, "deployed"> | Omit<ConfigPatchRegistryEntry, "deployed">;
|
|
25
|
+
export type RegisterEntry = Omit<SkillDirRegistryEntry, "deployed"> | Omit<FileWriteRegistryEntry, "deployed"> | Omit<ConfigPatchRegistryEntry, "deployed"> | Omit<FrontmatterEmitRegistryEntry, "deployed">;
|
|
26
26
|
export declare function registerDeployment(home: string, targetPath: string, entry: RegisterEntry, persistence?: RegistryPersistence): Promise<void>;
|
|
27
27
|
export declare function unregisterDeployment(home: string, targetPath: string, persistence?: RegistryPersistence): Promise<void>;
|
|
28
28
|
export declare function lookupDeployment(home: string, targetPath: string, persistence?: RegistryPersistence): Promise<RegistryEntry | null>;
|
package/dist/core/preflight.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export interface PreflightWarning {
|
|
|
3
3
|
kind: "policy" | "config-authority" | "info" | "precedence" | "budget";
|
|
4
4
|
message: string;
|
|
5
5
|
}
|
|
6
|
-
export declare function runPreflight(options: CliOptions, manifest: Manifest,
|
|
6
|
+
export declare function runPreflight(options: CliOptions, manifest: Manifest, home: string, detectedAgents: AgentId[]): Promise<PreflightWarning[]>;
|