@kuznai/inception-engine 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,9 +50,10 @@ Before executing, the deploy command runs preflight analysis on instruction file
50
50
  | Skills (SKILL.md) | All agents via manifest and CLI | All agents |
51
51
  | File write | All agents via manifest and CLI | All agents |
52
52
  | Config patch (JSON merge) | All agents via manifest and CLI | All agents |
53
- | MCP Servers | claude-code (`scope: "global"` → `~/.claude.json`; `scope: "repo"` → `{repo}/.claude/mcp.json`; `scope: "workspace"` → `{workspace}/.claude/mcp.json`), gemini-cli, codex, antigravity, opencode; github-copilot with `scope: "repo"` deploys to `{repo}/.vscode/mcp.json` and `scope: "workspace"` deploys to `{workspace}/.vscode/mcp.json`; github-copilot with `scope: "global"` (default) is unsupported and warns | claude-code, gemini-cli, codex, antigravity, opencode, github-copilot |
53
+ | MCP Servers | claude-code (`scope: "global"` → `~/.claude.json`; `scope: "repo"` → `{repo}/.claude/mcp.json`; `scope: "workspace"` → `{workspace}/.claude/mcp.json`), gemini-cli, codex, antigravity (`scope: "global"` → `~/.gemini/antigravity/mcp_config.json`; `scope: "repo"` → `{repo}/.agents/rules/{name}.md`), opencode; github-copilot with `scope: "repo"` deploys to `{repo}/.vscode/mcp.json` and `scope: "workspace"` deploys to `{workspace}/.vscode/mcp.json`; github-copilot with `scope: "global"` (default) is unsupported and warns | claude-code, gemini-cli, codex, antigravity, opencode, github-copilot |
54
54
  | Global/Repo/Workspace Rules Files | `scope: "global"` and `scope: "repo"` are supported on the implemented agent surfaces; `scope: "workspace"` is supported for `claude-code`, `codex`, and `gemini-cli`; `github-copilot` reads Claude-native rules via `claude-code` and has no separate rules deployment target | All supported agents |
55
55
  | Permissions / Approval Config | claude-code (`~/.claude/settings.json`), codex (`~/.codex/config.toml`), opencode (`~/.config/opencode/opencode.json` on POSIX, `%APPDATA%\\opencode\\opencode.json` on Windows); other agents are warned and skipped | claude-code, codex, opencode |
56
+ | Execution Hooks | claude-code (`~/.claude/settings.json`), github-copilot (`planned`); other agents are warned and skipped | claude-code |
56
57
  | Agent Definitions | claude-code (`{repo}/.claude/agents/{name}.md`), gemini-cli (`{repo}/.gemini/agents/{name}.md` or `.toml`, plus `scope: "global"` to `~/.gemini/agents/{name}.md` or `.toml`), antigravity (`{repo}/.agents/rules/{name}.md`), opencode (`{repo}/.opencode/agents/{name}.md`, plus `scope: "global"` to the user config dir), github-copilot (`{repo}/.github/copilot/agents/{name}.md`, with migration from legacy `.github/agents/{name}.agent.md`); codex is warned and skipped | All supported agents |
57
58
  | `init` manifest generation | Scans `SKILL.md` directories (`skills`), `.md` files with Claude-first agent mapping (`agentRules`), `mcp-servers.json` (`mcpServers`), and agent-definition Markdown files (`agentDefinitions`); shared surfaces default to the primary deploy target instead of shared riders; emits hints for `files/` and `configs/` directories | N/A |
58
59
  | Instruction preflight analysis | Emits capability warnings for implementation-only, planned, unsupported, and shared-through surfaces used by the manifest; emits `precedence` warnings when an agent has multiple `agentRules` scopes active simultaneously or the same source file is deployed to multiple scopes; emits `budget` warnings when `agentRules` or `agentDefinitions` source files exceed 50 KB; emits GitHub Copilot enterprise-policy warnings when local configuration may be overridden; emits a `config-authority` warning when Gemini CLI's `settings.json` contains an `instructionFilename` override that differs from the deploy target | N/A |
@@ -135,6 +136,17 @@ Create an `inception.json` file at the root of your skills directory:
135
136
  "path": "agents/code-reviewer.md",
136
137
  "agents": ["claude-code", "opencode", "github-copilot"]
137
138
  }
139
+ ],
140
+ "hooks": [
141
+ {
142
+ "name": "pre-exec-check",
143
+ "agents": ["claude-code"],
144
+ "config": {
145
+ "hooks": {
146
+ "pre_exec": "scripts/check-env.sh"
147
+ }
148
+ }
149
+ }
138
150
  ]
139
151
  }
140
152
  ```
@@ -166,17 +178,18 @@ Each **mcpServer** entry registers an MCP server into the agent's config file by
166
178
  - **name** - Unique identifier (same format as skill names); used as the server's key in the config
167
179
  - **agents** - Array of agent IDs to register this server with
168
180
  - **config** - Raw server descriptor object. For the currently supported JSON-backed adapters, inception-engine requires at least one non-empty `command` or `url` field, validates `args` as an array of strings when present, and validates `env` as a string-to-string object when present. Additional keys are passed through verbatim.
169
- - **scope** - `"global"` (default), `"repo"`, or `"workspace"`. For most agents this field is ignored (they have only a single user-level config path). For `github-copilot`, `scope` selects the target file:
181
+ - **scope** - `"global"` (default), `"repo"`, `"workspace"`, or `"devcontainer"`. For most agents this field is ignored (they have only a single user-level config path). For `github-copilot`, `scope` selects the target file:
170
182
  - `"global"` — unsupported; emits a warning and is skipped
171
183
  - `"repo"` — deploys to `{repo}/.vscode/mcp.json` under the `servers` key
172
184
  - `"workspace"` — deploys to `{workspace}/.vscode/mcp.json` under the `servers` key
185
+ - `"devcontainer"` — deploys to `{repo}/.devcontainer/devcontainer.json` under the `customizations.vscode.mcp.servers` key
173
186
 
174
187
  GitHub Copilot's `.vscode/mcp.json` uses `servers` (not `mcpServers`) as the top-level key and optionally accepts a `type` field (`"stdio"` | `"sse"` | `"http"`) in the server descriptor, which is passed through verbatim.
175
188
 
176
189
  MCP server registration is supported for all agents. Inception-engine automatically uses the correct adapter for each agent's configuration schema:
177
- - **JSON (Merge Patch)**: `claude-code` (`~/.claude.json` for `scope: "global"`; `{repo}/.claude/mcp.json` for `scope: "repo"`; `{workspace}/.claude/mcp.json` for `scope: "workspace"`), `gemini-cli` (`~/.gemini/settings.json`), `opencode` (`~/.config/opencode/opencode.json` using the custom `"mcp"` key), and `github-copilot` (`{repo}/.vscode/mcp.json` or `{workspace}/.vscode/mcp.json` using the `"servers"` key).
190
+ - **JSON (Merge Patch)**: `claude-code` (`~/.claude.json` for `scope: "global"`; `{repo}/.claude/mcp.json` for `scope: "repo"`; `{workspace}/.claude/mcp.json` for `scope: "workspace"`), `gemini-cli` (`~/.gemini/settings.json`), `antigravity` (`~/.gemini/antigravity/mcp_config.json` for `scope: "global"`), `opencode` (`~/.config/opencode/opencode.json` using the custom `"mcp"` key), and `github-copilot` (`{repo}/.vscode/mcp.json`, `{workspace}/.vscode/mcp.json`, or `{repo}/.devcontainer/devcontainer.json`).
178
191
  - **TOML (Patch)**: `codex` (`~/.codex/config.toml`).
179
- - **Markdown Frontmatter (Emit)**: `antigravity` (repo-local `.agents/rules/{name}.md` files).
192
+ - **Markdown Frontmatter (Emit)**: `antigravity` (`{repo}/.agents/rules/{name}.md` for `scope: "repo"`).
180
193
 
181
194
  For Markdown frontmatter targets, inception-engine now records patch-level provenance for the emitted frontmatter block. Deploy merges only the owned frontmatter keys, preserves unrelated frontmatter and Markdown body content, and `revert` removes only the engine-owned keys instead of deleting the whole file unless inception-engine originally created an otherwise-empty file.
182
195
 
@@ -248,6 +261,31 @@ Valid `approval_policy` values are `"auto"`, `"manual"`, `"suggest"`, and `"on-f
248
261
 
249
262
  Revert restores the previous config values using the undo patch recorded at deploy time.
250
263
 
264
+ Each **hooks** entry deploys lifecycle-binding configurations to an agent's execution hook surface:
265
+
266
+ - **name** - Unique identifier (same format as skill names)
267
+ - **agents** - Array of agent IDs to deploy this entry to
268
+ - **config** - Hook configuration payload; shape is validated per agent (see below)
269
+
270
+ Hook deployment is currently supported for `claude-code`. For `github-copilot`, it is marked as `planned`. Other agents emit a warning and are skipped.
271
+
272
+ For `claude-code`, the config is merged into `~/.claude/settings.json`. Only the `hooks` key is accepted:
273
+
274
+ ```json
275
+ {
276
+ "name": "pre-exec-check",
277
+ "agents": ["claude-code"],
278
+ "config": {
279
+ "hooks": {
280
+ "pre_exec": "scripts/check-env.sh",
281
+ "post_exec": "scripts/notify.sh"
282
+ }
283
+ }
284
+ }
285
+ ```
286
+
287
+ Revert restores the previous config values using the undo patch recorded at deploy time.
288
+
251
289
  Each **agentDefinitions** entry deploys an agent-definition file to the agent-definition directory of each targeted agent. `scope: "repo"` is the default, and some agents also support `scope: "global"`:
252
290
 
253
291
  - **name** - Unique identifier (same format as skill names); used as the definition filename
@@ -271,6 +309,8 @@ Global targets where supported:
271
309
 
272
310
  For GitHub Copilot, deploy also records migration from the legacy `{repo}/.github/agents/{name}.agent.md` path so older installs can be cleaned up safely.
273
311
 
312
+ For GitHub Copilot, the `tools` YAML frontmatter field in agent definition files declares which MCP tools and built-in Copilot tools the agent has access to (agent-level tool mapping). Inception-engine validates that `tools`, when present, is an array of strings. Deploy agent-level tool mappings through `agentDefinitions` by including the `tools` field in your source file's frontmatter - no separate manifest section is needed.
313
+
274
314
  Revert removes the deployed agent-definition file.
275
315
 
276
316
  ## Creating Skills
@@ -18,6 +18,7 @@ export const AGENT_REGISTRY = [
18
18
  mcpConfig: "documented",
19
19
  agentRules: "documented",
20
20
  permissions: "documented",
21
+ hooks: "documented",
21
22
  agentDefinitions: "documented",
22
23
  },
23
24
  mcpSupport: {
@@ -68,6 +69,14 @@ export const AGENT_REGISTRY = [
68
69
  windows: ["{home}", ".claude", "settings.json"],
69
70
  },
70
71
  },
72
+ hooksSupport: {
73
+ status: "supported",
74
+ schemaLabel: "JSON hooks config",
75
+ path: {
76
+ posix: ["{home}", ".claude", "settings.json"],
77
+ windows: ["{home}", ".claude", "settings.json"],
78
+ },
79
+ },
71
80
  agentDefinitionsRepoSupport: {
72
81
  status: "supported",
73
82
  schemaLabel: "repo-local agent definition Markdown file",
@@ -103,6 +112,7 @@ export const AGENT_REGISTRY = [
103
112
  detectBinary: "documented",
104
113
  agentRules: "documented",
105
114
  permissions: "documented",
115
+ hooks: "provisional",
106
116
  },
107
117
  mcpSupport: {
108
118
  status: "supported",
@@ -136,6 +146,11 @@ export const AGENT_REGISTRY = [
136
146
  windows: ["{home}", ".codex", "config.toml"],
137
147
  },
138
148
  },
149
+ hooksSupport: {
150
+ status: "unsupported",
151
+ schemaLabel: "global hooks surface",
152
+ reason: "OpenAI Codex does not expose a documented lifecycle-hook config surface in config.toml",
153
+ },
139
154
  agentDefinitionsRepoSupport: {
140
155
  status: "unsupported",
141
156
  schemaLabel: "dedicated agent definition directory",
@@ -168,6 +183,7 @@ export const AGENT_REGISTRY = [
168
183
  detectBinary: "documented",
169
184
  mcpConfig: "documented",
170
185
  agentRules: "documented",
186
+ hooks: "provisional",
171
187
  agentDefinitions: "documented",
172
188
  },
173
189
  mcpSupport: {
@@ -202,6 +218,12 @@ export const AGENT_REGISTRY = [
202
218
  schemaLabel: "global permissions surface",
203
219
  reason: "Gemini CLI does not expose a documented global per-user permission or approval config surface",
204
220
  },
221
+ hooksSupport: {
222
+ status: "planned",
223
+ schemaLabel: "global hooks surface",
224
+ plannedSurface: "settings.json hooks field",
225
+ reason: "Gemini CLI execution hooks are emerging and not yet settled",
226
+ },
205
227
  agentDefinitionsSupport: {
206
228
  status: "supported",
207
229
  schemaLabel: "global agent definition Markdown file",
@@ -263,6 +285,14 @@ export const AGENT_REGISTRY = [
263
285
  agentDefinitions: "documented",
264
286
  },
265
287
  mcpSupport: {
288
+ status: "supported",
289
+ schemaLabel: "global mcp_config.json",
290
+ path: {
291
+ posix: ["{home}", ".gemini", "antigravity", "mcp_config.json"],
292
+ windows: ["{home}", ".gemini", "antigravity", "mcp_config.json"],
293
+ },
294
+ },
295
+ mcpRepoSupport: {
266
296
  status: "supported",
267
297
  schemaLabel: "repo-local frontmatter-emit MCP rules",
268
298
  path: {
@@ -296,6 +326,11 @@ export const AGENT_REGISTRY = [
296
326
  schemaLabel: "global permissions surface",
297
327
  reason: "Antigravity does not expose a documented global per-user permission or approval config surface",
298
328
  },
329
+ hooksSupport: {
330
+ status: "unsupported",
331
+ schemaLabel: "global hooks surface",
332
+ reason: "Antigravity does not expose a documented global per-user hook config surface",
333
+ },
299
334
  agentDefinitionsRepoSupport: {
300
335
  status: "supported",
301
336
  schemaLabel: "repo-local agent definition Markdown file",
@@ -330,6 +365,7 @@ export const AGENT_REGISTRY = [
330
365
  agentRules: "documented",
331
366
  agentDefinitions: "documented",
332
367
  permissions: "documented",
368
+ hooks: "provisional",
333
369
  },
334
370
  mcpSupport: {
335
371
  status: "supported",
@@ -364,6 +400,11 @@ export const AGENT_REGISTRY = [
364
400
  windows: ["{appdata}", "opencode", "opencode.json"],
365
401
  },
366
402
  },
403
+ hooksSupport: {
404
+ status: "unsupported",
405
+ schemaLabel: "global hooks surface",
406
+ reason: "OpenCode does not expose a documented lifecycle-hook surface",
407
+ },
367
408
  agentDefinitionsSupport: {
368
409
  status: "supported",
369
410
  schemaLabel: "global agent definition Markdown file",
@@ -404,6 +445,7 @@ export const AGENT_REGISTRY = [
404
445
  detectBinary: "documented",
405
446
  agentDefinitions: "documented",
406
447
  mcpConfig: "documented",
448
+ hooks: "provisional",
407
449
  },
408
450
  mcpSupport: {
409
451
  status: "unsupported",
@@ -472,11 +514,26 @@ export const AGENT_REGISTRY = [
472
514
  windows: ["{workspace}", "CLAUDE.md"],
473
515
  },
474
516
  },
517
+ mcpDevcontainerSupport: {
518
+ status: "supported",
519
+ schemaLabel: "devcontainer.json MCP surface",
520
+ mcpPatchKey: "customizations.vscode.mcp.servers",
521
+ path: {
522
+ posix: ["{repo}", ".devcontainer", "devcontainer.json"],
523
+ windows: ["{repo}", ".devcontainer", "devcontainer.json"],
524
+ },
525
+ },
475
526
  permissionsSupport: {
476
527
  status: "unsupported",
477
528
  schemaLabel: "global permissions surface",
478
529
  reason: "GitHub Copilot permissions are managed via organization policy, not a deployable per-user config surface",
479
530
  },
531
+ hooksSupport: {
532
+ status: "planned",
533
+ schemaLabel: "binary execution hooks",
534
+ plannedSurface: "agent frontmatter pre_exec/post_exec hooks",
535
+ reason: "GitHub Copilot binary hook support is planned and requires further validation of the stdin/stdout contract",
536
+ },
480
537
  agentDefinitionsRepoSupport: {
481
538
  status: "supported",
482
539
  schemaLabel: "repo-local agent definition Markdown file",
@@ -485,7 +542,20 @@ export const AGENT_REGISTRY = [
485
542
  windows: ["{repo}", ".github", "copilot", "agents", "{name}.md"],
486
543
  },
487
544
  },
545
+ // GitHub Copilot agent definitions declare MCP tool access via a top-level
546
+ // 'tools' array in YAML frontmatter. This surface is deployed through
547
+ // agentDefinitions - no separate deploy action is emitted.
548
+ mcpAgentFrontmatterSupport: {
549
+ status: "supported",
550
+ schemaLabel: "agent definition frontmatter tools field",
551
+ surfaceKind: { kind: "native" },
552
+ path: {
553
+ posix: ["{repo}", ".github", "copilot", "agents", "{name}.md"],
554
+ windows: ["{repo}", ".github", "copilot", "agents", "{name}.md"],
555
+ },
556
+ },
488
557
  policyNote: "Organization policies may override locally deployed configuration. Verify with your GitHub org admin if deployed skills or rules are not active.",
558
+ unsupportedSurfaces: [],
489
559
  instructionFrontmatterRequired: true,
490
560
  enterprisePolicyDetection: true,
491
561
  },
@@ -0,0 +1,8 @@
1
+ import type { HookEntry } from "../../schemas/manifest.ts";
2
+ import type { AgentId, ConfigPatchDeployAction, ConfigPatchRevertAction, PlanWarning, TomlPatchDeployAction, TomlPatchRevertAction } from "../../types.ts";
3
+ export interface HooksAdapterResult {
4
+ actions: Array<ConfigPatchDeployAction | TomlPatchDeployAction>;
5
+ warnings: PlanWarning[];
6
+ }
7
+ export declare function compileHookActions(entry: HookEntry, detectedAgents: AgentId[], home: string): HooksAdapterResult;
8
+ export declare function compileHookReverts(entry: HookEntry, agentFilter: AgentId[] | null, home: string): Array<ConfigPatchRevertAction | TomlPatchRevertAction>;
@@ -0,0 +1,86 @@
1
+ import path from "node:path";
2
+ import { planCapabilityForDeploy, resolveCapabilitySurface, } from "../capabilities.js";
3
+ import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
4
+ import { validateHookConfigShape } from "../validation.js";
5
+ function isTomlTarget(targetPath) {
6
+ return path.extname(targetPath).toLowerCase() === ".toml";
7
+ }
8
+ export function compileHookActions(entry, detectedAgents, home) {
9
+ const actions = [];
10
+ const warnings = [];
11
+ const platform = getPlatformKey();
12
+ for (const agentId of entry.agents) {
13
+ if (!detectedAgents.includes(agentId))
14
+ continue;
15
+ const plan = planCapabilityForDeploy({
16
+ agentId,
17
+ capability: "hooks",
18
+ entryName: entry.name,
19
+ targetAgentIds: entry.agents,
20
+ });
21
+ if (plan.outcome === "warn") {
22
+ warnings.push(plan.warning);
23
+ continue;
24
+ }
25
+ if (plan.outcome === "native" || plan.outcome === "redundant")
26
+ continue;
27
+ const support = resolveCapabilitySurface(agentId, "hooks").support;
28
+ if (!support)
29
+ continue;
30
+ validateHookConfigShape(entry.config, entry.name, agentId);
31
+ const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home);
32
+ const resolvedTarget = path.resolve(rawTarget);
33
+ const confidence = plan.confidence ?? "provisional";
34
+ if (isTomlTarget(resolvedTarget)) {
35
+ actions.push({
36
+ kind: "toml-patch",
37
+ skill: entry.name,
38
+ agent: agentId,
39
+ target: resolvedTarget,
40
+ config: entry.config,
41
+ confidence,
42
+ });
43
+ }
44
+ else {
45
+ actions.push({
46
+ kind: "config-patch",
47
+ skill: entry.name,
48
+ agent: agentId,
49
+ target: resolvedTarget,
50
+ patch: entry.config,
51
+ confidence,
52
+ });
53
+ }
54
+ }
55
+ return { actions, warnings };
56
+ }
57
+ export function compileHookReverts(entry, agentFilter, home) {
58
+ const actions = [];
59
+ const platform = getPlatformKey();
60
+ for (const agentId of entry.agents) {
61
+ if (agentFilter && !agentFilter.includes(agentId))
62
+ continue;
63
+ const support = resolveCapabilitySurface(agentId, "hooks").support;
64
+ if (!support)
65
+ continue;
66
+ const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home);
67
+ const target = path.resolve(rawTarget);
68
+ if (isTomlTarget(target)) {
69
+ actions.push({
70
+ kind: "toml-patch",
71
+ skill: entry.name,
72
+ agent: agentId,
73
+ target,
74
+ });
75
+ }
76
+ else {
77
+ actions.push({
78
+ kind: "config-patch",
79
+ skill: entry.name,
80
+ agent: agentId,
81
+ target,
82
+ });
83
+ }
84
+ }
85
+ return actions;
86
+ }
@@ -1,13 +1,14 @@
1
- import type { AgentDefinitionEntry, AgentRuleEntry, McpServerEntry, PermissionsEntry } from "../../schemas/manifest.ts";
1
+ import type { AgentDefinitionEntry, AgentRuleEntry, HookEntry, McpServerEntry, PermissionsEntry } from "../../schemas/manifest.ts";
2
2
  import type { AgentId, ConfigPatchDeployAction, FileWriteDeployAction, FrontmatterEmitDeployAction, PlanWarning, TomlPatchDeployAction } from "../../types.ts";
3
3
  import { compileAgentDefinitionReverts } from "./agent-definitions.ts";
4
+ import { compileHookReverts } from "./hooks.ts";
4
5
  import { compileMcpServerReverts } from "./mcp.ts";
5
6
  import { compilePermissionsReverts } from "./permissions.ts";
6
7
  import { compileAgentRuleReverts } from "./rules.ts";
7
- export { compileAgentDefinitionReverts, compileAgentRuleReverts, compileMcpServerReverts, compilePermissionsReverts, };
8
+ export { compileAgentDefinitionReverts, compileAgentRuleReverts, compileHookReverts, compileMcpServerReverts, compilePermissionsReverts, };
8
9
  export type AdapterAction = ConfigPatchDeployAction | FileWriteDeployAction | TomlPatchDeployAction | FrontmatterEmitDeployAction;
9
10
  export interface AdapterResult {
10
11
  actions: AdapterAction[];
11
12
  warnings: PlanWarning[];
12
13
  }
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>;
14
+ 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, hooks?: HookEntry[]): Promise<AdapterResult>;
@@ -1,30 +1,27 @@
1
1
  import { compileAgentDefinitionActions, compileAgentDefinitionReverts, } from "./agent-definitions.js";
2
+ import { compileHookActions, compileHookReverts } from "./hooks.js";
2
3
  import { compileMcpServerActions, compileMcpServerReverts } from "./mcp.js";
3
4
  import { compilePermissionsActions, compilePermissionsReverts, } from "./permissions.js";
4
5
  import { compileAgentRuleActions, compileAgentRuleReverts } from "./rules.js";
5
- export { compileAgentDefinitionReverts, compileAgentRuleReverts, compileMcpServerReverts, compilePermissionsReverts, };
6
- export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, agentDefinitions, workspace) {
6
+ export { compileAgentDefinitionReverts, compileAgentRuleReverts, compileHookReverts, compileMcpServerReverts, compilePermissionsReverts, };
7
+ async function compileAll(entries, fn) {
8
+ const results = await Promise.all(entries.map(fn));
9
+ return {
10
+ actions: results.flatMap((r) => r.actions),
11
+ warnings: results.flatMap((r) => r.warnings),
12
+ };
13
+ }
14
+ export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, agentDefinitions, workspace, hooks = []) {
7
15
  const actions = [];
8
16
  const warnings = [];
9
- for (const entry of mcpServers) {
10
- const r = compileMcpServerActions(entry, detectedAgents, home, repo, workspace);
11
- actions.push(...r.actions);
12
- warnings.push(...r.warnings);
13
- }
14
- for (const entry of agentRules) {
15
- const r = await compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace);
16
- actions.push(...r.actions);
17
- warnings.push(...r.warnings);
18
- }
19
- for (const entry of permissions) {
20
- const r = compilePermissionsActions(entry, detectedAgents, home);
21
- actions.push(...r.actions);
22
- warnings.push(...r.warnings);
23
- }
24
- for (const entry of agentDefinitions ?? []) {
25
- const r = await compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace);
26
- actions.push(...r.actions);
27
- warnings.push(...r.warnings);
28
- }
17
+ const [mcp, rules, perms, defs, hookResults] = await Promise.all([
18
+ compileAll(mcpServers, (entry) => compileMcpServerActions(entry, detectedAgents, home, repo, workspace)),
19
+ compileAll(agentRules, (entry) => compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace)),
20
+ compileAll(permissions, (entry) => compilePermissionsActions(entry, detectedAgents, home)),
21
+ compileAll(agentDefinitions ?? [], (entry) => compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace)),
22
+ compileAll(hooks, (entry) => compileHookActions(entry, detectedAgents, home)),
23
+ ]);
24
+ actions.push(...mcp.actions, ...rules.actions, ...perms.actions, ...defs.actions, ...hookResults.actions);
25
+ warnings.push(...mcp.warnings, ...rules.warnings, ...perms.warnings, ...defs.warnings, ...hookResults.warnings);
29
26
  return { actions, warnings };
30
27
  }
@@ -16,6 +16,20 @@ function isMarkdownTarget(targetPath) {
16
16
  const ext = path.extname(targetPath).toLowerCase();
17
17
  return ext === ".md" || ext === ".markdown";
18
18
  }
19
+ /**
20
+ * Builds a nested object from a dot-separated key path.
21
+ * e.g. ("a.b", {c: 1}) -> {a: {b: {c: 1}}}
22
+ */
23
+ function buildNestedPatch(keyPath, leafValue) {
24
+ const parts = keyPath.split(".");
25
+ let current = {
26
+ [parts[parts.length - 1]]: leafValue,
27
+ };
28
+ for (let i = parts.length - 2; i >= 0; i--) {
29
+ current = { [parts[i]]: current };
30
+ }
31
+ return current;
32
+ }
19
33
  function buildMcpDeployAction(entry, agentId, resolvedTarget, confidence, mcpPatchKey) {
20
34
  if (isMarkdownTarget(resolvedTarget)) {
21
35
  return {
@@ -42,7 +56,7 @@ function buildMcpDeployAction(entry, agentId, resolvedTarget, confidence, mcpPat
42
56
  skill: entry.name,
43
57
  agent: agentId,
44
58
  target: resolvedTarget,
45
- patch: { [mcpPatchKey]: { [entry.name]: entry.config } },
59
+ patch: buildNestedPatch(mcpPatchKey, { [entry.name]: entry.config }),
46
60
  confidence,
47
61
  };
48
62
  }
@@ -1,5 +1,5 @@
1
1
  import type { AgentId, AgentSurfaceSupport, CapabilityKind, Confidence, PlanWarning, SupportedAgentSurface } from "../types.ts";
2
- type CapabilityScope = "global" | "repo" | "workspace";
2
+ type CapabilityScope = "global" | "repo" | "workspace" | "devcontainer";
3
3
  export interface ResolvedCapabilitySurface {
4
4
  agentId: AgentId;
5
5
  capability: CapabilityKind;
@@ -19,6 +19,11 @@ function resolveMcpSupport(agentId, scope) {
19
19
  if (scope === "workspace") {
20
20
  return (agent?.mcpWorkspaceSupport ?? agent?.mcpRepoSupport ?? agent?.mcpSupport);
21
21
  }
22
+ if (scope === "devcontainer") {
23
+ return (agent?.mcpDevcontainerSupport ??
24
+ agent?.mcpRepoSupport ??
25
+ agent?.mcpSupport);
26
+ }
22
27
  return agent?.mcpSupport;
23
28
  }
24
29
  function resolveAgentDefinitionsSupport(agentId, scope) {
@@ -45,6 +50,8 @@ function capabilityLabel(capability) {
45
50
  return "permissions";
46
51
  case "agentDefinitions":
47
52
  return "agentDefinitions";
53
+ case "hooks":
54
+ return "hooks";
48
55
  }
49
56
  }
50
57
  function capabilitySurfaceLabel(capability) {
@@ -59,6 +66,8 @@ function capabilitySurfaceLabel(capability) {
59
66
  return "permissions";
60
67
  case "agentDefinitions":
61
68
  return "agent-definitions";
69
+ case "hooks":
70
+ return "hooks";
62
71
  }
63
72
  }
64
73
  function resolveSkillsCapability(agentId) {
@@ -110,9 +119,15 @@ function getSurfaceRecordAndConfidence(agentId, capability, scope) {
110
119
  confidence: agent.provenance.permissions,
111
120
  };
112
121
  }
122
+ if (capability === "agentDefinitions") {
123
+ return {
124
+ supportRecord: resolveAgentDefinitionsSupport(agentId, scope),
125
+ confidence: agent.provenance.agentDefinitions,
126
+ };
127
+ }
113
128
  return {
114
- supportRecord: resolveAgentDefinitionsSupport(agentId, scope),
115
- confidence: agent.provenance.agentDefinitions,
129
+ supportRecord: agent.hooksSupport,
130
+ confidence: agent.provenance.hooks,
116
131
  };
117
132
  }
118
133
  function resolveUnsupportedSurface(agentId, capability, supportRecord, confidence) {