@kuznai/inception-engine 0.21.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 +42 -4
- package/dist/src/config/agents.js +55 -11
- package/dist/src/core/adapters/hooks.d.ts +8 -0
- package/dist/src/core/adapters/hooks.js +86 -0
- package/dist/src/core/adapters/index.d.ts +4 -3
- package/dist/src/core/adapters/index.js +7 -5
- package/dist/src/core/adapters/mcp.js +15 -1
- package/dist/src/core/capabilities.d.ts +1 -1
- package/dist/src/core/capabilities.js +17 -2
- package/dist/src/core/deploy.js +1 -1
- package/dist/src/core/init.js +18 -10
- package/dist/src/core/validation.d.ts +1 -0
- package/dist/src/core/validation.js +4 -0
- package/dist/src/schemas/manifest.d.ts +27 -0
- package/dist/src/schemas/manifest.js +11 -1
- package/dist/src/types.d.ts +6 -2
- package/dist/test/unit/adapters.test.js +16 -2
- package/dist/test/unit/deploy.test.js +5 -3
- package/dist/test/unit/preflight.test.js +5 -7
- package/dist/test/unit/revert.test.js +8 -0
- package/package.json +1 -1
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 `"
|
|
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
|
|
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
|
|
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
|
|
@@ -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",
|
|
@@ -473,16 +515,25 @@ export const AGENT_REGISTRY = [
|
|
|
473
515
|
},
|
|
474
516
|
},
|
|
475
517
|
mcpDevcontainerSupport: {
|
|
476
|
-
status: "
|
|
518
|
+
status: "supported",
|
|
477
519
|
schemaLabel: "devcontainer.json MCP surface",
|
|
478
|
-
|
|
479
|
-
|
|
520
|
+
mcpPatchKey: "customizations.vscode.mcp.servers",
|
|
521
|
+
path: {
|
|
522
|
+
posix: ["{repo}", ".devcontainer", "devcontainer.json"],
|
|
523
|
+
windows: ["{repo}", ".devcontainer", "devcontainer.json"],
|
|
524
|
+
},
|
|
480
525
|
},
|
|
481
526
|
permissionsSupport: {
|
|
482
527
|
status: "unsupported",
|
|
483
528
|
schemaLabel: "global permissions surface",
|
|
484
529
|
reason: "GitHub Copilot permissions are managed via organization policy, not a deployable per-user config surface",
|
|
485
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
|
+
},
|
|
486
537
|
agentDefinitionsRepoSupport: {
|
|
487
538
|
status: "supported",
|
|
488
539
|
schemaLabel: "repo-local agent definition Markdown file",
|
|
@@ -504,14 +555,7 @@ export const AGENT_REGISTRY = [
|
|
|
504
555
|
},
|
|
505
556
|
},
|
|
506
557
|
policyNote: "Organization policies may override locally deployed configuration. Verify with your GitHub org admin if deployed skills or rules are not active.",
|
|
507
|
-
unsupportedSurfaces: [
|
|
508
|
-
{
|
|
509
|
-
status: "planned",
|
|
510
|
-
schemaLabel: "devcontainer.json MCP surface",
|
|
511
|
-
plannedSurface: "devcontainer.json customizations.vscode.mcp.servers",
|
|
512
|
-
reason: 'devcontainer.json MCP support is planned — use scope: "repo" or scope: "workspace" to target .vscode/mcp.json in the meantime',
|
|
513
|
-
},
|
|
514
|
-
],
|
|
558
|
+
unsupportedSurfaces: [],
|
|
515
559
|
instructionFrontmatterRequired: true,
|
|
516
560
|
enterprisePolicyDetection: true,
|
|
517
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,8 +1,9 @@
|
|
|
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 { compileAgentDefinitionReverts, compileAgentRuleReverts, compileHookReverts, compileMcpServerReverts, compilePermissionsReverts, };
|
|
6
7
|
async function compileAll(entries, fn) {
|
|
7
8
|
const results = await Promise.all(entries.map(fn));
|
|
8
9
|
return {
|
|
@@ -10,16 +11,17 @@ async function compileAll(entries, fn) {
|
|
|
10
11
|
warnings: results.flatMap((r) => r.warnings),
|
|
11
12
|
};
|
|
12
13
|
}
|
|
13
|
-
export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, agentDefinitions, workspace) {
|
|
14
|
+
export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, agentDefinitions, workspace, hooks = []) {
|
|
14
15
|
const actions = [];
|
|
15
16
|
const warnings = [];
|
|
16
|
-
const [mcp, rules, perms, defs] = await Promise.all([
|
|
17
|
+
const [mcp, rules, perms, defs, hookResults] = await Promise.all([
|
|
17
18
|
compileAll(mcpServers, (entry) => compileMcpServerActions(entry, detectedAgents, home, repo, workspace)),
|
|
18
19
|
compileAll(agentRules, (entry) => compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace)),
|
|
19
20
|
compileAll(permissions, (entry) => compilePermissionsActions(entry, detectedAgents, home)),
|
|
20
21
|
compileAll(agentDefinitions ?? [], (entry) => compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace)),
|
|
22
|
+
compileAll(hooks, (entry) => compileHookActions(entry, detectedAgents, home)),
|
|
21
23
|
]);
|
|
22
|
-
actions.push(...mcp.actions, ...rules.actions, ...perms.actions, ...defs.actions);
|
|
23
|
-
warnings.push(...mcp.warnings, ...rules.warnings, ...perms.warnings, ...defs.warnings);
|
|
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);
|
|
24
26
|
return { actions, warnings };
|
|
25
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:
|
|
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:
|
|
115
|
-
confidence: agent.provenance.
|
|
129
|
+
supportRecord: agent.hooksSupport,
|
|
130
|
+
confidence: agent.provenance.hooks,
|
|
116
131
|
};
|
|
117
132
|
}
|
|
118
133
|
function resolveUnsupportedSurface(agentId, capability, supportRecord, confidence) {
|
package/dist/src/core/deploy.js
CHANGED
|
@@ -280,7 +280,7 @@ export async function planDeploy(manifest, sourceDir, detectedAgents, home, repo
|
|
|
280
280
|
...(await planFileWriteActions(manifest, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repoDir, workspace)),
|
|
281
281
|
...planConfigPatchActions(manifest, detectedAgents, home, repoDir, workspace),
|
|
282
282
|
];
|
|
283
|
-
const adapterResult = await compileAdapterActions(manifest.mcpServers, manifest.agentRules, manifest.permissions ?? [], sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repoDir, manifest.agentDefinitions ?? [], workspace);
|
|
283
|
+
const adapterResult = await compileAdapterActions(manifest.mcpServers, manifest.agentRules, manifest.permissions ?? [], sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repoDir, manifest.agentDefinitions ?? [], workspace, manifest.hooks ?? []);
|
|
284
284
|
actions.push(...adapterResult.actions);
|
|
285
285
|
const warnings = [
|
|
286
286
|
...skillPlan.warnings,
|
package/dist/src/core/init.js
CHANGED
|
@@ -269,16 +269,24 @@ function agentsForDefinitionSubdir(subdir) {
|
|
|
269
269
|
*/
|
|
270
270
|
async function isSkippedMcpFile(subdir, absPath, relPath) {
|
|
271
271
|
const hasMcpSurfaceHere = AGENT_REGISTRY.some((agent) => {
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
272
|
+
const supports = [
|
|
273
|
+
agent.mcpSupport,
|
|
274
|
+
agent.mcpRepoSupport,
|
|
275
|
+
agent.mcpWorkspaceSupport,
|
|
276
|
+
];
|
|
277
|
+
for (const support of supports) {
|
|
278
|
+
if (support?.status !== "supported")
|
|
279
|
+
continue;
|
|
280
|
+
const tmpl = support.path.posix;
|
|
281
|
+
const repoIdx = tmpl.indexOf("{repo}");
|
|
282
|
+
const nameIdx = tmpl.findIndex((s) => s.includes("{name}"));
|
|
283
|
+
if (repoIdx === -1 || nameIdx === -1 || nameIdx <= repoIdx)
|
|
284
|
+
continue;
|
|
285
|
+
const prefix = tmpl.slice(repoIdx + 1, nameIdx).join("/");
|
|
286
|
+
if (prefix === subdir)
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
return false;
|
|
282
290
|
});
|
|
283
291
|
if (!hasMcpSurfaceHere)
|
|
284
292
|
return false;
|
|
@@ -4,6 +4,7 @@ export declare function validateSourcePath(source: string, skillPath: string, re
|
|
|
4
4
|
export declare function validateSourceFile(sourcePath: string, manifestPath: string): Promise<void>;
|
|
5
5
|
export declare function validateMcpServerConfigShape(config: Record<string, unknown>, entryName: string, agentId: string): void;
|
|
6
6
|
export declare function validatePermissionsConfigShape(config: Record<string, unknown>, entryName: string, agentId: string): void;
|
|
7
|
+
export declare function validateHookConfigShape(_config: Record<string, unknown>, _entryName: string, _agentId: string): void;
|
|
7
8
|
export declare function validateAgentRuleMarkdownPath(manifestPath: string, agentId: string): void;
|
|
8
9
|
export declare function validateSkillDefinitionFile(sourcePath: string, manifestPath: string): Promise<{
|
|
9
10
|
attributes: Record<string, unknown>;
|
|
@@ -163,6 +163,10 @@ export function validatePermissionsConfigShape(config, entryName, agentId) {
|
|
|
163
163
|
validateOpenCodePermissions(config, entryName);
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
+
export function validateHookConfigShape(_config, _entryName, _agentId) {
|
|
167
|
+
// Placeholder for agent-specific hook validation logic.
|
|
168
|
+
// Currently allows any record as a hook payload.
|
|
169
|
+
}
|
|
166
170
|
export function validateAgentRuleMarkdownPath(manifestPath, agentId) {
|
|
167
171
|
const extension = path.extname(manifestPath).toLowerCase();
|
|
168
172
|
if (extension !== ".md" && extension !== ".markdown") {
|
|
@@ -63,6 +63,7 @@ export declare const McpServerEntrySchema: z.ZodObject<{
|
|
|
63
63
|
global: "global";
|
|
64
64
|
repo: "repo";
|
|
65
65
|
workspace: "workspace";
|
|
66
|
+
devcontainer: "devcontainer";
|
|
66
67
|
}>>;
|
|
67
68
|
}, z.core.$strip>;
|
|
68
69
|
export declare const AgentRuleEntrySchema: z.ZodObject<{
|
|
@@ -111,6 +112,18 @@ export declare const AgentDefinitionEntrySchema: z.ZodObject<{
|
|
|
111
112
|
workspace: "workspace";
|
|
112
113
|
}>>;
|
|
113
114
|
}, z.core.$strip>;
|
|
115
|
+
export declare const HookEntrySchema: z.ZodObject<{
|
|
116
|
+
name: z.ZodString;
|
|
117
|
+
agents: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
118
|
+
"claude-code": "claude-code";
|
|
119
|
+
codex: "codex";
|
|
120
|
+
"gemini-cli": "gemini-cli";
|
|
121
|
+
antigravity: "antigravity";
|
|
122
|
+
opencode: "opencode";
|
|
123
|
+
"github-copilot": "github-copilot";
|
|
124
|
+
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
125
|
+
config: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
126
|
+
}, z.core.$strip>;
|
|
114
127
|
export declare const ManifestSchema: z.ZodObject<{
|
|
115
128
|
skills: z.ZodArray<z.ZodObject<{
|
|
116
129
|
name: z.ZodString;
|
|
@@ -165,6 +178,7 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
165
178
|
global: "global";
|
|
166
179
|
repo: "repo";
|
|
167
180
|
workspace: "workspace";
|
|
181
|
+
devcontainer: "devcontainer";
|
|
168
182
|
}>>;
|
|
169
183
|
}, z.core.$strip>>>;
|
|
170
184
|
agentRules: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
@@ -213,6 +227,18 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
213
227
|
workspace: "workspace";
|
|
214
228
|
}>>;
|
|
215
229
|
}, z.core.$strip>>>;
|
|
230
|
+
hooks: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
231
|
+
name: z.ZodString;
|
|
232
|
+
agents: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
233
|
+
"claude-code": "claude-code";
|
|
234
|
+
codex: "codex";
|
|
235
|
+
"gemini-cli": "gemini-cli";
|
|
236
|
+
antigravity: "antigravity";
|
|
237
|
+
opencode: "opencode";
|
|
238
|
+
"github-copilot": "github-copilot";
|
|
239
|
+
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
240
|
+
config: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
241
|
+
}, z.core.$strip>>>;
|
|
216
242
|
}, z.core.$strip>;
|
|
217
243
|
export type SkillEntry = z.infer<typeof SkillEntrySchema>;
|
|
218
244
|
export type FileEntry = z.infer<typeof FileEntrySchema>;
|
|
@@ -221,6 +247,7 @@ export type McpServerEntry = z.infer<typeof McpServerEntrySchema>;
|
|
|
221
247
|
export type AgentRuleEntry = z.infer<typeof AgentRuleEntrySchema>;
|
|
222
248
|
export type PermissionsEntry = z.infer<typeof PermissionsEntrySchema>;
|
|
223
249
|
export type AgentDefinitionEntry = z.infer<typeof AgentDefinitionEntrySchema>;
|
|
250
|
+
export type HookEntry = z.infer<typeof HookEntrySchema>;
|
|
224
251
|
export type Manifest = z.infer<typeof ManifestSchema>;
|
|
225
252
|
export declare const AgentListSchema: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string[], string>>, z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
226
253
|
"claude-code": "claude-code";
|
|
@@ -88,7 +88,9 @@ export const McpServerEntrySchema = z.object({
|
|
|
88
88
|
// config files for agents that support them (e.g. GitHub Copilot's
|
|
89
89
|
// .vscode/mcp.json). For agents without scope-specific surfaces the
|
|
90
90
|
// adapter falls back to the global surface or emits a warning.
|
|
91
|
-
scope: z
|
|
91
|
+
scope: z
|
|
92
|
+
.enum(["global", "repo", "workspace", "devcontainer"])
|
|
93
|
+
.default("global"),
|
|
92
94
|
});
|
|
93
95
|
export const AgentRuleEntrySchema = z.object({
|
|
94
96
|
name: nameField,
|
|
@@ -124,6 +126,13 @@ export const AgentDefinitionEntrySchema = z.object({
|
|
|
124
126
|
// within the deployed repository (default).
|
|
125
127
|
scope: z.enum(["global", "repo", "workspace"]).default("repo"),
|
|
126
128
|
});
|
|
129
|
+
export const HookEntrySchema = z.object({
|
|
130
|
+
name: nameField,
|
|
131
|
+
agents: agentsField,
|
|
132
|
+
// Raw hook config payload validated per agent by the hooks adapter.
|
|
133
|
+
// Supports lifecycle-binding hooks (e.g. pre-exec, post-exec) for various agents.
|
|
134
|
+
config: z.record(z.string(), z.unknown()),
|
|
135
|
+
});
|
|
127
136
|
export const ManifestSchema = z.object({
|
|
128
137
|
skills: z.array(SkillEntrySchema).superRefine((skills, ctx) => {
|
|
129
138
|
const seen = new Set();
|
|
@@ -144,6 +153,7 @@ export const ManifestSchema = z.object({
|
|
|
144
153
|
agentRules: z.array(AgentRuleEntrySchema).default([]),
|
|
145
154
|
permissions: z.array(PermissionsEntrySchema).default([]),
|
|
146
155
|
agentDefinitions: z.array(AgentDefinitionEntrySchema).default([]),
|
|
156
|
+
hooks: z.array(HookEntrySchema).optional(),
|
|
147
157
|
});
|
|
148
158
|
// Parses the --agents CLI flag: comma-separated agent IDs → AgentId[]
|
|
149
159
|
export const AgentListSchema = z
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { AgentId } from "./schemas/manifest.ts";
|
|
2
|
-
export type { AgentDefinitionEntry, AgentId, ConfigEntry, FileEntry, Manifest, SkillEntry, } from "./schemas/manifest.ts";
|
|
2
|
+
export type { AgentDefinitionEntry, AgentId, ConfigEntry, FileEntry, HookEntry, Manifest, SkillEntry, } from "./schemas/manifest.ts";
|
|
3
3
|
export interface AgentPaths {
|
|
4
4
|
posix: string[];
|
|
5
5
|
windows: string[];
|
|
6
6
|
}
|
|
7
7
|
export type Confidence = "documented" | "implementation-only" | "provisional";
|
|
8
|
-
export type CapabilityKind = "skills" | "mcpServers" | "agentRules" | "permissions" | "agentDefinitions";
|
|
8
|
+
export type CapabilityKind = "skills" | "mcpServers" | "agentRules" | "permissions" | "hooks" | "agentDefinitions";
|
|
9
9
|
export interface SupportedAgentSurface {
|
|
10
10
|
status: "supported";
|
|
11
11
|
/**
|
|
@@ -67,6 +67,7 @@ export interface AgentProvenance {
|
|
|
67
67
|
mcpConfig?: Confidence;
|
|
68
68
|
agentRules?: Confidence;
|
|
69
69
|
permissions?: Confidence;
|
|
70
|
+
hooks?: Confidence;
|
|
70
71
|
agentDefinitions?: Confidence;
|
|
71
72
|
}
|
|
72
73
|
export interface AgentConfig {
|
|
@@ -97,6 +98,9 @@ export interface AgentConfig {
|
|
|
97
98
|
agentRulesRepoSupport?: AgentSurfaceSupport;
|
|
98
99
|
agentRulesWorkspaceSupport?: AgentSurfaceSupport;
|
|
99
100
|
permissionsSupport?: AgentSurfaceSupport;
|
|
101
|
+
hooksSupport?: AgentSurfaceSupport;
|
|
102
|
+
hooksRepoSupport?: AgentSurfaceSupport;
|
|
103
|
+
hooksWorkspaceSupport?: AgentSurfaceSupport;
|
|
100
104
|
agentDefinitionsSupport?: AgentSurfaceSupport;
|
|
101
105
|
agentDefinitionsRepoSupport?: AgentSurfaceSupport;
|
|
102
106
|
agentDefinitionsWorkspaceSupport?: AgentSurfaceSupport;
|
|
@@ -137,13 +137,13 @@ describe("compileMcpServerActions", () => {
|
|
|
137
137
|
servers: { "my-mcp": { command: "s" } },
|
|
138
138
|
});
|
|
139
139
|
});
|
|
140
|
-
it("returns a frontmatter-emit action for antigravity MCP", () => {
|
|
140
|
+
it("returns a frontmatter-emit action for antigravity MCP with scope: repo", () => {
|
|
141
141
|
const home = "/home/test";
|
|
142
142
|
const { actions, warnings } = compileMcpServerActions({
|
|
143
143
|
name: "my-mcp",
|
|
144
144
|
agents: ["antigravity"],
|
|
145
145
|
config: { command: "s" },
|
|
146
|
-
scope: "
|
|
146
|
+
scope: "repo",
|
|
147
147
|
}, ["antigravity"], home, "/repo/test");
|
|
148
148
|
assert.equal(actions.length, 1);
|
|
149
149
|
assert.equal(warnings.length, 0);
|
|
@@ -151,6 +151,20 @@ describe("compileMcpServerActions", () => {
|
|
|
151
151
|
assert.equal(actions[0]?.agent, "antigravity");
|
|
152
152
|
assertPathEndsWith(actions[0]?.target ?? "", ".agents/rules/my-mcp.md", `expected target to end with .agents/rules/my-mcp.md, got ${actions[0]?.target}`);
|
|
153
153
|
});
|
|
154
|
+
it("returns a config-patch action for antigravity MCP with scope: global", () => {
|
|
155
|
+
const home = "/home/test";
|
|
156
|
+
const { actions, warnings } = compileMcpServerActions({
|
|
157
|
+
name: "my-mcp",
|
|
158
|
+
agents: ["antigravity"],
|
|
159
|
+
config: { command: "s" },
|
|
160
|
+
scope: "global",
|
|
161
|
+
}, ["antigravity"], home);
|
|
162
|
+
assert.equal(actions.length, 1);
|
|
163
|
+
assert.equal(warnings.length, 0);
|
|
164
|
+
assert.equal(actions[0]?.kind, "config-patch");
|
|
165
|
+
assert.equal(actions[0]?.agent, "antigravity");
|
|
166
|
+
assertPathEndsWith(actions[0]?.target ?? "", ".gemini/antigravity/mcp_config.json", `expected target to end with .gemini/antigravity/mcp_config.json, got ${actions[0]?.target}`);
|
|
167
|
+
});
|
|
154
168
|
it("throws when a supported MCP target is missing both command and url", () => {
|
|
155
169
|
assert.throws(() => compileMcpServerActions({
|
|
156
170
|
name: "my-mcp",
|
|
@@ -30,6 +30,7 @@ const testManifest = {
|
|
|
30
30
|
agentRules: [],
|
|
31
31
|
permissions: [],
|
|
32
32
|
agentDefinitions: [],
|
|
33
|
+
hooks: [],
|
|
33
34
|
};
|
|
34
35
|
describe("planDeploy", () => {
|
|
35
36
|
it("creates actions for detected agents only", async () => {
|
|
@@ -647,8 +648,9 @@ describe("planDeploy", () => {
|
|
|
647
648
|
assert.ok(geminiAction, "expected a gemini-cli action");
|
|
648
649
|
assert.match(geminiAction.target, /[\\/]\.gemini[\\/]settings\.json$/);
|
|
649
650
|
assert.ok(antigravityAction, "expected an antigravity action");
|
|
650
|
-
// Antigravity's target for mcpServer is .
|
|
651
|
-
assert.
|
|
651
|
+
// Antigravity's target for global mcpServer is .gemini/antigravity/mcp_config.json
|
|
652
|
+
assert.equal(antigravityAction.kind, "config-patch");
|
|
653
|
+
assert.match(antigravityAction.target, /[\\/]\.gemini[\\/]antigravity[\\/]mcp_config\.json$/);
|
|
652
654
|
}
|
|
653
655
|
finally {
|
|
654
656
|
await rm(sourceDir, { recursive: true });
|
|
@@ -802,7 +804,7 @@ describe("planDeploy", () => {
|
|
|
802
804
|
name: "my-tool",
|
|
803
805
|
agents: ["antigravity"],
|
|
804
806
|
config: { command: "npx", args: ["-y", "my-tool"] },
|
|
805
|
-
scope: "
|
|
807
|
+
scope: "repo",
|
|
806
808
|
},
|
|
807
809
|
],
|
|
808
810
|
agentRules: [],
|
|
@@ -125,24 +125,22 @@ describe("runPreflight", () => {
|
|
|
125
125
|
assert.equal(implementationOnlyWarning, undefined, `expected no implementation-only warning, got: ${implementationOnlyWarning?.message}`);
|
|
126
126
|
});
|
|
127
127
|
});
|
|
128
|
-
describe("github-copilot
|
|
129
|
-
it("emits
|
|
128
|
+
describe("github-copilot devcontainer support", () => {
|
|
129
|
+
it("emits no capability warning for devcontainer MCP when Copilot is detected and devcontainer scope is targeted", async () => {
|
|
130
130
|
const manifestWithMcp = {
|
|
131
131
|
...emptyManifest,
|
|
132
132
|
mcpServers: [
|
|
133
133
|
{
|
|
134
134
|
name: "test-mcp",
|
|
135
|
-
scope: "
|
|
135
|
+
scope: "devcontainer",
|
|
136
136
|
agents: ["github-copilot"],
|
|
137
137
|
config: { command: "node", args: ["server.js"] },
|
|
138
138
|
},
|
|
139
139
|
],
|
|
140
140
|
};
|
|
141
141
|
const warnings = await runPreflight(baseOptions, manifestWithMcp, "/home/test", ["github-copilot"]);
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
assert.ok(plannedWarning, "expected a planned surface warning for Copilot devcontainer when MCP is targeted");
|
|
145
|
-
assert.match(plannedWarning.message, /planned/);
|
|
142
|
+
const capabilityWarning = warnings.find((w) => w.kind === "info" && /mcpServers/.test(w.message));
|
|
143
|
+
assert.equal(capabilityWarning, undefined, `expected no MCP capability warning for devcontainer scope, got: ${capabilityWarning?.message}`);
|
|
146
144
|
});
|
|
147
145
|
});
|
|
148
146
|
describe("instruction precedence warnings", () => {
|
|
@@ -46,6 +46,7 @@ describe("planRevertAll", () => {
|
|
|
46
46
|
agentRules: [],
|
|
47
47
|
permissions: [],
|
|
48
48
|
agentDefinitions: [],
|
|
49
|
+
hooks: [],
|
|
49
50
|
};
|
|
50
51
|
const actions = planRevertAll(multiAgentManifest, "/home/test");
|
|
51
52
|
assert.equal(actions.length, 3);
|
|
@@ -75,6 +76,7 @@ describe("planRevertAll", () => {
|
|
|
75
76
|
agentRules: [],
|
|
76
77
|
permissions: [],
|
|
77
78
|
agentDefinitions: [],
|
|
79
|
+
hooks: [],
|
|
78
80
|
};
|
|
79
81
|
const actions = planRevertAll(manifest, "/home/test");
|
|
80
82
|
assert.equal(actions.length, 5);
|
|
@@ -712,6 +714,7 @@ describe("planRevert — mcpServers and agentRules", () => {
|
|
|
712
714
|
agentRules: [],
|
|
713
715
|
permissions: [],
|
|
714
716
|
agentDefinitions: [],
|
|
717
|
+
hooks: [],
|
|
715
718
|
};
|
|
716
719
|
const actions = planRevert(manifest, ["claude-code"], home);
|
|
717
720
|
assert.equal(actions.length, 1);
|
|
@@ -735,6 +738,7 @@ describe("planRevert — mcpServers and agentRules", () => {
|
|
|
735
738
|
agentRules: [],
|
|
736
739
|
permissions: [],
|
|
737
740
|
agentDefinitions: [],
|
|
741
|
+
hooks: [],
|
|
738
742
|
};
|
|
739
743
|
const actions = planRevert(manifest, ["codex"], home);
|
|
740
744
|
assert.equal(actions.length, 0);
|
|
@@ -755,6 +759,7 @@ describe("planRevert — mcpServers and agentRules", () => {
|
|
|
755
759
|
],
|
|
756
760
|
permissions: [],
|
|
757
761
|
agentDefinitions: [],
|
|
762
|
+
hooks: [],
|
|
758
763
|
};
|
|
759
764
|
const actions = planRevert(manifest, ["claude-code"], home);
|
|
760
765
|
assert.equal(actions.length, 1);
|
|
@@ -778,6 +783,7 @@ describe("planRevert — mcpServers and agentRules", () => {
|
|
|
778
783
|
],
|
|
779
784
|
permissions: [],
|
|
780
785
|
agentDefinitions: [],
|
|
786
|
+
hooks: [],
|
|
781
787
|
};
|
|
782
788
|
const actions = planRevert(manifest, ["codex"], home);
|
|
783
789
|
assert.equal(actions.length, 0);
|
|
@@ -801,6 +807,7 @@ describe("planRevertAll — mcpServers and agentRules", () => {
|
|
|
801
807
|
agentRules: [],
|
|
802
808
|
permissions: [],
|
|
803
809
|
agentDefinitions: [],
|
|
810
|
+
hooks: [],
|
|
804
811
|
};
|
|
805
812
|
const actions = planRevertAll(manifest, home);
|
|
806
813
|
assert.equal(actions.length, 2);
|
|
@@ -824,6 +831,7 @@ describe("planRevertAll — mcpServers and agentRules", () => {
|
|
|
824
831
|
],
|
|
825
832
|
permissions: [],
|
|
826
833
|
agentDefinitions: [],
|
|
834
|
+
hooks: [],
|
|
827
835
|
};
|
|
828
836
|
const actions = planRevertAll(manifest, home);
|
|
829
837
|
assert.equal(actions.length, 2);
|