@kuznai/inception-engine 0.18.0 → 0.20.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 +72 -43
- package/dist/{config → src/config}/agents.js +174 -24
- package/dist/src/core/adapters/agent-definitions.d.ts +8 -0
- package/dist/src/core/adapters/agent-definitions.js +162 -0
- package/dist/{core → src/core}/adapters/frontmatter.d.ts +10 -0
- package/dist/{core → src/core}/adapters/frontmatter.js +23 -6
- package/dist/{core → src/core}/adapters/index.d.ts +1 -1
- package/dist/{core → src/core}/adapters/index.js +4 -4
- package/dist/{core → src/core}/adapters/mcp.d.ts +2 -2
- package/dist/{core → src/core}/adapters/mcp.js +21 -24
- package/dist/{core → src/core}/adapters/permissions.js +16 -20
- package/dist/{core → src/core}/adapters/rules.d.ts +2 -2
- package/dist/src/core/adapters/rules.js +126 -0
- package/dist/src/core/capabilities.d.ts +47 -0
- package/dist/src/core/capabilities.js +280 -0
- package/dist/{core → src/core}/deploy.d.ts +1 -1
- package/dist/{core → src/core}/deploy.js +273 -92
- package/dist/{core → src/core}/init.js +126 -52
- package/dist/src/core/merge-patch.d.ts +4 -0
- package/dist/src/core/merge-patch.js +46 -0
- package/dist/{core → src/core}/ownership.d.ts +2 -2
- package/dist/{core → src/core}/ownership.js +13 -0
- package/dist/{core → src/core}/preflight.d.ts +1 -1
- package/dist/src/core/preflight.js +280 -0
- package/dist/{core → src/core}/resolve.d.ts +1 -1
- package/dist/{core → src/core}/resolve.js +7 -4
- package/dist/{core → src/core}/revert.js +95 -33
- package/dist/{core → src/core}/runtime-paths.d.ts +2 -1
- package/dist/{core → src/core}/runtime-paths.js +30 -23
- package/dist/{core → src/core}/validation.d.ts +10 -1
- package/dist/{core → src/core}/validation.js +75 -0
- package/dist/src/formatters.d.ts +5 -0
- package/dist/src/formatters.js +58 -0
- package/dist/{index.js → src/index.js} +8 -24
- package/dist/src/logger.js +62 -0
- package/dist/{schemas → src/schemas}/manifest.d.ts +22 -0
- package/dist/{schemas → src/schemas}/manifest.js +19 -5
- package/dist/{schemas → src/schemas}/registry.d.ts +74 -0
- package/dist/{schemas → src/schemas}/registry.js +18 -3
- package/dist/{types.d.ts → src/types.d.ts} +60 -0
- package/dist/test/helpers/fs.d.ts +2 -0
- package/dist/test/helpers/fs.js +17 -0
- package/dist/test/helpers/path.d.ts +10 -0
- package/dist/test/helpers/path.js +15 -0
- package/dist/test/helpers/skill-dir-scenarios.d.ts +7 -0
- package/dist/test/helpers/skill-dir-scenarios.js +206 -0
- package/dist/test/helpers/skill-dir.d.ts +7 -0
- package/dist/test/helpers/skill-dir.js +46 -0
- package/dist/test/os/cross-platform/cross-platform.test.d.ts +1 -0
- package/dist/test/os/cross-platform/cross-platform.test.js +328 -0
- package/dist/test/os/posix/skill-dir.test.d.ts +1 -0
- package/dist/test/os/posix/skill-dir.test.js +19 -0
- package/dist/test/os/windows/revert-integration.test.d.ts +1 -0
- package/dist/test/os/windows/revert-integration.test.js +72 -0
- package/dist/test/os/windows/skill-dir.test.d.ts +1 -0
- package/dist/test/os/windows/skill-dir.test.js +19 -0
- package/dist/test/unit/adapters.test.d.ts +1 -0
- package/dist/test/unit/adapters.test.js +947 -0
- package/dist/test/unit/capabilities.test.d.ts +1 -0
- package/dist/test/unit/capabilities.test.js +71 -0
- package/dist/test/unit/cli.test.d.ts +1 -0
- package/dist/test/unit/cli.test.js +834 -0
- package/dist/test/unit/deploy.test.d.ts +1 -0
- package/dist/test/unit/deploy.test.js +2481 -0
- package/dist/test/unit/detect.test.d.ts +1 -0
- package/dist/test/unit/detect.test.js +192 -0
- package/dist/test/unit/enterprise.test.d.ts +1 -0
- package/dist/test/unit/enterprise.test.js +106 -0
- package/dist/test/unit/frontmatter.test.d.ts +1 -0
- package/dist/test/unit/frontmatter.test.js +102 -0
- package/dist/test/unit/gemini-north-star.test.d.ts +1 -0
- package/dist/test/unit/gemini-north-star.test.js +243 -0
- package/dist/test/unit/manifest.test.d.ts +1 -0
- package/dist/test/unit/manifest.test.js +737 -0
- package/dist/test/unit/migration-golden.test.d.ts +1 -0
- package/dist/test/unit/migration-golden.test.js +197 -0
- package/dist/test/unit/ownership.test.d.ts +1 -0
- package/dist/test/unit/ownership.test.js +587 -0
- package/dist/test/unit/preflight.test.d.ts +1 -0
- package/dist/test/unit/preflight.test.js +501 -0
- package/dist/test/unit/resolve.test.d.ts +1 -0
- package/dist/test/unit/resolve.test.js +119 -0
- package/dist/test/unit/revert.test.d.ts +1 -0
- package/dist/test/unit/revert.test.js +1004 -0
- package/dist/test/unit/toml.test.d.ts +1 -0
- package/dist/test/unit/toml.test.js +55 -0
- package/package.json +1 -1
- package/dist/core/adapters/agent-definitions.d.ts +0 -20
- package/dist/core/adapters/agent-definitions.js +0 -90
- package/dist/core/adapters/rules.js +0 -103
- package/dist/core/preflight.js +0 -98
- package/dist/logger.js +0 -61
- /package/dist/{config → src/config}/agents.d.ts +0 -0
- /package/dist/{config → src/config}/manifest.d.ts +0 -0
- /package/dist/{config → src/config}/manifest.js +0 -0
- /package/dist/{core → src/core}/adapters/permissions.d.ts +0 -0
- /package/dist/{core → src/core}/adapters/toml.d.ts +0 -0
- /package/dist/{core → src/core}/adapters/toml.js +0 -0
- /package/dist/{core → src/core}/detect.d.ts +0 -0
- /package/dist/{core → src/core}/detect.js +0 -0
- /package/dist/{core → src/core}/init.d.ts +0 -0
- /package/dist/{core → src/core}/revert.d.ts +0 -0
- /package/dist/{errors.d.ts → src/errors.d.ts} +0 -0
- /package/dist/{errors.js → src/errors.js} +0 -0
- /package/dist/{index.d.ts → src/index.d.ts} +0 -0
- /package/dist/{logger.d.ts → src/logger.d.ts} +0 -0
- /package/dist/{schemas → src/schemas}/errors.d.ts +0 -0
- /package/dist/{schemas → src/schemas}/errors.js +0 -0
- /package/dist/{types.js → src/types.js} +0 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { AGENT_REGISTRY_BY_ID } from "../../config/agents.js";
|
|
3
|
+
import { planCapabilityForDeploy, resolveCapabilitySurface, } from "../capabilities.js";
|
|
4
|
+
import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
|
|
5
|
+
import { validateAgentRuleMarkdownPath, validateInstructionFileRequirements, validateSourceFile, validateSourcePath, } from "../validation.js";
|
|
6
|
+
/**
|
|
7
|
+
* Compiles deploy actions for agentDefinitions manifest entries.
|
|
8
|
+
*
|
|
9
|
+
* Agent definition files are Markdown (typically with YAML frontmatter) or
|
|
10
|
+
* TOML files that define custom agents or subagents for a given platform.
|
|
11
|
+
* Unlike agentRules (which target a single shared global file per agent),
|
|
12
|
+
* each definition entry produces a separate file named after the entry in
|
|
13
|
+
* the agent's dedicated agent-definitions directory.
|
|
14
|
+
*
|
|
15
|
+
* Target paths use the `{repo}` placeholder so definitions land in the
|
|
16
|
+
* repository being deployed, not the user's home directory.
|
|
17
|
+
*
|
|
18
|
+
* TOML source files (e.g. Gemini CLI `.gemini/agents/*.toml`) bypass
|
|
19
|
+
* Markdown-specific validation and are deployed verbatim.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Returns the TOML-specific surface for an agent (global or repo-local),
|
|
23
|
+
* or undefined when the agent has no TOML definition surface.
|
|
24
|
+
*/
|
|
25
|
+
function resolveTomlDefinitionsSurface(agentId, scope) {
|
|
26
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
27
|
+
if (!agent)
|
|
28
|
+
return undefined;
|
|
29
|
+
const tomlSupport = scope === "repo"
|
|
30
|
+
? (agent.agentDefinitionsTomlRepoSupport ??
|
|
31
|
+
agent.agentDefinitionsTomlSupport)
|
|
32
|
+
: agent.agentDefinitionsTomlSupport;
|
|
33
|
+
if (!tomlSupport || tomlSupport.status !== "supported")
|
|
34
|
+
return undefined;
|
|
35
|
+
return tomlSupport;
|
|
36
|
+
}
|
|
37
|
+
/** Returns a warning when the requested scope cannot be resolved, or null when it is fine. */
|
|
38
|
+
function scopeAvailabilityWarning(entry, agentId, repo, workspace) {
|
|
39
|
+
if (entry.scope === "repo" && !repo) {
|
|
40
|
+
return {
|
|
41
|
+
kind: "confidence",
|
|
42
|
+
message: `agentDefinitions: scope "repo" requires a repository path but none was resolved — skipping "${entry.name}" for agent "${agentId}"`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (entry.scope === "workspace" && !workspace && !repo) {
|
|
46
|
+
return {
|
|
47
|
+
kind: "confidence",
|
|
48
|
+
message: `agentDefinitions: scope "workspace" requires a workspace or repository path but none was resolved — skipping "${entry.name}" for agent "${agentId}"`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
function resolveSupportedTargets(entry, targetAgents, home, repo, workspace) {
|
|
54
|
+
const targets = [];
|
|
55
|
+
const warnings = [];
|
|
56
|
+
const platform = getPlatformKey();
|
|
57
|
+
const isToml = path.extname(entry.path).toLowerCase() === ".toml";
|
|
58
|
+
for (const agentId of targetAgents) {
|
|
59
|
+
const plan = planCapabilityForDeploy({
|
|
60
|
+
agentId,
|
|
61
|
+
capability: "agentDefinitions",
|
|
62
|
+
entryName: entry.name,
|
|
63
|
+
targetAgentIds: targetAgents,
|
|
64
|
+
scope: entry.scope,
|
|
65
|
+
});
|
|
66
|
+
if (plan.outcome === "warn") {
|
|
67
|
+
warnings.push(plan.warning);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (plan.outcome === "native" || plan.outcome === "redundant")
|
|
71
|
+
continue;
|
|
72
|
+
// For TOML sources, use the agent's TOML-specific surface instead of the
|
|
73
|
+
// default Markdown surface. Agents without a TOML surface are skipped.
|
|
74
|
+
const support = isToml
|
|
75
|
+
? resolveTomlDefinitionsSurface(agentId, entry.scope)
|
|
76
|
+
: resolveCapabilitySurface(agentId, "agentDefinitions", entry.scope)
|
|
77
|
+
.support;
|
|
78
|
+
if (!support)
|
|
79
|
+
continue;
|
|
80
|
+
const scopeWarning = scopeAvailabilityWarning(entry, agentId, repo, workspace);
|
|
81
|
+
if (scopeWarning) {
|
|
82
|
+
warnings.push(scopeWarning);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
targets.push({
|
|
86
|
+
agentId,
|
|
87
|
+
confidence: plan.confidence ?? "provisional",
|
|
88
|
+
target: resolvePlaceholders(support.path[platform], entry.name, home, repo, workspace),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return { targets, warnings };
|
|
92
|
+
}
|
|
93
|
+
async function createAgentDefinitionActions(entry, source, supportedTargets, home, repo, workspace) {
|
|
94
|
+
const actions = [];
|
|
95
|
+
const isToml = path.extname(entry.path).toLowerCase() === ".toml";
|
|
96
|
+
for (const target of supportedTargets) {
|
|
97
|
+
// TOML definition files have no YAML frontmatter — skip Markdown validation.
|
|
98
|
+
if (!isToml) {
|
|
99
|
+
validateAgentRuleMarkdownPath(entry.path, target.agentId);
|
|
100
|
+
await validateInstructionFileRequirements(source, entry.path, target.agentId);
|
|
101
|
+
}
|
|
102
|
+
let migratedFrom;
|
|
103
|
+
if (target.agentId === "github-copilot") {
|
|
104
|
+
migratedFrom = [
|
|
105
|
+
resolvePlaceholders(["{repo}", ".github", "agents", "{name}.agent.md"], entry.name, home, repo, workspace),
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
actions.push({
|
|
109
|
+
kind: "file-write",
|
|
110
|
+
skill: entry.name,
|
|
111
|
+
agent: target.agentId,
|
|
112
|
+
source,
|
|
113
|
+
target: target.target,
|
|
114
|
+
confidence: target.confidence,
|
|
115
|
+
migratedFrom,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return actions;
|
|
119
|
+
}
|
|
120
|
+
export async function compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace) {
|
|
121
|
+
const targetAgents = entry.agents.filter((agentId) => detectedAgents.includes(agentId));
|
|
122
|
+
if (targetAgents.length === 0) {
|
|
123
|
+
return { actions: [], warnings: [] };
|
|
124
|
+
}
|
|
125
|
+
const { targets: supportedTargets, warnings } = resolveSupportedTargets(entry, targetAgents, home, repo, workspace);
|
|
126
|
+
if (supportedTargets.length === 0) {
|
|
127
|
+
return { actions: [], warnings };
|
|
128
|
+
}
|
|
129
|
+
// Validate the shared source file only when at least one target is active.
|
|
130
|
+
const source = path.resolve(sourceDir, entry.path);
|
|
131
|
+
await validateSourcePath(source, entry.path, resolvedSourceDir, realRoot);
|
|
132
|
+
await validateSourceFile(source, entry.path);
|
|
133
|
+
const actions = await createAgentDefinitionActions(entry, source, supportedTargets, home, repo, workspace);
|
|
134
|
+
return { actions, warnings };
|
|
135
|
+
}
|
|
136
|
+
export function compileAgentDefinitionReverts(entry, agentFilter, home, repo, workspace) {
|
|
137
|
+
const actions = [];
|
|
138
|
+
const platform = getPlatformKey();
|
|
139
|
+
const isToml = path.extname(entry.path).toLowerCase() === ".toml";
|
|
140
|
+
for (const agentId of entry.agents) {
|
|
141
|
+
if (agentFilter && !agentFilter.includes(agentId))
|
|
142
|
+
continue;
|
|
143
|
+
const support = isToml
|
|
144
|
+
? resolveTomlDefinitionsSurface(agentId, entry.scope)
|
|
145
|
+
: resolveCapabilitySurface(agentId, "agentDefinitions", entry.scope)
|
|
146
|
+
.support;
|
|
147
|
+
if (!support)
|
|
148
|
+
continue;
|
|
149
|
+
if (entry.scope === "repo" && !repo)
|
|
150
|
+
continue;
|
|
151
|
+
if (entry.scope === "workspace" && !workspace && !repo)
|
|
152
|
+
continue;
|
|
153
|
+
const target = resolvePlaceholders(support.path[platform], entry.name, home, repo, workspace);
|
|
154
|
+
actions.push({
|
|
155
|
+
kind: "file-write",
|
|
156
|
+
skill: entry.name,
|
|
157
|
+
agent: agentId,
|
|
158
|
+
target,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return actions;
|
|
162
|
+
}
|
|
@@ -12,6 +12,7 @@ export declare function splitFrontmatter(raw: string): {
|
|
|
12
12
|
export declare function parseFrontmatterDocument<T = Record<string, unknown>>(raw: string): {
|
|
13
13
|
attributes: T;
|
|
14
14
|
body: string;
|
|
15
|
+
hasFrontmatter: boolean;
|
|
15
16
|
};
|
|
16
17
|
/**
|
|
17
18
|
* Serializes an object to a YAML block.
|
|
@@ -21,6 +22,9 @@ export declare function serializeFrontmatter(data: Record<string, unknown>): str
|
|
|
21
22
|
* Builds the full content of a frontmatter-bearing Markdown file.
|
|
22
23
|
*/
|
|
23
24
|
export declare function buildFrontmatterDocument(frontmatter: Record<string, unknown>, body?: string): string;
|
|
25
|
+
export declare function buildMarkdownDocument(frontmatter: Record<string, unknown>, body?: string, options?: {
|
|
26
|
+
hasFrontmatter?: boolean;
|
|
27
|
+
}): string;
|
|
24
28
|
/**
|
|
25
29
|
* Reads an existing `.md` file and parses its frontmatter.
|
|
26
30
|
* Returns `{ attributes: {}, body: "" }` if the file does not exist.
|
|
@@ -29,6 +33,12 @@ export declare function readFrontmatterFile(filePath: string): Promise<{
|
|
|
29
33
|
attributes: Record<string, unknown>;
|
|
30
34
|
body: string;
|
|
31
35
|
}>;
|
|
36
|
+
export declare function readFrontmatterDocumentFile(filePath: string): Promise<{
|
|
37
|
+
exists: boolean;
|
|
38
|
+
hasFrontmatter: boolean;
|
|
39
|
+
attributes: Record<string, unknown>;
|
|
40
|
+
body: string;
|
|
41
|
+
}>;
|
|
32
42
|
/**
|
|
33
43
|
* Atomically writes a Markdown file with the given frontmatter and optional
|
|
34
44
|
* body. Preserves any existing body content when `preserveBody` is true.
|
|
@@ -26,10 +26,10 @@ export function splitFrontmatter(raw) {
|
|
|
26
26
|
export function parseFrontmatterDocument(raw) {
|
|
27
27
|
const { frontmatter, body } = splitFrontmatter(raw);
|
|
28
28
|
if (!(frontmatter || raw.startsWith("---"))) {
|
|
29
|
-
return { attributes: {}, body };
|
|
29
|
+
return { attributes: {}, body, hasFrontmatter: false };
|
|
30
30
|
}
|
|
31
31
|
const attributes = YAML.parse(frontmatter);
|
|
32
|
-
return { attributes: (attributes || {}), body };
|
|
32
|
+
return { attributes: (attributes || {}), body, hasFrontmatter: true };
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* Serializes an object to a YAML block.
|
|
@@ -42,9 +42,15 @@ export function serializeFrontmatter(data) {
|
|
|
42
42
|
* Builds the full content of a frontmatter-bearing Markdown file.
|
|
43
43
|
*/
|
|
44
44
|
export function buildFrontmatterDocument(frontmatter, body = "") {
|
|
45
|
+
return buildMarkdownDocument(frontmatter, body, { hasFrontmatter: true });
|
|
46
|
+
}
|
|
47
|
+
export function buildMarkdownDocument(frontmatter, body = "", options = {}) {
|
|
48
|
+
const cleanBody = body.trimStart();
|
|
49
|
+
if (Object.keys(frontmatter).length === 0 && !options.hasFrontmatter) {
|
|
50
|
+
return cleanBody;
|
|
51
|
+
}
|
|
45
52
|
const serialized = serializeFrontmatter(frontmatter);
|
|
46
53
|
// Ensure exactly one newline after the closing delimiter before the body
|
|
47
|
-
const cleanBody = body.trimStart();
|
|
48
54
|
const separator = cleanBody ? "\n\n" : "\n";
|
|
49
55
|
return `---\n${serialized}\n---\n${separator}${cleanBody}`;
|
|
50
56
|
}
|
|
@@ -58,17 +64,28 @@ function createAtomicTempPath(targetPath) {
|
|
|
58
64
|
* Returns `{ attributes: {}, body: "" }` if the file does not exist.
|
|
59
65
|
*/
|
|
60
66
|
export async function readFrontmatterFile(filePath) {
|
|
67
|
+
const document = await readFrontmatterDocumentFile(filePath);
|
|
68
|
+
return { attributes: document.attributes, body: document.body };
|
|
69
|
+
}
|
|
70
|
+
export async function readFrontmatterDocumentFile(filePath) {
|
|
61
71
|
let raw;
|
|
62
72
|
try {
|
|
63
73
|
raw = await readFile(filePath, "utf-8");
|
|
64
74
|
}
|
|
65
75
|
catch (err) {
|
|
66
76
|
const code = err.code;
|
|
67
|
-
if (code === "ENOENT")
|
|
68
|
-
return {
|
|
77
|
+
if (code === "ENOENT") {
|
|
78
|
+
return {
|
|
79
|
+
exists: false,
|
|
80
|
+
hasFrontmatter: false,
|
|
81
|
+
attributes: {},
|
|
82
|
+
body: "",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
69
85
|
throw err;
|
|
70
86
|
}
|
|
71
|
-
|
|
87
|
+
const parsed = parseFrontmatterDocument(raw);
|
|
88
|
+
return { exists: true, ...parsed };
|
|
72
89
|
}
|
|
73
90
|
/**
|
|
74
91
|
* Atomically writes a Markdown file with the given frontmatter and optional
|
|
@@ -10,4 +10,4 @@ export interface AdapterResult {
|
|
|
10
10
|
actions: AdapterAction[];
|
|
11
11
|
warnings: PlanWarning[];
|
|
12
12
|
}
|
|
13
|
-
export declare function compileAdapterActions(mcpServers: McpServerEntry[], agentRules: AgentRuleEntry[], permissions: PermissionsEntry[], sourceDir: string, resolvedSourceDir: string, realRoot: string, detectedAgents: AgentId[], home: string, repo?: string, agentDefinitions?: AgentDefinitionEntry[]): Promise<AdapterResult>;
|
|
13
|
+
export declare function compileAdapterActions(mcpServers: McpServerEntry[], agentRules: AgentRuleEntry[], permissions: PermissionsEntry[], sourceDir: string, resolvedSourceDir: string, realRoot: string, detectedAgents: AgentId[], home: string, repo?: string, agentDefinitions?: AgentDefinitionEntry[], workspace?: string): Promise<AdapterResult>;
|
|
@@ -3,16 +3,16 @@ import { compileMcpServerActions, compileMcpServerReverts } from "./mcp.js";
|
|
|
3
3
|
import { compilePermissionsActions, compilePermissionsReverts, } from "./permissions.js";
|
|
4
4
|
import { compileAgentRuleActions, compileAgentRuleReverts } from "./rules.js";
|
|
5
5
|
export { compileAgentDefinitionReverts, compileAgentRuleReverts, compileMcpServerReverts, compilePermissionsReverts, };
|
|
6
|
-
export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, agentDefinitions) {
|
|
6
|
+
export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, agentDefinitions, workspace) {
|
|
7
7
|
const actions = [];
|
|
8
8
|
const warnings = [];
|
|
9
9
|
for (const entry of mcpServers) {
|
|
10
|
-
const r = compileMcpServerActions(entry, detectedAgents, home, repo);
|
|
10
|
+
const r = compileMcpServerActions(entry, detectedAgents, home, repo, workspace);
|
|
11
11
|
actions.push(...r.actions);
|
|
12
12
|
warnings.push(...r.warnings);
|
|
13
13
|
}
|
|
14
14
|
for (const entry of agentRules) {
|
|
15
|
-
const r = await compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo);
|
|
15
|
+
const r = await compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace);
|
|
16
16
|
actions.push(...r.actions);
|
|
17
17
|
warnings.push(...r.warnings);
|
|
18
18
|
}
|
|
@@ -22,7 +22,7 @@ export async function compileAdapterActions(mcpServers, agentRules, permissions,
|
|
|
22
22
|
warnings.push(...r.warnings);
|
|
23
23
|
}
|
|
24
24
|
for (const entry of agentDefinitions ?? []) {
|
|
25
|
-
const r = await compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo);
|
|
25
|
+
const r = await compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace);
|
|
26
26
|
actions.push(...r.actions);
|
|
27
27
|
warnings.push(...r.warnings);
|
|
28
28
|
}
|
|
@@ -4,5 +4,5 @@ export interface McpAdapterResult {
|
|
|
4
4
|
actions: Array<ConfigPatchDeployAction | TomlPatchDeployAction | FrontmatterEmitDeployAction>;
|
|
5
5
|
warnings: PlanWarning[];
|
|
6
6
|
}
|
|
7
|
-
export declare function compileMcpServerActions(entry: McpServerEntry, detectedAgents: AgentId[], home: string, repo?: string): McpAdapterResult;
|
|
8
|
-
export declare function compileMcpServerReverts(entry: McpServerEntry, agentFilter: AgentId[] | null, home: string, repo?: string): Array<ConfigPatchRevertAction | TomlPatchRevertAction | FrontmatterEmitRevertAction>;
|
|
7
|
+
export declare function compileMcpServerActions(entry: McpServerEntry, detectedAgents: AgentId[], home: string, repo?: string, workspace?: string): McpAdapterResult;
|
|
8
|
+
export declare function compileMcpServerReverts(entry: McpServerEntry, agentFilter: AgentId[] | null, home: string, repo?: string, workspace?: string): Array<ConfigPatchRevertAction | TomlPatchRevertAction | FrontmatterEmitRevertAction>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import { planCapabilityForDeploy, resolveCapabilitySurface, } from "../capabilities.js";
|
|
3
3
|
import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
|
|
4
4
|
import { validateMcpServerConfigShape } from "../validation.js";
|
|
5
5
|
/**
|
|
@@ -46,51 +46,48 @@ 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();
|
|
53
53
|
for (const agentId of entry.agents) {
|
|
54
54
|
if (!detectedAgents.includes(agentId))
|
|
55
55
|
continue;
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
const plan = planCapabilityForDeploy({
|
|
57
|
+
agentId,
|
|
58
|
+
capability: "mcpServers",
|
|
59
|
+
entryName: entry.name,
|
|
60
|
+
targetAgentIds: entry.agents,
|
|
61
|
+
scope: entry.scope,
|
|
62
|
+
});
|
|
63
|
+
if (plan.outcome === "warn") {
|
|
64
|
+
warnings.push(plan.warning);
|
|
63
65
|
continue;
|
|
64
66
|
}
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
67
|
+
if (plan.outcome === "native" || plan.outcome === "redundant")
|
|
68
|
+
continue;
|
|
69
|
+
const support = resolveCapabilitySurface(agentId, "mcpServers", entry.scope).support;
|
|
70
|
+
if (!support)
|
|
70
71
|
continue;
|
|
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
|
-
const confidence =
|
|
75
|
+
const confidence = plan.confidence ?? "provisional";
|
|
76
76
|
const mcpPatchKey = support.mcpPatchKey ?? "mcpServers";
|
|
77
77
|
actions.push(buildMcpDeployAction(entry, agentId, resolvedTarget, confidence, mcpPatchKey));
|
|
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) {
|
|
85
85
|
if (agentFilter && !agentFilter.includes(agentId))
|
|
86
86
|
continue;
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
if (!support ||
|
|
90
|
-
support.status === "unsupported" ||
|
|
91
|
-
support.status === "planned")
|
|
87
|
+
const support = resolveCapabilitySurface(agentId, "mcpServers", entry.scope).support;
|
|
88
|
+
if (!support)
|
|
92
89
|
continue;
|
|
93
|
-
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home, repo);
|
|
90
|
+
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home, repo, workspace);
|
|
94
91
|
const target = path.resolve(rawTarget);
|
|
95
92
|
if (isMarkdownTarget(target)) {
|
|
96
93
|
actions.push({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import { planCapabilityForDeploy, resolveCapabilitySurface, } from "../capabilities.js";
|
|
3
3
|
import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
|
|
4
4
|
import { validatePermissionsConfigShape } from "../validation.js";
|
|
5
5
|
function isTomlTarget(targetPath) {
|
|
@@ -12,26 +12,25 @@ export function compilePermissionsActions(entry, detectedAgents, home) {
|
|
|
12
12
|
for (const agentId of entry.agents) {
|
|
13
13
|
if (!detectedAgents.includes(agentId))
|
|
14
14
|
continue;
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
const plan = planCapabilityForDeploy({
|
|
16
|
+
agentId,
|
|
17
|
+
capability: "permissions",
|
|
18
|
+
entryName: entry.name,
|
|
19
|
+
targetAgentIds: entry.agents,
|
|
20
|
+
});
|
|
21
|
+
if (plan.outcome === "warn") {
|
|
22
|
+
warnings.push(plan.warning);
|
|
22
23
|
continue;
|
|
23
24
|
}
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
});
|
|
25
|
+
if (plan.outcome === "native" || plan.outcome === "redundant")
|
|
26
|
+
continue;
|
|
27
|
+
const support = resolveCapabilitySurface(agentId, "permissions").support;
|
|
28
|
+
if (!support)
|
|
29
29
|
continue;
|
|
30
|
-
}
|
|
31
30
|
validatePermissionsConfigShape(entry.config, entry.name, agentId);
|
|
32
31
|
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home);
|
|
33
32
|
const resolvedTarget = path.resolve(rawTarget);
|
|
34
|
-
const confidence =
|
|
33
|
+
const confidence = plan.confidence ?? "provisional";
|
|
35
34
|
if (isTomlTarget(resolvedTarget)) {
|
|
36
35
|
actions.push({
|
|
37
36
|
kind: "toml-patch",
|
|
@@ -61,11 +60,8 @@ export function compilePermissionsReverts(entry, agentFilter, home) {
|
|
|
61
60
|
for (const agentId of entry.agents) {
|
|
62
61
|
if (agentFilter && !agentFilter.includes(agentId))
|
|
63
62
|
continue;
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
if (!support ||
|
|
67
|
-
support.status === "unsupported" ||
|
|
68
|
-
support.status === "planned")
|
|
63
|
+
const support = resolveCapabilitySurface(agentId, "permissions").support;
|
|
64
|
+
if (!support)
|
|
69
65
|
continue;
|
|
70
66
|
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home);
|
|
71
67
|
const target = path.resolve(rawTarget);
|
|
@@ -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[];
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { planCapabilityForDeploy, resolveCapabilitySurface, } from "../capabilities.js";
|
|
3
|
+
import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
|
|
4
|
+
import { validateAgentRuleMarkdownPath, validateInstructionFileRequirements, validateSourceFile, validateSourcePath, } from "../validation.js";
|
|
5
|
+
function resolveAgentTarget(agentId, entry, home, repo, platform, workspace, allTargetAgentIds) {
|
|
6
|
+
const plan = planCapabilityForDeploy({
|
|
7
|
+
agentId,
|
|
8
|
+
capability: "agentRules",
|
|
9
|
+
entryName: entry.name,
|
|
10
|
+
targetAgentIds: allTargetAgentIds ?? [agentId],
|
|
11
|
+
scope: entry.scope,
|
|
12
|
+
});
|
|
13
|
+
if (plan.outcome === "warn")
|
|
14
|
+
return plan.warning;
|
|
15
|
+
if (plan.outcome === "native" || plan.outcome === "redundant")
|
|
16
|
+
return null;
|
|
17
|
+
const surface = resolveCapabilitySurface(agentId, "agentRules", entry.scope);
|
|
18
|
+
const support = surface.support;
|
|
19
|
+
if (entry.scope === "repo" && !repo) {
|
|
20
|
+
return {
|
|
21
|
+
kind: "confidence",
|
|
22
|
+
message: `agentRules: scope "repo" requires a repository path but none was resolved — skipping "${entry.name}" for agent "${agentId}"`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (entry.scope === "workspace" && !workspace && !repo) {
|
|
26
|
+
return {
|
|
27
|
+
kind: "confidence",
|
|
28
|
+
message: `agentRules: scope "workspace" requires a workspace or repository path but none was resolved — skipping "${entry.name}" for agent "${agentId}"`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
agentId,
|
|
33
|
+
confidence: plan.confidence,
|
|
34
|
+
target: resolvePlaceholders(support?.path[platform] ?? [], entry.name, home, repo, workspace),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace) {
|
|
38
|
+
const actions = [];
|
|
39
|
+
const warnings = [];
|
|
40
|
+
const platform = getPlatformKey();
|
|
41
|
+
const targetAgents = entry.agents.filter((agentId) => detectedAgents.includes(agentId));
|
|
42
|
+
const supportedTargets = [];
|
|
43
|
+
if (targetAgents.length === 0) {
|
|
44
|
+
return { actions, warnings };
|
|
45
|
+
}
|
|
46
|
+
for (const agentId of targetAgents) {
|
|
47
|
+
const result = resolveAgentTarget(agentId, entry, home, repo, platform, workspace, targetAgents);
|
|
48
|
+
if (result === null) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if ("kind" in result) {
|
|
52
|
+
warnings.push(result);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
supportedTargets.push(result);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (supportedTargets.length === 0) {
|
|
59
|
+
return { actions, warnings };
|
|
60
|
+
}
|
|
61
|
+
// Deduplicate: when a shared-via rider's primary is also in supportedTargets,
|
|
62
|
+
// skip the rider — the primary agent writes the shared surface. As a fallback,
|
|
63
|
+
// also dedup by resolved path so no target file is written twice.
|
|
64
|
+
const seenTargetPaths = new Set();
|
|
65
|
+
const dedupedTargets = [];
|
|
66
|
+
for (const t of supportedTargets) {
|
|
67
|
+
const surface = resolveCapabilitySurface(t.agentId, "agentRules", entry.scope);
|
|
68
|
+
if (surface.surfaceKind === "shared-via" &&
|
|
69
|
+
surface.sharedVia &&
|
|
70
|
+
supportedTargets.some((o) => o.agentId === surface.sharedVia && o.target === t.target)) {
|
|
71
|
+
// Primary agent is present and writes the same target — skip rider.
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (!seenTargetPaths.has(t.target)) {
|
|
75
|
+
seenTargetPaths.add(t.target);
|
|
76
|
+
dedupedTargets.push(t);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Validate the shared source file only when at least one target uses the
|
|
80
|
+
// current rules adapter surface.
|
|
81
|
+
const source = path.resolve(sourceDir, entry.path);
|
|
82
|
+
await validateSourcePath(source, entry.path, resolvedSourceDir, realRoot);
|
|
83
|
+
await validateSourceFile(source, entry.path);
|
|
84
|
+
for (const target of dedupedTargets) {
|
|
85
|
+
validateAgentRuleMarkdownPath(entry.path, target.agentId);
|
|
86
|
+
await validateInstructionFileRequirements(source, entry.path, target.agentId);
|
|
87
|
+
actions.push({
|
|
88
|
+
kind: "file-write",
|
|
89
|
+
skill: entry.name,
|
|
90
|
+
agent: target.agentId,
|
|
91
|
+
source,
|
|
92
|
+
target: target.target,
|
|
93
|
+
confidence: target.confidence,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return { actions, warnings };
|
|
97
|
+
}
|
|
98
|
+
export function compileAgentRuleReverts(entry, agentFilter, home, repo, workspace) {
|
|
99
|
+
const actions = [];
|
|
100
|
+
const platform = getPlatformKey();
|
|
101
|
+
const seenRevertTargets = new Set();
|
|
102
|
+
for (const agentId of entry.agents) {
|
|
103
|
+
if (agentFilter && !agentFilter.includes(agentId))
|
|
104
|
+
continue;
|
|
105
|
+
const surface = resolveCapabilitySurface(agentId, "agentRules", entry.scope);
|
|
106
|
+
const support = surface.support;
|
|
107
|
+
if (surface.supportStatus !== "supported" || !support) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (entry.scope === "repo" && !repo)
|
|
111
|
+
continue;
|
|
112
|
+
if (entry.scope === "workspace" && !workspace && !repo)
|
|
113
|
+
continue;
|
|
114
|
+
const target = resolvePlaceholders(support.path[platform], entry.name, home, repo, workspace);
|
|
115
|
+
if (seenRevertTargets.has(target))
|
|
116
|
+
continue;
|
|
117
|
+
seenRevertTargets.add(target);
|
|
118
|
+
actions.push({
|
|
119
|
+
kind: "file-write",
|
|
120
|
+
skill: entry.name,
|
|
121
|
+
agent: agentId,
|
|
122
|
+
target,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return actions;
|
|
126
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { AgentId, AgentSurfaceSupport, CapabilityKind, Confidence, PlanWarning, SupportedAgentSurface } from "../types.ts";
|
|
2
|
+
type CapabilityScope = "global" | "repo" | "workspace";
|
|
3
|
+
export interface ResolvedCapabilitySurface {
|
|
4
|
+
agentId: AgentId;
|
|
5
|
+
capability: CapabilityKind;
|
|
6
|
+
supportStatus: "supported" | "unsupported" | "planned";
|
|
7
|
+
support?: SupportedAgentSurface;
|
|
8
|
+
supportRecord?: AgentSurfaceSupport;
|
|
9
|
+
confidence?: Confidence;
|
|
10
|
+
schemaLabel: string;
|
|
11
|
+
surfaceKind: "agent-specific" | "native" | "shared-via";
|
|
12
|
+
sharedVia?: AgentId;
|
|
13
|
+
requiresPrimary: boolean;
|
|
14
|
+
canDeployDirectlyWhenPrimaryAbsent: boolean;
|
|
15
|
+
includeInInitByDefault: boolean;
|
|
16
|
+
reason?: string;
|
|
17
|
+
plannedSurface?: string;
|
|
18
|
+
}
|
|
19
|
+
export type DeployCapabilityPlan = {
|
|
20
|
+
outcome: "action";
|
|
21
|
+
confidence?: Confidence;
|
|
22
|
+
support?: SupportedAgentSurface;
|
|
23
|
+
} | {
|
|
24
|
+
outcome: "redundant";
|
|
25
|
+
confidence?: Confidence;
|
|
26
|
+
support?: SupportedAgentSurface;
|
|
27
|
+
sharedVia: AgentId;
|
|
28
|
+
} | {
|
|
29
|
+
outcome: "warn";
|
|
30
|
+
warning: PlanWarning;
|
|
31
|
+
} | {
|
|
32
|
+
outcome: "native";
|
|
33
|
+
};
|
|
34
|
+
export declare function resolveCapabilitySurface(agentId: AgentId, capability: CapabilityKind, scope?: CapabilityScope): ResolvedCapabilitySurface;
|
|
35
|
+
export declare function planCapabilityForDeploy(opts: {
|
|
36
|
+
agentId: AgentId;
|
|
37
|
+
capability: CapabilityKind;
|
|
38
|
+
entryName: string;
|
|
39
|
+
targetAgentIds: AgentId[];
|
|
40
|
+
scope?: CapabilityScope;
|
|
41
|
+
}): DeployCapabilityPlan;
|
|
42
|
+
export declare function shouldInitIncludeAgent(agentId: AgentId, capability: CapabilityKind, scope?: CapabilityScope): boolean;
|
|
43
|
+
export declare function describeCapabilityConfidence(agentId: AgentId, capability: CapabilityKind, scope?: CapabilityScope): {
|
|
44
|
+
confidence?: Confidence;
|
|
45
|
+
message: string | null;
|
|
46
|
+
};
|
|
47
|
+
export {};
|