@kuznai/inception-engine 0.17.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 +40 -13
- package/dist/config/agents.js +127 -5
- package/dist/core/adapters/agent-definitions.d.ts +2 -2
- package/dist/core/adapters/agent-definitions.js +6 -5
- package/dist/core/adapters/frontmatter.d.ts +17 -5
- package/dist/core/adapters/frontmatter.js +36 -57
- 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 +99 -28
- package/dist/core/deploy.d.ts +1 -1
- package/dist/core/deploy.js +206 -54
- package/dist/core/init.js +129 -49
- package/dist/core/ownership.d.ts +2 -2
- package/dist/core/preflight.d.ts +2 -2
- package/dist/core/preflight.js +142 -2
- 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 +73 -35
- 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 +10 -0
- package/dist/schemas/manifest.js +10 -3
- package/dist/schemas/registry.d.ts +38 -0
- package/dist/schemas/registry.js +7 -0
- package/dist/types.d.ts +48 -0
- package/package.json +2 -2
|
@@ -46,7 +46,7 @@ function buildMcpDeployAction(entry, agentId, resolvedTarget, confidence, mcpPat
|
|
|
46
46
|
confidence,
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
-
export function compileMcpServerActions(entry, detectedAgents, home, repo) {
|
|
49
|
+
export function compileMcpServerActions(entry, detectedAgents, home, repo, workspace) {
|
|
50
50
|
const actions = [];
|
|
51
51
|
const warnings = [];
|
|
52
52
|
const platform = getPlatformKey();
|
|
@@ -70,7 +70,7 @@ export function compileMcpServerActions(entry, detectedAgents, home, repo) {
|
|
|
70
70
|
continue;
|
|
71
71
|
}
|
|
72
72
|
validateMcpServerConfigShape(entry.config, entry.name, agentId);
|
|
73
|
-
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home, repo);
|
|
73
|
+
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home, repo, workspace);
|
|
74
74
|
const resolvedTarget = path.resolve(rawTarget);
|
|
75
75
|
const confidence = agent.provenance.mcpConfig ?? "provisional";
|
|
76
76
|
const mcpPatchKey = support.mcpPatchKey ?? "mcpServers";
|
|
@@ -78,7 +78,7 @@ export function compileMcpServerActions(entry, detectedAgents, home, repo) {
|
|
|
78
78
|
}
|
|
79
79
|
return { actions, warnings };
|
|
80
80
|
}
|
|
81
|
-
export function compileMcpServerReverts(entry, agentFilter, home, repo) {
|
|
81
|
+
export function compileMcpServerReverts(entry, agentFilter, home, repo, workspace) {
|
|
82
82
|
const actions = [];
|
|
83
83
|
const platform = getPlatformKey();
|
|
84
84
|
for (const agentId of entry.agents) {
|
|
@@ -90,7 +90,7 @@ export function compileMcpServerReverts(entry, agentFilter, home, repo) {
|
|
|
90
90
|
support.status === "unsupported" ||
|
|
91
91
|
support.status === "planned")
|
|
92
92
|
continue;
|
|
93
|
-
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home, repo);
|
|
93
|
+
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home, repo, workspace);
|
|
94
94
|
const target = path.resolve(rawTarget);
|
|
95
95
|
if (isMarkdownTarget(target)) {
|
|
96
96
|
actions.push({
|
|
@@ -4,5 +4,5 @@ export interface RulesAdapterResult {
|
|
|
4
4
|
actions: FileWriteDeployAction[];
|
|
5
5
|
warnings: PlanWarning[];
|
|
6
6
|
}
|
|
7
|
-
export declare function compileAgentRuleActions(entry: AgentRuleEntry, sourceDir: string, resolvedSourceDir: string, realRoot: string, detectedAgents: AgentId[], home: string, repo?: string): Promise<RulesAdapterResult>;
|
|
8
|
-
export declare function compileAgentRuleReverts(entry: AgentRuleEntry, agentFilter: AgentId[] | null, home: string, repo?: string): FileWriteRevertAction[];
|
|
7
|
+
export declare function compileAgentRuleActions(entry: AgentRuleEntry, sourceDir: string, resolvedSourceDir: string, realRoot: string, detectedAgents: AgentId[], home: string, repo?: string, workspace?: string): Promise<RulesAdapterResult>;
|
|
8
|
+
export declare function compileAgentRuleReverts(entry: AgentRuleEntry, agentFilter: AgentId[] | null, home: string, repo?: string, workspace?: string): FileWriteRevertAction[];
|
|
@@ -1,8 +1,64 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { AGENT_REGISTRY_BY_ID } from "../../config/agents.js";
|
|
3
3
|
import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
|
|
4
|
-
import { validateAgentRuleMarkdownPath, validateSourceFile, validateSourcePath, } from "../validation.js";
|
|
5
|
-
|
|
4
|
+
import { validateAgentRuleMarkdownPath, validateInstructionFileRequirements, validateSourceFile, validateSourcePath, } from "../validation.js";
|
|
5
|
+
function resolveRulesSupport(agentId, scope) {
|
|
6
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
7
|
+
if (scope === "repo") {
|
|
8
|
+
return agent?.agentRulesRepoSupport ?? agent?.agentRulesSupport;
|
|
9
|
+
}
|
|
10
|
+
if (scope === "workspace") {
|
|
11
|
+
return (agent?.agentRulesWorkspaceSupport ??
|
|
12
|
+
agent?.agentRulesRepoSupport ??
|
|
13
|
+
agent?.agentRulesSupport);
|
|
14
|
+
}
|
|
15
|
+
return agent?.agentRulesSupport;
|
|
16
|
+
}
|
|
17
|
+
function resolveAgentTarget(agentId, entry, home, repo, platform, workspace, allTargetAgentIds) {
|
|
18
|
+
const support = resolveRulesSupport(agentId, entry.scope);
|
|
19
|
+
if (!support || support.status === "unsupported") {
|
|
20
|
+
return {
|
|
21
|
+
kind: "confidence",
|
|
22
|
+
message: `agentRules: agent "${agentId}" uses ${support?.schemaLabel ?? "an unsupported instruction schema"} (unsupported) and ${support?.status === "unsupported" ? support.reason : "does not expose a supported rules adapter"} — skipping "${entry.name}"`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (support.status === "planned") {
|
|
26
|
+
return {
|
|
27
|
+
kind: "confidence",
|
|
28
|
+
message: `agentRules: agent "${agentId}" rules support is planned via ${support.plannedSurface} — skipping "${entry.name}" until that surface is implemented`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// shared-via: when requiresPrimary is set and the primary agent is absent
|
|
32
|
+
// from the target list, emit a guidance warning instead of deploying.
|
|
33
|
+
if (support.surfaceKind?.kind === "shared-via" &&
|
|
34
|
+
support.surfaceKind.requiresPrimary &&
|
|
35
|
+
allTargetAgentIds &&
|
|
36
|
+
!allTargetAgentIds.includes(support.surfaceKind.via)) {
|
|
37
|
+
return {
|
|
38
|
+
kind: "confidence",
|
|
39
|
+
message: `agentRules: agent "${agentId}" reads this surface via "${support.surfaceKind.via}" — add "${support.surfaceKind.via}" to the entry's agents list to deploy to this surface, or deploy via the "${support.surfaceKind.via}" agentRules target instead`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (entry.scope === "repo" && !repo) {
|
|
43
|
+
return {
|
|
44
|
+
kind: "confidence",
|
|
45
|
+
message: `agentRules: scope "repo" requires a repository path but none was resolved — skipping "${entry.name}" for agent "${agentId}"`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (entry.scope === "workspace" && !workspace && !repo) {
|
|
49
|
+
return {
|
|
50
|
+
kind: "confidence",
|
|
51
|
+
message: `agentRules: scope "workspace" requires a workspace or repository path but none was resolved — skipping "${entry.name}" for agent "${agentId}"`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
55
|
+
return {
|
|
56
|
+
agentId,
|
|
57
|
+
confidence: agent?.provenance.agentRules ?? "provisional",
|
|
58
|
+
target: resolvePlaceholders(support.path[platform], entry.name, home, repo, workspace),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export async function compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace) {
|
|
6
62
|
const actions = [];
|
|
7
63
|
const warnings = [];
|
|
8
64
|
const platform = getPlatformKey();
|
|
@@ -12,38 +68,45 @@ export async function compileAgentRuleActions(entry, sourceDir, resolvedSourceDi
|
|
|
12
68
|
return { actions, warnings };
|
|
13
69
|
}
|
|
14
70
|
for (const agentId of targetAgents) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
warnings.push({
|
|
19
|
-
kind: "confidence",
|
|
20
|
-
message: `agentRules: agent "${agentId}" uses ${support?.schemaLabel ?? "an unsupported instruction schema"} and ${support?.status === "unsupported" ? support.reason : "does not expose a supported rules adapter"} — skipping "${entry.name}"`,
|
|
21
|
-
});
|
|
22
|
-
continue;
|
|
71
|
+
const result = resolveAgentTarget(agentId, entry, home, repo, platform, workspace, targetAgents);
|
|
72
|
+
if ("kind" in result) {
|
|
73
|
+
warnings.push(result);
|
|
23
74
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
kind: "confidence",
|
|
27
|
-
message: `agentRules: agent "${agentId}" rules support is planned via ${support.plannedSurface} — skipping "${entry.name}" until that surface is implemented`,
|
|
28
|
-
});
|
|
29
|
-
continue;
|
|
75
|
+
else {
|
|
76
|
+
supportedTargets.push(result);
|
|
30
77
|
}
|
|
31
|
-
supportedTargets.push({
|
|
32
|
-
agentId,
|
|
33
|
-
confidence: agent.provenance.agentRules ?? "provisional",
|
|
34
|
-
target: resolvePlaceholders(support.path[platform], entry.name, home, repo),
|
|
35
|
-
});
|
|
36
78
|
}
|
|
37
79
|
if (supportedTargets.length === 0) {
|
|
38
80
|
return { actions, warnings };
|
|
39
81
|
}
|
|
82
|
+
// Deduplicate: when a shared-via rider's primary is also in supportedTargets,
|
|
83
|
+
// skip the rider — the primary agent writes the shared surface. As a fallback,
|
|
84
|
+
// also dedup by resolved path so no target file is written twice.
|
|
85
|
+
const seenTargetPaths = new Set();
|
|
86
|
+
const dedupedTargets = [];
|
|
87
|
+
for (const t of supportedTargets) {
|
|
88
|
+
const sup = resolveRulesSupport(t.agentId, entry.scope);
|
|
89
|
+
if (sup?.status === "supported" &&
|
|
90
|
+
sup.surfaceKind?.kind === "shared-via" &&
|
|
91
|
+
supportedTargets.some((o) => o.agentId ===
|
|
92
|
+
sup.surfaceKind.via &&
|
|
93
|
+
o.target === t.target)) {
|
|
94
|
+
// Primary agent is present and writes the same target — skip rider.
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (!seenTargetPaths.has(t.target)) {
|
|
98
|
+
seenTargetPaths.add(t.target);
|
|
99
|
+
dedupedTargets.push(t);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
40
102
|
// Validate the shared source file only when at least one target uses the
|
|
41
|
-
// current
|
|
103
|
+
// current rules adapter surface.
|
|
42
104
|
const source = path.resolve(sourceDir, entry.path);
|
|
43
105
|
await validateSourcePath(source, entry.path, resolvedSourceDir, realRoot);
|
|
44
106
|
await validateSourceFile(source, entry.path);
|
|
45
|
-
for (const target of
|
|
107
|
+
for (const target of dedupedTargets) {
|
|
46
108
|
validateAgentRuleMarkdownPath(entry.path, target.agentId);
|
|
109
|
+
await validateInstructionFileRequirements(source, entry.path, target.agentId);
|
|
47
110
|
actions.push({
|
|
48
111
|
kind: "file-write",
|
|
49
112
|
skill: entry.name,
|
|
@@ -55,19 +118,27 @@ export async function compileAgentRuleActions(entry, sourceDir, resolvedSourceDi
|
|
|
55
118
|
}
|
|
56
119
|
return { actions, warnings };
|
|
57
120
|
}
|
|
58
|
-
export function compileAgentRuleReverts(entry, agentFilter, home, repo) {
|
|
121
|
+
export function compileAgentRuleReverts(entry, agentFilter, home, repo, workspace) {
|
|
59
122
|
const actions = [];
|
|
60
123
|
const platform = getPlatformKey();
|
|
124
|
+
const seenRevertTargets = new Set();
|
|
61
125
|
for (const agentId of entry.agents) {
|
|
62
126
|
if (agentFilter && !agentFilter.includes(agentId))
|
|
63
127
|
continue;
|
|
64
|
-
const
|
|
65
|
-
const support = agent?.agentRulesSupport;
|
|
128
|
+
const support = resolveRulesSupport(agentId, entry.scope);
|
|
66
129
|
if (!support ||
|
|
67
130
|
support.status === "unsupported" ||
|
|
68
|
-
support.status === "planned")
|
|
131
|
+
support.status === "planned") {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (entry.scope === "repo" && !repo)
|
|
135
|
+
continue;
|
|
136
|
+
if (entry.scope === "workspace" && !workspace && !repo)
|
|
137
|
+
continue;
|
|
138
|
+
const target = resolvePlaceholders(support.path[platform], entry.name, home, repo, workspace);
|
|
139
|
+
if (seenRevertTargets.has(target))
|
|
69
140
|
continue;
|
|
70
|
-
|
|
141
|
+
seenRevertTargets.add(target);
|
|
71
142
|
actions.push({
|
|
72
143
|
kind: "file-write",
|
|
73
144
|
skill: entry.name,
|
package/dist/core/deploy.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentId, DeployAction, Manifest, PlannedChange, PlanWarning, SkillDirDeployAction } from "../types.ts";
|
|
2
2
|
import { type RegistryPersistence } from "./ownership.ts";
|
|
3
|
-
export declare function planDeploy(manifest: Manifest, sourceDir: string, detectedAgents: AgentId[], home: string): Promise<{
|
|
3
|
+
export declare function planDeploy(manifest: Manifest, sourceDir: string, detectedAgents: AgentId[], home: string, repo?: string, workspace?: string): Promise<{
|
|
4
4
|
actions: DeployAction[];
|
|
5
5
|
warnings: PlanWarning[];
|
|
6
6
|
}>;
|
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 {
|