@kuznai/inception-engine 0.14.1 → 0.15.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
@@ -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, but what works today is intentionally narrow: it scans for directories containing `SKILL.md` and generates starter `skills` entries plus empty `mcpServers` and `agentRules` arrays. It does not infer `files`, `configs`, MCP server definitions, or rules files from the repository yet.
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,7 @@ 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
- | `init` manifest generation | Scans `SKILL.md` directories and writes starter `skills` entries | N/A |
53
+ | `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
54
 
55
55
  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
56
 
@@ -164,7 +164,7 @@ The `name` and `description` fields in the frontmatter are used by most agents.
164
164
 
165
165
  ## `init` Command
166
166
 
167
- `init` is meant to bootstrap a repository that already has skill folders. It recursively scans the target directory, treats any directory containing `SKILL.md` as a skill, and writes a starter `inception.json`.
167
+ `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
168
 
169
169
  Current `init` behavior:
170
170
 
@@ -173,12 +173,30 @@ Current `init` behavior:
173
173
  - Applies either the `--agents` list or all currently known agent IDs
174
174
  - Refuses to overwrite an existing `inception.json` unless `--force` is provided
175
175
  - Supports `--dry-run` so you can inspect the generated manifest before writing it
176
+ - 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
177
+ - Reads `mcp-servers.json` from the repo root (if present) and generates `mcpServers` entries; invalid entries are warned and skipped
178
+ - Emits guidance when a `files/` or `configs/` directory is detected at the repo root
176
179
 
177
180
  Current `init` limitations:
178
181
 
179
182
  - It does not reconcile generated manifest entries against `SKILL.md` frontmatter values
180
- - It does not infer `files`, `configs`, `mcpServers`, or `agentRules`
181
- - It does not reconcile generated output with the longer-term Claude-first portability direction
183
+ - `files` and `configs` entries cannot be inferred automatically since deployment targets are system-specific; add them manually after `init`
184
+
185
+ ### Repo Conventions Recognized by `init`
186
+
187
+ 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`:
188
+
189
+ ```json
190
+ [
191
+ {
192
+ "name": "my-server",
193
+ "agents": ["claude-code", "gemini-cli"],
194
+ "config": { "command": "npx", "args": ["-y", "my-mcp-server"] }
195
+ }
196
+ ]
197
+ ```
198
+
199
+ Invalid entries are warned and skipped; the rest are written into the generated manifest verbatim.
182
200
 
183
201
  ## CLI Reference
184
202
 
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, 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: ["github-copilot"] },
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,53 @@ 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 emitDirectoryHints(baseDir) {
201
+ for (const [dir, section] of [
202
+ ["files", "files"],
203
+ ["configs", "configs"],
204
+ ]) {
205
+ try {
206
+ await access(path.join(baseDir, dir));
207
+ logger.info(`Detected ${dir}/ directory — add ${section} entries manually to the generated manifest with target paths.`);
208
+ }
209
+ catch {
210
+ // directory does not exist — silent
211
+ }
212
+ }
213
+ }
163
214
  async function manifestExists(manifestPath) {
164
215
  try {
165
216
  await access(manifestPath);
@@ -169,9 +220,27 @@ async function manifestExists(manifestPath) {
169
220
  return false;
170
221
  }
171
222
  }
223
+ function logVerboseManifest(skills, agentRules, mcpServers) {
224
+ for (const s of skills) {
225
+ logger.detail(`${s.name} → ${s.path}`);
226
+ }
227
+ if (agentRules.length > 0) {
228
+ logger.detail("agentRules:");
229
+ for (const r of agentRules) {
230
+ logger.detail(` ${r.name} → ${r.path} [${r.agents.join(", ")}]`);
231
+ }
232
+ }
233
+ if (mcpServers.length > 0) {
234
+ logger.detail("mcpServers:");
235
+ for (const m of mcpServers) {
236
+ logger.detail(` ${m.name} [${m.agents.join(", ")}]`);
237
+ }
238
+ }
239
+ }
172
240
  export async function runInit(options) {
173
241
  const { directory, dryRun, force, verbose } = options;
174
242
  const agents = options.agents ?? [...AGENT_IDS];
243
+ const agentRulesCapableAgents = agents.filter((id) => AGENT_REGISTRY_BY_ID[id].agentRulesSupport?.status !== "unsupported");
175
244
  const manifestPath = path.join(directory, "inception.json");
176
245
  if (!dryRun && (await manifestExists(manifestPath)) && !force) {
177
246
  logger.error(`Error: ${manifestPath} already exists. Use --force to overwrite.`);
@@ -186,33 +255,28 @@ export async function runInit(options) {
186
255
  const skillNamesSeen = new Set(skills.map((s) => s.name));
187
256
  const skillDirRelPaths = new Set(found.map((f) => f.relPath));
188
257
  const agentRulesCandidates = await findAgentRulesCandidates(directory, skillDirRelPaths);
189
- const agentRules = buildAgentRules(agentRulesCandidates, agents, skillNamesSeen);
258
+ const agentRules = buildAgentRules(agentRulesCandidates, agentRulesCapableAgents, skillNamesSeen);
259
+ const mcpServers = await loadMcpServers(directory);
190
260
  const manifest = {
191
261
  skills,
192
262
  files: [],
193
263
  configs: [],
194
- mcpServers: [],
264
+ mcpServers,
195
265
  agentRules,
196
266
  };
197
267
  const json = `${JSON.stringify(manifest, null, 2)}\n`;
198
268
  if (dryRun) {
199
- logger.info(`${dryRunPrefix(true)}Would write ${manifestPath} with ${skills.length} skill(s) and ${agentRules.length} agentRule(s):`);
269
+ logger.info(`${dryRunPrefix(true)}Would write ${manifestPath} with ${skills.length} skill(s), ${agentRules.length} agentRule(s), and ${mcpServers.length} mcpServer(s):`);
200
270
  logger.info("");
201
271
  logger.info(json);
272
+ await emitDirectoryHints(directory);
202
273
  return 0;
203
274
  }
204
275
  await writeFile(manifestPath, json, "utf-8");
205
- logger.info(`Generated ${manifestPath} with ${skills.length} skill(s) and ${agentRules.length} agentRule(s).`);
276
+ logger.info(`Generated ${manifestPath} with ${skills.length} skill(s), ${agentRules.length} agentRule(s), and ${mcpServers.length} mcpServer(s).`);
206
277
  if (verbose) {
207
- for (const s of skills) {
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
- }
278
+ logVerboseManifest(skills, agentRules, mcpServers);
216
279
  }
280
+ await emitDirectoryHints(directory);
217
281
  return 0;
218
282
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kuznai/inception-engine",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "Deploy AI agent skills from a git repo to user home directories",
5
5
  "license": "MIT",
6
6
  "author": "Damian Piątkowski",