@kuznai/inception-engine 0.25.1 → 1.0.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 CHANGED
@@ -10,6 +10,27 @@ The broader portability layer is the roadmap direction, but this README focuses
10
10
 
11
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`. Shared surfaces now default to the primary deploy target in generated manifests: for example, `init` emits `claude-code` rather than `github-copilot` for shared Claude-native skills and rules, and emits `gemini-cli` rather than `antigravity` for shared `GEMINI.md` rules surfaces. `files` and `configs` remain empty — `init` emits guidance when it detects a `files/` or `configs/` directory.
12
12
 
13
+ ## SBOM
14
+
15
+ This repo can generate an SPDX SBOM without adding any new runtime dependency by using npm's built-in `sbom` command:
16
+
17
+ ```bash
18
+ npm ci
19
+ npm sbom --sbom-format spdx --omit dev > sbom.spdx.json
20
+ ```
21
+
22
+ `--omit dev` keeps the SBOM focused on the published CLI's runtime dependency graph.
23
+
24
+ For GitHub Actions, the repository includes [`.github/workflows/sbom.yml`](.github/workflows/sbom.yml). It:
25
+
26
+ - installs dependencies and runs the normal release checks
27
+ - creates the npm package tarball with `npm pack`
28
+ - generates `dist-artifacts/sbom.spdx.json` via `npm sbom`
29
+ - uploads both the tarball and SBOM as workflow artifacts
30
+ - creates a GitHub artifact attestation that binds the SBOM to the packaged tarball
31
+
32
+ Run it manually with `workflow_dispatch`, or let it run automatically when a GitHub Release is published.
33
+
13
34
  ## Quick Start
14
35
 
15
36
  ```bash
@@ -28,6 +49,7 @@ inception-engine reads a manifest file (`inception.json`) from the target direct
28
49
  Managed skills overwrite their previous version. If a target exists but was not created by inception-engine, deployment refuses to replace it. On POSIX systems, symlinks mean updates to the source repo are reflected immediately.
29
50
 
30
51
  Before executing, the deploy command runs preflight analysis on instruction files and capability planning inputs. It validates that `agentRules` and `agentDefinitions` for targets requiring specific structure (like `github-copilot` and `antigravity`) include valid YAML frontmatter with `name` and `description` fields. For `github-copilot`, it further ensures either `tools` or `instructions` are defined; for `antigravity`, it validates the shape of any `mcp-servers` or `mcpServers` defined in the frontmatter. It also warns when a manifest targets a surface that is implementation-only, planned, unsupported, or shared through another agent, when the same agent will have multiple `agentRules` scopes active simultaneously, when the same source file is deployed to multiple scopes (duplicate-content risk), when `agentRules` or `agentDefinitions` source files exceed 50 KB (context-budget risk), and when GitHub Copilot appears to be running under enterprise-managed policy that may override local configuration. Warnings are printed but do not block deployment; structural validation failures block deployment for the affected targets.
52
+ Agent-specific preflight reads and deploy actions targeting distinct files now run with bounded concurrency, while warning order, dry-run output, and deploy result ordering remain deterministic.
31
53
 
32
54
  ## Agent Compatibility Matrix
33
55
 
@@ -183,7 +205,7 @@ Each **file** entry deploys a single file to an agent's configuration location:
183
205
 
184
206
  - **name** - Unique identifier (same format as skill names)
185
207
  - **path** - Relative path to the source file within the repo
186
- - **target** - Destination path using a placeholder prefix: `{home}`, `{appdata}` (Windows), or `{xdg_config}` (Linux). For example: `{home}/.claude/settings.json`
208
+ - **target** - Destination path using a placeholder prefix: `{home}`, `{appdata}`, `{xdg_config}`, `{repo}`, or `{workspace}`. For example: `{home}/.claude/settings.json`
187
209
  - **agents** - Array of agent IDs to deploy this file to
188
210
 
189
211
  Each **config** entry applies a [JSON merge patch (RFC 7386)](https://datatracker.ietf.org/doc/html/rfc7386) to an existing agent config file:
@@ -230,6 +252,7 @@ Each **agentRules** entry deploys a Markdown instruction file to an agent's supp
230
252
  - `"workspace"` — deploys to the workspace-root instruction file when an agent exposes one (e.g., `{workspace}/CLAUDE.md` for `claude-code`); when unsupported, deployment is skipped with a warning
231
253
  - `"copilot-repo"` — deploys to GitHub Copilot's native repo-level instruction file at `{repo}/.github/copilot-instructions.md` (`github-copilot` only; other agents are warned and skipped)
232
254
  - `"copilot-scoped"` — deploys to `{repo}/.github/instructions/{name}.instructions.md` where `{name}` is the manifest entry name (`github-copilot` only; other agents are warned and skipped)
255
+ - **targetDir** - Optional relative subdirectory under the selected `{repo}` or `{workspace}` root. Only valid with `scope: "repo"` or `scope: "workspace"`. This lets one manifest entry target nested workspaces such as `apps/frontend/CLAUDE.md` without changing the source file path.
233
256
 
234
257
  Instruction rule deployment is supported for implemented global, repo, and workspace surfaces. The target path depends on the agent and the `scope`:
235
258
 
@@ -256,7 +279,7 @@ Each **permissions** entry deploys execution and safety-oriented configuration t
256
279
  - **agents** - Array of agent IDs to deploy this entry to
257
280
  - **config** - Permission config payload; shape is validated per agent (see below)
258
281
 
259
- Permission deployment is currently supported for `claude-code` and `codex`. Other agents emit a warning and are skipped.
282
+ Permission deployment is currently supported for `claude-code`, `codex`, and `opencode`. Other agents emit a warning and are skipped.
260
283
 
261
284
  For `claude-code`, the config is merged into `~/.claude/settings.json`. Only the `permissions` key is accepted:
262
285
 
@@ -289,6 +312,24 @@ For `codex`, the config is merged into `~/.codex/config.toml`. Only the `approva
289
312
 
290
313
  Valid `approval_policy` values are `"auto"`, `"manual"`, `"suggest"`, and `"on-failure"`.
291
314
 
315
+ For `opencode`, the config is merged into `~/.config/opencode/opencode.json` on POSIX or `%APPDATA%\\opencode\\opencode.json` on Windows. Only the `permissions` key is accepted:
316
+
317
+ ```json
318
+ {
319
+ "name": "opencode-permissions",
320
+ "agents": ["opencode"],
321
+ "config": {
322
+ "permissions": {
323
+ "allow": ["Read", "Glob"],
324
+ "ask": ["Bash(git push:*)"],
325
+ "deny": ["Bash(rm:*)"]
326
+ }
327
+ }
328
+ }
329
+ ```
330
+
331
+ `allow`, `ask`, and `deny` are optional string arrays.
332
+
292
333
  Revert restores the previous config values using the undo patch recorded at deploy time.
293
334
 
294
335
  Each **hooks** entry deploys lifecycle-binding configurations to an agent's execution hook surface:
@@ -325,6 +366,29 @@ For `claude-code`, the config is merged into `~/.claude/settings.json`. Only the
325
366
 
326
367
  Revert restores the previous config values using the undo patch recorded at deploy time.
327
368
 
369
+ Each **executionConfigs** entry deploys agent execution or safety settings to an agent's modeled runtime config surface:
370
+
371
+ - **name** - Unique identifier (same format as skill names)
372
+ - **agents** - Array of agent IDs to deploy this entry to
373
+ - **config** - Raw execution-config payload; today this is modeled for `gemini-cli` and patched into its settings file
374
+
375
+ Execution-config deployment is currently supported for `gemini-cli`. Other agents emit a warning and are skipped. Because this surface is still treated as provisional, preflight emits a config-authority warning when you target it so you can sanity-check the current upstream behavior.
376
+
377
+ For `gemini-cli`, the config is merged into `~/.gemini/settings.json`:
378
+
379
+ ```json
380
+ {
381
+ "name": "gemini-safety",
382
+ "agents": ["gemini-cli"],
383
+ "config": {
384
+ "safeMode": true,
385
+ "sandbox": "workspace-write"
386
+ }
387
+ }
388
+ ```
389
+
390
+ Values are passed through as a JSON merge patch so the tool can model emerging Gemini CLI execution settings without inventing a separate schema for each key. Revert restores the previous config values using the undo patch recorded at deploy time.
391
+
328
392
  Each **agentDefinitions** entry deploys an agent-definition file to the agent-definition directory of each targeted agent. `scope: "repo"` is the default, and some agents also support `scope: "global"`:
329
393
 
330
394
  - **name** - Unique identifier (same format as skill names); used as the definition filename
@@ -371,7 +435,7 @@ The `name` and `description` fields in the frontmatter are used by most agents.
371
435
 
372
436
  ## `init` Command
373
437
 
374
- `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`.
438
+ `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`. The scan uses a bounded-concurrency streaming directory walk so large repositories scale more predictably without changing discovery behavior.
375
439
 
376
440
  Current `init` behavior:
377
441
 
@@ -1,8 +1,9 @@
1
1
  import type { AgentDefinitionEntry } from "../../schemas/manifest.ts";
2
2
  import type { AgentId, FileWriteDeployAction, FileWriteRevertAction, PlanWarning } from "../../types.ts";
3
+ import { type SourcePathValidator } from "../validation.ts";
3
4
  export interface AgentDefinitionsAdapterResult {
4
5
  actions: FileWriteDeployAction[];
5
6
  warnings: PlanWarning[];
6
7
  }
7
- export declare function compileAgentDefinitionActions(entry: AgentDefinitionEntry, sourceDir: string, resolvedSourceDir: string, realRoot: string, detectedAgents: AgentId[], home: string, repo?: string, workspace?: string): Promise<AgentDefinitionsAdapterResult>;
8
+ export declare function compileAgentDefinitionActions(entry: AgentDefinitionEntry, sourceDir: string, resolvedSourceDir: string, validateSource: SourcePathValidator, detectedAgents: AgentId[], home: string, repo?: string, workspace?: string): Promise<AgentDefinitionsAdapterResult>;
8
9
  export declare function compileAgentDefinitionReverts(entry: AgentDefinitionEntry, agentFilter: AgentId[] | null, home: string, repo?: string, workspace?: string): FileWriteRevertAction[];
@@ -2,7 +2,7 @@ import path from "node:path";
2
2
  import { AGENT_REGISTRY_BY_ID } from "../../config/agents.js";
3
3
  import { planCapabilityForDeploy, resolveCapabilitySurface, } from "../capabilities.js";
4
4
  import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
5
- import { validateAgentRuleMarkdownPath, validateInstructionFileRequirements, validateSourceFile, validateSourcePath, } from "../validation.js";
5
+ import { instructionRequiresFrontmatter, parseInstructionDocument, validateAgentRuleMarkdownPath, validateInstructionAgentRequirements, validateSourceFile, } from "../validation.js";
6
6
  /**
7
7
  * Compiles deploy actions for agentDefinitions manifest entries.
8
8
  *
@@ -93,11 +93,20 @@ function resolveSupportedTargets(entry, targetAgents, home, repo, workspace) {
93
93
  async function createAgentDefinitionActions(entry, source, supportedTargets, home, repo, workspace) {
94
94
  const actions = [];
95
95
  const isToml = path.extname(entry.path).toLowerCase() === ".toml";
96
+ // Parse the shared source document once when any supported target requires
97
+ // frontmatter validation, then reuse the attributes for per-agent checks.
98
+ const needsFrontmatter = !isToml &&
99
+ supportedTargets.some((target) => instructionRequiresFrontmatter(target.agentId));
100
+ const parsedAttributes = needsFrontmatter
101
+ ? (await parseInstructionDocument(source, entry.path)).attributes
102
+ : null;
96
103
  for (const target of supportedTargets) {
97
104
  // TOML definition files have no YAML frontmatter — skip Markdown validation.
98
105
  if (!isToml) {
99
106
  validateAgentRuleMarkdownPath(entry.path, target.agentId);
100
- await validateInstructionFileRequirements(source, entry.path, target.agentId);
107
+ if (parsedAttributes !== null) {
108
+ validateInstructionAgentRequirements(parsedAttributes, entry.path, target.agentId);
109
+ }
101
110
  }
102
111
  let migratedFrom;
103
112
  if (target.agentId === "github-copilot") {
@@ -117,7 +126,7 @@ async function createAgentDefinitionActions(entry, source, supportedTargets, hom
117
126
  }
118
127
  return actions;
119
128
  }
120
- export async function compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace) {
129
+ export async function compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, validateSource, detectedAgents, home, repo, workspace) {
121
130
  const targetAgents = entry.agents.filter((agentId) => detectedAgents.includes(agentId));
122
131
  if (targetAgents.length === 0) {
123
132
  return { actions: [], warnings: [] };
@@ -128,7 +137,7 @@ export async function compileAgentDefinitionActions(entry, sourceDir, resolvedSo
128
137
  }
129
138
  // Validate the shared source file only when at least one target is active.
130
139
  const source = path.resolve(sourceDir, entry.path);
131
- await validateSourcePath(source, entry.path, resolvedSourceDir, realRoot);
140
+ await validateSource(source, entry.path, resolvedSourceDir);
132
141
  await validateSourceFile(source, entry.path);
133
142
  const actions = await createAgentDefinitionActions(entry, source, supportedTargets, home, repo, workspace);
134
143
  return { actions, warnings };
@@ -1,5 +1,6 @@
1
1
  import type { AgentDefinitionEntry, AgentRuleEntry, ExecutionConfigEntry, HookEntry, McpServerEntry, PermissionsEntry } from "../../schemas/manifest.ts";
2
2
  import type { AgentId, ConfigPatchDeployAction, FileWriteDeployAction, FrontmatterEmitDeployAction, PlanWarning, TomlPatchDeployAction } from "../../types.ts";
3
+ import type { SourcePathValidator } from "../validation.ts";
3
4
  import { compileAgentDefinitionReverts } from "./agent-definitions.ts";
4
5
  import { compileExecutionConfigReverts } from "./execution-config.ts";
5
6
  import { compileHookReverts } from "./hooks.ts";
@@ -12,4 +13,4 @@ export interface AdapterResult {
12
13
  actions: AdapterAction[];
13
14
  warnings: PlanWarning[];
14
15
  }
15
- export declare function compileAdapterActions(mcpServers: McpServerEntry[], agentRules: AgentRuleEntry[], permissions: PermissionsEntry[], sourceDir: string, resolvedSourceDir: string, realRoot: string, detectedAgents: AgentId[], home: string, repo?: string, agentDefinitions?: AgentDefinitionEntry[], workspace?: string, hooks?: HookEntry[], executionConfigs?: ExecutionConfigEntry[]): Promise<AdapterResult>;
16
+ export declare function compileAdapterActions(mcpServers: McpServerEntry[], agentRules: AgentRuleEntry[], permissions: PermissionsEntry[], sourceDir: string, resolvedSourceDir: string, validateSource: SourcePathValidator, detectedAgents: AgentId[], home: string, repo?: string, agentDefinitions?: AgentDefinitionEntry[], workspace?: string, hooks?: HookEntry[], executionConfigs?: ExecutionConfigEntry[]): Promise<AdapterResult>;
@@ -12,14 +12,14 @@ async function compileAll(entries, fn) {
12
12
  warnings: results.flatMap((r) => r.warnings),
13
13
  };
14
14
  }
15
- export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, agentDefinitions, workspace, hooks = [], executionConfigs = []) {
15
+ export async function compileAdapterActions(mcpServers, agentRules, permissions, sourceDir, resolvedSourceDir, validateSource, detectedAgents, home, repo, agentDefinitions, workspace, hooks = [], executionConfigs = []) {
16
16
  const actions = [];
17
17
  const warnings = [];
18
18
  const [mcp, rules, perms, defs, hookResults, execResults] = await Promise.all([
19
19
  compileAll(mcpServers, (entry) => compileMcpServerActions(entry, detectedAgents, home, repo, workspace)),
20
- compileAll(agentRules, (entry) => compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace)),
20
+ compileAll(agentRules, (entry) => compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, validateSource, detectedAgents, home, repo, workspace)),
21
21
  compileAll(permissions, (entry) => compilePermissionsActions(entry, detectedAgents, home)),
22
- compileAll(agentDefinitions ?? [], (entry) => compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace)),
22
+ compileAll(agentDefinitions ?? [], (entry) => compileAgentDefinitionActions(entry, sourceDir, resolvedSourceDir, validateSource, detectedAgents, home, repo, workspace)),
23
23
  compileAll(hooks, (entry) => compileHookActions(entry, detectedAgents, home)),
24
24
  compileAll(executionConfigs, (entry) => compileExecutionConfigActions(entry, detectedAgents, home)),
25
25
  ]);
@@ -1,8 +1,9 @@
1
1
  import type { AgentRuleEntry } from "../../schemas/manifest.ts";
2
2
  import type { AgentId, FileWriteDeployAction, FileWriteRevertAction, PlanWarning } from "../../types.ts";
3
+ import { type SourcePathValidator } from "../validation.ts";
3
4
  export interface RulesAdapterResult {
4
5
  actions: FileWriteDeployAction[];
5
6
  warnings: PlanWarning[];
6
7
  }
7
- export declare function compileAgentRuleActions(entry: AgentRuleEntry, sourceDir: string, resolvedSourceDir: string, realRoot: string, detectedAgents: AgentId[], home: string, repo?: string, workspace?: string): Promise<RulesAdapterResult>;
8
+ export declare function compileAgentRuleActions(entry: AgentRuleEntry, sourceDir: string, resolvedSourceDir: string, validateSource: SourcePathValidator, detectedAgents: AgentId[], home: string, repo?: string, workspace?: string): Promise<RulesAdapterResult>;
8
9
  export declare function compileAgentRuleReverts(entry: AgentRuleEntry, agentFilter: AgentId[] | null, home: string, repo?: string, workspace?: string): FileWriteRevertAction[];
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { planCapabilityForDeploy, resolveCapabilitySurface, } from "../capabilities.js";
3
3
  import { getPlatformKey, resolvePlaceholders } from "../resolve.js";
4
- import { validateAgentRuleMarkdownPath, validateInstructionFileRequirements, validateSourceFile, validateSourcePath, } from "../validation.js";
4
+ import { instructionRequiresFrontmatter, parseInstructionDocument, validateAgentRuleMarkdownPath, validateInstructionAgentRequirements, validateSourceFile, } from "../validation.js";
5
5
  function requiresRepoPath(scope) {
6
6
  return (scope === "repo" || scope === "copilot-repo" || scope === "copilot-scoped");
7
7
  }
@@ -72,7 +72,7 @@ function resolveAgentTarget(agentId, entry, home, repo, platform, workspace, all
72
72
  target: resolvePlaceholders(injectTargetDir(support?.path[platform] ?? [], entry.targetDir), entry.name, home, repo, workspace),
73
73
  };
74
74
  }
75
- export async function compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, realRoot, detectedAgents, home, repo, workspace) {
75
+ export async function compileAgentRuleActions(entry, sourceDir, resolvedSourceDir, validateSource, detectedAgents, home, repo, workspace) {
76
76
  const actions = [];
77
77
  const warnings = [];
78
78
  const platform = getPlatformKey();
@@ -103,17 +103,24 @@ export async function compileAgentRuleActions(entry, sourceDir, resolvedSourceDi
103
103
  // Validate the shared source file only when at least one target uses the
104
104
  // current rules adapter surface.
105
105
  const source = path.resolve(sourceDir, entry.path);
106
- await validateSourcePath(source, entry.path, resolvedSourceDir, realRoot);
106
+ await validateSource(source, entry.path, resolvedSourceDir);
107
107
  await validateSourceFile(source, entry.path);
108
108
  // Native Copilot instruction files (.github/copilot-instructions.md and
109
109
  // .github/instructions/*.instructions.md) are plain markdown and do not
110
110
  // require agent-definition-style frontmatter (tools/instructions keys).
111
111
  // Skip the instructionFrontmatterRequired check for these scopes.
112
112
  const skipFrontmatterValidation = entry.scope === "copilot-repo" || entry.scope === "copilot-scoped";
113
+ // Parse the shared source document once when any deduped target requires
114
+ // frontmatter validation, then reuse the attributes for per-agent checks.
115
+ const needsFrontmatter = !skipFrontmatterValidation &&
116
+ dedupedTargets.some((target) => instructionRequiresFrontmatter(target.agentId));
117
+ const parsedAttributes = needsFrontmatter
118
+ ? (await parseInstructionDocument(source, entry.path)).attributes
119
+ : null;
113
120
  for (const target of dedupedTargets) {
114
121
  validateAgentRuleMarkdownPath(entry.path, target.agentId);
115
- if (!skipFrontmatterValidation) {
116
- await validateInstructionFileRequirements(source, entry.path, target.agentId);
122
+ if (!skipFrontmatterValidation && parsedAttributes !== null) {
123
+ validateInstructionAgentRequirements(parsedAttributes, entry.path, target.agentId);
117
124
  }
118
125
  actions.push({
119
126
  kind: "file-write",
@@ -0,0 +1,5 @@
1
+ export declare const DEFAULT_BOUNDED_CONCURRENCY: number;
2
+ export declare function mapConcurrentOrdered<T, R>(items: readonly T[], worker: (item: T, index: number) => Promise<R>, options?: {
3
+ concurrency?: number;
4
+ signal?: AbortSignal;
5
+ }): Promise<Array<R | undefined>>;
@@ -0,0 +1,19 @@
1
+ import os from "node:os";
2
+ export const DEFAULT_BOUNDED_CONCURRENCY = Math.max(1, Math.min(8, os.availableParallelism()));
3
+ export async function mapConcurrentOrdered(items, worker, options = {}) {
4
+ const { signal } = options;
5
+ const concurrency = Math.max(1, Math.min(options.concurrency ?? DEFAULT_BOUNDED_CONCURRENCY, items.length));
6
+ const results = new Array(items.length);
7
+ let nextIndex = 0;
8
+ async function runWorker() {
9
+ while (nextIndex < items.length) {
10
+ if (signal?.aborted)
11
+ return;
12
+ const index = nextIndex;
13
+ nextIndex += 1;
14
+ results[index] = await worker(items[index], index);
15
+ }
16
+ }
17
+ await Promise.all(Array.from({ length: concurrency }, () => runWorker()));
18
+ return results;
19
+ }