@kuznai/inception-engine 0.16.0 → 0.17.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 +43 -1
- package/dist/config/agents.js +59 -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/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 +10 -1
- package/dist/core/deploy.js +5 -3
- package/dist/core/init.js +179 -9
- package/dist/core/resolve.js +4 -0
- package/dist/core/revert.js +5 -1
- package/dist/schemas/manifest.d.ts +25 -0
- package/dist/schemas/manifest.js +9 -0
- package/dist/types.d.ts +26 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,7 +51,8 @@ Managed skills overwrite their previous version. If a target exists but was not
|
|
|
51
51
|
| 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
52
|
| Global/Repo Rules Files | All agents (antigravity uses repo-local `.agents/rules/`); github-copilot reads CLAUDE.md natively (deploy via claude-code) | All agents |
|
|
53
53
|
| Permissions / Approval Config | claude-code (`~/.claude/settings.json`), codex (`~/.codex/config.toml`); other agents are warned and skipped | claude-code, codex |
|
|
54
|
-
|
|
|
54
|
+
| 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 |
|
|
55
|
+
| `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 |
|
|
55
56
|
|
|
56
57
|
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
58
|
|
|
@@ -111,6 +112,13 @@ Create an `inception.json` file at the root of your skills directory:
|
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
114
|
}
|
|
115
|
+
],
|
|
116
|
+
"agentDefinitions": [
|
|
117
|
+
{
|
|
118
|
+
"name": "code-reviewer",
|
|
119
|
+
"path": "agents/code-reviewer.md",
|
|
120
|
+
"agents": ["claude-code", "opencode", "github-copilot"]
|
|
121
|
+
}
|
|
114
122
|
]
|
|
115
123
|
}
|
|
116
124
|
```
|
|
@@ -199,6 +207,23 @@ Valid `approval_policy` values are `"auto"`, `"manual"`, `"suggest"`, and `"on-f
|
|
|
199
207
|
|
|
200
208
|
Revert restores the previous config values using the undo patch recorded at deploy time.
|
|
201
209
|
|
|
210
|
+
Each **agentDefinitions** entry deploys a Markdown agent-definition file to the repo-local agent-definitions directory of each targeted agent:
|
|
211
|
+
|
|
212
|
+
- **name** - Unique identifier (same format as skill names); used as the definition filename
|
|
213
|
+
- **path** - Relative path to the source Markdown file within the repo; must be a `.md` or `.markdown` file
|
|
214
|
+
- **agents** - Array of agent IDs to deploy this definition to
|
|
215
|
+
|
|
216
|
+
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.
|
|
217
|
+
|
|
218
|
+
All targets are repo-local (they land inside the repository being deployed, not the user's home directory):
|
|
219
|
+
- **claude-code**: `{repo}/.claude/agents/{name}.md`
|
|
220
|
+
- **gemini-cli**: `{repo}/.gemini/agents/{name}.md`
|
|
221
|
+
- **antigravity**: `{repo}/.agents/rules/{name}.md`
|
|
222
|
+
- **opencode**: `{repo}/.opencode/agents/{name}.md`
|
|
223
|
+
- **github-copilot**: `{repo}/.github/agents/{name}.agent.md` (note the `.agent.md` suffix)
|
|
224
|
+
|
|
225
|
+
Revert removes the deployed agent-definition file.
|
|
226
|
+
|
|
202
227
|
## Creating Skills
|
|
203
228
|
|
|
204
229
|
Each skill is a directory containing at minimum a `SKILL.md` file with YAML frontmatter:
|
|
@@ -231,6 +256,7 @@ Current `init` behavior:
|
|
|
231
256
|
- Reads `mcp-servers.json` from the repo root (if present) and generates `mcpServers` entries; invalid entries are warned and skipped
|
|
232
257
|
- Reads `files-manifest.json` from the repo root (if present) and generates `files` entries; invalid entries are warned and skipped
|
|
233
258
|
- Reads `configs-manifest.json` from the repo root (if present) and generates `configs` entries; invalid entries are warned and skipped
|
|
259
|
+
- 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
260
|
- Emits guidance to create a sidecar manifest when a `files/` or `configs/` directory is detected but the corresponding sidecar file is absent
|
|
235
261
|
|
|
236
262
|
Current `init` limitations:
|
|
@@ -280,6 +306,22 @@ Place a `configs-manifest.json` file at the repo root to have `init` populate th
|
|
|
280
306
|
|
|
281
307
|
Invalid entries are warned and skipped; the rest are written into the generated manifest verbatim.
|
|
282
308
|
|
|
309
|
+
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`:
|
|
310
|
+
|
|
311
|
+
```json
|
|
312
|
+
[
|
|
313
|
+
{
|
|
314
|
+
"name": "code-reviewer",
|
|
315
|
+
"path": "agents/code-reviewer.md",
|
|
316
|
+
"agents": ["claude-code", "opencode", "github-copilot"]
|
|
317
|
+
}
|
|
318
|
+
]
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
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`).
|
|
322
|
+
|
|
323
|
+
Invalid entries are warned and skipped; the rest are written into the generated manifest verbatim.
|
|
324
|
+
|
|
283
325
|
## CLI Reference
|
|
284
326
|
|
|
285
327
|
```
|
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",
|
|
@@ -43,6 +44,14 @@ export const AGENT_REGISTRY = [
|
|
|
43
44
|
windows: ["{home}", ".claude", "settings.json"],
|
|
44
45
|
},
|
|
45
46
|
},
|
|
47
|
+
agentDefinitionsSupport: {
|
|
48
|
+
status: "supported",
|
|
49
|
+
schemaLabel: "repo-local agent definition Markdown file",
|
|
50
|
+
path: {
|
|
51
|
+
posix: ["{repo}", ".claude", "agents", "{name}.md"],
|
|
52
|
+
windows: ["{repo}", ".claude", "agents", "{name}.md"],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
46
55
|
},
|
|
47
56
|
{
|
|
48
57
|
id: "codex",
|
|
@@ -87,6 +96,11 @@ export const AGENT_REGISTRY = [
|
|
|
87
96
|
windows: ["{home}", ".codex", "config.toml"],
|
|
88
97
|
},
|
|
89
98
|
},
|
|
99
|
+
agentDefinitionsSupport: {
|
|
100
|
+
status: "unsupported",
|
|
101
|
+
schemaLabel: "dedicated agent definition directory",
|
|
102
|
+
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",
|
|
103
|
+
},
|
|
90
104
|
},
|
|
91
105
|
{
|
|
92
106
|
id: "gemini-cli",
|
|
@@ -106,6 +120,7 @@ export const AGENT_REGISTRY = [
|
|
|
106
120
|
detectBinary: "documented",
|
|
107
121
|
mcpConfig: "documented",
|
|
108
122
|
agentRules: "documented",
|
|
123
|
+
agentDefinitions: "implementation-only",
|
|
109
124
|
},
|
|
110
125
|
mcpSupport: {
|
|
111
126
|
status: "supported",
|
|
@@ -128,6 +143,14 @@ export const AGENT_REGISTRY = [
|
|
|
128
143
|
schemaLabel: "global permissions surface",
|
|
129
144
|
reason: "Gemini CLI does not expose a documented global per-user permission or approval config surface",
|
|
130
145
|
},
|
|
146
|
+
agentDefinitionsSupport: {
|
|
147
|
+
status: "supported",
|
|
148
|
+
schemaLabel: "repo-local agent definition Markdown file",
|
|
149
|
+
path: {
|
|
150
|
+
posix: ["{repo}", ".gemini", "agents", "{name}.md"],
|
|
151
|
+
windows: ["{repo}", ".gemini", "agents", "{name}.md"],
|
|
152
|
+
},
|
|
153
|
+
},
|
|
131
154
|
},
|
|
132
155
|
{
|
|
133
156
|
id: "antigravity",
|
|
@@ -145,6 +168,8 @@ export const AGENT_REGISTRY = [
|
|
|
145
168
|
skills: "implementation-only",
|
|
146
169
|
detectPaths: "implementation-only",
|
|
147
170
|
detectBinary: "provisional",
|
|
171
|
+
agentRules: "documented",
|
|
172
|
+
agentDefinitions: "documented",
|
|
148
173
|
},
|
|
149
174
|
mcpSupport: {
|
|
150
175
|
status: "supported",
|
|
@@ -167,6 +192,14 @@ export const AGENT_REGISTRY = [
|
|
|
167
192
|
schemaLabel: "global permissions surface",
|
|
168
193
|
reason: "Antigravity does not expose a documented global per-user permission or approval config surface",
|
|
169
194
|
},
|
|
195
|
+
agentDefinitionsSupport: {
|
|
196
|
+
status: "supported",
|
|
197
|
+
schemaLabel: "repo-local agent definition Markdown file",
|
|
198
|
+
path: {
|
|
199
|
+
posix: ["{repo}", ".agents", "rules", "{name}.md"],
|
|
200
|
+
windows: ["{repo}", ".agents", "rules", "{name}.md"],
|
|
201
|
+
},
|
|
202
|
+
},
|
|
170
203
|
},
|
|
171
204
|
{
|
|
172
205
|
id: "opencode",
|
|
@@ -185,6 +218,7 @@ export const AGENT_REGISTRY = [
|
|
|
185
218
|
detectPaths: "documented",
|
|
186
219
|
detectBinary: "documented",
|
|
187
220
|
agentRules: "documented",
|
|
221
|
+
agentDefinitions: "documented",
|
|
188
222
|
},
|
|
189
223
|
mcpSupport: {
|
|
190
224
|
status: "supported",
|
|
@@ -208,28 +242,37 @@ export const AGENT_REGISTRY = [
|
|
|
208
242
|
schemaLabel: "global permissions surface",
|
|
209
243
|
reason: "OpenCode does not expose a documented global per-user permission or approval config surface",
|
|
210
244
|
},
|
|
245
|
+
agentDefinitionsSupport: {
|
|
246
|
+
status: "supported",
|
|
247
|
+
schemaLabel: "repo-local agent definition Markdown file",
|
|
248
|
+
path: {
|
|
249
|
+
posix: ["{repo}", ".opencode", "agents", "{name}.md"],
|
|
250
|
+
windows: ["{repo}", ".opencode", "agents", "{name}.md"],
|
|
251
|
+
},
|
|
252
|
+
},
|
|
211
253
|
},
|
|
212
254
|
{
|
|
213
255
|
id: "github-copilot",
|
|
214
256
|
displayName: "GitHub Copilot",
|
|
215
|
-
skills:
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
257
|
+
// No `skills` field: GitHub Copilot natively executes Claude-style skills
|
|
258
|
+
// from `.claude/skills/` (the same path used by claude-code). Deploying
|
|
259
|
+
// via the `claude-code` skills target automatically covers Copilot — no
|
|
260
|
+
// separate `~/.copilot/skills/` path is needed or maintained.
|
|
219
261
|
detectPaths: {
|
|
220
262
|
posix: ["{home}", ".copilot"],
|
|
221
263
|
windows: ["{home}", ".copilot"],
|
|
222
264
|
},
|
|
223
265
|
detectBinary: "github-copilot",
|
|
224
266
|
provenance: {
|
|
225
|
-
skills: "documented",
|
|
226
267
|
detectPaths: "documented",
|
|
227
268
|
detectBinary: "documented",
|
|
269
|
+
agentDefinitions: "documented",
|
|
228
270
|
},
|
|
229
271
|
mcpSupport: {
|
|
230
|
-
status: "
|
|
272
|
+
status: "planned",
|
|
231
273
|
schemaLabel: "repo-scoped MCP surfaces",
|
|
232
|
-
|
|
274
|
+
plannedSurface: "devcontainer (.devcontainer/devcontainer.json) and agent-frontmatter (.github/agents/*.agent.md)",
|
|
275
|
+
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
276
|
},
|
|
234
277
|
agentRulesSupport: {
|
|
235
278
|
status: "unsupported",
|
|
@@ -241,7 +284,15 @@ export const AGENT_REGISTRY = [
|
|
|
241
284
|
schemaLabel: "global permissions surface",
|
|
242
285
|
reason: "GitHub Copilot permissions are managed via organization policy, not a deployable per-user config surface",
|
|
243
286
|
},
|
|
244
|
-
|
|
287
|
+
agentDefinitionsSupport: {
|
|
288
|
+
status: "supported",
|
|
289
|
+
schemaLabel: "repo-local agent definition Markdown file",
|
|
290
|
+
path: {
|
|
291
|
+
posix: ["{repo}", ".github", "agents", "{name}.agent.md"],
|
|
292
|
+
windows: ["{repo}", ".github", "agents", "{name}.agent.md"],
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
policyNote: "Organization policies may override locally deployed configuration. Verify with your GitHub org admin if deployed skills or rules are not active.",
|
|
245
296
|
},
|
|
246
297
|
];
|
|
247
298
|
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,12 +1,13 @@
|
|
|
1
|
-
import type { AgentRuleEntry, McpServerEntry, PermissionsEntry } from "../../schemas/manifest.ts";
|
|
1
|
+
import type { AgentDefinitionEntry, AgentRuleEntry, McpServerEntry, PermissionsEntry } from "../../schemas/manifest.ts";
|
|
2
2
|
import type { AgentId, ConfigPatchDeployAction, FileWriteDeployAction, FrontmatterEmitDeployAction, PlanWarning, TomlPatchDeployAction } from "../../types.ts";
|
|
3
|
+
import { compileAgentDefinitionReverts } from "./agent-definitions.ts";
|
|
3
4
|
import { compileMcpServerReverts } from "./mcp.ts";
|
|
4
5
|
import { compilePermissionsReverts } from "./permissions.ts";
|
|
5
6
|
import { compileAgentRuleReverts } from "./rules.ts";
|
|
6
|
-
export { compileAgentRuleReverts, compileMcpServerReverts, compilePermissionsReverts, };
|
|
7
|
+
export { compileAgentDefinitionReverts, compileAgentRuleReverts, compileMcpServerReverts, compilePermissionsReverts, };
|
|
7
8
|
export type AdapterAction = ConfigPatchDeployAction | FileWriteDeployAction | TomlPatchDeployAction | FrontmatterEmitDeployAction;
|
|
8
9
|
export interface AdapterResult {
|
|
9
10
|
actions: AdapterAction[];
|
|
10
11
|
warnings: PlanWarning[];
|
|
11
12
|
}
|
|
12
|
-
export declare function compileAdapterActions(mcpServers: McpServerEntry[], agentRules: AgentRuleEntry[], permissions: PermissionsEntry[], sourceDir: string, resolvedSourceDir: string, realRoot: string, detectedAgents: AgentId[], home: string, repo?: string): Promise<AdapterResult>;
|
|
13
|
+
export declare function compileAdapterActions(mcpServers: McpServerEntry[], agentRules: AgentRuleEntry[], permissions: PermissionsEntry[], sourceDir: string, resolvedSourceDir: string, realRoot: string, detectedAgents: AgentId[], home: string, repo?: string, agentDefinitions?: AgentDefinitionEntry[]): Promise<AdapterResult>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { compileAgentDefinitionActions, compileAgentDefinitionReverts, } from "./agent-definitions.js";
|
|
1
2
|
import { compileMcpServerActions, compileMcpServerReverts } from "./mcp.js";
|
|
2
3
|
import { compilePermissionsActions, compilePermissionsReverts, } from "./permissions.js";
|
|
3
4
|
import { compileAgentRuleActions, compileAgentRuleReverts } from "./rules.js";
|
|
4
|
-
export { compileAgentRuleReverts, compileMcpServerReverts, compilePermissionsReverts, };
|
|
5
|
-
export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo) {
|
|
5
|
+
export { compileAgentDefinitionReverts, compileAgentRuleReverts, compileMcpServerReverts, compilePermissionsReverts, };
|
|
6
|
+
export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, agentDefinitions) {
|
|
6
7
|
const actions = [];
|
|
7
8
|
const warnings = [];
|
|
8
9
|
for (const entry of mcpServers) {
|
|
@@ -20,5 +21,10 @@ export async function compileAdapterActions(mcpServers, agentRules, permissions,
|
|
|
20
21
|
actions.push(...r.actions);
|
|
21
22
|
warnings.push(...r.warnings);
|
|
22
23
|
}
|
|
24
|
+
for (const entry of agentDefinitions ?? []) {
|
|
25
|
+
const r = await compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo);
|
|
26
|
+
actions.push(...r.actions);
|
|
27
|
+
warnings.push(...r.warnings);
|
|
28
|
+
}
|
|
23
29
|
return { actions, warnings };
|
|
24
30
|
}
|
|
@@ -62,6 +62,13 @@ export function compileMcpServerActions(entry, detectedAgents, home, repo) {
|
|
|
62
62
|
});
|
|
63
63
|
continue;
|
|
64
64
|
}
|
|
65
|
+
if (support.status === "planned") {
|
|
66
|
+
warnings.push({
|
|
67
|
+
kind: "confidence",
|
|
68
|
+
message: `mcpServers: agent "${agentId}" MCP support is planned via ${support.plannedSurface} — skipping "${entry.name}" until that surface is implemented`,
|
|
69
|
+
});
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
65
72
|
validateMcpServerConfigShape(entry.config, entry.name, agentId);
|
|
66
73
|
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home, repo);
|
|
67
74
|
const resolvedTarget = path.resolve(rawTarget);
|
|
@@ -79,7 +86,9 @@ export function compileMcpServerReverts(entry, agentFilter, home, repo) {
|
|
|
79
86
|
continue;
|
|
80
87
|
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
81
88
|
const support = agent?.mcpSupport;
|
|
82
|
-
if (!support ||
|
|
89
|
+
if (!support ||
|
|
90
|
+
support.status === "unsupported" ||
|
|
91
|
+
support.status === "planned")
|
|
83
92
|
continue;
|
|
84
93
|
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home, repo);
|
|
85
94
|
const target = path.resolve(rawTarget);
|
|
@@ -21,6 +21,13 @@ export function compilePermissionsActions(entry, detectedAgents, home) {
|
|
|
21
21
|
});
|
|
22
22
|
continue;
|
|
23
23
|
}
|
|
24
|
+
if (support.status === "planned") {
|
|
25
|
+
warnings.push({
|
|
26
|
+
kind: "confidence",
|
|
27
|
+
message: `permissions: agent "${agentId}" permissions support is planned via ${support.plannedSurface} — skipping "${entry.name}" until that surface is implemented`,
|
|
28
|
+
});
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
24
31
|
validatePermissionsConfigShape(entry.config, entry.name, agentId);
|
|
25
32
|
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home);
|
|
26
33
|
const resolvedTarget = path.resolve(rawTarget);
|
|
@@ -56,7 +63,9 @@ export function compilePermissionsReverts(entry, agentFilter, home) {
|
|
|
56
63
|
continue;
|
|
57
64
|
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
58
65
|
const support = agent?.permissionsSupport;
|
|
59
|
-
if (!support ||
|
|
66
|
+
if (!support ||
|
|
67
|
+
support.status === "unsupported" ||
|
|
68
|
+
support.status === "planned")
|
|
60
69
|
continue;
|
|
61
70
|
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home);
|
|
62
71
|
const target = path.resolve(rawTarget);
|
|
@@ -21,6 +21,13 @@ export async function compileAgentRuleActions(entry, sourceDir, resolvedSourceDi
|
|
|
21
21
|
});
|
|
22
22
|
continue;
|
|
23
23
|
}
|
|
24
|
+
if (support.status === "planned") {
|
|
25
|
+
warnings.push({
|
|
26
|
+
kind: "confidence",
|
|
27
|
+
message: `agentRules: agent "${agentId}" rules support is planned via ${support.plannedSurface} — skipping "${entry.name}" until that surface is implemented`,
|
|
28
|
+
});
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
24
31
|
supportedTargets.push({
|
|
25
32
|
agentId,
|
|
26
33
|
confidence: agent.provenance.agentRules ?? "provisional",
|
|
@@ -56,7 +63,9 @@ export function compileAgentRuleReverts(entry, agentFilter, home, repo) {
|
|
|
56
63
|
continue;
|
|
57
64
|
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
58
65
|
const support = agent?.agentRulesSupport;
|
|
59
|
-
if (!support ||
|
|
66
|
+
if (!support ||
|
|
67
|
+
support.status === "unsupported" ||
|
|
68
|
+
support.status === "planned")
|
|
60
69
|
continue;
|
|
61
70
|
const target = resolvePlaceholders(support.path[platform], entry.name, home, repo);
|
|
62
71
|
actions.push({
|
package/dist/core/deploy.js
CHANGED
|
@@ -91,7 +91,7 @@ function detectAmbiguities(detectedAgents, manifest) {
|
|
|
91
91
|
if (bothAgents(entry.agents)) {
|
|
92
92
|
warnings.push({
|
|
93
93
|
kind: "ambiguity",
|
|
94
|
-
message: `Both "gemini-cli" and "antigravity" are listed in agentRules entry "${entry.name}". They
|
|
94
|
+
message: `Both "gemini-cli" and "antigravity" are listed in agentRules entry "${entry.name}". They write to distinct surfaces ("gemini-cli" → ~/.gemini/GEMINI.md, "antigravity" → {repo}/.agents/rules/${entry.name}.md) from the same source file — verify that deploying to both produces the intended behavior on each agent.`,
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -119,6 +119,8 @@ async function planSkillDirActions(manifest, sourceDir, resolvedSourceDir, realR
|
|
|
119
119
|
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
120
120
|
if (!agent)
|
|
121
121
|
continue;
|
|
122
|
+
if (!agent.skills)
|
|
123
|
+
continue;
|
|
122
124
|
actions.push({
|
|
123
125
|
kind: "skill-dir",
|
|
124
126
|
skill: skill.name,
|
|
@@ -126,7 +128,7 @@ async function planSkillDirActions(manifest, sourceDir, resolvedSourceDir, realR
|
|
|
126
128
|
source,
|
|
127
129
|
target: resolveAgentSkillPath(agent, skill.name, home),
|
|
128
130
|
method,
|
|
129
|
-
confidence: agent.provenance.skills,
|
|
131
|
+
confidence: agent.provenance.skills ?? "provisional",
|
|
130
132
|
});
|
|
131
133
|
}
|
|
132
134
|
}
|
|
@@ -192,7 +194,7 @@ export async function planDeploy(manifest, sourceDir, detectedAgents, home) {
|
|
|
192
194
|
...(await planFileWriteActions(manifest, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home)),
|
|
193
195
|
...planConfigPatchActions(manifest, detectedAgents, home),
|
|
194
196
|
];
|
|
195
|
-
const adapterResult = await compileAdapterActions(manifest.mcpServers, manifest.agentRules, manifest.permissions ?? [], sourceDir, resolvedSourceDir, realRoot, detectedAgents, home);
|
|
197
|
+
const adapterResult = await compileAdapterActions(manifest.mcpServers, manifest.agentRules, manifest.permissions ?? [], sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, undefined, manifest.agentDefinitions ?? []);
|
|
196
198
|
actions.push(...adapterResult.actions);
|
|
197
199
|
const warnings = [
|
|
198
200
|
...detectAmbiguities(detectedAgents, manifest),
|
package/dist/core/init.js
CHANGED
|
@@ -2,7 +2,7 @@ import { access, readFile, readdir, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { AGENT_REGISTRY_BY_ID } from "../config/agents.js";
|
|
4
4
|
import { dryRunPrefix, logger } from "../logger.js";
|
|
5
|
-
import { AGENT_IDS, ConfigEntrySchema, FileEntrySchema, McpServerEntrySchema, } from "../schemas/manifest.js";
|
|
5
|
+
import { AGENT_IDS, AgentDefinitionEntrySchema, ConfigEntrySchema, FileEntrySchema, McpServerEntrySchema, } from "../schemas/manifest.js";
|
|
6
6
|
const SAFE_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
7
7
|
// Ordered list: first match wins. Catch-all is applied at call site.
|
|
8
8
|
const AGENT_RULES_FILE_PATTERNS = [
|
|
@@ -27,6 +27,16 @@ const AGENT_RULES_SUBDIRS = [
|
|
|
27
27
|
".github",
|
|
28
28
|
".agents/rules",
|
|
29
29
|
];
|
|
30
|
+
// Conventional subdirectories that contain agent definition files.
|
|
31
|
+
// These are the agent-specific directories that each agent scans for
|
|
32
|
+
// subagent/persona definitions at runtime.
|
|
33
|
+
const AGENT_DEFINITION_SUBDIRS = [
|
|
34
|
+
".claude/agents",
|
|
35
|
+
".gemini/agents",
|
|
36
|
+
".agents/rules",
|
|
37
|
+
".opencode/agents",
|
|
38
|
+
".github/agents",
|
|
39
|
+
];
|
|
30
40
|
async function findSkillDirs(baseDir, dir, found) {
|
|
31
41
|
let entries;
|
|
32
42
|
try {
|
|
@@ -164,6 +174,117 @@ function buildAgentRules(candidates, activeAgents, skillNamesSeen) {
|
|
|
164
174
|
}
|
|
165
175
|
return rules;
|
|
166
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Derives the name for an agent definition entry from its file name.
|
|
179
|
+
* For GitHub Copilot's `{name}.agent.md` naming convention, strips the
|
|
180
|
+
* `.agent` infix in addition to the `.md` extension.
|
|
181
|
+
*/
|
|
182
|
+
function deriveAgentDefinitionName(relPath, fileName) {
|
|
183
|
+
const ext = path.extname(fileName).toLowerCase();
|
|
184
|
+
// Strip the extension to get the base name, then strip any trailing ".agent"
|
|
185
|
+
// suffix (GitHub Copilot convention: foo.agent.md → foo).
|
|
186
|
+
let baseName = path.basename(fileName, ext);
|
|
187
|
+
if (baseName.endsWith(".agent")) {
|
|
188
|
+
baseName = baseName.slice(0, -".agent".length);
|
|
189
|
+
}
|
|
190
|
+
const rawName = baseName.toLowerCase().replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
191
|
+
if (!SAFE_NAME_RE.test(rawName)) {
|
|
192
|
+
logger.warn("init", `Skipping "${relPath}": could not derive a valid agentDefinitions name`);
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
return rawName;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Maps a known agent-definition subdirectory to the agent IDs that own it.
|
|
199
|
+
* Returns null when the subdir is not agent-specific (fall back to all agents
|
|
200
|
+
* that support agentDefinitions).
|
|
201
|
+
*/
|
|
202
|
+
function agentsForDefinitionSubdir(subdir) {
|
|
203
|
+
switch (subdir) {
|
|
204
|
+
case ".claude/agents":
|
|
205
|
+
return ["claude-code"];
|
|
206
|
+
case ".gemini/agents":
|
|
207
|
+
return ["gemini-cli"];
|
|
208
|
+
case ".agents/rules":
|
|
209
|
+
return ["antigravity"];
|
|
210
|
+
case ".opencode/agents":
|
|
211
|
+
return ["opencode"];
|
|
212
|
+
case ".github/agents":
|
|
213
|
+
return ["github-copilot"];
|
|
214
|
+
default:
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function scanDefinitionSubdir(subdir, baseDir, seen, skillDirRelPaths, agentRulesRelPaths, candidates) {
|
|
219
|
+
const suggestedAgents = agentsForDefinitionSubdir(subdir) ?? [];
|
|
220
|
+
const dir = path.join(baseDir, subdir);
|
|
221
|
+
let entries;
|
|
222
|
+
try {
|
|
223
|
+
entries = await readdir(dir, { withFileTypes: true, encoding: "utf-8" });
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
for (const entry of entries) {
|
|
229
|
+
if (!entry.isFile())
|
|
230
|
+
continue;
|
|
231
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
232
|
+
if (ext !== ".md" && ext !== ".markdown")
|
|
233
|
+
continue;
|
|
234
|
+
const absPath = path.join(dir, entry.name);
|
|
235
|
+
const relPath = path.relative(baseDir, absPath).split(path.sep).join("/");
|
|
236
|
+
if (seen.has(relPath) ||
|
|
237
|
+
isInsideSkillDir(relPath, skillDirRelPaths) ||
|
|
238
|
+
agentRulesRelPaths.has(relPath))
|
|
239
|
+
continue;
|
|
240
|
+
const name = deriveAgentDefinitionName(relPath, entry.name);
|
|
241
|
+
if (name === null)
|
|
242
|
+
continue;
|
|
243
|
+
seen.add(relPath);
|
|
244
|
+
candidates.push({ relPath, name, suggestedAgents });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async function findAgentDefinitionCandidates(baseDir, skillDirRelPaths, agentRulesRelPaths) {
|
|
248
|
+
const candidates = [];
|
|
249
|
+
const seen = new Set();
|
|
250
|
+
for (const subdir of AGENT_DEFINITION_SUBDIRS) {
|
|
251
|
+
await scanDefinitionSubdir(subdir, baseDir, seen, skillDirRelPaths, agentRulesRelPaths, candidates);
|
|
252
|
+
}
|
|
253
|
+
return candidates;
|
|
254
|
+
}
|
|
255
|
+
function buildAgentDefinitions(candidates, activeAgents, namesSeen) {
|
|
256
|
+
const definitions = [];
|
|
257
|
+
const localNamesSeen = new Set(namesSeen);
|
|
258
|
+
const definitionsCapableAgents = activeAgents.filter((id) => AGENT_REGISTRY_BY_ID[id].agentDefinitionsSupport?.status !==
|
|
259
|
+
"unsupported");
|
|
260
|
+
for (const { relPath, name: rawName, suggestedAgents } of candidates) {
|
|
261
|
+
// Use suggested agents (from the dir that was scanned), intersected with
|
|
262
|
+
// active agents that support agentDefinitions. Fall back to all capable
|
|
263
|
+
// agents if the intersection is empty.
|
|
264
|
+
const intersection = suggestedAgents.length > 0
|
|
265
|
+
? suggestedAgents.filter((a) => definitionsCapableAgents.includes(a))
|
|
266
|
+
: [];
|
|
267
|
+
const agents = intersection.length > 0 ? intersection : definitionsCapableAgents;
|
|
268
|
+
if (agents.length === 0)
|
|
269
|
+
continue;
|
|
270
|
+
// Resolve name collision
|
|
271
|
+
let name = rawName;
|
|
272
|
+
if (localNamesSeen.has(name)) {
|
|
273
|
+
const candidate = `${name}-agent`;
|
|
274
|
+
if (SAFE_NAME_RE.test(candidate)) {
|
|
275
|
+
logger.warn("init", `agentDefinitions name "${name}" collides with an existing name; using "${candidate}"`);
|
|
276
|
+
name = candidate;
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
logger.warn("init", `Skipping "${relPath}": name "${name}" collides and fallback is invalid`);
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
localNamesSeen.add(name);
|
|
284
|
+
definitions.push({ name, path: relPath, agents });
|
|
285
|
+
}
|
|
286
|
+
return definitions;
|
|
287
|
+
}
|
|
167
288
|
async function loadMcpServers(baseDir) {
|
|
168
289
|
const filePath = path.join(baseDir, "mcp-servers.json");
|
|
169
290
|
let raw;
|
|
@@ -263,6 +384,39 @@ async function loadConfigsManifest(baseDir) {
|
|
|
263
384
|
}
|
|
264
385
|
return results;
|
|
265
386
|
}
|
|
387
|
+
async function loadAgentDefinitionsManifest(baseDir) {
|
|
388
|
+
const filePath = path.join(baseDir, "agent-definitions-manifest.json");
|
|
389
|
+
let raw;
|
|
390
|
+
try {
|
|
391
|
+
raw = await readFile(filePath, "utf-8");
|
|
392
|
+
}
|
|
393
|
+
catch {
|
|
394
|
+
return [];
|
|
395
|
+
}
|
|
396
|
+
let parsed;
|
|
397
|
+
try {
|
|
398
|
+
parsed = JSON.parse(raw);
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
logger.warn("init", "agent-definitions-manifest.json: invalid JSON, skipping");
|
|
402
|
+
return [];
|
|
403
|
+
}
|
|
404
|
+
if (!Array.isArray(parsed)) {
|
|
405
|
+
logger.warn("init", "agent-definitions-manifest.json: expected a JSON array, skipping");
|
|
406
|
+
return [];
|
|
407
|
+
}
|
|
408
|
+
const results = [];
|
|
409
|
+
for (let i = 0; i < parsed.length; i++) {
|
|
410
|
+
const result = AgentDefinitionEntrySchema.safeParse(parsed[i]);
|
|
411
|
+
if (result.success) {
|
|
412
|
+
results.push(result.data);
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
logger.warn("init", `agent-definitions-manifest.json: entry[${i}] invalid, skipping`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return results;
|
|
419
|
+
}
|
|
266
420
|
async function emitDirectoryHints(baseDir, filesLoaded, configsLoaded) {
|
|
267
421
|
for (const [dir, section, loaded, sidecar] of [
|
|
268
422
|
["files", "files", filesLoaded, "files-manifest.json"],
|
|
@@ -288,16 +442,20 @@ async function manifestExists(manifestPath) {
|
|
|
288
442
|
return false;
|
|
289
443
|
}
|
|
290
444
|
}
|
|
291
|
-
function
|
|
445
|
+
function logPathAgentEntries(label, entries) {
|
|
446
|
+
if (entries.length === 0)
|
|
447
|
+
return;
|
|
448
|
+
logger.detail(`${label}:`);
|
|
449
|
+
for (const e of entries) {
|
|
450
|
+
logger.detail(` ${e.name} → ${e.path} [${e.agents.join(", ")}]`);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function logVerboseManifest(skills, agentRules, mcpServers, files, configs, agentDefinitions) {
|
|
292
454
|
for (const s of skills) {
|
|
293
455
|
logger.detail(`${s.name} → ${s.path}`);
|
|
294
456
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
for (const r of agentRules) {
|
|
298
|
-
logger.detail(` ${r.name} → ${r.path} [${r.agents.join(", ")}]`);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
457
|
+
logPathAgentEntries("agentRules", agentRules);
|
|
458
|
+
logPathAgentEntries("agentDefinitions", agentDefinitions);
|
|
301
459
|
if (mcpServers.length > 0) {
|
|
302
460
|
logger.detail("mcpServers:");
|
|
303
461
|
for (const m of mcpServers) {
|
|
@@ -336,6 +494,16 @@ export async function runInit(options) {
|
|
|
336
494
|
const skillDirRelPaths = new Set(found.map((f) => f.relPath));
|
|
337
495
|
const agentRulesCandidates = await findAgentRulesCandidates(directory, skillDirRelPaths);
|
|
338
496
|
const agentRules = buildAgentRules(agentRulesCandidates, agentRulesCapableAgents, skillNamesSeen);
|
|
497
|
+
const allNamesSeen = new Set([
|
|
498
|
+
...skillNamesSeen,
|
|
499
|
+
...agentRules.map((r) => r.name),
|
|
500
|
+
]);
|
|
501
|
+
const agentRulesRelPaths = new Set(agentRules.map((r) => r.path));
|
|
502
|
+
const agentDefinitionCandidates = await findAgentDefinitionCandidates(directory, skillDirRelPaths, agentRulesRelPaths);
|
|
503
|
+
const discoveredDefinitions = buildAgentDefinitions(agentDefinitionCandidates, agents, allNamesSeen);
|
|
504
|
+
// Sidecar file overrides take precedence over auto-discovery
|
|
505
|
+
const sidecarDefinitions = await loadAgentDefinitionsManifest(directory);
|
|
506
|
+
const agentDefinitions = sidecarDefinitions.length > 0 ? sidecarDefinitions : discoveredDefinitions;
|
|
339
507
|
const mcpServers = await loadMcpServers(directory);
|
|
340
508
|
const files = await loadFilesManifest(directory);
|
|
341
509
|
const configs = await loadConfigsManifest(directory);
|
|
@@ -345,12 +513,14 @@ export async function runInit(options) {
|
|
|
345
513
|
configs,
|
|
346
514
|
mcpServers,
|
|
347
515
|
agentRules,
|
|
516
|
+
agentDefinitions,
|
|
348
517
|
};
|
|
349
518
|
const json = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
350
519
|
function summarize() {
|
|
351
520
|
const parts = [
|
|
352
521
|
`${skills.length} skill(s)`,
|
|
353
522
|
`${agentRules.length} agentRule(s)`,
|
|
523
|
+
`${agentDefinitions.length} agentDefinition(s)`,
|
|
354
524
|
`${mcpServers.length} mcpServer(s)`,
|
|
355
525
|
`${files.length} file(s)`,
|
|
356
526
|
`${configs.length} config(s)`,
|
|
@@ -367,7 +537,7 @@ export async function runInit(options) {
|
|
|
367
537
|
await writeFile(manifestPath, json, "utf-8");
|
|
368
538
|
logger.info(`Generated ${manifestPath} with ${summarize()}.`);
|
|
369
539
|
if (verbose) {
|
|
370
|
-
logVerboseManifest(skills, agentRules, mcpServers, files, configs);
|
|
540
|
+
logVerboseManifest(skills, agentRules, mcpServers, files, configs, agentDefinitions);
|
|
371
541
|
}
|
|
372
542
|
await emitDirectoryHints(directory, files.length, configs.length);
|
|
373
543
|
return 0;
|
package/dist/core/resolve.js
CHANGED
|
@@ -85,6 +85,10 @@ export function getDeployMethod() {
|
|
|
85
85
|
return process.platform === "win32" ? "copy" : "symlink";
|
|
86
86
|
}
|
|
87
87
|
export function resolveAgentSkillPathFor(agent, skillName, home, platform) {
|
|
88
|
+
if (!agent.skills) {
|
|
89
|
+
throw new Error(`Agent "${agent.id}" does not have a skills deployment path. ` +
|
|
90
|
+
`Deploy skills via another agent target that covers this agent natively (e.g. claude-code for github-copilot).`);
|
|
91
|
+
}
|
|
88
92
|
return resolvePlaceholders(agent.skills[platform], skillName, home);
|
|
89
93
|
}
|
|
90
94
|
export function resolveAgentDetectPathFor(agent, home, platform) {
|
package/dist/core/revert.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { lstat, readFile, rm, unlink, writeFile } from "node:fs/promises";
|
|
2
2
|
import { AGENT_REGISTRY_BY_ID } from "../config/agents.js";
|
|
3
3
|
import { logger } from "../logger.js";
|
|
4
|
-
import { compileAgentRuleReverts, compileMcpServerReverts, compilePermissionsReverts, } from "./adapters/index.js";
|
|
4
|
+
import { compileAgentDefinitionReverts, compileAgentRuleReverts, compileMcpServerReverts, compilePermissionsReverts, } from "./adapters/index.js";
|
|
5
5
|
import { revertTomlMcpPatch } from "./adapters/toml.js";
|
|
6
6
|
import { lookupDeployment, unregisterDeployment, } from "./ownership.js";
|
|
7
7
|
import { resolveAgentSkillPath } from "./resolve.js";
|
|
@@ -15,6 +15,8 @@ function buildSkillDirReverts(manifest, home, agentFilter) {
|
|
|
15
15
|
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
16
16
|
if (!agent)
|
|
17
17
|
continue;
|
|
18
|
+
if (!agent.skills)
|
|
19
|
+
continue;
|
|
18
20
|
actions.push({
|
|
19
21
|
kind: "skill-dir",
|
|
20
22
|
skill: skill.name,
|
|
@@ -71,6 +73,7 @@ export function planRevert(manifest, detectedAgents, home, repo) {
|
|
|
71
73
|
...(manifest.mcpServers ?? []).flatMap((e) => compileMcpServerReverts(e, detectedAgents, home, repo)),
|
|
72
74
|
...(manifest.agentRules ?? []).flatMap((e) => compileAgentRuleReverts(e, detectedAgents, home, repo)),
|
|
73
75
|
...(manifest.permissions ?? []).flatMap((e) => compilePermissionsReverts(e, detectedAgents, home)),
|
|
76
|
+
...(manifest.agentDefinitions ?? []).flatMap((e) => compileAgentDefinitionReverts(e, detectedAgents, home, repo)),
|
|
74
77
|
];
|
|
75
78
|
}
|
|
76
79
|
export function planRevertAll(manifest, home, repo) {
|
|
@@ -81,6 +84,7 @@ export function planRevertAll(manifest, home, repo) {
|
|
|
81
84
|
...(manifest.mcpServers ?? []).flatMap((e) => compileMcpServerReverts(e, null, home, repo)),
|
|
82
85
|
...(manifest.agentRules ?? []).flatMap((e) => compileAgentRuleReverts(e, null, home, repo)),
|
|
83
86
|
...(manifest.permissions ?? []).flatMap((e) => compilePermissionsReverts(e, null, home)),
|
|
87
|
+
...(manifest.agentDefinitions ?? []).flatMap((e) => compileAgentDefinitionReverts(e, null, home, repo)),
|
|
84
88
|
];
|
|
85
89
|
}
|
|
86
90
|
function recordOutcome(result, action, counts, failed) {
|
|
@@ -84,6 +84,18 @@ export declare const PermissionsEntrySchema: z.ZodObject<{
|
|
|
84
84
|
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
85
85
|
config: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
86
86
|
}, z.core.$strip>;
|
|
87
|
+
export declare const AgentDefinitionEntrySchema: z.ZodObject<{
|
|
88
|
+
name: z.ZodString;
|
|
89
|
+
agents: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
90
|
+
"claude-code": "claude-code";
|
|
91
|
+
codex: "codex";
|
|
92
|
+
"gemini-cli": "gemini-cli";
|
|
93
|
+
antigravity: "antigravity";
|
|
94
|
+
opencode: "opencode";
|
|
95
|
+
"github-copilot": "github-copilot";
|
|
96
|
+
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
97
|
+
path: z.ZodString;
|
|
98
|
+
}, z.core.$strip>;
|
|
87
99
|
export declare const ManifestSchema: z.ZodObject<{
|
|
88
100
|
skills: z.ZodArray<z.ZodObject<{
|
|
89
101
|
name: z.ZodString;
|
|
@@ -159,6 +171,18 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
159
171
|
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
160
172
|
config: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
161
173
|
}, z.core.$strip>>>;
|
|
174
|
+
agentDefinitions: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
175
|
+
name: z.ZodString;
|
|
176
|
+
agents: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
177
|
+
"claude-code": "claude-code";
|
|
178
|
+
codex: "codex";
|
|
179
|
+
"gemini-cli": "gemini-cli";
|
|
180
|
+
antigravity: "antigravity";
|
|
181
|
+
opencode: "opencode";
|
|
182
|
+
"github-copilot": "github-copilot";
|
|
183
|
+
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
184
|
+
path: z.ZodString;
|
|
185
|
+
}, z.core.$strip>>>;
|
|
162
186
|
}, z.core.$strip>;
|
|
163
187
|
export type SkillEntry = z.infer<typeof SkillEntrySchema>;
|
|
164
188
|
export type FileEntry = z.infer<typeof FileEntrySchema>;
|
|
@@ -166,6 +190,7 @@ export type ConfigEntry = z.infer<typeof ConfigEntrySchema>;
|
|
|
166
190
|
export type McpServerEntry = z.infer<typeof McpServerEntrySchema>;
|
|
167
191
|
export type AgentRuleEntry = z.infer<typeof AgentRuleEntrySchema>;
|
|
168
192
|
export type PermissionsEntry = z.infer<typeof PermissionsEntrySchema>;
|
|
193
|
+
export type AgentDefinitionEntry = z.infer<typeof AgentDefinitionEntrySchema>;
|
|
169
194
|
export type Manifest = z.infer<typeof ManifestSchema>;
|
|
170
195
|
export declare const AgentListSchema: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string[], string>>, z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
171
196
|
"claude-code": "claude-code";
|
package/dist/schemas/manifest.js
CHANGED
|
@@ -98,6 +98,14 @@ export const PermissionsEntrySchema = z.object({
|
|
|
98
98
|
// For codex: { approval_policy?: "auto" | "manual" | "suggest" | "on-failure" }
|
|
99
99
|
config: z.record(z.string(), z.unknown()),
|
|
100
100
|
});
|
|
101
|
+
export const AgentDefinitionEntrySchema = z.object({
|
|
102
|
+
name: nameField,
|
|
103
|
+
agents: agentsField,
|
|
104
|
+
// Relative path to the agent definition Markdown file within the source bundle.
|
|
105
|
+
// Must be a .md or .markdown file containing YAML frontmatter that describes
|
|
106
|
+
// the agent's persona, instructions, and any tool configuration.
|
|
107
|
+
path: sourcePathField,
|
|
108
|
+
});
|
|
101
109
|
export const ManifestSchema = z.object({
|
|
102
110
|
skills: z.array(SkillEntrySchema).superRefine((skills, ctx) => {
|
|
103
111
|
const seen = new Set();
|
|
@@ -117,6 +125,7 @@ export const ManifestSchema = z.object({
|
|
|
117
125
|
mcpServers: z.array(McpServerEntrySchema).default([]),
|
|
118
126
|
agentRules: z.array(AgentRuleEntrySchema).default([]),
|
|
119
127
|
permissions: z.array(PermissionsEntrySchema).default([]),
|
|
128
|
+
agentDefinitions: z.array(AgentDefinitionEntrySchema).default([]),
|
|
120
129
|
});
|
|
121
130
|
// Parses the --agents CLI flag: comma-separated agent IDs → AgentId[]
|
|
122
131
|
export const AgentListSchema = z
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentId } from "./schemas/manifest.ts";
|
|
2
|
-
export type { AgentId, ConfigEntry, FileEntry, Manifest, SkillEntry, } from "./schemas/manifest.ts";
|
|
2
|
+
export type { AgentDefinitionEntry, AgentId, ConfigEntry, FileEntry, Manifest, SkillEntry, } from "./schemas/manifest.ts";
|
|
3
3
|
export interface AgentPaths {
|
|
4
4
|
posix: string[];
|
|
5
5
|
windows: string[];
|
|
@@ -16,25 +16,47 @@ export interface UnsupportedAgentSurface {
|
|
|
16
16
|
schemaLabel: string;
|
|
17
17
|
reason: string;
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* A surface that is confirmed as Copilot-specific and genuinely justified for
|
|
21
|
+
* future dedicated implementation, but not yet supported. Distinct from
|
|
22
|
+
* "unsupported" (which means the surface is either covered by another target or
|
|
23
|
+
* will never be added) so adapters can emit a forward-looking "planned" notice
|
|
24
|
+
* rather than a blocking skip warning.
|
|
25
|
+
*/
|
|
26
|
+
export interface PlannedAgentSurface {
|
|
27
|
+
status: "planned";
|
|
28
|
+
schemaLabel: string;
|
|
29
|
+
/** Short description of the concrete file / config surface to be implemented. */
|
|
30
|
+
plannedSurface: string;
|
|
31
|
+
reason: string;
|
|
32
|
+
}
|
|
33
|
+
export type AgentSurfaceSupport = SupportedAgentSurface | UnsupportedAgentSurface | PlannedAgentSurface;
|
|
20
34
|
export interface AgentProvenance {
|
|
21
|
-
|
|
35
|
+
/** Omit when the agent has no separate skill deployment path. */
|
|
36
|
+
skills?: Confidence;
|
|
22
37
|
detectPaths: Confidence;
|
|
23
38
|
detectBinary: Confidence;
|
|
24
39
|
mcpConfig?: Confidence;
|
|
25
40
|
agentRules?: Confidence;
|
|
26
41
|
permissions?: Confidence;
|
|
42
|
+
agentDefinitions?: Confidence;
|
|
27
43
|
}
|
|
28
44
|
export interface AgentConfig {
|
|
29
45
|
id: AgentId;
|
|
30
46
|
displayName: string;
|
|
31
|
-
|
|
47
|
+
/**
|
|
48
|
+
* The on-disk path template for deploying skills to this agent.
|
|
49
|
+
* Omit for agents that consume skills from another agent's path natively
|
|
50
|
+
* (e.g. GitHub Copilot reads `.claude/skills/` directly).
|
|
51
|
+
*/
|
|
52
|
+
skills?: AgentPaths;
|
|
32
53
|
detectPaths: AgentPaths;
|
|
33
54
|
detectBinary: string | null;
|
|
34
55
|
provenance: AgentProvenance;
|
|
35
56
|
mcpSupport?: AgentSurfaceSupport;
|
|
36
57
|
agentRulesSupport?: AgentSurfaceSupport;
|
|
37
58
|
permissionsSupport?: AgentSurfaceSupport;
|
|
59
|
+
agentDefinitionsSupport?: AgentSurfaceSupport;
|
|
38
60
|
policyNote?: string;
|
|
39
61
|
}
|
|
40
62
|
export interface PlanWarning {
|