@kuznai/inception-engine 0.14.1 → 0.16.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 +105 -5
- package/dist/config/agents.js +38 -0
- package/dist/core/adapters/index.d.ts +4 -3
- package/dist/core/adapters/index.js +8 -2
- package/dist/core/adapters/permissions.d.ts +8 -0
- package/dist/core/adapters/permissions.js +81 -0
- package/dist/core/deploy.js +1 -1
- package/dist/core/init.js +174 -18
- package/dist/core/revert.js +3 -1
- package/dist/core/validation.d.ts +1 -0
- package/dist/core/validation.js +45 -0
- package/dist/schemas/manifest.d.ts +25 -0
- package/dist/schemas/manifest.js +9 -0
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ GitHub Copilot is no longer treated as a separate instruction or skill target in
|
|
|
8
8
|
|
|
9
9
|
The broader portability layer is the roadmap direction, but this README focuses on what is working now.
|
|
10
10
|
|
|
11
|
-
`init` is available as a bootstrap command
|
|
11
|
+
`init` is available as a bootstrap command. It scans for directories containing `SKILL.md`, discovers agent-rules Markdown files using the Claude-first portability conventions, and reads `mcp-servers.json` from the repo root to populate `mcpServers`. `files` and `configs` remain empty — `init` emits guidance when it detects a `files/` or `configs/` directory.
|
|
12
12
|
|
|
13
13
|
## Quick Start
|
|
14
14
|
|
|
@@ -50,7 +50,8 @@ Managed skills overwrite their previous version. If a target exists but was not
|
|
|
50
50
|
| Config patch (JSON merge) | All agents via manifest and CLI | All agents |
|
|
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
|
+
| `init` manifest generation | Scans `SKILL.md` directories (`skills`), `.md` files with Claude-first agent mapping (`agentRules`), and `mcp-servers.json` (`mcpServers`); emits hints for `files/` and `configs/` directories | N/A |
|
|
54
55
|
|
|
55
56
|
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.
|
|
56
57
|
|
|
@@ -98,6 +99,18 @@ Create an `inception.json` file at the root of your skills directory:
|
|
|
98
99
|
"path": "rules/CLAUDE.md",
|
|
99
100
|
"agents": ["claude-code"]
|
|
100
101
|
}
|
|
102
|
+
],
|
|
103
|
+
"permissions": [
|
|
104
|
+
{
|
|
105
|
+
"name": "safety-defaults",
|
|
106
|
+
"agents": ["claude-code"],
|
|
107
|
+
"config": {
|
|
108
|
+
"permissions": {
|
|
109
|
+
"allow": ["Read", "Glob"],
|
|
110
|
+
"deny": ["Bash(rm:*)"]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
101
114
|
]
|
|
102
115
|
}
|
|
103
116
|
```
|
|
@@ -145,6 +158,47 @@ Each **agentRules** entry deploys a Markdown instruction file to an agent's supp
|
|
|
145
158
|
|
|
146
159
|
Instruction rule deployment is supported for all agents. For most agents, this targets a single global rules file (e.g., `~/.claude/CLAUDE.md` for `claude-code`, `~/.codex/AGENTS.md` for `codex`, `~/.gemini/GEMINI.md` for `gemini-cli`, and `~/.config/opencode/AGENTS.md` for `opencode`). For `antigravity`, inception-engine deploys to repo-local instruction surfaces at `{repo}/.agents/rules/{name}.md`. For `github-copilot`, no separate deployment is needed because Copilot reads `CLAUDE.md` natively — target it via the `claude-code` agentRules entry and it reaches Copilot automatically. Revert removes the deployed rules file.
|
|
147
160
|
|
|
161
|
+
Each **permissions** entry deploys execution and safety-oriented configuration to an agent's permission or approval surface:
|
|
162
|
+
|
|
163
|
+
- **name** - Unique identifier (same format as skill names)
|
|
164
|
+
- **agents** - Array of agent IDs to deploy this entry to
|
|
165
|
+
- **config** - Permission config payload; shape is validated per agent (see below)
|
|
166
|
+
|
|
167
|
+
Permission deployment is currently supported for `claude-code` and `codex`. Other agents emit a warning and are skipped.
|
|
168
|
+
|
|
169
|
+
For `claude-code`, the config is merged into `~/.claude/settings.json`. Only the `permissions` key is accepted:
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"name": "safety-defaults",
|
|
174
|
+
"agents": ["claude-code"],
|
|
175
|
+
"config": {
|
|
176
|
+
"permissions": {
|
|
177
|
+
"allow": ["Read", "Glob", "Bash(git:*)"],
|
|
178
|
+
"deny": ["Bash(rm:*)"]
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Both `allow` and `deny` are optional string arrays. Tool patterns follow Claude Code's permission glob syntax (e.g., `Bash(git:*)` to allow all git commands).
|
|
185
|
+
|
|
186
|
+
For `codex`, the config is merged into `~/.codex/config.toml`. Only the `approval_policy` key is accepted:
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"name": "codex-approval",
|
|
191
|
+
"agents": ["codex"],
|
|
192
|
+
"config": {
|
|
193
|
+
"approval_policy": "suggest"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Valid `approval_policy` values are `"auto"`, `"manual"`, `"suggest"`, and `"on-failure"`.
|
|
199
|
+
|
|
200
|
+
Revert restores the previous config values using the undo patch recorded at deploy time.
|
|
201
|
+
|
|
148
202
|
## Creating Skills
|
|
149
203
|
|
|
150
204
|
Each skill is a directory containing at minimum a `SKILL.md` file with YAML frontmatter:
|
|
@@ -164,7 +218,7 @@ The `name` and `description` fields in the frontmatter are used by most agents.
|
|
|
164
218
|
|
|
165
219
|
## `init` Command
|
|
166
220
|
|
|
167
|
-
`init` is meant to bootstrap a repository that already has
|
|
221
|
+
`init` is meant to bootstrap a repository that already has skills and related manifest assets. It recursively scans the target directory, treats any directory containing `SKILL.md` as a skill, discovers supported instruction and MCP conventions, and writes a starter `inception.json`.
|
|
168
222
|
|
|
169
223
|
Current `init` behavior:
|
|
170
224
|
|
|
@@ -173,12 +227,58 @@ Current `init` behavior:
|
|
|
173
227
|
- Applies either the `--agents` list or all currently known agent IDs
|
|
174
228
|
- Refuses to overwrite an existing `inception.json` unless `--force` is provided
|
|
175
229
|
- Supports `--dry-run` so you can inspect the generated manifest before writing it
|
|
230
|
+
- Discovers agent-rules Markdown files in the root and conventional subdirectories (`rules/`, `instructions/`, `.github/`, `.agents/rules/`), 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 agents whose agentRules surface is unsupported
|
|
231
|
+
- Reads `mcp-servers.json` from the repo root (if present) and generates `mcpServers` entries; invalid entries are warned and skipped
|
|
232
|
+
- Reads `files-manifest.json` from the repo root (if present) and generates `files` entries; invalid entries are warned and skipped
|
|
233
|
+
- Reads `configs-manifest.json` from the repo root (if present) and generates `configs` entries; invalid entries are warned and skipped
|
|
234
|
+
- Emits guidance to create a sidecar manifest when a `files/` or `configs/` directory is detected but the corresponding sidecar file is absent
|
|
176
235
|
|
|
177
236
|
Current `init` limitations:
|
|
178
237
|
|
|
179
238
|
- It does not reconcile generated manifest entries against `SKILL.md` frontmatter values
|
|
180
|
-
-
|
|
181
|
-
|
|
239
|
+
- `files` and `configs` target paths are deployment-system-specific and cannot be inferred; they must be provided explicitly in `files-manifest.json` and `configs-manifest.json`
|
|
240
|
+
|
|
241
|
+
### Repo Conventions Recognized by `init`
|
|
242
|
+
|
|
243
|
+
Place a `mcp-servers.json` file at the repo root to have `init` populate the `mcpServers` section automatically. The file must be a JSON array of MCP server entries using the same schema as the `mcpServers` field in `inception.json`:
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
[
|
|
247
|
+
{
|
|
248
|
+
"name": "my-server",
|
|
249
|
+
"agents": ["claude-code", "gemini-cli"],
|
|
250
|
+
"config": { "command": "npx", "args": ["-y", "my-mcp-server"] }
|
|
251
|
+
}
|
|
252
|
+
]
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Place a `files-manifest.json` file at the repo root to have `init` populate the `files` section automatically. The file must be a JSON array of file entries using the same schema as the `files` field in `inception.json`:
|
|
256
|
+
|
|
257
|
+
```json
|
|
258
|
+
[
|
|
259
|
+
{
|
|
260
|
+
"name": "my-settings",
|
|
261
|
+
"path": "files/settings.json",
|
|
262
|
+
"target": "{home}/.claude/settings.json",
|
|
263
|
+
"agents": ["claude-code"]
|
|
264
|
+
}
|
|
265
|
+
]
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Place a `configs-manifest.json` file at the repo root to have `init` populate the `configs` section automatically. The file must be a JSON array of config entries using the same schema as the `configs` field in `inception.json`:
|
|
269
|
+
|
|
270
|
+
```json
|
|
271
|
+
[
|
|
272
|
+
{
|
|
273
|
+
"name": "enable-feature",
|
|
274
|
+
"target": "{home}/.claude/settings.json",
|
|
275
|
+
"patch": { "someFeature": true },
|
|
276
|
+
"agents": ["claude-code"]
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Invalid entries are warned and skipped; the rest are written into the generated manifest verbatim.
|
|
182
282
|
|
|
183
283
|
## CLI Reference
|
|
184
284
|
|
package/dist/config/agents.js
CHANGED
|
@@ -17,6 +17,7 @@ export const AGENT_REGISTRY = [
|
|
|
17
17
|
detectBinary: "documented",
|
|
18
18
|
mcpConfig: "documented",
|
|
19
19
|
agentRules: "documented",
|
|
20
|
+
permissions: "documented",
|
|
20
21
|
},
|
|
21
22
|
mcpSupport: {
|
|
22
23
|
status: "supported",
|
|
@@ -34,6 +35,14 @@ export const AGENT_REGISTRY = [
|
|
|
34
35
|
windows: ["{home}", ".claude", "CLAUDE.md"],
|
|
35
36
|
},
|
|
36
37
|
},
|
|
38
|
+
permissionsSupport: {
|
|
39
|
+
status: "supported",
|
|
40
|
+
schemaLabel: "JSON permissions config",
|
|
41
|
+
path: {
|
|
42
|
+
posix: ["{home}", ".claude", "settings.json"],
|
|
43
|
+
windows: ["{home}", ".claude", "settings.json"],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
37
46
|
},
|
|
38
47
|
{
|
|
39
48
|
id: "codex",
|
|
@@ -52,6 +61,7 @@ export const AGENT_REGISTRY = [
|
|
|
52
61
|
detectPaths: "documented",
|
|
53
62
|
detectBinary: "documented",
|
|
54
63
|
agentRules: "documented",
|
|
64
|
+
permissions: "documented",
|
|
55
65
|
},
|
|
56
66
|
mcpSupport: {
|
|
57
67
|
status: "supported",
|
|
@@ -69,6 +79,14 @@ export const AGENT_REGISTRY = [
|
|
|
69
79
|
windows: ["{home}", ".codex", "AGENTS.md"],
|
|
70
80
|
},
|
|
71
81
|
},
|
|
82
|
+
permissionsSupport: {
|
|
83
|
+
status: "supported",
|
|
84
|
+
schemaLabel: "TOML approval policy config",
|
|
85
|
+
path: {
|
|
86
|
+
posix: ["{home}", ".codex", "config.toml"],
|
|
87
|
+
windows: ["{home}", ".codex", "config.toml"],
|
|
88
|
+
},
|
|
89
|
+
},
|
|
72
90
|
},
|
|
73
91
|
{
|
|
74
92
|
id: "gemini-cli",
|
|
@@ -105,6 +123,11 @@ export const AGENT_REGISTRY = [
|
|
|
105
123
|
windows: ["{home}", ".gemini", "GEMINI.md"],
|
|
106
124
|
},
|
|
107
125
|
},
|
|
126
|
+
permissionsSupport: {
|
|
127
|
+
status: "unsupported",
|
|
128
|
+
schemaLabel: "global permissions surface",
|
|
129
|
+
reason: "Gemini CLI does not expose a documented global per-user permission or approval config surface",
|
|
130
|
+
},
|
|
108
131
|
},
|
|
109
132
|
{
|
|
110
133
|
id: "antigravity",
|
|
@@ -139,6 +162,11 @@ export const AGENT_REGISTRY = [
|
|
|
139
162
|
windows: ["{repo}", ".agents", "rules", "{name}.md"],
|
|
140
163
|
},
|
|
141
164
|
},
|
|
165
|
+
permissionsSupport: {
|
|
166
|
+
status: "unsupported",
|
|
167
|
+
schemaLabel: "global permissions surface",
|
|
168
|
+
reason: "Antigravity does not expose a documented global per-user permission or approval config surface",
|
|
169
|
+
},
|
|
142
170
|
},
|
|
143
171
|
{
|
|
144
172
|
id: "opencode",
|
|
@@ -175,6 +203,11 @@ export const AGENT_REGISTRY = [
|
|
|
175
203
|
windows: ["{appdata}", "opencode", "AGENTS.md"],
|
|
176
204
|
},
|
|
177
205
|
},
|
|
206
|
+
permissionsSupport: {
|
|
207
|
+
status: "unsupported",
|
|
208
|
+
schemaLabel: "global permissions surface",
|
|
209
|
+
reason: "OpenCode does not expose a documented global per-user permission or approval config surface",
|
|
210
|
+
},
|
|
178
211
|
},
|
|
179
212
|
{
|
|
180
213
|
id: "github-copilot",
|
|
@@ -203,6 +236,11 @@ export const AGENT_REGISTRY = [
|
|
|
203
236
|
schemaLabel: "Claude-native shared instructions",
|
|
204
237
|
reason: 'GitHub Copilot reads CLAUDE.md natively, so deploy via the "claude-code" agentRules target instead of a separate rules surface',
|
|
205
238
|
},
|
|
239
|
+
permissionsSupport: {
|
|
240
|
+
status: "unsupported",
|
|
241
|
+
schemaLabel: "global permissions surface",
|
|
242
|
+
reason: "GitHub Copilot permissions are managed via organization policy, not a deployable per-user config surface",
|
|
243
|
+
},
|
|
206
244
|
policyNote: "Organization policies may override locally deployed skills. Verify with your GitHub org admin if deployed skills are not active.",
|
|
207
245
|
},
|
|
208
246
|
];
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type { AgentRuleEntry, McpServerEntry } from "../../schemas/manifest.ts";
|
|
1
|
+
import type { AgentRuleEntry, McpServerEntry, PermissionsEntry } from "../../schemas/manifest.ts";
|
|
2
2
|
import type { AgentId, ConfigPatchDeployAction, FileWriteDeployAction, FrontmatterEmitDeployAction, PlanWarning, TomlPatchDeployAction } from "../../types.ts";
|
|
3
3
|
import { compileMcpServerReverts } from "./mcp.ts";
|
|
4
|
+
import { compilePermissionsReverts } from "./permissions.ts";
|
|
4
5
|
import { compileAgentRuleReverts } from "./rules.ts";
|
|
5
|
-
export { compileAgentRuleReverts, compileMcpServerReverts };
|
|
6
|
+
export { compileAgentRuleReverts, compileMcpServerReverts, compilePermissionsReverts, };
|
|
6
7
|
export type AdapterAction = ConfigPatchDeployAction | FileWriteDeployAction | TomlPatchDeployAction | FrontmatterEmitDeployAction;
|
|
7
8
|
export interface AdapterResult {
|
|
8
9
|
actions: AdapterAction[];
|
|
9
10
|
warnings: PlanWarning[];
|
|
10
11
|
}
|
|
11
|
-
export declare function compileAdapterActions(mcpServers: McpServerEntry[], agentRules: AgentRuleEntry[], sourceDir: string, resolvedSourceDir: string, realRoot: string, detectedAgents: AgentId[], home: string, repo?: string): Promise<AdapterResult>;
|
|
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>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { compileMcpServerActions, compileMcpServerReverts } from "./mcp.js";
|
|
2
|
+
import { compilePermissionsActions, compilePermissionsReverts, } from "./permissions.js";
|
|
2
3
|
import { compileAgentRuleActions, compileAgentRuleReverts } from "./rules.js";
|
|
3
|
-
export { compileAgentRuleReverts, compileMcpServerReverts };
|
|
4
|
-
export async function compileAdapterActions(mcpServers, agentRules, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo) {
|
|
4
|
+
export { compileAgentRuleReverts, compileMcpServerReverts, compilePermissionsReverts, };
|
|
5
|
+
export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo) {
|
|
5
6
|
const actions = [];
|
|
6
7
|
const warnings = [];
|
|
7
8
|
for (const entry of mcpServers) {
|
|
@@ -14,5 +15,10 @@ export async function compileAdapterActions(mcpServers, agentRules, sourceDir, r
|
|
|
14
15
|
actions.push(...r.actions);
|
|
15
16
|
warnings.push(...r.warnings);
|
|
16
17
|
}
|
|
18
|
+
for (const entry of permissions) {
|
|
19
|
+
const r = compilePermissionsActions(entry, detectedAgents, home);
|
|
20
|
+
actions.push(...r.actions);
|
|
21
|
+
warnings.push(...r.warnings);
|
|
22
|
+
}
|
|
17
23
|
return { actions, warnings };
|
|
18
24
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PermissionsEntry } from "../../schemas/manifest.ts";
|
|
2
|
+
import type { AgentId, ConfigPatchDeployAction, ConfigPatchRevertAction, PlanWarning, TomlPatchDeployAction, TomlPatchRevertAction } from "../../types.ts";
|
|
3
|
+
export interface PermissionsAdapterResult {
|
|
4
|
+
actions: Array<ConfigPatchDeployAction | TomlPatchDeployAction>;
|
|
5
|
+
warnings: PlanWarning[];
|
|
6
|
+
}
|
|
7
|
+
export declare function compilePermissionsActions(entry: PermissionsEntry, detectedAgents: AgentId[], home: string): PermissionsAdapterResult;
|
|
8
|
+
export declare function compilePermissionsReverts(entry: PermissionsEntry, agentFilter: AgentId[] | null, home: string): Array<ConfigPatchRevertAction | TomlPatchRevertAction>;
|
|
@@ -0,0 +1,81 @@
|
|
|
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 { validatePermissionsConfigShape } from "../validation.js";
|
|
5
|
+
function isTomlTarget(targetPath) {
|
|
6
|
+
return path.extname(targetPath).toLowerCase() === ".toml";
|
|
7
|
+
}
|
|
8
|
+
export function compilePermissionsActions(entry, detectedAgents, home) {
|
|
9
|
+
const actions = [];
|
|
10
|
+
const warnings = [];
|
|
11
|
+
const platform = getPlatformKey();
|
|
12
|
+
for (const agentId of entry.agents) {
|
|
13
|
+
if (!detectedAgents.includes(agentId))
|
|
14
|
+
continue;
|
|
15
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
16
|
+
const support = agent?.permissionsSupport;
|
|
17
|
+
if (!support || support.status === "unsupported") {
|
|
18
|
+
warnings.push({
|
|
19
|
+
kind: "confidence",
|
|
20
|
+
message: `permissions: agent "${agentId}" ${support?.status === "unsupported" ? support.reason : "does not expose a supported permissions adapter"} — skipping "${entry.name}"`,
|
|
21
|
+
});
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
validatePermissionsConfigShape(entry.config, entry.name, agentId);
|
|
25
|
+
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home);
|
|
26
|
+
const resolvedTarget = path.resolve(rawTarget);
|
|
27
|
+
const confidence = agent.provenance.permissions ?? "provisional";
|
|
28
|
+
if (isTomlTarget(resolvedTarget)) {
|
|
29
|
+
actions.push({
|
|
30
|
+
kind: "toml-patch",
|
|
31
|
+
skill: entry.name,
|
|
32
|
+
agent: agentId,
|
|
33
|
+
target: resolvedTarget,
|
|
34
|
+
config: entry.config,
|
|
35
|
+
confidence,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
actions.push({
|
|
40
|
+
kind: "config-patch",
|
|
41
|
+
skill: entry.name,
|
|
42
|
+
agent: agentId,
|
|
43
|
+
target: resolvedTarget,
|
|
44
|
+
patch: entry.config,
|
|
45
|
+
confidence,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { actions, warnings };
|
|
50
|
+
}
|
|
51
|
+
export function compilePermissionsReverts(entry, agentFilter, home) {
|
|
52
|
+
const actions = [];
|
|
53
|
+
const platform = getPlatformKey();
|
|
54
|
+
for (const agentId of entry.agents) {
|
|
55
|
+
if (agentFilter && !agentFilter.includes(agentId))
|
|
56
|
+
continue;
|
|
57
|
+
const agent = AGENT_REGISTRY_BY_ID[agentId];
|
|
58
|
+
const support = agent?.permissionsSupport;
|
|
59
|
+
if (!support || support.status === "unsupported")
|
|
60
|
+
continue;
|
|
61
|
+
const rawTarget = resolvePlaceholders(support.path[platform], entry.name, home);
|
|
62
|
+
const target = path.resolve(rawTarget);
|
|
63
|
+
if (isTomlTarget(target)) {
|
|
64
|
+
actions.push({
|
|
65
|
+
kind: "toml-patch",
|
|
66
|
+
skill: entry.name,
|
|
67
|
+
agent: agentId,
|
|
68
|
+
target,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
actions.push({
|
|
73
|
+
kind: "config-patch",
|
|
74
|
+
skill: entry.name,
|
|
75
|
+
agent: agentId,
|
|
76
|
+
target,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return actions;
|
|
81
|
+
}
|
package/dist/core/deploy.js
CHANGED
|
@@ -192,7 +192,7 @@ export async function planDeploy(manifest, sourceDir, detectedAgents, home) {
|
|
|
192
192
|
...(await planFileWriteActions(manifest, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home)),
|
|
193
193
|
...planConfigPatchActions(manifest, detectedAgents, home),
|
|
194
194
|
];
|
|
195
|
-
const adapterResult = await compileAdapterActions(manifest.mcpServers, manifest.agentRules, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home);
|
|
195
|
+
const adapterResult = await compileAdapterActions(manifest.mcpServers, manifest.agentRules, manifest.permissions ?? [], sourceDir, resolvedSourceDir, realRoot, detectedAgents, home);
|
|
196
196
|
actions.push(...adapterResult.actions);
|
|
197
197
|
const warnings = [
|
|
198
198
|
...detectAmbiguities(detectedAgents, manifest),
|
package/dist/core/init.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { access, readdir, writeFile } from "node:fs/promises";
|
|
1
|
+
import { access, readFile, readdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { AGENT_REGISTRY_BY_ID } from "../config/agents.js";
|
|
3
4
|
import { dryRunPrefix, logger } from "../logger.js";
|
|
4
|
-
import { AGENT_IDS } from "../schemas/manifest.js";
|
|
5
|
+
import { AGENT_IDS, ConfigEntrySchema, FileEntrySchema, McpServerEntrySchema, } from "../schemas/manifest.js";
|
|
5
6
|
const SAFE_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
6
7
|
// Ordered list: first match wins. Catch-all is applied at call site.
|
|
7
8
|
const AGENT_RULES_FILE_PATTERNS = [
|
|
@@ -17,7 +18,7 @@ const AGENT_RULES_FILE_PATTERNS = [
|
|
|
17
18
|
fileNames: ["gemini.md", "gemini-instructions.md"],
|
|
18
19
|
agents: ["gemini-cli", "antigravity"],
|
|
19
20
|
},
|
|
20
|
-
{ fileNames: ["copilot-instructions.md"], agents: ["
|
|
21
|
+
{ fileNames: ["copilot-instructions.md"], agents: ["claude-code"] },
|
|
21
22
|
];
|
|
22
23
|
// Conventional subdirectory names to scan one level deep for .md files.
|
|
23
24
|
const AGENT_RULES_SUBDIRS = [
|
|
@@ -142,6 +143,9 @@ function buildAgentRules(candidates, activeAgents, skillNamesSeen) {
|
|
|
142
143
|
// Intersect with active agents; fall back to full active list if empty
|
|
143
144
|
const intersection = defaultAgents.filter((a) => activeAgents.includes(a));
|
|
144
145
|
const agents = intersection.length > 0 ? intersection : activeAgents;
|
|
146
|
+
// Skip if no capable agents remain (e.g. --agents github-copilot only)
|
|
147
|
+
if (agents.length === 0)
|
|
148
|
+
continue;
|
|
145
149
|
// Resolve name collision with skill names
|
|
146
150
|
let name = rawName;
|
|
147
151
|
if (namesSeen.has(name)) {
|
|
@@ -160,6 +164,121 @@ function buildAgentRules(candidates, activeAgents, skillNamesSeen) {
|
|
|
160
164
|
}
|
|
161
165
|
return rules;
|
|
162
166
|
}
|
|
167
|
+
async function loadMcpServers(baseDir) {
|
|
168
|
+
const filePath = path.join(baseDir, "mcp-servers.json");
|
|
169
|
+
let raw;
|
|
170
|
+
try {
|
|
171
|
+
raw = await readFile(filePath, "utf-8");
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
let parsed;
|
|
177
|
+
try {
|
|
178
|
+
parsed = JSON.parse(raw);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
logger.warn("init", "mcp-servers.json: invalid JSON, skipping");
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
if (!Array.isArray(parsed)) {
|
|
185
|
+
logger.warn("init", "mcp-servers.json: expected a JSON array, skipping");
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
const results = [];
|
|
189
|
+
for (let i = 0; i < parsed.length; i++) {
|
|
190
|
+
const result = McpServerEntrySchema.safeParse(parsed[i]);
|
|
191
|
+
if (result.success) {
|
|
192
|
+
results.push(result.data);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
logger.warn("init", `mcp-servers.json: entry[${i}] invalid, skipping`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return results;
|
|
199
|
+
}
|
|
200
|
+
async function loadFilesManifest(baseDir) {
|
|
201
|
+
const filePath = path.join(baseDir, "files-manifest.json");
|
|
202
|
+
let raw;
|
|
203
|
+
try {
|
|
204
|
+
raw = await readFile(filePath, "utf-8");
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
let parsed;
|
|
210
|
+
try {
|
|
211
|
+
parsed = JSON.parse(raw);
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
logger.warn("init", "files-manifest.json: invalid JSON, skipping");
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
if (!Array.isArray(parsed)) {
|
|
218
|
+
logger.warn("init", "files-manifest.json: expected a JSON array, skipping");
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
const results = [];
|
|
222
|
+
for (let i = 0; i < parsed.length; i++) {
|
|
223
|
+
const result = FileEntrySchema.safeParse(parsed[i]);
|
|
224
|
+
if (result.success) {
|
|
225
|
+
results.push(result.data);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
logger.warn("init", `files-manifest.json: entry[${i}] invalid, skipping`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return results;
|
|
232
|
+
}
|
|
233
|
+
async function loadConfigsManifest(baseDir) {
|
|
234
|
+
const filePath = path.join(baseDir, "configs-manifest.json");
|
|
235
|
+
let raw;
|
|
236
|
+
try {
|
|
237
|
+
raw = await readFile(filePath, "utf-8");
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
let parsed;
|
|
243
|
+
try {
|
|
244
|
+
parsed = JSON.parse(raw);
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
logger.warn("init", "configs-manifest.json: invalid JSON, skipping");
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
if (!Array.isArray(parsed)) {
|
|
251
|
+
logger.warn("init", "configs-manifest.json: expected a JSON array, skipping");
|
|
252
|
+
return [];
|
|
253
|
+
}
|
|
254
|
+
const results = [];
|
|
255
|
+
for (let i = 0; i < parsed.length; i++) {
|
|
256
|
+
const result = ConfigEntrySchema.safeParse(parsed[i]);
|
|
257
|
+
if (result.success) {
|
|
258
|
+
results.push(result.data);
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
logger.warn("init", `configs-manifest.json: entry[${i}] invalid, skipping`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return results;
|
|
265
|
+
}
|
|
266
|
+
async function emitDirectoryHints(baseDir, filesLoaded, configsLoaded) {
|
|
267
|
+
for (const [dir, section, loaded, sidecar] of [
|
|
268
|
+
["files", "files", filesLoaded, "files-manifest.json"],
|
|
269
|
+
["configs", "configs", configsLoaded, "configs-manifest.json"],
|
|
270
|
+
]) {
|
|
271
|
+
try {
|
|
272
|
+
await access(path.join(baseDir, dir));
|
|
273
|
+
if (loaded === 0) {
|
|
274
|
+
logger.info(`Detected ${dir}/ directory — create ${sidecar} at the repo root to have init populate ${section} entries automatically.`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
// directory does not exist — silent
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
163
282
|
async function manifestExists(manifestPath) {
|
|
164
283
|
try {
|
|
165
284
|
await access(manifestPath);
|
|
@@ -169,9 +288,39 @@ async function manifestExists(manifestPath) {
|
|
|
169
288
|
return false;
|
|
170
289
|
}
|
|
171
290
|
}
|
|
291
|
+
function logVerboseManifest(skills, agentRules, mcpServers, files, configs) {
|
|
292
|
+
for (const s of skills) {
|
|
293
|
+
logger.detail(`${s.name} → ${s.path}`);
|
|
294
|
+
}
|
|
295
|
+
if (agentRules.length > 0) {
|
|
296
|
+
logger.detail("agentRules:");
|
|
297
|
+
for (const r of agentRules) {
|
|
298
|
+
logger.detail(` ${r.name} → ${r.path} [${r.agents.join(", ")}]`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (mcpServers.length > 0) {
|
|
302
|
+
logger.detail("mcpServers:");
|
|
303
|
+
for (const m of mcpServers) {
|
|
304
|
+
logger.detail(` ${m.name} [${m.agents.join(", ")}]`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (files.length > 0) {
|
|
308
|
+
logger.detail("files:");
|
|
309
|
+
for (const f of files) {
|
|
310
|
+
logger.detail(` ${f.name} → ${f.target} [${f.agents.join(", ")}]`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (configs.length > 0) {
|
|
314
|
+
logger.detail("configs:");
|
|
315
|
+
for (const c of configs) {
|
|
316
|
+
logger.detail(` ${c.name} → ${c.target} [${c.agents.join(", ")}]`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
172
320
|
export async function runInit(options) {
|
|
173
321
|
const { directory, dryRun, force, verbose } = options;
|
|
174
322
|
const agents = options.agents ?? [...AGENT_IDS];
|
|
323
|
+
const agentRulesCapableAgents = agents.filter((id) => AGENT_REGISTRY_BY_ID[id].agentRulesSupport?.status !== "unsupported");
|
|
175
324
|
const manifestPath = path.join(directory, "inception.json");
|
|
176
325
|
if (!dryRun && (await manifestExists(manifestPath)) && !force) {
|
|
177
326
|
logger.error(`Error: ${manifestPath} already exists. Use --force to overwrite.`);
|
|
@@ -186,33 +335,40 @@ export async function runInit(options) {
|
|
|
186
335
|
const skillNamesSeen = new Set(skills.map((s) => s.name));
|
|
187
336
|
const skillDirRelPaths = new Set(found.map((f) => f.relPath));
|
|
188
337
|
const agentRulesCandidates = await findAgentRulesCandidates(directory, skillDirRelPaths);
|
|
189
|
-
const agentRules = buildAgentRules(agentRulesCandidates,
|
|
338
|
+
const agentRules = buildAgentRules(agentRulesCandidates, agentRulesCapableAgents, skillNamesSeen);
|
|
339
|
+
const mcpServers = await loadMcpServers(directory);
|
|
340
|
+
const files = await loadFilesManifest(directory);
|
|
341
|
+
const configs = await loadConfigsManifest(directory);
|
|
190
342
|
const manifest = {
|
|
191
343
|
skills,
|
|
192
|
-
files
|
|
193
|
-
configs
|
|
194
|
-
mcpServers
|
|
344
|
+
files,
|
|
345
|
+
configs,
|
|
346
|
+
mcpServers,
|
|
195
347
|
agentRules,
|
|
196
348
|
};
|
|
197
349
|
const json = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
350
|
+
function summarize() {
|
|
351
|
+
const parts = [
|
|
352
|
+
`${skills.length} skill(s)`,
|
|
353
|
+
`${agentRules.length} agentRule(s)`,
|
|
354
|
+
`${mcpServers.length} mcpServer(s)`,
|
|
355
|
+
`${files.length} file(s)`,
|
|
356
|
+
`${configs.length} config(s)`,
|
|
357
|
+
];
|
|
358
|
+
return parts.join(", ");
|
|
359
|
+
}
|
|
198
360
|
if (dryRun) {
|
|
199
|
-
logger.info(`${dryRunPrefix(true)}Would write ${manifestPath} with ${
|
|
361
|
+
logger.info(`${dryRunPrefix(true)}Would write ${manifestPath} with ${summarize()}:`);
|
|
200
362
|
logger.info("");
|
|
201
363
|
logger.info(json);
|
|
364
|
+
await emitDirectoryHints(directory, files.length, configs.length);
|
|
202
365
|
return 0;
|
|
203
366
|
}
|
|
204
367
|
await writeFile(manifestPath, json, "utf-8");
|
|
205
|
-
logger.info(`Generated ${manifestPath} with ${
|
|
368
|
+
logger.info(`Generated ${manifestPath} with ${summarize()}.`);
|
|
206
369
|
if (verbose) {
|
|
207
|
-
|
|
208
|
-
logger.detail(`${s.name} → ${s.path}`);
|
|
209
|
-
}
|
|
210
|
-
if (agentRules.length > 0) {
|
|
211
|
-
logger.detail("agentRules:");
|
|
212
|
-
for (const r of agentRules) {
|
|
213
|
-
logger.detail(` ${r.name} → ${r.path} [${r.agents.join(", ")}]`);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
370
|
+
logVerboseManifest(skills, agentRules, mcpServers, files, configs);
|
|
216
371
|
}
|
|
372
|
+
await emitDirectoryHints(directory, files.length, configs.length);
|
|
217
373
|
return 0;
|
|
218
374
|
}
|
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, } from "./adapters/index.js";
|
|
4
|
+
import { 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";
|
|
@@ -70,6 +70,7 @@ export function planRevert(manifest, detectedAgents, home, repo) {
|
|
|
70
70
|
...buildConfigPatchReverts(manifest, home, detectedAgents),
|
|
71
71
|
...(manifest.mcpServers ?? []).flatMap((e) => compileMcpServerReverts(e, detectedAgents, home, repo)),
|
|
72
72
|
...(manifest.agentRules ?? []).flatMap((e) => compileAgentRuleReverts(e, detectedAgents, home, repo)),
|
|
73
|
+
...(manifest.permissions ?? []).flatMap((e) => compilePermissionsReverts(e, detectedAgents, home)),
|
|
73
74
|
];
|
|
74
75
|
}
|
|
75
76
|
export function planRevertAll(manifest, home, repo) {
|
|
@@ -79,6 +80,7 @@ export function planRevertAll(manifest, home, repo) {
|
|
|
79
80
|
...buildConfigPatchReverts(manifest, home, null),
|
|
80
81
|
...(manifest.mcpServers ?? []).flatMap((e) => compileMcpServerReverts(e, null, home, repo)),
|
|
81
82
|
...(manifest.agentRules ?? []).flatMap((e) => compileAgentRuleReverts(e, null, home, repo)),
|
|
83
|
+
...(manifest.permissions ?? []).flatMap((e) => compilePermissionsReverts(e, null, home)),
|
|
82
84
|
];
|
|
83
85
|
}
|
|
84
86
|
function recordOutcome(result, action, counts, failed) {
|
|
@@ -2,5 +2,6 @@ export declare function sourceAccessError(err: unknown, sourcePath: string): str
|
|
|
2
2
|
export declare function validateSourcePath(source: string, skillPath: string, resolvedSourceDir: string, realRoot: string): Promise<void>;
|
|
3
3
|
export declare function validateSourceFile(sourcePath: string, manifestPath: string): Promise<void>;
|
|
4
4
|
export declare function validateMcpServerConfigShape(config: Record<string, unknown>, entryName: string, agentId: string): void;
|
|
5
|
+
export declare function validatePermissionsConfigShape(config: Record<string, unknown>, entryName: string, agentId: string): void;
|
|
5
6
|
export declare function validateAgentRuleMarkdownPath(manifestPath: string, agentId: string): void;
|
|
6
7
|
export declare function validateSkillDefinitionFile(sourcePath: string, manifestPath: string): Promise<void>;
|
package/dist/core/validation.js
CHANGED
|
@@ -96,6 +96,51 @@ export function validateMcpServerConfigShape(config, entryName, agentId) {
|
|
|
96
96
|
throw new UserError("DEPLOY_FAILED", `mcpServers entry "${entryName}" for agent "${agentId}" must define "env" as an object of string values when present`);
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
+
const CODEX_APPROVAL_POLICY_VALUES = [
|
|
100
|
+
"auto",
|
|
101
|
+
"manual",
|
|
102
|
+
"suggest",
|
|
103
|
+
"on-failure",
|
|
104
|
+
];
|
|
105
|
+
function rejectUnknownKeys(config, allowed, entryName, agentId) {
|
|
106
|
+
const unknownKeys = Object.keys(config).filter((k) => !allowed.includes(k));
|
|
107
|
+
if (unknownKeys.length > 0) {
|
|
108
|
+
throw new UserError("DEPLOY_FAILED", `permissions entry "${entryName}" for agent "${agentId}" contains unrecognized keys: ${unknownKeys.join(", ")}. Only ${allowed.map((k) => `"${k}"`).join(", ")} is allowed.`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function validateClaudeCodePermissions(config, entryName) {
|
|
112
|
+
rejectUnknownKeys(config, ["permissions"], entryName, "claude-code");
|
|
113
|
+
const perms = config.permissions;
|
|
114
|
+
if (perms === undefined)
|
|
115
|
+
return;
|
|
116
|
+
if (typeof perms !== "object" || perms === null || Array.isArray(perms)) {
|
|
117
|
+
throw new UserError("DEPLOY_FAILED", `permissions entry "${entryName}" for agent "claude-code" must define "permissions" as an object`);
|
|
118
|
+
}
|
|
119
|
+
const permsObj = perms;
|
|
120
|
+
for (const key of ["allow", "deny"]) {
|
|
121
|
+
const val = permsObj[key];
|
|
122
|
+
if (val !== undefined &&
|
|
123
|
+
(!Array.isArray(val) || val.some((item) => typeof item !== "string"))) {
|
|
124
|
+
throw new UserError("DEPLOY_FAILED", `permissions entry "${entryName}" for agent "claude-code" must define "permissions.${key}" as an array of strings when present`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function validateCodexPermissions(config, entryName) {
|
|
129
|
+
rejectUnknownKeys(config, ["approval_policy"], entryName, "codex");
|
|
130
|
+
const policy = config.approval_policy;
|
|
131
|
+
if (policy !== undefined &&
|
|
132
|
+
!CODEX_APPROVAL_POLICY_VALUES.includes(policy)) {
|
|
133
|
+
throw new UserError("DEPLOY_FAILED", `permissions entry "${entryName}" for agent "codex" must define "approval_policy" as one of: ${CODEX_APPROVAL_POLICY_VALUES.join(", ")}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
export function validatePermissionsConfigShape(config, entryName, agentId) {
|
|
137
|
+
if (agentId === "claude-code") {
|
|
138
|
+
validateClaudeCodePermissions(config, entryName);
|
|
139
|
+
}
|
|
140
|
+
else if (agentId === "codex") {
|
|
141
|
+
validateCodexPermissions(config, entryName);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
99
144
|
export function validateAgentRuleMarkdownPath(manifestPath, agentId) {
|
|
100
145
|
const extension = path.extname(manifestPath).toLowerCase();
|
|
101
146
|
if (extension !== ".md" && extension !== ".markdown") {
|
|
@@ -72,6 +72,18 @@ export declare const AgentRuleEntrySchema: z.ZodObject<{
|
|
|
72
72
|
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
73
73
|
path: z.ZodString;
|
|
74
74
|
}, z.core.$strip>;
|
|
75
|
+
export declare const PermissionsEntrySchema: z.ZodObject<{
|
|
76
|
+
name: z.ZodString;
|
|
77
|
+
agents: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
78
|
+
"claude-code": "claude-code";
|
|
79
|
+
codex: "codex";
|
|
80
|
+
"gemini-cli": "gemini-cli";
|
|
81
|
+
antigravity: "antigravity";
|
|
82
|
+
opencode: "opencode";
|
|
83
|
+
"github-copilot": "github-copilot";
|
|
84
|
+
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
85
|
+
config: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
86
|
+
}, z.core.$strip>;
|
|
75
87
|
export declare const ManifestSchema: z.ZodObject<{
|
|
76
88
|
skills: z.ZodArray<z.ZodObject<{
|
|
77
89
|
name: z.ZodString;
|
|
@@ -135,12 +147,25 @@ export declare const ManifestSchema: z.ZodObject<{
|
|
|
135
147
|
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
136
148
|
path: z.ZodString;
|
|
137
149
|
}, z.core.$strip>>>;
|
|
150
|
+
permissions: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
151
|
+
name: z.ZodString;
|
|
152
|
+
agents: z.ZodPipe<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
153
|
+
"claude-code": "claude-code";
|
|
154
|
+
codex: "codex";
|
|
155
|
+
"gemini-cli": "gemini-cli";
|
|
156
|
+
antigravity: "antigravity";
|
|
157
|
+
opencode: "opencode";
|
|
158
|
+
"github-copilot": "github-copilot";
|
|
159
|
+
}>>>, z.ZodTransform<("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[], ("claude-code" | "codex" | "gemini-cli" | "antigravity" | "opencode" | "github-copilot")[]>>;
|
|
160
|
+
config: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
161
|
+
}, z.core.$strip>>>;
|
|
138
162
|
}, z.core.$strip>;
|
|
139
163
|
export type SkillEntry = z.infer<typeof SkillEntrySchema>;
|
|
140
164
|
export type FileEntry = z.infer<typeof FileEntrySchema>;
|
|
141
165
|
export type ConfigEntry = z.infer<typeof ConfigEntrySchema>;
|
|
142
166
|
export type McpServerEntry = z.infer<typeof McpServerEntrySchema>;
|
|
143
167
|
export type AgentRuleEntry = z.infer<typeof AgentRuleEntrySchema>;
|
|
168
|
+
export type PermissionsEntry = z.infer<typeof PermissionsEntrySchema>;
|
|
144
169
|
export type Manifest = z.infer<typeof ManifestSchema>;
|
|
145
170
|
export declare const AgentListSchema: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string[], string>>, z.ZodArray<z.ZodPipe<z.ZodString, z.ZodEnum<{
|
|
146
171
|
"claude-code": "claude-code";
|
package/dist/schemas/manifest.js
CHANGED
|
@@ -90,6 +90,14 @@ export const AgentRuleEntrySchema = z.object({
|
|
|
90
90
|
// Relative path to the rules/instruction file within the source bundle.
|
|
91
91
|
path: sourcePathField,
|
|
92
92
|
});
|
|
93
|
+
export const PermissionsEntrySchema = z.object({
|
|
94
|
+
name: nameField,
|
|
95
|
+
agents: agentsField,
|
|
96
|
+
// Raw permission config payload validated per agent by the permissions adapter.
|
|
97
|
+
// For claude-code: { permissions: { allow?: string[], deny?: string[] } }
|
|
98
|
+
// For codex: { approval_policy?: "auto" | "manual" | "suggest" | "on-failure" }
|
|
99
|
+
config: z.record(z.string(), z.unknown()),
|
|
100
|
+
});
|
|
93
101
|
export const ManifestSchema = z.object({
|
|
94
102
|
skills: z.array(SkillEntrySchema).superRefine((skills, ctx) => {
|
|
95
103
|
const seen = new Set();
|
|
@@ -108,6 +116,7 @@ export const ManifestSchema = z.object({
|
|
|
108
116
|
configs: z.array(ConfigEntrySchema).default([]),
|
|
109
117
|
mcpServers: z.array(McpServerEntrySchema).default([]),
|
|
110
118
|
agentRules: z.array(AgentRuleEntrySchema).default([]),
|
|
119
|
+
permissions: z.array(PermissionsEntrySchema).default([]),
|
|
111
120
|
});
|
|
112
121
|
// Parses the --agents CLI flag: comma-separated agent IDs → AgentId[]
|
|
113
122
|
export const AgentListSchema = z
|
package/dist/types.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface AgentProvenance {
|
|
|
23
23
|
detectBinary: Confidence;
|
|
24
24
|
mcpConfig?: Confidence;
|
|
25
25
|
agentRules?: Confidence;
|
|
26
|
+
permissions?: Confidence;
|
|
26
27
|
}
|
|
27
28
|
export interface AgentConfig {
|
|
28
29
|
id: AgentId;
|
|
@@ -33,6 +34,7 @@ export interface AgentConfig {
|
|
|
33
34
|
provenance: AgentProvenance;
|
|
34
35
|
mcpSupport?: AgentSurfaceSupport;
|
|
35
36
|
agentRulesSupport?: AgentSurfaceSupport;
|
|
37
|
+
permissionsSupport?: AgentSurfaceSupport;
|
|
36
38
|
policyNote?: string;
|
|
37
39
|
}
|
|
38
40
|
export interface PlanWarning {
|