@kuznai/inception-engine 0.22.0 → 0.23.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 +45 -8
- package/dist/src/config/agents.js +39 -0
- package/dist/src/core/adapters/execution-config.d.ts +14 -0
- package/dist/src/core/adapters/execution-config.js +60 -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/rules.js +50 -19
- package/dist/src/core/capabilities.d.ts +1 -1
- package/dist/src/core/capabilities.js +18 -2
- package/dist/src/core/deploy.js +1 -1
- package/dist/src/core/init.js +69 -13
- package/dist/src/core/preflight.js +28 -0
- package/dist/src/core/revert.js +5 -1
- package/dist/src/core/validation.d.ts +1 -1
- package/dist/src/core/validation.js +56 -3
- package/dist/src/schemas/manifest.d.ts +31 -0
- package/dist/src/schemas/manifest.js +39 -3
- package/dist/src/types.d.ts +6 -2
- package/dist/test/unit/adapters.test.js +264 -1
- package/dist/test/unit/deploy.test.js +26 -0
- package/dist/test/unit/init-fixture.test.js +85 -1
- package/dist/test/unit/manifest.test.js +72 -0
- package/dist/test/unit/preflight.test.js +86 -0
- package/dist/test/unit/revert.test.js +65 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,9 +51,10 @@ Before executing, the deploy command runs preflight analysis on instruction file
|
|
|
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
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
|
-
| 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
|
|
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` (shared-via) and also supports native `scope: "copilot-repo"` (`{repo}/.github/copilot-instructions.md`) and `scope: "copilot-scoped"` (`{repo}/.github/instructions/{name}.instructions.md`) | 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
56
|
| Execution Hooks | claude-code (`~/.claude/settings.json`), github-copilot (`planned`); other agents are warned and skipped | claude-code |
|
|
57
|
+
| Execution / Safety Config | gemini-cli (`~/.gemini/settings.json`); other agents are warned and skipped | gemini-cli |
|
|
57
58
|
| 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 |
|
|
58
59
|
| `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 |
|
|
59
60
|
| 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 |
|
|
@@ -143,10 +144,29 @@ Create an `inception.json` file at the root of your skills directory:
|
|
|
143
144
|
"agents": ["claude-code"],
|
|
144
145
|
"config": {
|
|
145
146
|
"hooks": {
|
|
146
|
-
"
|
|
147
|
+
"PreToolUse": [
|
|
148
|
+
{
|
|
149
|
+
"matcher": "Bash",
|
|
150
|
+
"hooks": [{ "type": "command", "command": "scripts/check-env.sh" }]
|
|
151
|
+
}
|
|
152
|
+
],
|
|
153
|
+
"Stop": [
|
|
154
|
+
{
|
|
155
|
+
"hooks": [{ "type": "command", "command": "scripts/notify.sh" }]
|
|
156
|
+
}
|
|
157
|
+
]
|
|
147
158
|
}
|
|
148
159
|
}
|
|
149
160
|
}
|
|
161
|
+
],
|
|
162
|
+
"executionConfigs": [
|
|
163
|
+
{
|
|
164
|
+
"name": "gemini-safety",
|
|
165
|
+
"agents": ["gemini-cli"],
|
|
166
|
+
"config": {
|
|
167
|
+
"safeMode": true
|
|
168
|
+
}
|
|
169
|
+
}
|
|
150
170
|
]
|
|
151
171
|
}
|
|
152
172
|
```
|
|
@@ -202,10 +222,12 @@ Each **agentRules** entry deploys a Markdown instruction file to an agent's supp
|
|
|
202
222
|
- **name** - Unique identifier (same format as skill names)
|
|
203
223
|
- **path** - Relative path to the source Markdown file within the repo; supported rules adapters require a `.md` or `.markdown` source path
|
|
204
224
|
- **agents** - Array of agent IDs to deploy this file to
|
|
205
|
-
- **scope** - `"global"` (default), `"repo"`, or `"
|
|
225
|
+
- **scope** - `"global"` (default), `"repo"`, `"workspace"`, `"copilot-repo"`, or `"copilot-scoped"`. Controls which instruction surface is targeted:
|
|
206
226
|
- `"global"` — deploys to the agent's home-directory instruction file when that surface is supported (e.g., `~/.claude/CLAUDE.md` for `claude-code`)
|
|
207
227
|
- `"repo"` — deploys to the project-root instruction file inside the deployed repository when that surface is supported (e.g., `{repo}/CLAUDE.md` for `claude-code`)
|
|
208
228
|
- `"workspace"` — deploys to the workspace-root instruction file when an agent exposes one (e.g., `{workspace}/CLAUDE.md` for `claude-code`); when unsupported, deployment is skipped with a warning
|
|
229
|
+
- `"copilot-repo"` — deploys to GitHub Copilot's native repo-level instruction file at `{repo}/.github/copilot-instructions.md` (`github-copilot` only; other agents are warned and skipped)
|
|
230
|
+
- `"copilot-scoped"` — deploys to `{repo}/.github/instructions/{name}.instructions.md` where `{name}` is the manifest entry name (`github-copilot` only; other agents are warned and skipped)
|
|
209
231
|
|
|
210
232
|
Instruction rule deployment is supported for implemented global, repo, and workspace surfaces. The target path depends on the agent and the `scope`:
|
|
211
233
|
|
|
@@ -218,7 +240,13 @@ Instruction rule deployment is supported for implemented global, repo, and works
|
|
|
218
240
|
| `opencode` | `~/.config/opencode/AGENTS.md` | `{repo}/AGENTS.md` | unsupported; warns and skips |
|
|
219
241
|
| `github-copilot` | unsupported / Claude-first | deploy via `claude-code` | unsupported; deploy via `claude-code` with `scope: "workspace"` |
|
|
220
242
|
|
|
221
|
-
|
|
243
|
+
GitHub Copilot also exposes two native instruction surfaces via dedicated scope values:
|
|
244
|
+
|
|
245
|
+
| Agent | `scope: "copilot-repo"` | `scope: "copilot-scoped"` |
|
|
246
|
+
|---|---|---|
|
|
247
|
+
| `github-copilot` | `{repo}/.github/copilot-instructions.md` | `{repo}/.github/instructions/{name}.instructions.md` |
|
|
248
|
+
|
|
249
|
+
For `antigravity`, `agentRules` now targets the shared GEMINI.md surface (`~/.gemini/GEMINI.md` for `global`, `{repo}/GEMINI.md` for `repo`) — the same paths used by `gemini-cli`. When both agents appear in the same entry, deduplication ensures only one write action is emitted. `workspace` scope is not supported and is skipped with a warning. For `github-copilot`, the Claude-first path (targeting via `claude-code` agentRules) remains the recommended approach for shared rules; the `copilot-repo` and `copilot-scoped` scopes are for Copilot-specific instructions that should not be shared with other agents. When both are present, preflight emits a precedence warning. Revert removes the deployed rules file.
|
|
222
250
|
|
|
223
251
|
Each **permissions** entry deploys execution and safety-oriented configuration to an agent's permission or approval surface:
|
|
224
252
|
|
|
@@ -269,7 +297,7 @@ Each **hooks** entry deploys lifecycle-binding configurations to an agent's exec
|
|
|
269
297
|
|
|
270
298
|
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
299
|
|
|
272
|
-
For `claude-code`, the config is merged into `~/.claude/settings.json`. Only the `hooks` key is accepted:
|
|
300
|
+
For `claude-code`, the config is merged into `~/.claude/settings.json`. Only the `hooks` key is accepted. Event names follow Claude Code's settings.json hooks surface (e.g. `PreToolUse`, `PostToolUse`, `Notification`, `Stop`, `SubagentStop`). Each event maps to an array of hook matchers; each matcher has an optional `matcher` string and a required `hooks` array of `{ type: "command", command: string }` objects:
|
|
273
301
|
|
|
274
302
|
```json
|
|
275
303
|
{
|
|
@@ -277,8 +305,17 @@ For `claude-code`, the config is merged into `~/.claude/settings.json`. Only the
|
|
|
277
305
|
"agents": ["claude-code"],
|
|
278
306
|
"config": {
|
|
279
307
|
"hooks": {
|
|
280
|
-
"
|
|
281
|
-
|
|
308
|
+
"PreToolUse": [
|
|
309
|
+
{
|
|
310
|
+
"matcher": "Bash",
|
|
311
|
+
"hooks": [{ "type": "command", "command": "scripts/check-env.sh" }]
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
"Stop": [
|
|
315
|
+
{
|
|
316
|
+
"hooks": [{ "type": "command", "command": "scripts/notify.sh" }]
|
|
317
|
+
}
|
|
318
|
+
]
|
|
282
319
|
}
|
|
283
320
|
}
|
|
284
321
|
}
|
|
@@ -341,7 +378,7 @@ Current `init` behavior:
|
|
|
341
378
|
- Applies either the `--agents` list or all currently known agent IDs
|
|
342
379
|
- Refuses to overwrite an existing `inception.json` unless `--force` is provided
|
|
343
380
|
- Supports `--plan` so you can inspect the generated manifest before writing it
|
|
344
|
-
- Discovers agent-rules Markdown files in the root and conventional subdirectories (`rules/`, `instructions/`, `.github/`), mapping them to agents using Claude-first portability conventions: `copilot-instructions.md` maps to `claude-code` (Copilot reads `CLAUDE.md` natively), and the fallback for unrecognized files excludes unsupported agents plus shared-surface riders that should default to their primary deploy target
|
|
381
|
+
- Discovers agent-rules Markdown files in the root and conventional subdirectories (`rules/`, `instructions/`, `.github/`), mapping them to agents using Claude-first portability conventions: `copilot-instructions.md` found outside `.github/` maps to `claude-code` (Copilot reads `CLAUDE.md` natively), `.github/copilot-instructions.md` maps to `github-copilot` with `scope: "copilot-repo"`, and `*.instructions.md` files in `.github/instructions/` map to `github-copilot` with `scope: "copilot-scoped"`; the fallback for unrecognized files excludes unsupported agents plus shared-surface riders that should default to their primary deploy target
|
|
345
382
|
- Reads `mcp-servers.json` from the repo root (if present) and generates `mcpServers` entries; invalid entries are warned and skipped
|
|
346
383
|
- Reads `files-manifest.json` from the repo root (if present) and generates `files` entries; invalid entries are warned and skipped
|
|
347
384
|
- Reads `configs-manifest.json` from the repo root (if present) and generates `configs` entries; invalid entries are warned and skipped
|
|
@@ -185,6 +185,7 @@ export const AGENT_REGISTRY = [
|
|
|
185
185
|
agentRules: "documented",
|
|
186
186
|
hooks: "provisional",
|
|
187
187
|
agentDefinitions: "documented",
|
|
188
|
+
executionConfig: "provisional",
|
|
188
189
|
},
|
|
189
190
|
mcpSupport: {
|
|
190
191
|
status: "supported",
|
|
@@ -256,6 +257,14 @@ export const AGENT_REGISTRY = [
|
|
|
256
257
|
windows: ["{repo}", ".gemini", "agents", "{name}.toml"],
|
|
257
258
|
},
|
|
258
259
|
},
|
|
260
|
+
executionConfigSupport: {
|
|
261
|
+
status: "supported",
|
|
262
|
+
schemaLabel: "Settings execution config",
|
|
263
|
+
path: {
|
|
264
|
+
posix: ["{home}", ".gemini", "settings.json"],
|
|
265
|
+
windows: ["{home}", ".gemini", "settings.json"],
|
|
266
|
+
},
|
|
267
|
+
},
|
|
259
268
|
agentRulesWorkspaceSupport: {
|
|
260
269
|
status: "supported",
|
|
261
270
|
schemaLabel: "workspace-local GEMINI.md",
|
|
@@ -443,6 +452,7 @@ export const AGENT_REGISTRY = [
|
|
|
443
452
|
provenance: {
|
|
444
453
|
detectPaths: "documented",
|
|
445
454
|
detectBinary: "documented",
|
|
455
|
+
agentRules: "documented",
|
|
446
456
|
agentDefinitions: "documented",
|
|
447
457
|
mcpConfig: "documented",
|
|
448
458
|
hooks: "provisional",
|
|
@@ -514,6 +524,35 @@ export const AGENT_REGISTRY = [
|
|
|
514
524
|
windows: ["{workspace}", "CLAUDE.md"],
|
|
515
525
|
},
|
|
516
526
|
},
|
|
527
|
+
// GitHub Copilot native repo-level instruction file. Not shared via
|
|
528
|
+
// CLAUDE.md; this is Copilot's own .github/copilot-instructions.md surface
|
|
529
|
+
// deployed directly without the shared-via deduplication path.
|
|
530
|
+
agentRulesCopilotRepoSupport: {
|
|
531
|
+
status: "supported",
|
|
532
|
+
surfaceKind: { kind: "agent-specific" },
|
|
533
|
+
schemaLabel: "repo-local .github/copilot-instructions.md",
|
|
534
|
+
path: {
|
|
535
|
+
posix: ["{repo}", ".github", "copilot-instructions.md"],
|
|
536
|
+
windows: ["{repo}", ".github", "copilot-instructions.md"],
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
// GitHub Copilot scoped instruction files. Each entry deploys to
|
|
540
|
+
// .github/instructions/{name}.instructions.md where {name} comes from
|
|
541
|
+
// the manifest entry name field.
|
|
542
|
+
agentRulesCopilotScopedSupport: {
|
|
543
|
+
status: "supported",
|
|
544
|
+
surfaceKind: { kind: "agent-specific" },
|
|
545
|
+
schemaLabel: "scoped .github/instructions/{name}.instructions.md",
|
|
546
|
+
path: {
|
|
547
|
+
posix: ["{repo}", ".github", "instructions", "{name}.instructions.md"],
|
|
548
|
+
windows: [
|
|
549
|
+
"{repo}",
|
|
550
|
+
".github",
|
|
551
|
+
"instructions",
|
|
552
|
+
"{name}.instructions.md",
|
|
553
|
+
],
|
|
554
|
+
},
|
|
555
|
+
},
|
|
517
556
|
mcpDevcontainerSupport: {
|
|
518
557
|
status: "supported",
|
|
519
558
|
schemaLabel: "devcontainer.json MCP surface",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ExecutionConfigEntry } from "../../schemas/manifest.ts";
|
|
2
|
+
import type { AgentId, ConfigPatchDeployAction, ConfigPatchRevertAction, PlanWarning } from "../../types.ts";
|
|
3
|
+
export interface ExecutionConfigAdapterResult {
|
|
4
|
+
actions: ConfigPatchDeployAction[];
|
|
5
|
+
warnings: PlanWarning[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Compiles deploy actions for executionConfigs manifest entries.
|
|
9
|
+
*
|
|
10
|
+
* Execution configuration settings (like Gemini CLI's safeMode) are patched
|
|
11
|
+
* directly into the agent's primary settings file using config-patch actions.
|
|
12
|
+
*/
|
|
13
|
+
export declare function compileExecutionConfigActions(entry: ExecutionConfigEntry, detectedAgents: AgentId[], home: string): ExecutionConfigAdapterResult;
|
|
14
|
+
export declare function compileExecutionConfigReverts(entry: ExecutionConfigEntry, agentFilter: AgentId[] | null, home: string): ConfigPatchRevertAction[];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { AGENT_REGISTRY_BY_ID } from "../../config/agents.js";
|
|
3
|
+
import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
|
|
4
|
+
/**
|
|
5
|
+
* Compiles deploy actions for executionConfigs manifest entries.
|
|
6
|
+
*
|
|
7
|
+
* Execution configuration settings (like Gemini CLI's safeMode) are patched
|
|
8
|
+
* directly into the agent's primary settings file using config-patch actions.
|
|
9
|
+
*/
|
|
10
|
+
export function compileExecutionConfigActions(entry, detectedAgents, home) {
|
|
11
|
+
const actions = [];
|
|
12
|
+
const warnings = [];
|
|
13
|
+
const platform = getPlatformKey();
|
|
14
|
+
for (const agentId of entry.agents) {
|
|
15
|
+
if (!detectedAgents.includes(agentId))
|
|
16
|
+
continue;
|
|
17
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
18
|
+
if (!agent?.executionConfigSupport ||
|
|
19
|
+
agent.executionConfigSupport.status !== "supported") {
|
|
20
|
+
warnings.push({
|
|
21
|
+
kind: "confidence",
|
|
22
|
+
message: `Agent "${agentId}" does not support modeled execution configuration surfaces — skipping "${entry.name}"`,
|
|
23
|
+
});
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const rawTarget = resolvePlaceholders(agent.executionConfigSupport.path[platform], entry.name, home);
|
|
27
|
+
const target = path.resolve(rawTarget);
|
|
28
|
+
actions.push({
|
|
29
|
+
kind: "config-patch",
|
|
30
|
+
skill: entry.name,
|
|
31
|
+
agent: agentId,
|
|
32
|
+
target,
|
|
33
|
+
patch: entry.config,
|
|
34
|
+
confidence: "provisional",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return { actions, warnings };
|
|
38
|
+
}
|
|
39
|
+
export function compileExecutionConfigReverts(entry, agentFilter, home) {
|
|
40
|
+
const actions = [];
|
|
41
|
+
const platform = getPlatformKey();
|
|
42
|
+
for (const agentId of entry.agents) {
|
|
43
|
+
if (agentFilter && !agentFilter.includes(agentId))
|
|
44
|
+
continue;
|
|
45
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
46
|
+
if (!agent?.executionConfigSupport ||
|
|
47
|
+
agent.executionConfigSupport.status !== "supported") {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const rawTarget = resolvePlaceholders(agent.executionConfigSupport.path[platform], entry.name, home);
|
|
51
|
+
const target = path.resolve(rawTarget);
|
|
52
|
+
actions.push({
|
|
53
|
+
kind: "config-patch",
|
|
54
|
+
skill: entry.name,
|
|
55
|
+
agent: agentId,
|
|
56
|
+
target,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return actions;
|
|
60
|
+
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import type { AgentDefinitionEntry, AgentRuleEntry, HookEntry, McpServerEntry, PermissionsEntry } from "../../schemas/manifest.ts";
|
|
1
|
+
import type { AgentDefinitionEntry, AgentRuleEntry, ExecutionConfigEntry, 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 { compileExecutionConfigReverts } from "./execution-config.ts";
|
|
4
5
|
import { compileHookReverts } from "./hooks.ts";
|
|
5
6
|
import { compileMcpServerReverts } from "./mcp.ts";
|
|
6
7
|
import { compilePermissionsReverts } from "./permissions.ts";
|
|
7
8
|
import { compileAgentRuleReverts } from "./rules.ts";
|
|
8
|
-
export { compileAgentDefinitionReverts, compileAgentRuleReverts, compileHookReverts, compileMcpServerReverts, compilePermissionsReverts, };
|
|
9
|
+
export { compileAgentDefinitionReverts, compileAgentRuleReverts, compileExecutionConfigReverts, compileHookReverts, compileMcpServerReverts, compilePermissionsReverts, };
|
|
9
10
|
export type AdapterAction = ConfigPatchDeployAction | FileWriteDeployAction | TomlPatchDeployAction | FrontmatterEmitDeployAction;
|
|
10
11
|
export interface AdapterResult {
|
|
11
12
|
actions: AdapterAction[];
|
|
12
13
|
warnings: PlanWarning[];
|
|
13
14
|
}
|
|
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>;
|
|
15
|
+
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[], executionConfigs?: ExecutionConfigEntry[]): Promise<AdapterResult>;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { compileAgentDefinitionActions, compileAgentDefinitionReverts, } from "./agent-definitions.js";
|
|
2
|
+
import { compileExecutionConfigActions, compileExecutionConfigReverts, } from "./execution-config.js";
|
|
2
3
|
import { compileHookActions, compileHookReverts } from "./hooks.js";
|
|
3
4
|
import { compileMcpServerActions, compileMcpServerReverts } from "./mcp.js";
|
|
4
5
|
import { compilePermissionsActions, compilePermissionsReverts, } from "./permissions.js";
|
|
5
6
|
import { compileAgentRuleActions, compileAgentRuleReverts } from "./rules.js";
|
|
6
|
-
export { compileAgentDefinitionReverts, compileAgentRuleReverts, compileHookReverts, compileMcpServerReverts, compilePermissionsReverts, };
|
|
7
|
+
export { compileAgentDefinitionReverts, compileAgentRuleReverts, compileExecutionConfigReverts, compileHookReverts, compileMcpServerReverts, compilePermissionsReverts, };
|
|
7
8
|
async function compileAll(entries, fn) {
|
|
8
9
|
const results = await Promise.all(entries.map(fn));
|
|
9
10
|
return {
|
|
@@ -11,17 +12,18 @@ async function compileAll(entries, fn) {
|
|
|
11
12
|
warnings: results.flatMap((r) => r.warnings),
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
|
-
export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, agentDefinitions, workspace, hooks = []) {
|
|
15
|
+
export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, agentDefinitions, workspace, hooks = [], executionConfigs = []) {
|
|
15
16
|
const actions = [];
|
|
16
17
|
const warnings = [];
|
|
17
|
-
const [mcp, rules, perms, defs, hookResults] = await Promise.all([
|
|
18
|
+
const [mcp, rules, perms, defs, hookResults, execResults] = await Promise.all([
|
|
18
19
|
compileAll(mcpServers, (entry) => compileMcpServerActions(entry, detectedAgents, home, repo, workspace)),
|
|
19
20
|
compileAll(agentRules, (entry) => compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace)),
|
|
20
21
|
compileAll(permissions, (entry) => compilePermissionsActions(entry, detectedAgents, home)),
|
|
21
22
|
compileAll(agentDefinitions ?? [], (entry) => compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace)),
|
|
22
23
|
compileAll(hooks, (entry) => compileHookActions(entry, detectedAgents, home)),
|
|
24
|
+
compileAll(executionConfigs, (entry) => compileExecutionConfigActions(entry, detectedAgents, home)),
|
|
23
25
|
]);
|
|
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);
|
|
26
|
+
actions.push(...mcp.actions, ...rules.actions, ...perms.actions, ...defs.actions, ...hookResults.actions, ...execResults.actions);
|
|
27
|
+
warnings.push(...mcp.warnings, ...rules.warnings, ...perms.warnings, ...defs.warnings, ...hookResults.warnings, ...execResults.warnings);
|
|
26
28
|
return { actions, warnings };
|
|
27
29
|
}
|
|
@@ -2,6 +2,37 @@ import path from "node:path";
|
|
|
2
2
|
import { planCapabilityForDeploy, resolveCapabilitySurface, } from "../capabilities.js";
|
|
3
3
|
import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
|
|
4
4
|
import { validateAgentRuleMarkdownPath, validateInstructionFileRequirements, validateSourceFile, validateSourcePath, } from "../validation.js";
|
|
5
|
+
function requiresRepoPath(scope) {
|
|
6
|
+
return (scope === "repo" || scope === "copilot-repo" || scope === "copilot-scoped");
|
|
7
|
+
}
|
|
8
|
+
function deduplicateTargets(supportedTargets, scope) {
|
|
9
|
+
const seenTargetPaths = new Set();
|
|
10
|
+
const dedupedTargets = [];
|
|
11
|
+
for (const t of supportedTargets) {
|
|
12
|
+
const surface = resolveCapabilitySurface(t.agentId, "agentRules", scope);
|
|
13
|
+
if (surface.surfaceKind === "shared-via" &&
|
|
14
|
+
surface.sharedVia &&
|
|
15
|
+
supportedTargets.some((o) => o.agentId === surface.sharedVia && o.target === t.target)) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (!seenTargetPaths.has(t.target)) {
|
|
19
|
+
seenTargetPaths.add(t.target);
|
|
20
|
+
dedupedTargets.push(t);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return dedupedTargets;
|
|
24
|
+
}
|
|
25
|
+
function injectTargetDir(pathSegments, targetDir) {
|
|
26
|
+
if (!targetDir)
|
|
27
|
+
return pathSegments;
|
|
28
|
+
const placeholderIndex = pathSegments.findIndex((seg) => seg === "{repo}" || seg === "{workspace}");
|
|
29
|
+
if (placeholderIndex === -1)
|
|
30
|
+
return pathSegments;
|
|
31
|
+
const dirSegments = targetDir.split(/[\\/]+/).filter(Boolean);
|
|
32
|
+
const result = [...pathSegments];
|
|
33
|
+
result.splice(placeholderIndex + 1, 0, ...dirSegments);
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
5
36
|
function resolveAgentTarget(agentId, entry, home, repo, platform, workspace, allTargetAgentIds) {
|
|
6
37
|
const plan = planCapabilityForDeploy({
|
|
7
38
|
agentId,
|
|
@@ -28,10 +59,17 @@ function resolveAgentTarget(agentId, entry, home, repo, platform, workspace, all
|
|
|
28
59
|
message: `agentRules: scope "workspace" requires a workspace or repository path but none was resolved — skipping "${entry.name}" for agent "${agentId}"`,
|
|
29
60
|
};
|
|
30
61
|
}
|
|
62
|
+
if ((entry.scope === "copilot-repo" || entry.scope === "copilot-scoped") &&
|
|
63
|
+
!repo) {
|
|
64
|
+
return {
|
|
65
|
+
kind: "confidence",
|
|
66
|
+
message: `agentRules: scope "${entry.scope}" requires a repository path but none was resolved — skipping "${entry.name}" for agent "${agentId}"`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
31
69
|
return {
|
|
32
70
|
agentId,
|
|
33
71
|
confidence: plan.confidence,
|
|
34
|
-
target: resolvePlaceholders(support?.path[platform] ?? [], entry.name, home, repo, workspace),
|
|
72
|
+
target: resolvePlaceholders(injectTargetDir(support?.path[platform] ?? [], entry.targetDir), entry.name, home, repo, workspace),
|
|
35
73
|
};
|
|
36
74
|
}
|
|
37
75
|
export async function compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace) {
|
|
@@ -61,29 +99,22 @@ export async function compileAgentRuleActions(entry, sourceDir, resolvedSourceDi
|
|
|
61
99
|
// Deduplicate: when a shared-via rider's primary is also in supportedTargets,
|
|
62
100
|
// skip the rider — the primary agent writes the shared surface. As a fallback,
|
|
63
101
|
// also dedup by resolved path so no target file is written twice.
|
|
64
|
-
const
|
|
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
|
-
}
|
|
102
|
+
const dedupedTargets = deduplicateTargets(supportedTargets, entry.scope);
|
|
79
103
|
// Validate the shared source file only when at least one target uses the
|
|
80
104
|
// current rules adapter surface.
|
|
81
105
|
const source = path.resolve(sourceDir, entry.path);
|
|
82
106
|
await validateSourcePath(source, entry.path, resolvedSourceDir, realRoot);
|
|
83
107
|
await validateSourceFile(source, entry.path);
|
|
108
|
+
// Native Copilot instruction files (.github/copilot-instructions.md and
|
|
109
|
+
// .github/instructions/*.instructions.md) are plain markdown and do not
|
|
110
|
+
// require agent-definition-style frontmatter (tools/instructions keys).
|
|
111
|
+
// Skip the instructionFrontmatterRequired check for these scopes.
|
|
112
|
+
const skipFrontmatterValidation = entry.scope === "copilot-repo" || entry.scope === "copilot-scoped";
|
|
84
113
|
for (const target of dedupedTargets) {
|
|
85
114
|
validateAgentRuleMarkdownPath(entry.path, target.agentId);
|
|
86
|
-
|
|
115
|
+
if (!skipFrontmatterValidation) {
|
|
116
|
+
await validateInstructionFileRequirements(source, entry.path, target.agentId);
|
|
117
|
+
}
|
|
87
118
|
actions.push({
|
|
88
119
|
kind: "file-write",
|
|
89
120
|
skill: entry.name,
|
|
@@ -107,11 +138,11 @@ export function compileAgentRuleReverts(entry, agentFilter, home, repo, workspac
|
|
|
107
138
|
if (surface.supportStatus !== "supported" || !support) {
|
|
108
139
|
continue;
|
|
109
140
|
}
|
|
110
|
-
if (entry.scope
|
|
141
|
+
if (requiresRepoPath(entry.scope) && !repo)
|
|
111
142
|
continue;
|
|
112
143
|
if (entry.scope === "workspace" && !workspace && !repo)
|
|
113
144
|
continue;
|
|
114
|
-
const target = resolvePlaceholders(support.path[platform], entry.name, home, repo, workspace);
|
|
145
|
+
const target = resolvePlaceholders(injectTargetDir(support.path[platform], entry.targetDir), entry.name, home, repo, workspace);
|
|
115
146
|
if (seenRevertTargets.has(target))
|
|
116
147
|
continue;
|
|
117
148
|
seenRevertTargets.add(target);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentId, AgentSurfaceSupport, CapabilityKind, Confidence, PlanWarning, SupportedAgentSurface } from "../types.ts";
|
|
2
|
-
type CapabilityScope = "global" | "repo" | "workspace" | "devcontainer";
|
|
2
|
+
type CapabilityScope = "global" | "repo" | "workspace" | "devcontainer" | "copilot-repo" | "copilot-scoped";
|
|
3
3
|
export interface ResolvedCapabilitySurface {
|
|
4
4
|
agentId: AgentId;
|
|
5
5
|
capability: CapabilityKind;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { AGENT_REGISTRY_BY_ID } from "../config/agents.js";
|
|
2
2
|
function resolveAgentRulesSupport(agentId, scope) {
|
|
3
3
|
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
4
|
+
if (scope === "copilot-repo") {
|
|
5
|
+
return agent?.agentRulesCopilotRepoSupport;
|
|
6
|
+
}
|
|
7
|
+
if (scope === "copilot-scoped") {
|
|
8
|
+
return agent?.agentRulesCopilotScopedSupport;
|
|
9
|
+
}
|
|
4
10
|
if (scope === "repo") {
|
|
5
11
|
return agent?.agentRulesRepoSupport ?? agent?.agentRulesSupport;
|
|
6
12
|
}
|
|
@@ -52,6 +58,8 @@ function capabilityLabel(capability) {
|
|
|
52
58
|
return "agentDefinitions";
|
|
53
59
|
case "hooks":
|
|
54
60
|
return "hooks";
|
|
61
|
+
case "executionConfigs":
|
|
62
|
+
return "executionConfigs";
|
|
55
63
|
}
|
|
56
64
|
}
|
|
57
65
|
function capabilitySurfaceLabel(capability) {
|
|
@@ -68,6 +76,8 @@ function capabilitySurfaceLabel(capability) {
|
|
|
68
76
|
return "agent-definitions";
|
|
69
77
|
case "hooks":
|
|
70
78
|
return "hooks";
|
|
79
|
+
case "executionConfigs":
|
|
80
|
+
return "execution-config";
|
|
71
81
|
}
|
|
72
82
|
}
|
|
73
83
|
function resolveSkillsCapability(agentId) {
|
|
@@ -125,9 +135,15 @@ function getSurfaceRecordAndConfidence(agentId, capability, scope) {
|
|
|
125
135
|
confidence: agent.provenance.agentDefinitions,
|
|
126
136
|
};
|
|
127
137
|
}
|
|
138
|
+
if (capability === "hooks") {
|
|
139
|
+
return {
|
|
140
|
+
supportRecord: agent.hooksSupport,
|
|
141
|
+
confidence: agent.provenance.hooks,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
128
144
|
return {
|
|
129
|
-
supportRecord: agent.
|
|
130
|
-
confidence: agent.provenance.
|
|
145
|
+
supportRecord: agent.executionConfigSupport,
|
|
146
|
+
confidence: agent.provenance.executionConfig,
|
|
131
147
|
};
|
|
132
148
|
}
|
|
133
149
|
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, manifest.hooks ?? []);
|
|
283
|
+
const adapterResult = await compileAdapterActions(manifest.mcpServers, manifest.agentRules, manifest.permissions ?? [], sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repoDir, manifest.agentDefinitions ?? [], workspace, manifest.hooks ?? [], manifest.executionConfigs ?? []);
|
|
284
284
|
actions.push(...adapterResult.actions);
|
|
285
285
|
const warnings = [
|
|
286
286
|
...skillPlan.warnings,
|
package/dist/src/core/init.js
CHANGED
|
@@ -32,9 +32,10 @@ const AGENT_RULES_FILE_PATTERNS = (() => {
|
|
|
32
32
|
fileNames: [filename, filename.replace(".md", "-instructions.md")],
|
|
33
33
|
agents,
|
|
34
34
|
})),
|
|
35
|
-
// Convention mapping
|
|
36
|
-
//
|
|
37
|
-
//
|
|
35
|
+
// Convention mapping for copilot-instructions.md found outside .github/:
|
|
36
|
+
// map to claude-code since Copilot reads CLAUDE.md natively. Files at
|
|
37
|
+
// .github/copilot-instructions.md are handled separately as a native
|
|
38
|
+
// Copilot surface via the copilot-repo scope.
|
|
38
39
|
{
|
|
39
40
|
fileNames: ["copilot-instructions.md"],
|
|
40
41
|
agents: ["claude-code"],
|
|
@@ -183,18 +184,73 @@ async function findAgentRulesCandidates(baseDir, skillDirRelPaths) {
|
|
|
183
184
|
for (const subdir of AGENT_RULES_SUBDIRS) {
|
|
184
185
|
await scanDirForMarkdown(path.join(baseDir, subdir), baseDir, skillDirRelPaths, seen, candidates);
|
|
185
186
|
}
|
|
187
|
+
// Promote .github/copilot-instructions.md to the native copilot-repo scope.
|
|
188
|
+
// The general scanDirForMarkdown pass above picks it up via AGENT_RULES_SUBDIRS
|
|
189
|
+
// (".github"), so we just patch the candidate that was already added.
|
|
190
|
+
const copilotRepoRelPath = ".github/copilot-instructions.md";
|
|
191
|
+
const copilotRepoCandidate = candidates.find((c) => c.relPath === copilotRepoRelPath);
|
|
192
|
+
if (copilotRepoCandidate) {
|
|
193
|
+
copilotRepoCandidate.defaultAgents = ["github-copilot"];
|
|
194
|
+
copilotRepoCandidate.scope = "copilot-repo";
|
|
195
|
+
}
|
|
196
|
+
// Discover .github/instructions/*.instructions.md as copilot-scoped entries.
|
|
197
|
+
// These are not covered by the general scan (it only looks for .md/.markdown
|
|
198
|
+
// and the stem derivation would lose the .instructions suffix).
|
|
199
|
+
const instructionsDir = path.join(baseDir, ".github", "instructions");
|
|
200
|
+
let instrEntries;
|
|
201
|
+
try {
|
|
202
|
+
instrEntries = await readdir(instructionsDir, {
|
|
203
|
+
withFileTypes: true,
|
|
204
|
+
encoding: "utf-8",
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
instrEntries = [];
|
|
209
|
+
}
|
|
210
|
+
for (const entry of instrEntries) {
|
|
211
|
+
if (!entry.isFile())
|
|
212
|
+
continue;
|
|
213
|
+
const lower = entry.name.toLowerCase();
|
|
214
|
+
if (!lower.endsWith(".instructions.md"))
|
|
215
|
+
continue;
|
|
216
|
+
const absPath = path.join(instructionsDir, entry.name);
|
|
217
|
+
const relPath = path.relative(baseDir, absPath).split(path.sep).join("/");
|
|
218
|
+
if (seen.has(relPath) || isInsideSkillDir(relPath, skillDirRelPaths))
|
|
219
|
+
continue;
|
|
220
|
+
// Derive name by stripping ".instructions.md" suffix
|
|
221
|
+
const baseStem = entry.name.slice(0, -".instructions.md".length);
|
|
222
|
+
const rawName = baseStem.toLowerCase().replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
223
|
+
if (!SAFE_NAME_RE.test(rawName)) {
|
|
224
|
+
logger.warn("init", `Skipping "${relPath}": could not derive a valid agentRules name`);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
seen.add(relPath);
|
|
228
|
+
candidates.push({
|
|
229
|
+
relPath,
|
|
230
|
+
name: rawName,
|
|
231
|
+
defaultAgents: ["github-copilot"],
|
|
232
|
+
scope: "copilot-scoped",
|
|
233
|
+
});
|
|
234
|
+
}
|
|
186
235
|
return candidates;
|
|
187
236
|
}
|
|
188
|
-
function buildAgentRules(candidates, activeAgents, skillNamesSeen) {
|
|
237
|
+
function buildAgentRules(candidates, activeAgents, allAgents, skillNamesSeen) {
|
|
189
238
|
const rules = [];
|
|
190
239
|
const namesSeen = new Set(skillNamesSeen);
|
|
191
|
-
for (const { relPath, name: rawName } of candidates) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
240
|
+
for (const { relPath, name: rawName, defaultAgents: presetAgents, scope: presetScope, } of candidates) {
|
|
241
|
+
let agents;
|
|
242
|
+
if (presetAgents.length > 0) {
|
|
243
|
+
// Candidates with preset agents (e.g. copilot-repo, copilot-scoped) use
|
|
244
|
+
// those agents directly, intersected with the full agents list.
|
|
245
|
+
agents = presetAgents.filter((a) => allAgents.includes(a));
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
// Derive from filename pattern and intersect with active (capable) agents.
|
|
249
|
+
const defaults = defaultAgentsForFile(path.basename(relPath), activeAgents);
|
|
250
|
+
const ix = defaults.filter((a) => activeAgents.includes(a));
|
|
251
|
+
agents = ix.length > 0 ? ix : activeAgents;
|
|
252
|
+
}
|
|
253
|
+
// Skip if excluded by --agents or no capable agents remain
|
|
198
254
|
if (agents.length === 0)
|
|
199
255
|
continue;
|
|
200
256
|
// Resolve name collision with skill names
|
|
@@ -211,7 +267,7 @@ function buildAgentRules(candidates, activeAgents, skillNamesSeen) {
|
|
|
211
267
|
}
|
|
212
268
|
}
|
|
213
269
|
namesSeen.add(name);
|
|
214
|
-
rules.push({ name, path: relPath, agents, scope: "global" });
|
|
270
|
+
rules.push({ name, path: relPath, agents, scope: presetScope ?? "global" });
|
|
215
271
|
}
|
|
216
272
|
return rules;
|
|
217
273
|
}
|
|
@@ -576,7 +632,7 @@ export async function runInit(options) {
|
|
|
576
632
|
const skillNamesSeen = new Set(skills.map((s) => s.name));
|
|
577
633
|
const skillDirRelPaths = new Set(found.map((f) => f.relPath));
|
|
578
634
|
const agentRulesCandidates = await findAgentRulesCandidates(directory, skillDirRelPaths);
|
|
579
|
-
const agentRules = buildAgentRules(agentRulesCandidates, agentRulesCapableAgents, skillNamesSeen);
|
|
635
|
+
const agentRules = buildAgentRules(agentRulesCandidates, agentRulesCapableAgents, agents, skillNamesSeen);
|
|
580
636
|
const allNamesSeen = new Set([
|
|
581
637
|
...skillNamesSeen,
|
|
582
638
|
...agentRules.map((r) => r.name),
|