@kuznai/inception-engine 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -6
- package/dist/config/agents.js +104 -8
- package/dist/config/manifest.js +2 -0
- package/dist/core/adapters/agent-definitions.d.ts +20 -0
- package/dist/core/adapters/agent-definitions.js +90 -0
- package/dist/core/adapters/frontmatter.d.ts +17 -5
- package/dist/core/adapters/frontmatter.js +36 -57
- package/dist/core/adapters/index.d.ts +4 -3
- package/dist/core/adapters/index.js +8 -2
- package/dist/core/adapters/mcp.js +10 -1
- package/dist/core/adapters/permissions.js +10 -1
- package/dist/core/adapters/rules.js +49 -16
- package/dist/core/deploy.js +5 -3
- package/dist/core/init.js +181 -11
- package/dist/core/preflight.d.ts +2 -2
- package/dist/core/preflight.js +71 -1
- package/dist/core/resolve.js +4 -0
- package/dist/core/revert.js +5 -1
- package/dist/core/validation.js +21 -38
- package/dist/schemas/manifest.d.ts +33 -0
- package/dist/schemas/manifest.js +13 -0
- package/dist/types.d.ts +27 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -27,6 +27,8 @@ inception-engine reads a manifest file (`inception.json`) from the target direct
|
|
|
27
27
|
|
|
28
28
|
Managed skills overwrite their previous version. If a target exists but was not created by inception-engine, deployment refuses to replace it. On POSIX systems, symlinks mean updates to the source repo are reflected immediately.
|
|
29
29
|
|
|
30
|
+
Before executing, the deploy command runs preflight analysis on instruction files: it warns when the same agent will have both global and repo `agentRules` active simultaneously, when the same source file is deployed to both scopes (duplicate-content risk), and when `agentRules` or `agentDefinitions` source files exceed 50 KB (context-budget risk). Warnings are printed but do not block deployment.
|
|
31
|
+
|
|
30
32
|
## Agent Compatibility Matrix
|
|
31
33
|
|
|
32
34
|
| Agent | ID | Skills | macOS | Linux | Windows |
|
|
@@ -49,9 +51,11 @@ Managed skills overwrite their previous version. If a target exists but was not
|
|
|
49
51
|
| File write | All agents via manifest and CLI | All agents |
|
|
50
52
|
| Config patch (JSON merge) | All agents via manifest and CLI | All agents |
|
|
51
53
|
| MCP Servers | claude-code, gemini-cli, codex, antigravity, opencode; github-copilot repo-scoped surfaces are warned and skipped | claude-code, gemini-cli, codex, antigravity, opencode |
|
|
52
|
-
| Global/Repo Rules Files | All agents (antigravity uses repo-local `.agents/rules
|
|
54
|
+
| Global/Repo Rules Files | All agents via `scope: "global"` (home-dir) or `scope: "repo"` (project-root); antigravity always uses repo-local `.agents/rules/`; github-copilot reads CLAUDE.md natively (deploy via claude-code) | All agents |
|
|
53
55
|
| Permissions / Approval Config | claude-code (`~/.claude/settings.json`), codex (`~/.codex/config.toml`); other agents are warned and skipped | claude-code, codex |
|
|
54
|
-
|
|
|
56
|
+
| Agent Definitions | claude-code (`{repo}/.claude/agents/{name}.md`), gemini-cli (`{repo}/.gemini/agents/{name}.md`), antigravity (`{repo}/.agents/rules/{name}.md`), opencode (`{repo}/.opencode/agents/{name}.md`), github-copilot (`{repo}/.github/agents/{name}.agent.md`); codex is warned and skipped | All supported agents |
|
|
57
|
+
| `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`); emits hints for `files/` and `configs/` directories | N/A |
|
|
58
|
+
| Instruction preflight analysis | Emits `precedence` warnings when an agent has both global and repo `agentRules` active simultaneously (stacking advisory) or the same source file deployed to both scopes (duplicate-content warning); emits `budget` warnings when `agentRules` or `agentDefinitions` source files exceed 50 KB | N/A |
|
|
55
59
|
|
|
56
60
|
Features that depend on agent-specific config surfaces are intentionally conservative: if a target path or schema is not implemented with enough confidence, inception-engine warns and skips it rather than guessing.
|
|
57
61
|
|
|
@@ -97,7 +101,14 @@ Create an `inception.json` file at the root of your skills directory:
|
|
|
97
101
|
{
|
|
98
102
|
"name": "my-rules",
|
|
99
103
|
"path": "rules/CLAUDE.md",
|
|
100
|
-
"agents": ["claude-code"]
|
|
104
|
+
"agents": ["claude-code"],
|
|
105
|
+
"scope": "global"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "project-rules",
|
|
109
|
+
"path": "rules/CLAUDE.md",
|
|
110
|
+
"agents": ["claude-code", "codex", "gemini-cli", "opencode"],
|
|
111
|
+
"scope": "repo"
|
|
101
112
|
}
|
|
102
113
|
],
|
|
103
114
|
"permissions": [
|
|
@@ -111,6 +122,13 @@ Create an `inception.json` file at the root of your skills directory:
|
|
|
111
122
|
}
|
|
112
123
|
}
|
|
113
124
|
}
|
|
125
|
+
],
|
|
126
|
+
"agentDefinitions": [
|
|
127
|
+
{
|
|
128
|
+
"name": "code-reviewer",
|
|
129
|
+
"path": "agents/code-reviewer.md",
|
|
130
|
+
"agents": ["claude-code", "opencode", "github-copilot"]
|
|
131
|
+
}
|
|
114
132
|
]
|
|
115
133
|
}
|
|
116
134
|
```
|
|
@@ -150,13 +168,27 @@ MCP server registration is supported for all agents except GitHub Copilot. Incep
|
|
|
150
168
|
|
|
151
169
|
Revert removes the registered server entry from the respective configuration file. GitHub Copilot, which uses repo-scoped MCP surfaces not yet implemented by inception-engine, continues to emit a schema-aware warning and is skipped.
|
|
152
170
|
|
|
153
|
-
Each **agentRules** entry deploys a Markdown instruction file to an agent's supported
|
|
171
|
+
Each **agentRules** entry deploys a Markdown instruction file to an agent's supported instruction file location:
|
|
154
172
|
|
|
155
173
|
- **name** - Unique identifier (same format as skill names)
|
|
156
|
-
- **path** - Relative path to the source Markdown file within the repo; supported
|
|
174
|
+
- **path** - Relative path to the source Markdown file within the repo; supported rules adapters require a `.md` or `.markdown` source path
|
|
157
175
|
- **agents** - Array of agent IDs to deploy this file to
|
|
176
|
+
- **scope** - `"global"` (default) or `"repo"`. Controls which instruction surface is targeted:
|
|
177
|
+
- `"global"` — deploys to the agent's home-directory instruction file (e.g., `~/.claude/CLAUDE.md` for `claude-code`)
|
|
178
|
+
- `"repo"` — deploys to the project-root instruction file inside the deployed repository (e.g., `{repo}/CLAUDE.md` for `claude-code`)
|
|
179
|
+
|
|
180
|
+
Instruction rule deployment is supported for all agents. The target path depends on the agent and the `scope`:
|
|
181
|
+
|
|
182
|
+
| Agent | `scope: "global"` | `scope: "repo"` |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| `claude-code` | `~/.claude/CLAUDE.md` | `{repo}/CLAUDE.md` |
|
|
185
|
+
| `codex` | `~/.codex/AGENTS.md` | `{repo}/AGENTS.md` |
|
|
186
|
+
| `gemini-cli` | `~/.gemini/GEMINI.md` | `{repo}/GEMINI.md` |
|
|
187
|
+
| `antigravity` | `{repo}/.agents/rules/{name}.md` | `{repo}/.agents/rules/{name}.md` |
|
|
188
|
+
| `opencode` | `~/.config/opencode/AGENTS.md` | `{repo}/AGENTS.md` |
|
|
189
|
+
| `github-copilot` | unsupported — reads `CLAUDE.md` natively | unsupported — deploy via `claude-code` |
|
|
158
190
|
|
|
159
|
-
|
|
191
|
+
For `antigravity`, both scopes target the same repo-local surface (`{repo}/.agents/rules/{name}.md`) since Antigravity has no global home-directory instruction file. For `github-copilot`, no separate deployment is needed for either scope — target it via the `claude-code` agentRules entry and it reaches Copilot automatically. Revert removes the deployed rules file.
|
|
160
192
|
|
|
161
193
|
Each **permissions** entry deploys execution and safety-oriented configuration to an agent's permission or approval surface:
|
|
162
194
|
|
|
@@ -199,6 +231,23 @@ Valid `approval_policy` values are `"auto"`, `"manual"`, `"suggest"`, and `"on-f
|
|
|
199
231
|
|
|
200
232
|
Revert restores the previous config values using the undo patch recorded at deploy time.
|
|
201
233
|
|
|
234
|
+
Each **agentDefinitions** entry deploys a Markdown agent-definition file to the repo-local agent-definitions directory of each targeted agent:
|
|
235
|
+
|
|
236
|
+
- **name** - Unique identifier (same format as skill names); used as the definition filename
|
|
237
|
+
- **path** - Relative path to the source Markdown file within the repo; must be a `.md` or `.markdown` file
|
|
238
|
+
- **agents** - Array of agent IDs to deploy this definition to
|
|
239
|
+
|
|
240
|
+
Agent-definition deployment is supported for `claude-code`, `gemini-cli`, `antigravity`, `opencode`, and `github-copilot`. For `codex`, inception-engine emits a warning and skips the entry — use `agentRules` to deploy persona instructions to Codex instead.
|
|
241
|
+
|
|
242
|
+
All targets are repo-local (they land inside the repository being deployed, not the user's home directory):
|
|
243
|
+
- **claude-code**: `{repo}/.claude/agents/{name}.md`
|
|
244
|
+
- **gemini-cli**: `{repo}/.gemini/agents/{name}.md`
|
|
245
|
+
- **antigravity**: `{repo}/.agents/rules/{name}.md`
|
|
246
|
+
- **opencode**: `{repo}/.opencode/agents/{name}.md`
|
|
247
|
+
- **github-copilot**: `{repo}/.github/agents/{name}.agent.md` (note the `.agent.md` suffix)
|
|
248
|
+
|
|
249
|
+
Revert removes the deployed agent-definition file.
|
|
250
|
+
|
|
202
251
|
## Creating Skills
|
|
203
252
|
|
|
204
253
|
Each skill is a directory containing at minimum a `SKILL.md` file with YAML frontmatter:
|
|
@@ -231,6 +280,7 @@ Current `init` behavior:
|
|
|
231
280
|
- Reads `mcp-servers.json` from the repo root (if present) and generates `mcpServers` entries; invalid entries are warned and skipped
|
|
232
281
|
- Reads `files-manifest.json` from the repo root (if present) and generates `files` entries; invalid entries are warned and skipped
|
|
233
282
|
- Reads `configs-manifest.json` from the repo root (if present) and generates `configs` entries; invalid entries are warned and skipped
|
|
283
|
+
- Reads `agent-definitions-manifest.json` from the repo root (if present) and generates `agentDefinitions` entries; if absent, auto-discovers agent-definition Markdown files from `.claude/agents/`, `.gemini/agents/`, `.agents/rules/`, `.opencode/agents/`, and `.github/agents/`; invalid entries are warned and skipped
|
|
234
284
|
- Emits guidance to create a sidecar manifest when a `files/` or `configs/` directory is detected but the corresponding sidecar file is absent
|
|
235
285
|
|
|
236
286
|
Current `init` limitations:
|
|
@@ -280,6 +330,22 @@ Place a `configs-manifest.json` file at the repo root to have `init` populate th
|
|
|
280
330
|
|
|
281
331
|
Invalid entries are warned and skipped; the rest are written into the generated manifest verbatim.
|
|
282
332
|
|
|
333
|
+
Place an `agent-definitions-manifest.json` file at the repo root to have `init` populate the `agentDefinitions` section automatically. The file must be a JSON array of agent-definition entries using the same schema as the `agentDefinitions` field in `inception.json`:
|
|
334
|
+
|
|
335
|
+
```json
|
|
336
|
+
[
|
|
337
|
+
{
|
|
338
|
+
"name": "code-reviewer",
|
|
339
|
+
"path": "agents/code-reviewer.md",
|
|
340
|
+
"agents": ["claude-code", "opencode", "github-copilot"]
|
|
341
|
+
}
|
|
342
|
+
]
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
If no `agent-definitions-manifest.json` is present, `init` auto-discovers agent-definition Markdown files from five conventional subdirectories: `.claude/agents/`, `.gemini/agents/`, `.agents/rules/`, `.opencode/agents/`, and `.github/agents/`. Each discovered file is mapped to the owning agent(s). GitHub Copilot files named `{name}.agent.md` have the `.agent` infix stripped to derive the manifest name (e.g., `foo.agent.md` → name `foo`).
|
|
346
|
+
|
|
347
|
+
Invalid entries are warned and skipped; the rest are written into the generated manifest verbatim.
|
|
348
|
+
|
|
283
349
|
## CLI Reference
|
|
284
350
|
|
|
285
351
|
```
|
package/dist/config/agents.js
CHANGED
|
@@ -18,6 +18,7 @@ export const AGENT_REGISTRY = [
|
|
|
18
18
|
mcpConfig: "documented",
|
|
19
19
|
agentRules: "documented",
|
|
20
20
|
permissions: "documented",
|
|
21
|
+
agentDefinitions: "documented",
|
|
21
22
|
},
|
|
22
23
|
mcpSupport: {
|
|
23
24
|
status: "supported",
|
|
@@ -35,6 +36,14 @@ export const AGENT_REGISTRY = [
|
|
|
35
36
|
windows: ["{home}", ".claude", "CLAUDE.md"],
|
|
36
37
|
},
|
|
37
38
|
},
|
|
39
|
+
agentRulesRepoSupport: {
|
|
40
|
+
status: "supported",
|
|
41
|
+
schemaLabel: "repo-local CLAUDE.md",
|
|
42
|
+
path: {
|
|
43
|
+
posix: ["{repo}", "CLAUDE.md"],
|
|
44
|
+
windows: ["{repo}", "CLAUDE.md"],
|
|
45
|
+
},
|
|
46
|
+
},
|
|
38
47
|
permissionsSupport: {
|
|
39
48
|
status: "supported",
|
|
40
49
|
schemaLabel: "JSON permissions config",
|
|
@@ -43,6 +52,14 @@ export const AGENT_REGISTRY = [
|
|
|
43
52
|
windows: ["{home}", ".claude", "settings.json"],
|
|
44
53
|
},
|
|
45
54
|
},
|
|
55
|
+
agentDefinitionsSupport: {
|
|
56
|
+
status: "supported",
|
|
57
|
+
schemaLabel: "repo-local agent definition Markdown file",
|
|
58
|
+
path: {
|
|
59
|
+
posix: ["{repo}", ".claude", "agents", "{name}.md"],
|
|
60
|
+
windows: ["{repo}", ".claude", "agents", "{name}.md"],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
46
63
|
},
|
|
47
64
|
{
|
|
48
65
|
id: "codex",
|
|
@@ -79,6 +96,14 @@ export const AGENT_REGISTRY = [
|
|
|
79
96
|
windows: ["{home}", ".codex", "AGENTS.md"],
|
|
80
97
|
},
|
|
81
98
|
},
|
|
99
|
+
agentRulesRepoSupport: {
|
|
100
|
+
status: "supported",
|
|
101
|
+
schemaLabel: "repo-local AGENTS.md",
|
|
102
|
+
path: {
|
|
103
|
+
posix: ["{repo}", "AGENTS.md"],
|
|
104
|
+
windows: ["{repo}", "AGENTS.md"],
|
|
105
|
+
},
|
|
106
|
+
},
|
|
82
107
|
permissionsSupport: {
|
|
83
108
|
status: "supported",
|
|
84
109
|
schemaLabel: "TOML approval policy config",
|
|
@@ -87,6 +112,11 @@ export const AGENT_REGISTRY = [
|
|
|
87
112
|
windows: ["{home}", ".codex", "config.toml"],
|
|
88
113
|
},
|
|
89
114
|
},
|
|
115
|
+
agentDefinitionsSupport: {
|
|
116
|
+
status: "unsupported",
|
|
117
|
+
schemaLabel: "dedicated agent definition directory",
|
|
118
|
+
reason: "OpenAI Codex does not expose a documented dedicated per-agent definition directory surface separate from AGENTS.md and config.toml — use agentRules to deploy persona instructions instead",
|
|
119
|
+
},
|
|
90
120
|
},
|
|
91
121
|
{
|
|
92
122
|
id: "gemini-cli",
|
|
@@ -106,6 +136,7 @@ export const AGENT_REGISTRY = [
|
|
|
106
136
|
detectBinary: "documented",
|
|
107
137
|
mcpConfig: "documented",
|
|
108
138
|
agentRules: "documented",
|
|
139
|
+
agentDefinitions: "implementation-only",
|
|
109
140
|
},
|
|
110
141
|
mcpSupport: {
|
|
111
142
|
status: "supported",
|
|
@@ -123,11 +154,27 @@ export const AGENT_REGISTRY = [
|
|
|
123
154
|
windows: ["{home}", ".gemini", "GEMINI.md"],
|
|
124
155
|
},
|
|
125
156
|
},
|
|
157
|
+
agentRulesRepoSupport: {
|
|
158
|
+
status: "supported",
|
|
159
|
+
schemaLabel: "repo-local GEMINI.md",
|
|
160
|
+
path: {
|
|
161
|
+
posix: ["{repo}", "GEMINI.md"],
|
|
162
|
+
windows: ["{repo}", "GEMINI.md"],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
126
165
|
permissionsSupport: {
|
|
127
166
|
status: "unsupported",
|
|
128
167
|
schemaLabel: "global permissions surface",
|
|
129
168
|
reason: "Gemini CLI does not expose a documented global per-user permission or approval config surface",
|
|
130
169
|
},
|
|
170
|
+
agentDefinitionsSupport: {
|
|
171
|
+
status: "supported",
|
|
172
|
+
schemaLabel: "repo-local agent definition Markdown file",
|
|
173
|
+
path: {
|
|
174
|
+
posix: ["{repo}", ".gemini", "agents", "{name}.md"],
|
|
175
|
+
windows: ["{repo}", ".gemini", "agents", "{name}.md"],
|
|
176
|
+
},
|
|
177
|
+
},
|
|
131
178
|
},
|
|
132
179
|
{
|
|
133
180
|
id: "antigravity",
|
|
@@ -145,6 +192,8 @@ export const AGENT_REGISTRY = [
|
|
|
145
192
|
skills: "implementation-only",
|
|
146
193
|
detectPaths: "implementation-only",
|
|
147
194
|
detectBinary: "provisional",
|
|
195
|
+
agentRules: "documented",
|
|
196
|
+
agentDefinitions: "documented",
|
|
148
197
|
},
|
|
149
198
|
mcpSupport: {
|
|
150
199
|
status: "supported",
|
|
@@ -162,11 +211,27 @@ export const AGENT_REGISTRY = [
|
|
|
162
211
|
windows: ["{repo}", ".agents", "rules", "{name}.md"],
|
|
163
212
|
},
|
|
164
213
|
},
|
|
214
|
+
agentRulesRepoSupport: {
|
|
215
|
+
status: "supported",
|
|
216
|
+
schemaLabel: "repo-local Markdown rules file",
|
|
217
|
+
path: {
|
|
218
|
+
posix: ["{repo}", ".agents", "rules", "{name}.md"],
|
|
219
|
+
windows: ["{repo}", ".agents", "rules", "{name}.md"],
|
|
220
|
+
},
|
|
221
|
+
},
|
|
165
222
|
permissionsSupport: {
|
|
166
223
|
status: "unsupported",
|
|
167
224
|
schemaLabel: "global permissions surface",
|
|
168
225
|
reason: "Antigravity does not expose a documented global per-user permission or approval config surface",
|
|
169
226
|
},
|
|
227
|
+
agentDefinitionsSupport: {
|
|
228
|
+
status: "supported",
|
|
229
|
+
schemaLabel: "repo-local agent definition Markdown file",
|
|
230
|
+
path: {
|
|
231
|
+
posix: ["{repo}", ".agents", "rules", "{name}.md"],
|
|
232
|
+
windows: ["{repo}", ".agents", "rules", "{name}.md"],
|
|
233
|
+
},
|
|
234
|
+
},
|
|
170
235
|
},
|
|
171
236
|
{
|
|
172
237
|
id: "opencode",
|
|
@@ -185,6 +250,7 @@ export const AGENT_REGISTRY = [
|
|
|
185
250
|
detectPaths: "documented",
|
|
186
251
|
detectBinary: "documented",
|
|
187
252
|
agentRules: "documented",
|
|
253
|
+
agentDefinitions: "documented",
|
|
188
254
|
},
|
|
189
255
|
mcpSupport: {
|
|
190
256
|
status: "supported",
|
|
@@ -203,45 +269,75 @@ export const AGENT_REGISTRY = [
|
|
|
203
269
|
windows: ["{appdata}", "opencode", "AGENTS.md"],
|
|
204
270
|
},
|
|
205
271
|
},
|
|
272
|
+
agentRulesRepoSupport: {
|
|
273
|
+
status: "supported",
|
|
274
|
+
schemaLabel: "repo-local AGENTS.md",
|
|
275
|
+
path: {
|
|
276
|
+
posix: ["{repo}", "AGENTS.md"],
|
|
277
|
+
windows: ["{repo}", "AGENTS.md"],
|
|
278
|
+
},
|
|
279
|
+
},
|
|
206
280
|
permissionsSupport: {
|
|
207
281
|
status: "unsupported",
|
|
208
282
|
schemaLabel: "global permissions surface",
|
|
209
283
|
reason: "OpenCode does not expose a documented global per-user permission or approval config surface",
|
|
210
284
|
},
|
|
285
|
+
agentDefinitionsSupport: {
|
|
286
|
+
status: "supported",
|
|
287
|
+
schemaLabel: "repo-local agent definition Markdown file",
|
|
288
|
+
path: {
|
|
289
|
+
posix: ["{repo}", ".opencode", "agents", "{name}.md"],
|
|
290
|
+
windows: ["{repo}", ".opencode", "agents", "{name}.md"],
|
|
291
|
+
},
|
|
292
|
+
},
|
|
211
293
|
},
|
|
212
294
|
{
|
|
213
295
|
id: "github-copilot",
|
|
214
296
|
displayName: "GitHub Copilot",
|
|
215
|
-
skills:
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
297
|
+
// No `skills` field: GitHub Copilot natively executes Claude-style skills
|
|
298
|
+
// from `.claude/skills/` (the same path used by claude-code). Deploying
|
|
299
|
+
// via the `claude-code` skills target automatically covers Copilot — no
|
|
300
|
+
// separate `~/.copilot/skills/` path is needed or maintained.
|
|
219
301
|
detectPaths: {
|
|
220
302
|
posix: ["{home}", ".copilot"],
|
|
221
303
|
windows: ["{home}", ".copilot"],
|
|
222
304
|
},
|
|
223
305
|
detectBinary: "github-copilot",
|
|
224
306
|
provenance: {
|
|
225
|
-
skills: "documented",
|
|
226
307
|
detectPaths: "documented",
|
|
227
308
|
detectBinary: "documented",
|
|
309
|
+
agentDefinitions: "documented",
|
|
228
310
|
},
|
|
229
311
|
mcpSupport: {
|
|
230
|
-
status: "
|
|
312
|
+
status: "planned",
|
|
231
313
|
schemaLabel: "repo-scoped MCP surfaces",
|
|
232
|
-
|
|
314
|
+
plannedSurface: "devcontainer (.devcontainer/devcontainer.json) and agent-frontmatter (.github/agents/*.agent.md)",
|
|
315
|
+
reason: "GitHub Copilot MCP support will be implemented via repo-scoped devcontainer features and agent-frontmatter mappings — surfaces that are genuinely Copilot-specific and not covered by other agent targets",
|
|
233
316
|
},
|
|
234
317
|
agentRulesSupport: {
|
|
235
318
|
status: "unsupported",
|
|
236
319
|
schemaLabel: "Claude-native shared instructions",
|
|
237
320
|
reason: 'GitHub Copilot reads CLAUDE.md natively, so deploy via the "claude-code" agentRules target instead of a separate rules surface',
|
|
238
321
|
},
|
|
322
|
+
agentRulesRepoSupport: {
|
|
323
|
+
status: "unsupported",
|
|
324
|
+
schemaLabel: "repo-local CLAUDE.md",
|
|
325
|
+
reason: 'GitHub Copilot reads CLAUDE.md natively, so deploy via the "claude-code" agentRules target with scope: "repo" instead of a separate rules surface',
|
|
326
|
+
},
|
|
239
327
|
permissionsSupport: {
|
|
240
328
|
status: "unsupported",
|
|
241
329
|
schemaLabel: "global permissions surface",
|
|
242
330
|
reason: "GitHub Copilot permissions are managed via organization policy, not a deployable per-user config surface",
|
|
243
331
|
},
|
|
244
|
-
|
|
332
|
+
agentDefinitionsSupport: {
|
|
333
|
+
status: "supported",
|
|
334
|
+
schemaLabel: "repo-local agent definition Markdown file",
|
|
335
|
+
path: {
|
|
336
|
+
posix: ["{repo}", ".github", "agents", "{name}.agent.md"],
|
|
337
|
+
windows: ["{repo}", ".github", "agents", "{name}.agent.md"],
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
policyNote: "Organization policies may override locally deployed configuration. Verify with your GitHub org admin if deployed skills or rules are not active.",
|
|
245
341
|
},
|
|
246
342
|
];
|
|
247
343
|
export const AGENT_REGISTRY_BY_ID = Object.fromEntries(AGENT_REGISTRY.map((a) => [a.id, a]));
|
package/dist/config/manifest.js
CHANGED
|
@@ -42,6 +42,8 @@ function validateManifest(data, filePath) {
|
|
|
42
42
|
if (issuePath.length === 1 &&
|
|
43
43
|
(issuePath[0] === "mcpServers" ||
|
|
44
44
|
issuePath[0] === "agentRules" ||
|
|
45
|
+
issuePath[0] === "agentDefinitions" ||
|
|
46
|
+
issuePath[0] === "permissions" ||
|
|
45
47
|
issuePath[0] === "files" ||
|
|
46
48
|
issuePath[0] === "configs")) {
|
|
47
49
|
throw new UserError("MANIFEST_INVALID", `${filePath}: "${issuePath[0]}" must be an array`);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AgentDefinitionEntry } from "../../schemas/manifest.ts";
|
|
2
|
+
import type { AgentId, FileWriteDeployAction, FileWriteRevertAction, PlanWarning } from "../../types.ts";
|
|
3
|
+
export interface AgentDefinitionsAdapterResult {
|
|
4
|
+
actions: FileWriteDeployAction[];
|
|
5
|
+
warnings: PlanWarning[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Compiles deploy actions for agentDefinitions manifest entries.
|
|
9
|
+
*
|
|
10
|
+
* Agent definition files are Markdown files (typically with YAML frontmatter)
|
|
11
|
+
* that define custom agents or subagents for a given platform. Unlike
|
|
12
|
+
* agentRules (which target a single shared global file per agent), each
|
|
13
|
+
* definition entry produces a separate file named after the entry in the
|
|
14
|
+
* agent's dedicated agent-definitions directory (repo-local).
|
|
15
|
+
*
|
|
16
|
+
* Target paths use the `{repo}` placeholder so definitions land in the
|
|
17
|
+
* repository being deployed, not the user's home directory.
|
|
18
|
+
*/
|
|
19
|
+
export declare function compileAgentDefinitionActions(entry: AgentDefinitionEntry, sourceDir: string, resolvedSourceDir: string, realRoot: string, detectedAgents: AgentId[], home: string, repo?: string): Promise<AgentDefinitionsAdapterResult>;
|
|
20
|
+
export declare function compileAgentDefinitionReverts(entry: AgentDefinitionEntry, agentFilter: AgentId[] | null, home: string, repo?: string): FileWriteRevertAction[];
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { AGENT_REGISTRY_BY_ID } from "../../config/agents.js";
|
|
3
|
+
import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
|
|
4
|
+
import { validateAgentRuleMarkdownPath, validateSourceFile, validateSourcePath, } from "../validation.js";
|
|
5
|
+
/**
|
|
6
|
+
* Compiles deploy actions for agentDefinitions manifest entries.
|
|
7
|
+
*
|
|
8
|
+
* Agent definition files are Markdown files (typically with YAML frontmatter)
|
|
9
|
+
* that define custom agents or subagents for a given platform. Unlike
|
|
10
|
+
* agentRules (which target a single shared global file per agent), each
|
|
11
|
+
* definition entry produces a separate file named after the entry in the
|
|
12
|
+
* agent's dedicated agent-definitions directory (repo-local).
|
|
13
|
+
*
|
|
14
|
+
* Target paths use the `{repo}` placeholder so definitions land in the
|
|
15
|
+
* repository being deployed, not the user's home directory.
|
|
16
|
+
*/
|
|
17
|
+
export async function compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo) {
|
|
18
|
+
const actions = [];
|
|
19
|
+
const warnings = [];
|
|
20
|
+
const platform = getPlatformKey();
|
|
21
|
+
const targetAgents = entry.agents.filter((agentId) => detectedAgents.includes(agentId));
|
|
22
|
+
const supportedTargets = [];
|
|
23
|
+
if (targetAgents.length === 0) {
|
|
24
|
+
return { actions, warnings };
|
|
25
|
+
}
|
|
26
|
+
for (const agentId of targetAgents) {
|
|
27
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
28
|
+
const support = agent?.agentDefinitionsSupport;
|
|
29
|
+
if (!support || support.status === "unsupported") {
|
|
30
|
+
warnings.push({
|
|
31
|
+
kind: "confidence",
|
|
32
|
+
message: `agentDefinitions: agent "${agentId}" uses ${support?.schemaLabel ?? "an unsupported agent-definition schema"} and ${support?.status === "unsupported" ? support.reason : "does not expose a supported agent-definitions adapter"} — skipping "${entry.name}"`,
|
|
33
|
+
});
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (support.status === "planned") {
|
|
37
|
+
warnings.push({
|
|
38
|
+
kind: "confidence",
|
|
39
|
+
message: `agentDefinitions: agent "${agentId}" agent-definitions support is planned via ${support.plannedSurface} — skipping "${entry.name}" until that surface is implemented`,
|
|
40
|
+
});
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
supportedTargets.push({
|
|
44
|
+
agentId,
|
|
45
|
+
confidence: agent.provenance.agentDefinitions ?? "provisional",
|
|
46
|
+
target: resolvePlaceholders(support.path[platform], entry.name, home, repo),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (supportedTargets.length === 0) {
|
|
50
|
+
return { actions, warnings };
|
|
51
|
+
}
|
|
52
|
+
// Validate the shared source file only when at least one target is active.
|
|
53
|
+
const source = path.resolve(sourceDir, entry.path);
|
|
54
|
+
await validateSourcePath(source, entry.path, resolvedSourceDir, realRoot);
|
|
55
|
+
await validateSourceFile(source, entry.path);
|
|
56
|
+
for (const target of supportedTargets) {
|
|
57
|
+
validateAgentRuleMarkdownPath(entry.path, target.agentId);
|
|
58
|
+
actions.push({
|
|
59
|
+
kind: "file-write",
|
|
60
|
+
skill: entry.name,
|
|
61
|
+
agent: target.agentId,
|
|
62
|
+
source,
|
|
63
|
+
target: target.target,
|
|
64
|
+
confidence: target.confidence,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return { actions, warnings };
|
|
68
|
+
}
|
|
69
|
+
export function compileAgentDefinitionReverts(entry, agentFilter, home, repo) {
|
|
70
|
+
const actions = [];
|
|
71
|
+
const platform = getPlatformKey();
|
|
72
|
+
for (const agentId of entry.agents) {
|
|
73
|
+
if (agentFilter && !agentFilter.includes(agentId))
|
|
74
|
+
continue;
|
|
75
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
76
|
+
const support = agent?.agentDefinitionsSupport;
|
|
77
|
+
if (!support ||
|
|
78
|
+
support.status === "unsupported" ||
|
|
79
|
+
support.status === "planned")
|
|
80
|
+
continue;
|
|
81
|
+
const target = resolvePlaceholders(support.path[platform], entry.name, home, repo);
|
|
82
|
+
actions.push({
|
|
83
|
+
kind: "file-write",
|
|
84
|
+
skill: entry.name,
|
|
85
|
+
agent: agentId,
|
|
86
|
+
target,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return actions;
|
|
90
|
+
}
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Splits a Markdown string into YAML frontmatter and the remaining body.
|
|
3
|
+
* Expects the file to start with --- delimiter.
|
|
4
|
+
*/
|
|
5
|
+
export declare function splitFrontmatter(raw: string): {
|
|
6
|
+
frontmatter: string;
|
|
7
|
+
body: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Parses a Markdown string with YAML frontmatter.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseFrontmatterDocument<T = Record<string, unknown>>(raw: string): {
|
|
13
|
+
attributes: T;
|
|
14
|
+
body: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Serializes an object to a YAML block.
|
|
4
18
|
*/
|
|
5
19
|
export declare function serializeFrontmatter(data: Record<string, unknown>): string;
|
|
6
20
|
/**
|
|
7
21
|
* Builds the full content of a frontmatter-bearing Markdown file.
|
|
8
|
-
* If `body` is provided it is appended after the closing delimiter.
|
|
9
22
|
*/
|
|
10
23
|
export declare function buildFrontmatterDocument(frontmatter: Record<string, unknown>, body?: string): string;
|
|
11
24
|
/**
|
|
12
|
-
* Reads an existing `.md` file and parses its frontmatter
|
|
13
|
-
* `front-matter` package). Returns the parsed attributes and raw body.
|
|
25
|
+
* Reads an existing `.md` file and parses its frontmatter.
|
|
14
26
|
* Returns `{ attributes: {}, body: "" }` if the file does not exist.
|
|
15
27
|
*/
|
|
16
28
|
export declare function readFrontmatterFile(filePath: string): Promise<{
|
|
@@ -1,71 +1,52 @@
|
|
|
1
1
|
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (typeof value === "string") {
|
|
14
|
-
const needsQuotes = /[:#[\]{},|>&*!'"@`]|^\s|\s$/.test(value);
|
|
15
|
-
return needsQuotes
|
|
16
|
-
? `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`
|
|
17
|
-
: value;
|
|
3
|
+
import YAML from "yaml";
|
|
4
|
+
/**
|
|
5
|
+
* Splits a Markdown string into YAML frontmatter and the remaining body.
|
|
6
|
+
* Expects the file to start with --- delimiter.
|
|
7
|
+
*/
|
|
8
|
+
export function splitFrontmatter(raw) {
|
|
9
|
+
const lines = raw.split(/\r?\n/);
|
|
10
|
+
if (lines[0]?.trim() !== "---") {
|
|
11
|
+
return { frontmatter: "", body: raw };
|
|
18
12
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
? serializeScalar(item)
|
|
29
|
-
: JSON.stringify(item);
|
|
30
|
-
return `${pad} - ${scalar}`;
|
|
31
|
-
});
|
|
32
|
-
return [`${pad}${key}:`, ...items].join("\n");
|
|
33
|
-
}
|
|
34
|
-
function serializeObject(key, obj, pad) {
|
|
35
|
-
const inner = serializeFrontmatterAtDepth(obj, `${pad} `);
|
|
36
|
-
return inner ? `${pad}${key}:\n${inner}` : `${pad}${key}: {}`;
|
|
13
|
+
const closingIndex = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
14
|
+
if (closingIndex === -1) {
|
|
15
|
+
return { frontmatter: "", body: raw };
|
|
16
|
+
}
|
|
17
|
+
// Preserve the actual lines for the body to avoid normalizing line endings if not needed
|
|
18
|
+
// but for frontmatter block, we join with \n for the YAML parser
|
|
19
|
+
const frontmatter = lines.slice(1, closingIndex).join("\n");
|
|
20
|
+
const body = lines.slice(closingIndex + 1).join("\n");
|
|
21
|
+
return { frontmatter, body };
|
|
37
22
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
else if (typeof value === "object") {
|
|
47
|
-
lines.push(serializeObject(key, value, pad));
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
lines.push(`${pad}${key}: ${serializeScalar(value)}`);
|
|
51
|
-
}
|
|
23
|
+
/**
|
|
24
|
+
* Parses a Markdown string with YAML frontmatter.
|
|
25
|
+
*/
|
|
26
|
+
export function parseFrontmatterDocument(raw) {
|
|
27
|
+
const { frontmatter, body } = splitFrontmatter(raw);
|
|
28
|
+
if (!(frontmatter || raw.startsWith("---"))) {
|
|
29
|
+
return { attributes: {}, body };
|
|
52
30
|
}
|
|
53
|
-
|
|
31
|
+
const attributes = YAML.parse(frontmatter);
|
|
32
|
+
return { attributes: (attributes || {}), body };
|
|
54
33
|
}
|
|
55
34
|
/**
|
|
56
|
-
* Serializes
|
|
57
|
-
* `---` frontmatter delimiters.
|
|
35
|
+
* Serializes an object to a YAML block.
|
|
58
36
|
*/
|
|
59
37
|
export function serializeFrontmatter(data) {
|
|
60
|
-
|
|
38
|
+
// Using a consistent indentation that matches standard YAML
|
|
39
|
+
return YAML.stringify(data, { indent: 2 }).trim();
|
|
61
40
|
}
|
|
62
41
|
/**
|
|
63
42
|
* Builds the full content of a frontmatter-bearing Markdown file.
|
|
64
|
-
* If `body` is provided it is appended after the closing delimiter.
|
|
65
43
|
*/
|
|
66
44
|
export function buildFrontmatterDocument(frontmatter, body = "") {
|
|
67
45
|
const serialized = serializeFrontmatter(frontmatter);
|
|
68
|
-
|
|
46
|
+
// Ensure exactly one newline after the closing delimiter before the body
|
|
47
|
+
const cleanBody = body.trimStart();
|
|
48
|
+
const separator = cleanBody ? "\n\n" : "\n";
|
|
49
|
+
return `---\n${serialized}\n---\n${separator}${cleanBody}`;
|
|
69
50
|
}
|
|
70
51
|
function createAtomicTempPath(targetPath) {
|
|
71
52
|
return `${targetPath}.inception-tmp-${process.pid}-${Date.now()}-${Math.random()
|
|
@@ -73,8 +54,7 @@ function createAtomicTempPath(targetPath) {
|
|
|
73
54
|
.slice(2)}`;
|
|
74
55
|
}
|
|
75
56
|
/**
|
|
76
|
-
* Reads an existing `.md` file and parses its frontmatter
|
|
77
|
-
* `front-matter` package). Returns the parsed attributes and raw body.
|
|
57
|
+
* Reads an existing `.md` file and parses its frontmatter.
|
|
78
58
|
* Returns `{ attributes: {}, body: "" }` if the file does not exist.
|
|
79
59
|
*/
|
|
80
60
|
export async function readFrontmatterFile(filePath) {
|
|
@@ -88,8 +68,7 @@ export async function readFrontmatterFile(filePath) {
|
|
|
88
68
|
return { attributes: {}, body: "" };
|
|
89
69
|
throw err;
|
|
90
70
|
}
|
|
91
|
-
|
|
92
|
-
return { attributes: parsed.attributes, body: parsed.body };
|
|
71
|
+
return parseFrontmatterDocument(raw);
|
|
93
72
|
}
|
|
94
73
|
/**
|
|
95
74
|
* Atomically writes a Markdown file with the given frontmatter and optional
|