@omnidev-ai/cli 0.17.0 → 0.18.1

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.
Files changed (2) hide show
  1. package/dist/index.js +864 -371
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1287,85 +1287,6 @@ var init_dist = __esm(() => {
1287
1287
  dist_default = { parse, stringify, TomlDate, TomlError };
1288
1288
  });
1289
1289
 
1290
- // ../core/src/providers.ts
1291
- function normalizeProviderId(provider) {
1292
- if (provider in PROVIDER_ALIAS_MAP) {
1293
- return PROVIDER_ALIAS_MAP[provider];
1294
- }
1295
- throw new Error(`Unknown provider: ${provider}`);
1296
- }
1297
- function normalizeProviderApplicability(value, fieldName) {
1298
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
1299
- throw new Error(`${fieldName} must be a table of provider = boolean entries`);
1300
- }
1301
- const normalized = {};
1302
- for (const [rawProvider, rawEnabled] of Object.entries(value)) {
1303
- if (typeof rawEnabled !== "boolean") {
1304
- throw new Error(`${fieldName}.${rawProvider} must be a boolean`);
1305
- }
1306
- const canonicalProvider = normalizeProviderId(rawProvider);
1307
- const existing = normalized[canonicalProvider];
1308
- if (existing !== undefined && existing !== rawEnabled) {
1309
- throw new Error(`Conflicting provider entries in ${fieldName}: ${rawProvider} maps to ${canonicalProvider}`);
1310
- }
1311
- normalized[canonicalProvider] = rawEnabled;
1312
- }
1313
- return normalized;
1314
- }
1315
- var PROVIDER_ALIAS_MAP;
1316
- var init_providers = __esm(() => {
1317
- PROVIDER_ALIAS_MAP = {
1318
- claude: "claude-code",
1319
- "claude-code": "claude-code",
1320
- codex: "codex",
1321
- cursor: "cursor",
1322
- opencode: "opencode"
1323
- };
1324
- });
1325
-
1326
- // ../core/src/config/parser.ts
1327
- function parseOmniConfig(tomlContent) {
1328
- try {
1329
- return parse(tomlContent);
1330
- } catch (error) {
1331
- const message = error instanceof Error ? error.message : String(error);
1332
- throw new Error(`Invalid TOML in config: ${message}`);
1333
- }
1334
- }
1335
- function validateCapabilityConfig(parsed) {
1336
- const cap = parsed["capability"];
1337
- if (typeof cap !== "object" || cap === null) {
1338
- throw new Error("capability.id is required in capability.toml");
1339
- }
1340
- const capability = cap;
1341
- if (typeof capability["id"] !== "string") {
1342
- throw new Error("capability.id is required in capability.toml");
1343
- }
1344
- if (typeof capability["name"] !== "string") {
1345
- throw new Error("capability.name is required in capability.toml");
1346
- }
1347
- if (typeof capability["version"] !== "string") {
1348
- throw new Error("capability.version is required in capability.toml");
1349
- }
1350
- if (capability["providers"] !== undefined) {
1351
- capability["providers"] = normalizeProviderApplicability(capability["providers"], "capability.providers");
1352
- }
1353
- }
1354
- function parseCapabilityConfig(tomlContent) {
1355
- try {
1356
- const parsed = parse(tomlContent);
1357
- validateCapabilityConfig(parsed);
1358
- return parsed;
1359
- } catch (error) {
1360
- const message = error instanceof Error ? error.message : String(error);
1361
- throw new Error(`Invalid capability.toml: ${message}`);
1362
- }
1363
- }
1364
- var init_parser = __esm(() => {
1365
- init_dist();
1366
- init_providers();
1367
- });
1368
-
1369
1290
  // ../core/src/hooks/constants.ts
1370
1291
  var HOOK_EVENTS, MATCHER_EVENTS, PROMPT_HOOK_EVENTS, HOOK_TYPES, COMMON_TOOL_MATCHERS, NOTIFICATION_MATCHERS, SESSION_START_MATCHERS, PRE_COMPACT_MATCHERS, DEFAULT_COMMAND_TIMEOUT = 60, DEFAULT_PROMPT_TIMEOUT = 30, VARIABLE_MAPPINGS, HOOKS_CONFIG_FILENAME = "hooks.toml", CLAUDE_HOOKS_CONFIG_FILENAME = "hooks.json", HOOKS_DIRECTORY = "hooks";
1371
1292
  var init_constants = __esm(() => {
@@ -2451,14 +2372,90 @@ var init_loader = __esm(() => {
2451
2372
  init_constants();
2452
2373
  });
2453
2374
 
2454
- // ../core/src/capability/mcp-env.ts
2375
+ // ../core/src/providers.ts
2376
+ function normalizeProviderId(provider) {
2377
+ if (provider in PROVIDER_ALIAS_MAP) {
2378
+ return PROVIDER_ALIAS_MAP[provider];
2379
+ }
2380
+ throw new Error(`Unknown provider: ${provider}`);
2381
+ }
2382
+ function normalizeProviderApplicability(value, fieldName) {
2383
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
2384
+ throw new Error(`${fieldName} must be a table of provider = boolean entries`);
2385
+ }
2386
+ const normalized = {};
2387
+ for (const [rawProvider, rawEnabled] of Object.entries(value)) {
2388
+ if (typeof rawEnabled !== "boolean") {
2389
+ throw new Error(`${fieldName}.${rawProvider} must be a boolean`);
2390
+ }
2391
+ const canonicalProvider = normalizeProviderId(rawProvider);
2392
+ const existing = normalized[canonicalProvider];
2393
+ if (existing !== undefined && existing !== rawEnabled) {
2394
+ throw new Error(`Conflicting provider entries in ${fieldName}: ${rawProvider} maps to ${canonicalProvider}`);
2395
+ }
2396
+ normalized[canonicalProvider] = rawEnabled;
2397
+ }
2398
+ return normalized;
2399
+ }
2400
+ var PROVIDER_ALIAS_MAP;
2401
+ var init_providers = __esm(() => {
2402
+ PROVIDER_ALIAS_MAP = {
2403
+ claude: "claude-code",
2404
+ "claude-code": "claude-code",
2405
+ codex: "codex",
2406
+ cursor: "cursor",
2407
+ opencode: "opencode"
2408
+ };
2409
+ });
2410
+
2411
+ // ../core/src/config/parser.ts
2412
+ function parseOmniConfig(tomlContent) {
2413
+ try {
2414
+ return parse(tomlContent);
2415
+ } catch (error) {
2416
+ const message = error instanceof Error ? error.message : String(error);
2417
+ throw new Error(`Invalid TOML in config: ${message}`);
2418
+ }
2419
+ }
2420
+ function validateCapabilityConfig(parsed) {
2421
+ const cap = parsed["capability"];
2422
+ if (typeof cap !== "object" || cap === null) {
2423
+ throw new Error("capability.id is required in capability.toml");
2424
+ }
2425
+ const capability = cap;
2426
+ if (typeof capability["id"] !== "string") {
2427
+ throw new Error("capability.id is required in capability.toml");
2428
+ }
2429
+ if (typeof capability["name"] !== "string") {
2430
+ throw new Error("capability.name is required in capability.toml");
2431
+ }
2432
+ if (typeof capability["version"] !== "string") {
2433
+ throw new Error("capability.version is required in capability.toml");
2434
+ }
2435
+ if (capability["providers"] !== undefined) {
2436
+ capability["providers"] = normalizeProviderApplicability(capability["providers"], "capability.providers");
2437
+ }
2438
+ }
2439
+ function parseCapabilityConfig(tomlContent) {
2440
+ try {
2441
+ const parsed = parse(tomlContent);
2442
+ validateCapabilityConfig(parsed);
2443
+ return parsed;
2444
+ } catch (error) {
2445
+ const message = error instanceof Error ? error.message : String(error);
2446
+ throw new Error(`Invalid capability.toml: ${message}`);
2447
+ }
2448
+ }
2449
+ var init_parser = __esm(() => {
2450
+ init_dist();
2451
+ init_providers();
2452
+ });
2453
+
2454
+ // ../core/src/capability/env.ts
2455
2455
  import { existsSync as existsSync6 } from "node:fs";
2456
2456
  import { readFile as readFile3 } from "node:fs/promises";
2457
2457
  import { join as join4 } from "node:path";
2458
2458
  import { parseEnv } from "node:util";
2459
- function hasEnvPlaceholder(value) {
2460
- return ENV_PLACEHOLDER_DETECTOR.test(value);
2461
- }
2462
2459
  function mergeEnvSources(capabilityEnv) {
2463
2460
  const merged = { ...capabilityEnv };
2464
2461
  for (const [key, value] of Object.entries(process.env)) {
@@ -2468,13 +2465,21 @@ function mergeEnvSources(capabilityEnv) {
2468
2465
  }
2469
2466
  return merged;
2470
2467
  }
2471
- async function loadCapabilityEnv(capabilityPath) {
2468
+ async function loadCapabilityEnvVariables(capabilityPath) {
2472
2469
  const envPath = join4(capabilityPath, CAPABILITY_ENV_FILE);
2473
2470
  if (!existsSync6(envPath)) {
2474
- return {};
2471
+ return mergeEnvSources({});
2475
2472
  }
2476
2473
  const envContent = await readFile3(envPath, "utf-8");
2477
- return Object.fromEntries(Object.entries(parseEnv(envContent)).filter((entry) => typeof entry[1] === "string"));
2474
+ const capabilityEnv = Object.fromEntries(Object.entries(parseEnv(envContent)).filter((entry) => typeof entry[1] === "string"));
2475
+ return mergeEnvSources(capabilityEnv);
2476
+ }
2477
+ var CAPABILITY_ENV_FILE = ".env";
2478
+ var init_env = () => {};
2479
+
2480
+ // ../core/src/capability/mcp-env.ts
2481
+ function hasEnvPlaceholder(value) {
2482
+ return ENV_PLACEHOLDER_DETECTOR.test(value);
2478
2483
  }
2479
2484
  function resolveString(value, variables, capabilityId, fieldPath) {
2480
2485
  if (!hasEnvPlaceholder(value)) {
@@ -2510,35 +2515,35 @@ function mcpHasPlaceholders(mcp) {
2510
2515
  }
2511
2516
  return strings.some((value) => hasEnvPlaceholder(value));
2512
2517
  }
2513
- async function resolveCapabilityMcpEnv(config, capabilityPath) {
2518
+ async function resolveCapabilityMcpEnv(config, capabilityPath, variables) {
2514
2519
  if (!config.mcp || !mcpHasPlaceholders(config.mcp)) {
2515
2520
  return config;
2516
2521
  }
2517
- const variables = mergeEnvSources(await loadCapabilityEnv(capabilityPath));
2522
+ const resolvedVariables = variables ?? await loadCapabilityEnvVariables(capabilityPath);
2518
2523
  const resolvedMcp = { ...config.mcp };
2519
2524
  const capabilityId = config.capability.id;
2520
2525
  if (resolvedMcp.command) {
2521
- resolvedMcp.command = resolveString(resolvedMcp.command, variables, capabilityId, "mcp.command");
2526
+ resolvedMcp.command = resolveString(resolvedMcp.command, resolvedVariables, capabilityId, "mcp.command");
2522
2527
  }
2523
2528
  if (resolvedMcp.cwd) {
2524
- resolvedMcp.cwd = resolveString(resolvedMcp.cwd, variables, capabilityId, "mcp.cwd");
2529
+ resolvedMcp.cwd = resolveString(resolvedMcp.cwd, resolvedVariables, capabilityId, "mcp.cwd");
2525
2530
  }
2526
2531
  if (resolvedMcp.url) {
2527
- resolvedMcp.url = resolveString(resolvedMcp.url, variables, capabilityId, "mcp.url");
2532
+ resolvedMcp.url = resolveString(resolvedMcp.url, resolvedVariables, capabilityId, "mcp.url");
2528
2533
  }
2529
2534
  if (resolvedMcp.args) {
2530
- resolvedMcp.args = resolvedMcp.args.map((arg, index) => resolveString(arg, variables, capabilityId, `mcp.args[${index}]`));
2535
+ resolvedMcp.args = resolvedMcp.args.map((arg, index) => resolveString(arg, resolvedVariables, capabilityId, `mcp.args[${index}]`));
2531
2536
  }
2532
2537
  if (resolvedMcp.env) {
2533
2538
  resolvedMcp.env = Object.fromEntries(Object.entries(resolvedMcp.env).map(([key, value]) => [
2534
2539
  key,
2535
- resolveString(value, variables, capabilityId, `mcp.env.${key}`)
2540
+ resolveString(value, resolvedVariables, capabilityId, `mcp.env.${key}`)
2536
2541
  ]));
2537
2542
  }
2538
2543
  if (resolvedMcp.headers) {
2539
2544
  resolvedMcp.headers = Object.fromEntries(Object.entries(resolvedMcp.headers).map(([key, value]) => [
2540
2545
  key,
2541
- resolveString(value, variables, capabilityId, `mcp.headers.${key}`)
2546
+ resolveString(value, resolvedVariables, capabilityId, `mcp.headers.${key}`)
2542
2547
  ]));
2543
2548
  }
2544
2549
  return {
@@ -2546,8 +2551,9 @@ async function resolveCapabilityMcpEnv(config, capabilityPath) {
2546
2551
  mcp: resolvedMcp
2547
2552
  };
2548
2553
  }
2549
- var CAPABILITY_ENV_FILE = ".env", ENV_PLACEHOLDER, ENV_PLACEHOLDER_DETECTOR;
2554
+ var ENV_PLACEHOLDER, ENV_PLACEHOLDER_DETECTOR;
2550
2555
  var init_mcp_env = __esm(() => {
2556
+ init_env();
2551
2557
  ENV_PLACEHOLDER = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
2552
2558
  ENV_PLACEHOLDER_DETECTOR = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/;
2553
2559
  });
@@ -2582,9 +2588,45 @@ var init_rules = () => {};
2582
2588
  import { existsSync as existsSync8, readdirSync as readdirSync4 } from "node:fs";
2583
2589
  import { readFile as readFile5 } from "node:fs/promises";
2584
2590
  import { join as join6 } from "node:path";
2585
- async function loadSkills(capabilityPath, capabilityId) {
2591
+ function hasSkillPlaceholder(value) {
2592
+ return SKILL_PLACEHOLDER_DETECTOR.test(value);
2593
+ }
2594
+ function resolveSkillPlaceholders(content, variables, capabilityId, sourceLabel) {
2595
+ if (!hasSkillPlaceholder(content)) {
2596
+ return content;
2597
+ }
2598
+ return content.replace(SKILL_PLACEHOLDER, (match, variableName) => {
2599
+ const resolved = variables[variableName];
2600
+ if (resolved === undefined) {
2601
+ throw new Error(`Missing environment variable "${variableName}" required by capability "${capabilityId}" in ${sourceLabel} (placeholder "${match}")`);
2602
+ }
2603
+ return resolved;
2604
+ });
2605
+ }
2606
+ function parseSkillMarkdown(content, capabilityId, options) {
2607
+ const resolvedContent = options?.variables && options.sourceLabel ? resolveSkillPlaceholders(content, options.variables, capabilityId, options.sourceLabel) : content;
2608
+ const parsed = parseFrontmatterWithMarkdown(resolvedContent);
2609
+ if (!parsed) {
2610
+ const sourceLabel = options?.sourceLabel ?? "skill content";
2611
+ throw new Error(`Invalid SKILL.md format at ${sourceLabel}: missing YAML frontmatter`);
2612
+ }
2613
+ const frontmatter = parsed.frontmatter;
2614
+ const instructions = parsed.markdown;
2615
+ if (!frontmatter.name || !frontmatter.description) {
2616
+ const sourceLabel = options?.sourceLabel ?? "skill content";
2617
+ throw new Error(`Invalid SKILL.md at ${sourceLabel}: name and description required in frontmatter`);
2618
+ }
2619
+ return {
2620
+ name: frontmatter.name,
2621
+ description: frontmatter.description,
2622
+ instructions: instructions.trim(),
2623
+ capabilityId
2624
+ };
2625
+ }
2626
+ async function loadSkills(capabilityPath, capabilityId, variables) {
2586
2627
  const skills = [];
2587
2628
  const possibleDirNames = ["skills", "skill"];
2629
+ const resolvedVariables = variables ?? await loadCapabilityEnvVariables(capabilityPath);
2588
2630
  for (const dirName of possibleDirNames) {
2589
2631
  const dir = join6(capabilityPath, dirName);
2590
2632
  if (!existsSync8(dir)) {
@@ -2595,33 +2637,30 @@ async function loadSkills(capabilityPath, capabilityId) {
2595
2637
  if (entry.isDirectory()) {
2596
2638
  const skillPath = join6(dir, entry.name, "SKILL.md");
2597
2639
  if (existsSync8(skillPath)) {
2598
- const skill = await parseSkillFile(skillPath, capabilityId);
2599
- skills.push(skill);
2640
+ const skill = await parseSkillFile(skillPath, capabilityId, resolvedVariables, `skill file ${skillPath}`);
2641
+ skills.push({
2642
+ ...skill,
2643
+ sourcePath: join6(dir, entry.name)
2644
+ });
2600
2645
  }
2601
2646
  }
2602
2647
  }
2603
2648
  }
2604
2649
  return skills;
2605
2650
  }
2606
- async function parseSkillFile(filePath, capabilityId) {
2651
+ async function parseSkillFile(filePath, capabilityId, variables, sourceLabel) {
2607
2652
  const content = await readFile5(filePath, "utf-8");
2608
- const parsed = parseFrontmatterWithMarkdown(content);
2609
- if (!parsed) {
2610
- throw new Error(`Invalid SKILL.md format at ${filePath}: missing YAML frontmatter`);
2611
- }
2612
- const frontmatter = parsed.frontmatter;
2613
- const instructions = parsed.markdown;
2614
- if (!frontmatter.name || !frontmatter.description) {
2615
- throw new Error(`Invalid SKILL.md at ${filePath}: name and description required in frontmatter`);
2616
- }
2617
- return {
2618
- name: frontmatter.name,
2619
- description: frontmatter.description,
2620
- instructions: instructions.trim(),
2621
- capabilityId
2622
- };
2653
+ return parseSkillMarkdown(content, capabilityId, {
2654
+ variables,
2655
+ sourceLabel
2656
+ });
2623
2657
  }
2624
- var init_skills = () => {};
2658
+ var SKILL_PLACEHOLDER, SKILL_PLACEHOLDER_DETECTOR;
2659
+ var init_skills = __esm(() => {
2660
+ init_env();
2661
+ SKILL_PLACEHOLDER = /\{OMNIDEV_([A-Za-z_][A-Za-z0-9_]*)\}/g;
2662
+ SKILL_PLACEHOLDER_DETECTOR = /\{OMNIDEV_([A-Za-z_][A-Za-z0-9_]*)\}/;
2663
+ });
2625
2664
 
2626
2665
  // ../core/src/capability/subagents.ts
2627
2666
  import { existsSync as existsSync9, readdirSync as readdirSync5 } from "node:fs";
@@ -2767,40 +2806,13 @@ async function loadTypeDefinitions(capabilityPath) {
2767
2806
  }
2768
2807
  return readFile7(typesPath, "utf-8");
2769
2808
  }
2770
- function convertSkillExports(skillExports, capabilityId) {
2771
- return skillExports.map((skillExport) => {
2809
+ function convertSkillExports(skillExports, capabilityId, variables) {
2810
+ return skillExports.map((skillExport, index) => {
2772
2811
  const exportObj = skillExport;
2773
- const lines = exportObj.skillMd.split(`
2774
- `);
2775
- let name = "unnamed";
2776
- let description = "";
2777
- let instructions = exportObj.skillMd;
2778
- if (lines[0]?.trim() === "---") {
2779
- const endIndex = lines.findIndex((line, i) => i > 0 && line.trim() === "---");
2780
- if (endIndex > 0) {
2781
- const frontmatter = lines.slice(1, endIndex);
2782
- instructions = lines.slice(endIndex + 1).join(`
2783
- `).trim();
2784
- for (const line of frontmatter) {
2785
- const match = line.match(/^(\w+):\s*(.+)$/);
2786
- if (match?.[1] && match[2]) {
2787
- const key = match[1];
2788
- const value = match[2];
2789
- if (key === "name") {
2790
- name = value.replace(/^["']|["']$/g, "");
2791
- } else if (key === "description") {
2792
- description = value.replace(/^["']|["']$/g, "");
2793
- }
2794
- }
2795
- }
2796
- }
2797
- }
2798
- return {
2799
- name,
2800
- description,
2801
- instructions,
2802
- capabilityId
2803
- };
2812
+ return parseSkillMarkdown(exportObj.skillMd, capabilityId, {
2813
+ variables,
2814
+ sourceLabel: `programmatic skill export[${index}]`
2815
+ });
2804
2816
  });
2805
2817
  }
2806
2818
  function convertRuleExports(ruleExports, capabilityId) {
@@ -2953,8 +2965,9 @@ function mergeByName(fileBased, programmatic) {
2953
2965
  return Array.from(byName.values());
2954
2966
  }
2955
2967
  async function loadCapability(capabilityPath) {
2968
+ const capabilityEnvVariables = await loadCapabilityEnvVariables(capabilityPath);
2956
2969
  const rawConfig = await loadCapabilityConfig(capabilityPath);
2957
- const config = await resolveCapabilityMcpEnv(rawConfig, capabilityPath);
2970
+ const config = await resolveCapabilityMcpEnv(rawConfig, capabilityPath, capabilityEnvVariables);
2958
2971
  const id = config.capability.id;
2959
2972
  const exports = await importCapabilityExports(capabilityPath);
2960
2973
  const exportsAny = exports;
@@ -2969,8 +2982,8 @@ async function loadCapability(capabilityPath) {
2969
2982
  return;
2970
2983
  };
2971
2984
  const skillsExport = getExportValue("skills");
2972
- const programmaticSkills = Array.isArray(skillsExport) ? convertSkillExports(skillsExport, id) : [];
2973
- const fileSkills = await loadSkills(capabilityPath, id);
2985
+ const programmaticSkills = Array.isArray(skillsExport) ? convertSkillExports(skillsExport, id, capabilityEnvVariables) : [];
2986
+ const fileSkills = await loadSkills(capabilityPath, id, capabilityEnvVariables);
2974
2987
  const skills = mergeByName(fileSkills, programmaticSkills);
2975
2988
  const rulesExport = getExportValue("rules");
2976
2989
  const programmaticRules = Array.isArray(rulesExport) ? convertRuleExports(rulesExport, id) : [];
@@ -3018,11 +3031,12 @@ async function loadCapability(capabilityPath) {
3018
3031
  }
3019
3032
  var CAPABILITIES_DIR = ".omni/capabilities";
3020
3033
  var init_loader2 = __esm(() => {
3021
- init_parser();
3022
3034
  init_loader();
3023
- init_mcp_env();
3035
+ init_parser();
3024
3036
  init_commands();
3025
3037
  init_docs();
3038
+ init_env();
3039
+ init_mcp_env();
3026
3040
  init_rules();
3027
3041
  init_skills();
3028
3042
  init_subagents();
@@ -3513,7 +3527,7 @@ import { existsSync as existsSync13 } from "node:fs";
3513
3527
  import { spawn } from "node:child_process";
3514
3528
  import { cp, mkdir, readdir, readFile as readFile10, rename, rm, stat, writeFile as writeFile3 } from "node:fs/promises";
3515
3529
  import { join as join9 } from "node:path";
3516
- import { createHash } from "node:crypto";
3530
+ import { createHash as createHash2 } from "node:crypto";
3517
3531
  async function spawnCapture(command, args, options) {
3518
3532
  const timeout = options?.timeout ?? 60000;
3519
3533
  return await new Promise((resolve2, reject) => {
@@ -3722,7 +3736,7 @@ function escapeTomlString(value) {
3722
3736
  return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
3723
3737
  }
3724
3738
  async function computeContentHash(dirPath, excludePatterns = CONTENT_HASH_EXCLUDES) {
3725
- const hash = createHash("sha256");
3739
+ const hash = createHash2("sha256");
3726
3740
  const files = [];
3727
3741
  async function collectFiles(currentPath, relativeTo) {
3728
3742
  const entries = await readdir(currentPath, { withFileTypes: true });
@@ -5157,29 +5171,107 @@ var init_mcp_json = __esm(() => {
5157
5171
  });
5158
5172
 
5159
5173
  // ../core/src/state/manifest.ts
5160
- import { existsSync as existsSync17, mkdirSync as mkdirSync2, rmSync } from "node:fs";
5174
+ import { createHash as createHash3 } from "node:crypto";
5175
+ import { existsSync as existsSync17, mkdirSync as mkdirSync2, readdirSync as readdirSync7, rmSync } from "node:fs";
5176
+ import { dirname, join as join10 } from "node:path";
5161
5177
  import { readFile as readFile14, writeFile as writeFile7 } from "node:fs/promises";
5162
- async function loadManifest() {
5163
- if (!existsSync17(MANIFEST_PATH)) {
5178
+ function createEmptyManifest() {
5179
+ return {
5180
+ version: CURRENT_VERSION,
5181
+ syncedAt: new Date().toISOString(),
5182
+ capabilities: {},
5183
+ providers: {}
5184
+ };
5185
+ }
5186
+ function normalizeManifest(parsed) {
5187
+ if (parsed.version === CURRENT_VERSION && "providers" in parsed) {
5164
5188
  return {
5165
5189
  version: CURRENT_VERSION,
5166
- syncedAt: new Date().toISOString(),
5167
- capabilities: {}
5190
+ syncedAt: parsed.syncedAt,
5191
+ capabilities: parsed.capabilities,
5192
+ providers: parsed.providers ?? {}
5168
5193
  };
5169
5194
  }
5195
+ return {
5196
+ version: CURRENT_VERSION,
5197
+ syncedAt: parsed.syncedAt,
5198
+ capabilities: parsed.capabilities,
5199
+ providers: {}
5200
+ };
5201
+ }
5202
+ function hashContent2(content) {
5203
+ return createHash3("sha256").update(content).digest("hex");
5204
+ }
5205
+ function normalizePath(path) {
5206
+ return path.replace(/\\/g, "/");
5207
+ }
5208
+ function isDirectoryEmpty(path) {
5209
+ return readdirSync7(path).length === 0;
5210
+ }
5211
+ async function cleanupManagedOutput(output) {
5212
+ const outputPath = join10(process.cwd(), output.path);
5213
+ if (!existsSync17(outputPath)) {
5214
+ return { deleted: false };
5215
+ }
5216
+ if (output.cleanupStrategy === "remove-json-key") {
5217
+ if (!output.jsonKey) {
5218
+ return { deleted: false, reason: `missing jsonKey metadata for ${output.path}` };
5219
+ }
5220
+ let parsed;
5221
+ try {
5222
+ parsed = JSON.parse(await readFile14(outputPath, "utf-8"));
5223
+ } catch {
5224
+ return { deleted: false, reason: `could not parse JSON at ${output.path}` };
5225
+ }
5226
+ const currentValue = parsed[output.jsonKey];
5227
+ if (currentValue === undefined) {
5228
+ return { deleted: false };
5229
+ }
5230
+ if (hashContent2(JSON.stringify(currentValue)) !== output.hash) {
5231
+ return { deleted: false, reason: `managed section changed at ${output.path}` };
5232
+ }
5233
+ delete parsed[output.jsonKey];
5234
+ if (Object.keys(parsed).length === 0) {
5235
+ rmSync(outputPath);
5236
+ } else {
5237
+ await writeFile7(outputPath, `${JSON.stringify(parsed, null, 2)}
5238
+ `, "utf-8");
5239
+ }
5240
+ return { deleted: true };
5241
+ }
5242
+ const currentContent = await readFile14(outputPath, "utf-8");
5243
+ if (hashContent2(currentContent) !== output.hash) {
5244
+ return { deleted: false, reason: `managed file changed at ${output.path}` };
5245
+ }
5246
+ rmSync(outputPath);
5247
+ if (output.cleanupStrategy === "delete-file-and-prune-empty-parents" && output.pruneRoot) {
5248
+ const pruneRoot = join10(process.cwd(), output.pruneRoot);
5249
+ let currentDir = dirname(outputPath);
5250
+ while (normalizePath(currentDir).startsWith(normalizePath(pruneRoot)) && normalizePath(currentDir) !== normalizePath(pruneRoot) && existsSync17(currentDir) && isDirectoryEmpty(currentDir)) {
5251
+ rmSync(currentDir, { recursive: true });
5252
+ currentDir = dirname(currentDir);
5253
+ }
5254
+ }
5255
+ return { deleted: true };
5256
+ }
5257
+ async function loadManifest() {
5258
+ if (!existsSync17(MANIFEST_PATH)) {
5259
+ return createEmptyManifest();
5260
+ }
5170
5261
  const content = await readFile14(MANIFEST_PATH, "utf-8");
5171
- return JSON.parse(content);
5262
+ return normalizeManifest(JSON.parse(content));
5172
5263
  }
5173
5264
  async function saveManifest(manifest) {
5174
5265
  mkdirSync2(".omni/state", { recursive: true });
5175
5266
  await writeFile7(MANIFEST_PATH, `${JSON.stringify(manifest, null, 2)}
5176
5267
  `, "utf-8");
5177
5268
  }
5178
- function buildManifestFromCapabilities(capabilities2) {
5269
+ function buildManifestFromCapabilities(capabilities2, providerOutputs = new Map) {
5179
5270
  const manifest = {
5180
5271
  version: CURRENT_VERSION,
5181
5272
  syncedAt: new Date().toISOString(),
5182
- capabilities: {}
5273
+ capabilities: {},
5274
+ providers: {}
5183
5275
  };
5184
5276
  for (const cap of capabilities2) {
5185
5277
  const resources = {
@@ -5191,38 +5283,59 @@ function buildManifestFromCapabilities(capabilities2) {
5191
5283
  };
5192
5284
  manifest.capabilities[cap.id] = resources;
5193
5285
  }
5286
+ for (const [providerId, outputs] of providerOutputs) {
5287
+ manifest.providers[providerId] = {
5288
+ outputs: Object.fromEntries(outputs.map((output) => [output.path, output]))
5289
+ };
5290
+ }
5194
5291
  return manifest;
5195
5292
  }
5196
- async function cleanupStaleResources(previousManifest, currentCapabilityIds) {
5197
- const result = {
5293
+ async function cleanupStaleResources(_previousManifest, _currentCapabilityIds) {
5294
+ return {
5198
5295
  deletedSkills: [],
5199
5296
  deletedRules: [],
5200
5297
  deletedCommands: [],
5201
5298
  deletedSubagents: [],
5202
5299
  deletedMcps: []
5203
5300
  };
5204
- for (const [capId, resources] of Object.entries(previousManifest.capabilities)) {
5205
- if (currentCapabilityIds.has(capId)) {
5206
- continue;
5301
+ }
5302
+ async function cleanupStaleManagedOutputs(previousManifest, nextProviders) {
5303
+ const result = {
5304
+ deletedPaths: [],
5305
+ skippedPaths: []
5306
+ };
5307
+ const claimedPaths = new Set;
5308
+ for (const outputs of nextProviders.values()) {
5309
+ for (const output of outputs) {
5310
+ claimedPaths.add(output.path);
5207
5311
  }
5208
- for (const skillName of resources.skills) {
5209
- const skillDir = `.claude/skills/${skillName}`;
5210
- if (existsSync17(skillDir)) {
5211
- rmSync(skillDir, { recursive: true });
5212
- result.deletedSkills.push(skillName);
5312
+ }
5313
+ for (const [providerId, providerState] of Object.entries(previousManifest.providers)) {
5314
+ const nextPaths = new Set((nextProviders.get(providerId) ?? []).map((output) => output.path));
5315
+ for (const output of Object.values(providerState.outputs)) {
5316
+ if (nextPaths.has(output.path)) {
5317
+ continue;
5213
5318
  }
5214
- }
5215
- for (const ruleName of resources.rules) {
5216
- const rulePath = `.cursor/rules/omnidev-${ruleName}.mdc`;
5217
- if (existsSync17(rulePath)) {
5218
- rmSync(rulePath);
5219
- result.deletedRules.push(ruleName);
5319
+ if (claimedPaths.has(output.path)) {
5320
+ continue;
5321
+ }
5322
+ const cleanup = await cleanupManagedOutput(output);
5323
+ if (cleanup.deleted) {
5324
+ result.deletedPaths.push(output.path);
5325
+ } else if (cleanup.reason) {
5326
+ result.skippedPaths.push({
5327
+ path: output.path,
5328
+ reason: cleanup.reason
5329
+ });
5220
5330
  }
5221
5331
  }
5222
5332
  }
5223
5333
  return result;
5224
5334
  }
5225
- var MANIFEST_PATH = ".omni/state/manifest.json", CURRENT_VERSION = 1;
5335
+ function getProviderManagedOutputs(manifest, providerId) {
5336
+ return Object.values(manifest.providers[providerId]?.outputs ?? {});
5337
+ }
5338
+ var MANIFEST_PATH = ".omni/state/manifest.json", CURRENT_VERSION = 2;
5226
5339
  var init_manifest = () => {};
5227
5340
 
5228
5341
  // ../core/src/state/providers.ts
@@ -5370,16 +5483,42 @@ var init_state = __esm(() => {
5370
5483
 
5371
5484
  // ../core/src/sync.ts
5372
5485
  import { spawn as spawn2 } from "node:child_process";
5373
- import { mkdirSync as mkdirSync5 } from "node:fs";
5486
+ import { existsSync as existsSync20, mkdirSync as mkdirSync5, readFileSync as readFileSync3 } from "node:fs";
5487
+ import { join as join11 } from "node:path";
5488
+ function getDeclaredPackageManager(packageManager) {
5489
+ if (typeof packageManager !== "string" || packageManager.trim().length === 0) {
5490
+ return;
5491
+ }
5492
+ const atIndex = packageManager.indexOf("@");
5493
+ return atIndex === -1 ? packageManager : packageManager.slice(0, atIndex);
5494
+ }
5495
+ function resolveCapabilityInstallCommand(capabilityPath, options) {
5496
+ const packageJsonPath = join11(capabilityPath, "package.json");
5497
+ const packageLockPath = join11(capabilityPath, "package-lock.json");
5498
+ let packageManager;
5499
+ try {
5500
+ const pkgJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
5501
+ packageManager = getDeclaredPackageManager(pkgJson.packageManager);
5502
+ } catch {}
5503
+ if (!options.hasNpm) {
5504
+ throw new Error("npm is not installed. Install npm to install capability dependencies.");
5505
+ }
5506
+ if (packageManager && packageManager !== "npm") {
5507
+ throw new Error(`Capability at ${capabilityPath} declares packageManager=${packageManager}, but OmniDev only supports npm for capability dependencies.`);
5508
+ }
5509
+ return {
5510
+ cmd: "npm",
5511
+ args: [existsSync20(packageLockPath) ? "ci" : "install"]
5512
+ };
5513
+ }
5374
5514
  async function installCapabilityDependencies(silent) {
5375
- const { existsSync: existsSync20, readdirSync: readdirSync7, readFileSync: readFileSync3 } = await import("node:fs");
5376
- const { join: join10 } = await import("node:path");
5515
+ const { readdirSync: readdirSync8 } = await import("node:fs");
5377
5516
  const { parse: parse2 } = await Promise.resolve().then(() => (init_dist(), exports_dist));
5378
5517
  const capabilitiesDir = ".omni/capabilities";
5379
5518
  if (!existsSync20(capabilitiesDir)) {
5380
5519
  return;
5381
5520
  }
5382
- const entries = readdirSync7(capabilitiesDir, { withFileTypes: true });
5521
+ const entries = readdirSync8(capabilitiesDir, { withFileTypes: true });
5383
5522
  async function commandExists(cmd) {
5384
5523
  return await new Promise((resolve2) => {
5385
5524
  const proc = spawn2(cmd, ["--version"], { stdio: "ignore" });
@@ -5387,18 +5526,17 @@ async function installCapabilityDependencies(silent) {
5387
5526
  proc.on("close", (code) => resolve2(code === 0));
5388
5527
  });
5389
5528
  }
5390
- const hasBun = await commandExists("bun");
5391
- const hasNpm = hasBun ? false : await commandExists("npm");
5392
- if (!hasBun && !hasNpm) {
5393
- throw new Error("Neither Bun nor npm is installed. Install one of them to install capability dependencies.");
5529
+ const hasNpm = await commandExists("npm");
5530
+ if (!hasNpm) {
5531
+ throw new Error("npm is not installed. Install npm to install capability dependencies.");
5394
5532
  }
5395
5533
  for (const entry of entries) {
5396
5534
  if (!entry.isDirectory()) {
5397
5535
  continue;
5398
5536
  }
5399
- const capabilityPath = join10(capabilitiesDir, entry.name);
5400
- const packageJsonPath = join10(capabilityPath, "package.json");
5401
- const capabilityTomlPath = join10(capabilityPath, "capability.toml");
5537
+ const capabilityPath = join11(capabilitiesDir, entry.name);
5538
+ const packageJsonPath = join11(capabilityPath, "package.json");
5539
+ const capabilityTomlPath = join11(capabilityPath, "capability.toml");
5402
5540
  if (!existsSync20(packageJsonPath)) {
5403
5541
  continue;
5404
5542
  }
@@ -5413,9 +5551,9 @@ async function installCapabilityDependencies(silent) {
5413
5551
  }
5414
5552
  try {
5415
5553
  await new Promise((resolve2, reject) => {
5416
- const useNpmCi = hasNpm && existsSync20(join10(capabilityPath, "package-lock.json"));
5417
- const cmd = hasBun ? "bun" : "npm";
5418
- const args = hasBun ? ["install"] : useNpmCi ? ["ci"] : ["install"];
5554
+ const { cmd, args } = resolveCapabilityInstallCommand(capabilityPath, {
5555
+ hasNpm
5556
+ });
5419
5557
  const proc = spawn2(cmd, args, {
5420
5558
  cwd: capabilityPath,
5421
5559
  stdio: "pipe"
@@ -5436,7 +5574,7 @@ ${stderr}`));
5436
5574
  reject(error);
5437
5575
  });
5438
5576
  });
5439
- const hasIndexTs = existsSync20(join10(capabilityPath, "index.ts"));
5577
+ const hasIndexTs = existsSync20(join11(capabilityPath, "index.ts"));
5440
5578
  let hasBuildScript = false;
5441
5579
  try {
5442
5580
  const pkgJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
@@ -5444,9 +5582,7 @@ ${stderr}`));
5444
5582
  } catch {}
5445
5583
  if (hasBuildScript) {
5446
5584
  await new Promise((resolve2, reject) => {
5447
- const cmd = hasBun ? "bun" : "npm";
5448
- const args = ["run", "build"];
5449
- const proc = spawn2(cmd, args, {
5585
+ const proc = spawn2("npm", ["run", "build"], {
5450
5586
  cwd: capabilityPath,
5451
5587
  stdio: "pipe"
5452
5588
  });
@@ -5467,7 +5603,7 @@ ${stderr}`));
5467
5603
  });
5468
5604
  });
5469
5605
  } else if (hasIndexTs && !silent) {
5470
- const hasBuiltIndex = existsSync20(join10(capabilityPath, "dist", "index.js"));
5606
+ const hasBuiltIndex = existsSync20(join11(capabilityPath, "dist", "index.js"));
5471
5607
  if (!hasBuiltIndex) {
5472
5608
  console.warn(`Warning: Capability at ${capabilityPath} has index.ts but no build script.
5473
5609
  Add a "build" script to package.json (e.g., "build": "tsc") to compile TypeScript.`);
@@ -5538,8 +5674,8 @@ async function syncAgentConfiguration(options) {
5538
5674
  }
5539
5675
  mkdirSync5(".omni", { recursive: true });
5540
5676
  await syncMcpJson(capabilities2, previousManifest);
5541
- const newManifest = buildManifestFromCapabilities(capabilities2);
5542
- await saveManifest(newManifest);
5677
+ const enabledProviderIds = new Set(adapters.map((adapter) => String(adapter.id)));
5678
+ const successfulProviderOutputs = new Map;
5543
5679
  if (adapters.length > 0) {
5544
5680
  const config2 = await loadConfig();
5545
5681
  const ctx = {
@@ -5548,12 +5684,33 @@ async function syncAgentConfiguration(options) {
5548
5684
  };
5549
5685
  for (const adapter of adapters) {
5550
5686
  try {
5551
- await adapter.sync(bundle, ctx);
5687
+ const adapterResult = await adapter.sync(bundle, ctx);
5688
+ successfulProviderOutputs.set(String(adapter.id), adapterResult.managedOutputs ?? []);
5552
5689
  } catch (error) {
5553
5690
  console.error(`Error running ${adapter.displayName} adapter:`, error);
5554
5691
  }
5555
5692
  }
5556
5693
  }
5694
+ const nextProviderOutputs = new Map;
5695
+ if (adapters.length === 0) {
5696
+ for (const providerId of Object.keys(previousManifest.providers)) {
5697
+ nextProviderOutputs.set(providerId, getProviderManagedOutputs(previousManifest, providerId));
5698
+ }
5699
+ } else {
5700
+ for (const providerId of enabledProviderIds) {
5701
+ if (successfulProviderOutputs.has(providerId)) {
5702
+ nextProviderOutputs.set(providerId, successfulProviderOutputs.get(providerId) ?? []);
5703
+ continue;
5704
+ }
5705
+ nextProviderOutputs.set(providerId, getProviderManagedOutputs(previousManifest, providerId));
5706
+ }
5707
+ }
5708
+ const cleanupResult = await cleanupStaleManagedOutputs(previousManifest, nextProviderOutputs);
5709
+ for (const skipped of cleanupResult.skippedPaths) {
5710
+ console.warn(`Warning: skipped cleanup for ${skipped.path} (${skipped.reason})`);
5711
+ }
5712
+ const newManifest = buildManifestFromCapabilities(capabilities2, nextProviderOutputs);
5713
+ await saveManifest(newManifest);
5557
5714
  const result = {
5558
5715
  capabilities: capabilities2.map((c) => c.id),
5559
5716
  skillCount: bundle.skills.length,
@@ -5598,21 +5755,23 @@ var init_types3 = __esm(() => {
5598
5755
  unicode: true,
5599
5756
  symlinks: true,
5600
5757
  scripts: true,
5601
- binaries: false
5758
+ binaries: false,
5759
+ hiddenCommands: true
5602
5760
  }
5603
5761
  };
5604
5762
  DEFAULT_SCAN_SETTINGS = {
5605
5763
  unicode: true,
5606
5764
  symlinks: true,
5607
5765
  scripts: true,
5608
- binaries: false
5766
+ binaries: false,
5767
+ hiddenCommands: true
5609
5768
  };
5610
5769
  });
5611
5770
 
5612
5771
  // ../core/src/security/scanner.ts
5613
- import { existsSync as existsSync20 } from "node:fs";
5772
+ import { existsSync as existsSync21 } from "node:fs";
5614
5773
  import { lstat, readdir as readdir2, readFile as readFile17, readlink, realpath } from "node:fs/promises";
5615
- import { join as join10, relative, resolve as resolve2 } from "node:path";
5774
+ import { join as join12, relative, resolve as resolve2 } from "node:path";
5616
5775
  async function scanFileForUnicode(filePath, relativePath) {
5617
5776
  const findings = [];
5618
5777
  try {
@@ -5693,10 +5852,113 @@ async function scanFileForScripts(filePath, relativePath) {
5693
5852
  } catch {}
5694
5853
  return findings;
5695
5854
  }
5855
+ function extractHiddenRegions(fileContent) {
5856
+ const regions = [];
5857
+ HTML_COMMENT_RE.lastIndex = 0;
5858
+ for (let match = HTML_COMMENT_RE.exec(fileContent);match !== null; match = HTML_COMMENT_RE.exec(fileContent)) {
5859
+ const captured = match[1];
5860
+ if (captured === undefined)
5861
+ continue;
5862
+ const beforeMatch = fileContent.substring(0, match.index);
5863
+ const startLine = beforeMatch.split(`
5864
+ `).length;
5865
+ regions.push({ content: captured, startLine });
5866
+ }
5867
+ HIDDEN_REFERENCE_RE.lastIndex = 0;
5868
+ for (let match = HIDDEN_REFERENCE_RE.exec(fileContent);match !== null; match = HIDDEN_REFERENCE_RE.exec(fileContent)) {
5869
+ const captured = match[1];
5870
+ if (captured === undefined)
5871
+ continue;
5872
+ const beforeMatch = fileContent.substring(0, match.index);
5873
+ const startLine = beforeMatch.split(`
5874
+ `).length;
5875
+ regions.push({ content: captured, startLine });
5876
+ }
5877
+ return regions;
5878
+ }
5879
+ async function scanFileForHiddenCommands(filePath, relativePath) {
5880
+ const findings = [];
5881
+ try {
5882
+ const content = await readFile17(filePath, "utf-8");
5883
+ const hiddenRegions = extractHiddenRegions(content);
5884
+ for (const region of hiddenRegions) {
5885
+ const regionLines = region.content.split(`
5886
+ `);
5887
+ for (let i = 0;i < regionLines.length; i++) {
5888
+ const line = regionLines[i] ?? "";
5889
+ if (!line.trim())
5890
+ continue;
5891
+ for (const { pattern, message, severity } of HIDDEN_COMMAND_PATTERNS) {
5892
+ if (pattern.test(line)) {
5893
+ findings.push({
5894
+ type: "hidden_command",
5895
+ severity,
5896
+ file: relativePath,
5897
+ line: region.startLine + i,
5898
+ message: `Hidden in comment: ${message}`,
5899
+ details: line.trim().substring(0, 100)
5900
+ });
5901
+ }
5902
+ }
5903
+ for (const { pattern, message, severity } of NETWORK_REQUEST_PATTERNS) {
5904
+ if (pattern.test(line)) {
5905
+ findings.push({
5906
+ type: "network_request",
5907
+ severity: severity === "medium" ? "high" : severity,
5908
+ file: relativePath,
5909
+ line: region.startLine + i,
5910
+ message: `Hidden in comment: ${message}`,
5911
+ details: line.trim().substring(0, 100)
5912
+ });
5913
+ }
5914
+ }
5915
+ for (const { pattern, message, severity } of SUSPICIOUS_SCRIPT_PATTERNS) {
5916
+ if (pattern.test(line)) {
5917
+ findings.push({
5918
+ type: "hidden_command",
5919
+ severity: severity === "medium" ? "high" : severity,
5920
+ file: relativePath,
5921
+ line: region.startLine + i,
5922
+ message: `Hidden in comment: ${message}`,
5923
+ details: line.trim().substring(0, 100)
5924
+ });
5925
+ }
5926
+ }
5927
+ }
5928
+ }
5929
+ } catch {}
5930
+ return findings;
5931
+ }
5932
+ async function scanFileForNetworkRequests(filePath, relativePath) {
5933
+ const findings = [];
5934
+ try {
5935
+ const content = await readFile17(filePath, "utf-8");
5936
+ const lines = content.split(`
5937
+ `);
5938
+ for (let lineNum = 0;lineNum < lines.length; lineNum++) {
5939
+ const line = lines[lineNum];
5940
+ if (!line)
5941
+ continue;
5942
+ for (const { pattern, message, severity } of NETWORK_REQUEST_PATTERNS) {
5943
+ if (pattern.test(line)) {
5944
+ findings.push({
5945
+ type: "network_request",
5946
+ severity,
5947
+ file: relativePath,
5948
+ line: lineNum + 1,
5949
+ message,
5950
+ details: line.trim().substring(0, 100)
5951
+ });
5952
+ }
5953
+ }
5954
+ }
5955
+ } catch {}
5956
+ return findings;
5957
+ }
5696
5958
  async function checkSymlink(symlinkPath, relativePath, capabilityRoot) {
5697
5959
  try {
5698
5960
  const linkTarget = await readlink(symlinkPath);
5699
- const resolvedTarget = resolve2(join10(symlinkPath, "..", linkTarget));
5961
+ const resolvedTarget = resolve2(join12(symlinkPath, "..", linkTarget));
5700
5962
  const normalizedRoot = await realpath(capabilityRoot);
5701
5963
  if (linkTarget.startsWith("/")) {
5702
5964
  return {
@@ -5731,7 +5993,7 @@ function isTextFile(filePath) {
5731
5993
  async function scanCapability(capabilityId, capabilityPath, settings = DEFAULT_SCAN_SETTINGS) {
5732
5994
  const startTime = Date.now();
5733
5995
  const findings = [];
5734
- if (!existsSync20(capabilityPath)) {
5996
+ if (!existsSync21(capabilityPath)) {
5735
5997
  return {
5736
5998
  capabilityId,
5737
5999
  path: capabilityPath,
@@ -5743,7 +6005,7 @@ async function scanCapability(capabilityId, capabilityPath, settings = DEFAULT_S
5743
6005
  async function scanDirectory(dirPath) {
5744
6006
  const entries = await readdir2(dirPath, { withFileTypes: true });
5745
6007
  for (const entry of entries) {
5746
- const fullPath = join10(dirPath, entry.name);
6008
+ const fullPath = join12(dirPath, entry.name);
5747
6009
  const relativePath = relative(capabilityPath, fullPath);
5748
6010
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "__pycache__") {
5749
6011
  continue;
@@ -5770,17 +6032,27 @@ async function scanCapability(capabilityId, capabilityPath, settings = DEFAULT_S
5770
6032
  });
5771
6033
  }
5772
6034
  if (isTextFile(fullPath)) {
6035
+ const ext = fullPath.toLowerCase().substring(fullPath.lastIndexOf("."));
5773
6036
  if (settings.unicode) {
5774
6037
  const unicodeFindings = await scanFileForUnicode(fullPath, relativePath);
5775
6038
  findings.push(...unicodeFindings);
5776
6039
  }
5777
6040
  if (settings.scripts) {
5778
- const ext = fullPath.toLowerCase().substring(fullPath.lastIndexOf("."));
5779
6041
  if ([".sh", ".bash", ".zsh", ".fish", ".py", ".rb", ".js", ".ts"].includes(ext)) {
5780
6042
  const scriptFindings = await scanFileForScripts(fullPath, relativePath);
5781
6043
  findings.push(...scriptFindings);
5782
6044
  }
5783
6045
  }
6046
+ if (settings.hiddenCommands) {
6047
+ if ([".md", ".txt", ".yaml", ".yml", ".toml"].includes(ext)) {
6048
+ const hiddenFindings = await scanFileForHiddenCommands(fullPath, relativePath);
6049
+ findings.push(...hiddenFindings);
6050
+ }
6051
+ }
6052
+ if (settings.hiddenCommands) {
6053
+ const networkFindings = await scanFileForNetworkRequests(fullPath, relativePath);
6054
+ findings.push(...networkFindings);
6055
+ }
5784
6056
  }
5785
6057
  }
5786
6058
  }
@@ -5812,7 +6084,9 @@ async function scanCapabilities(capabilities2, config2 = {}) {
5812
6084
  symlink_escape: 0,
5813
6085
  symlink_absolute: 0,
5814
6086
  suspicious_script: 0,
5815
- binary_file: 0
6087
+ binary_file: 0,
6088
+ hidden_command: 0,
6089
+ network_request: 0
5816
6090
  };
5817
6091
  const findingsBySeverity = {
5818
6092
  low: 0,
@@ -5887,7 +6161,7 @@ function formatScanResults(summary, verbose = false) {
5887
6161
  return lines.join(`
5888
6162
  `);
5889
6163
  }
5890
- var UNICODE_PATTERNS, SUSPICIOUS_SCRIPT_PATTERNS, BINARY_EXTENSIONS, TEXT_EXTENSIONS;
6164
+ var UNICODE_PATTERNS, SUSPICIOUS_SCRIPT_PATTERNS, NETWORK_REQUEST_PATTERNS, HIDDEN_COMMAND_PATTERNS, BINARY_EXTENSIONS, TEXT_EXTENSIONS, HTML_COMMENT_RE, HIDDEN_REFERENCE_RE;
5891
6165
  var init_scanner = __esm(() => {
5892
6166
  init_types3();
5893
6167
  UNICODE_PATTERNS = {
@@ -5979,6 +6253,75 @@ var init_scanner = __esm(() => {
5979
6253
  severity: "high"
5980
6254
  }
5981
6255
  ];
6256
+ NETWORK_REQUEST_PATTERNS = [
6257
+ {
6258
+ pattern: /\bcurl\s+.*https?:\/\//i,
6259
+ message: "Outbound curl request detected",
6260
+ severity: "medium"
6261
+ },
6262
+ {
6263
+ pattern: /\bwget\s+.*https?:\/\//i,
6264
+ message: "Outbound wget request detected",
6265
+ severity: "medium"
6266
+ },
6267
+ {
6268
+ pattern: /\bfetch\s*\(\s*["'`]https?:\/\//i,
6269
+ message: "Outbound fetch() request detected",
6270
+ severity: "medium"
6271
+ },
6272
+ {
6273
+ pattern: /\b(?:http|https)\.(?:get|request|post|put)\s*\(/i,
6274
+ message: "Outbound HTTP request via Node.js http module",
6275
+ severity: "medium"
6276
+ },
6277
+ {
6278
+ pattern: /\brequests\.(?:get|post|put|delete|patch)\s*\(/i,
6279
+ message: "Outbound HTTP request via Python requests",
6280
+ severity: "medium"
6281
+ },
6282
+ {
6283
+ pattern: /\bnc\b.*\s\d{2,5}\b/i,
6284
+ message: "Netcat connection detected",
6285
+ severity: "high"
6286
+ },
6287
+ {
6288
+ pattern: /\b(?:Invoke-WebRequest|Invoke-RestMethod|iwr|irm)\b/i,
6289
+ message: "Outbound PowerShell web request detected",
6290
+ severity: "medium"
6291
+ }
6292
+ ];
6293
+ HIDDEN_COMMAND_PATTERNS = [
6294
+ {
6295
+ pattern: /`[^`]*(?:curl|wget|bash|sh|python|ruby|node|exec|eval|system)\s[^`]*`/i,
6296
+ message: "Executable command in backtick-wrapped code",
6297
+ severity: "critical"
6298
+ },
6299
+ {
6300
+ pattern: /\|\s*(?:ba)?sh\b/i,
6301
+ message: "Pipe to shell execution",
6302
+ severity: "critical"
6303
+ },
6304
+ {
6305
+ pattern: /(?:^|\s)(?:bash|sh|zsh)\s+-c\s+/i,
6306
+ message: "Shell invocation with -c flag",
6307
+ severity: "high"
6308
+ },
6309
+ {
6310
+ pattern: /\b(?:curl|wget)\s+.*https?:\/\//i,
6311
+ message: "Network fetch command detected",
6312
+ severity: "high"
6313
+ },
6314
+ {
6315
+ pattern: /\b(?:python|ruby|node)\s+-e\s+/i,
6316
+ message: "Inline script execution",
6317
+ severity: "high"
6318
+ },
6319
+ {
6320
+ pattern: /\beval\s*\(.*\)/i,
6321
+ message: "eval() call detected",
6322
+ severity: "high"
6323
+ }
6324
+ ];
5982
6325
  BINARY_EXTENSIONS = new Set([
5983
6326
  ".exe",
5984
6327
  ".dll",
@@ -6013,6 +6356,8 @@ var init_scanner = __esm(() => {
6013
6356
  ".py",
6014
6357
  ".rb"
6015
6358
  ]);
6359
+ HTML_COMMENT_RE = /<!--([\s\S]*?)-->/g;
6360
+ HIDDEN_REFERENCE_RE = /^\[.*?\]:\s*\S+\s+"(.+)"/gm;
6016
6361
  });
6017
6362
 
6018
6363
  // ../core/src/security/index.ts
@@ -6023,7 +6368,9 @@ var init_security = __esm(() => {
6023
6368
 
6024
6369
  // ../core/src/templates/agents.ts
6025
6370
  function generateAgentsTemplate() {
6026
- return `# Project Instructions
6371
+ return `> IMPORTANT: Do not edit this file directly. Edit \`OMNI.md\` instead. This file is a generated copy of \`OMNI.md\` and is ephemeral. Any persistent change must be made in \`OMNI.md\`.
6372
+
6373
+ # Project Instructions
6027
6374
 
6028
6375
  <!-- Add your project-specific instructions here -->
6029
6376
 
@@ -6150,7 +6497,9 @@ function formatDisplayName(kebabCase) {
6150
6497
 
6151
6498
  // ../core/src/templates/claude.ts
6152
6499
  function generateClaudeTemplate() {
6153
- return `# Project Instructions
6500
+ return `> IMPORTANT: Do not edit this file directly. Edit \`OMNI.md\` instead. This file is a generated copy of \`OMNI.md\` and is ephemeral. Any persistent change must be made in \`OMNI.md\`.
6501
+
6502
+ # Project Instructions
6154
6503
 
6155
6504
  <!-- Add your project-specific instructions here -->
6156
6505
  `;
@@ -6222,6 +6571,7 @@ __export(exports_src, {
6222
6571
  resolveEnabledCapabilities: () => resolveEnabledCapabilities,
6223
6572
  resolveCapabilityRootInConfig: () => resolveCapabilityRootInConfig,
6224
6573
  resolveCapabilityRoot: () => resolveCapabilityRoot,
6574
+ resolveCapabilityInstallCommand: () => resolveCapabilityInstallCommand,
6225
6575
  removeSecurityAllow: () => removeSecurityAllow,
6226
6576
  readSecurityAllows: () => readSecurityAllows,
6227
6577
  readMcpJson: () => readMcpJson,
@@ -6273,6 +6623,7 @@ __export(exports_src, {
6273
6623
  hasAnyHooks: () => hasAnyHooks,
6274
6624
  getVersion: () => getVersion,
6275
6625
  getSourceCapabilityPath: () => getSourceCapabilityPath,
6626
+ getProviderManagedOutputs: () => getProviderManagedOutputs,
6276
6627
  getLockFilePath: () => getLockFilePath,
6277
6628
  getHooksDirectory: () => getHooksDirectory,
6278
6629
  getHooksConfigPath: () => getHooksConfigPath,
@@ -6310,6 +6661,7 @@ __export(exports_src, {
6310
6661
  clearAllSecurityAllows: () => clearAllSecurityAllows,
6311
6662
  clearActiveProfileState: () => clearActiveProfileState,
6312
6663
  cleanupStaleResources: () => cleanupStaleResources,
6664
+ cleanupStaleManagedOutputs: () => cleanupStaleManagedOutputs,
6313
6665
  checkVersionMismatch: () => checkVersionMismatch,
6314
6666
  checkForUpdates: () => checkForUpdates,
6315
6667
  buildSyncBundle: () => buildSyncBundle,
@@ -6353,15 +6705,33 @@ var init_src = __esm(() => {
6353
6705
  import { run } from "@stricli/core";
6354
6706
 
6355
6707
  // src/lib/dynamic-app.ts
6356
- import { existsSync as existsSync30 } from "node:fs";
6708
+ import { existsSync as existsSync32 } from "node:fs";
6357
6709
  import { createRequire as createRequire2 } from "node:module";
6358
- import { join as join27 } from "node:path";
6710
+ import { join as join29 } from "node:path";
6359
6711
  import { buildApplication, buildRouteMap as buildRouteMap7 } from "@stricli/core";
6360
6712
 
6361
6713
  // src/commands/add.ts
6362
- import { existsSync as existsSync23 } from "node:fs";
6714
+ import { existsSync as existsSync24 } from "node:fs";
6363
6715
  import { basename as basename5, resolve as resolve3 } from "node:path";
6364
6716
 
6717
+ // ../adapters/src/writers/generic/managed-outputs.ts
6718
+ import { createHash } from "node:crypto";
6719
+ function hashContent(content) {
6720
+ return createHash("sha256").update(content).digest("hex");
6721
+ }
6722
+ function trimTrailingSlash(path) {
6723
+ return path.endsWith("/") ? path.slice(0, -1) : path;
6724
+ }
6725
+ function createManagedOutput(path, writerId, content, options) {
6726
+ return {
6727
+ path,
6728
+ writerId,
6729
+ hash: hashContent(content),
6730
+ cleanupStrategy: options?.cleanupStrategy ?? "delete-file",
6731
+ ...options?.pruneRoot ? { pruneRoot: trimTrailingSlash(options.pruneRoot) } : {},
6732
+ ...options?.jsonKey ? { jsonKey: options.jsonKey } : {}
6733
+ };
6734
+ }
6365
6735
  // ../adapters/src/writers/generic/executor.ts
6366
6736
  async function executeWriters(writerConfigs, bundle, projectRoot, providerId) {
6367
6737
  const seen = new Set;
@@ -6377,6 +6747,7 @@ async function executeWriters(writerConfigs, bundle, projectRoot, providerId) {
6377
6747
  uniqueConfigs.push(config);
6378
6748
  }
6379
6749
  const allFilesWritten = [];
6750
+ const allManagedOutputs = [];
6380
6751
  for (const config of uniqueConfigs) {
6381
6752
  const result = await config.writer.write(bundle, {
6382
6753
  outputPath: config.outputPath,
@@ -6384,29 +6755,31 @@ async function executeWriters(writerConfigs, bundle, projectRoot, providerId) {
6384
6755
  ...providerId ? { providerId } : {}
6385
6756
  });
6386
6757
  allFilesWritten.push(...result.filesWritten);
6758
+ allManagedOutputs.push(...result.managedOutputs ?? []);
6387
6759
  }
6388
6760
  return {
6389
6761
  filesWritten: allFilesWritten,
6390
- deduplicatedCount
6762
+ deduplicatedCount,
6763
+ managedOutputs: allManagedOutputs
6391
6764
  };
6392
6765
  }
6393
6766
  // ../adapters/src/writers/generic/hooks.ts
6394
6767
  init_src();
6395
- import { existsSync as existsSync21 } from "node:fs";
6768
+ import { existsSync as existsSync22 } from "node:fs";
6396
6769
  import { mkdir as mkdir2, readFile as readFile18, writeFile as writeFile10 } from "node:fs/promises";
6397
- import { dirname, join as join11 } from "node:path";
6770
+ import { dirname as dirname2, join as join13 } from "node:path";
6398
6771
  var HooksWriter = {
6399
6772
  id: "hooks",
6400
6773
  async write(bundle, ctx) {
6401
6774
  if (!bundle.hooks) {
6402
6775
  return { filesWritten: [] };
6403
6776
  }
6404
- const settingsPath = join11(ctx.projectRoot, ctx.outputPath);
6405
- const parentDir = dirname(settingsPath);
6777
+ const settingsPath = join13(ctx.projectRoot, ctx.outputPath);
6778
+ const parentDir = dirname2(settingsPath);
6406
6779
  await mkdir2(parentDir, { recursive: true });
6407
6780
  const claudeHooks = transformHooksConfig(bundle.hooks, "toClaude");
6408
6781
  let existingSettings = {};
6409
- if (existsSync21(settingsPath)) {
6782
+ if (existsSync22(settingsPath)) {
6410
6783
  try {
6411
6784
  const content = await readFile18(settingsPath, "utf-8");
6412
6785
  existingSettings = JSON.parse(content);
@@ -6421,14 +6794,20 @@ var HooksWriter = {
6421
6794
  await writeFile10(settingsPath, `${JSON.stringify(newSettings, null, 2)}
6422
6795
  `, "utf-8");
6423
6796
  return {
6424
- filesWritten: [ctx.outputPath]
6797
+ filesWritten: [ctx.outputPath],
6798
+ managedOutputs: [
6799
+ createManagedOutput(ctx.outputPath, this.id, JSON.stringify(claudeHooks), {
6800
+ cleanupStrategy: "remove-json-key",
6801
+ jsonKey: "hooks"
6802
+ })
6803
+ ]
6425
6804
  };
6426
6805
  }
6427
6806
  };
6428
6807
  // ../adapters/src/writers/generic/instructions-md.ts
6429
- import { existsSync as existsSync22 } from "node:fs";
6808
+ import { existsSync as existsSync23 } from "node:fs";
6430
6809
  import { mkdir as mkdir3, readFile as readFile19, writeFile as writeFile11 } from "node:fs/promises";
6431
- import { dirname as dirname2, join as join12 } from "node:path";
6810
+ import { dirname as dirname3, join as join14 } from "node:path";
6432
6811
 
6433
6812
  // ../adapters/src/writers/generic/omni-md.ts
6434
6813
  init_src();
@@ -6450,20 +6829,32 @@ function renderOmniMdForProvider(content, providerId) {
6450
6829
  }
6451
6830
 
6452
6831
  // ../adapters/src/writers/generic/instructions-md.ts
6832
+ function getGeneratedCopyNotice(outputPath) {
6833
+ if (outputPath !== "AGENTS.md" && outputPath !== "CLAUDE.md") {
6834
+ return "";
6835
+ }
6836
+ return "> IMPORTANT: Do not edit this file directly. Edit `OMNI.md` instead. This file is a generated copy of `OMNI.md` and is ephemeral. Any persistent change must be made in `OMNI.md`.";
6837
+ }
6453
6838
  var InstructionsMdWriter = {
6454
6839
  id: "instructions-md",
6455
6840
  async write(bundle, ctx) {
6456
- const outputFullPath = join12(ctx.projectRoot, ctx.outputPath);
6457
- const parentDir = dirname2(outputFullPath);
6841
+ const outputFullPath = join14(ctx.projectRoot, ctx.outputPath);
6842
+ const parentDir = dirname3(outputFullPath);
6458
6843
  if (parentDir !== ctx.projectRoot) {
6459
6844
  await mkdir3(parentDir, { recursive: true });
6460
6845
  }
6461
- const omniMdPath = join12(ctx.projectRoot, "OMNI.md");
6846
+ const omniMdPath = join14(ctx.projectRoot, "OMNI.md");
6462
6847
  let omniMdContent = "";
6463
- if (existsSync22(omniMdPath)) {
6848
+ if (existsSync23(omniMdPath)) {
6464
6849
  omniMdContent = renderOmniMdForProvider(await readFile19(omniMdPath, "utf-8"), ctx.providerId);
6465
6850
  }
6466
6851
  let content = omniMdContent;
6852
+ const generatedCopyNotice = getGeneratedCopyNotice(ctx.outputPath);
6853
+ if (generatedCopyNotice) {
6854
+ content = content ? `${generatedCopyNotice}
6855
+
6856
+ ${content}` : generatedCopyNotice;
6857
+ }
6467
6858
  if (bundle.instructionsContent) {
6468
6859
  content += `
6469
6860
 
@@ -6472,23 +6863,30 @@ ${bundle.instructionsContent}
6472
6863
  }
6473
6864
  await writeFile11(outputFullPath, content, "utf-8");
6474
6865
  return {
6475
- filesWritten: [ctx.outputPath]
6866
+ filesWritten: [ctx.outputPath],
6867
+ managedOutputs: [createManagedOutput(ctx.outputPath, this.id, content)]
6476
6868
  };
6477
6869
  }
6478
6870
  };
6479
6871
  // ../adapters/src/writers/generic/skills.ts
6480
- import { mkdir as mkdir4, writeFile as writeFile12 } from "node:fs/promises";
6481
- import { join as join13 } from "node:path";
6872
+ import { cp as cp2, mkdir as mkdir4, readdir as readdir3, rm as rm2, writeFile as writeFile12 } from "node:fs/promises";
6873
+ import { join as join15, relative as relative2 } from "node:path";
6482
6874
  var SkillsWriter = {
6483
6875
  id: "skills",
6484
6876
  async write(bundle, ctx) {
6485
- const skillsDir = join13(ctx.projectRoot, ctx.outputPath);
6877
+ const skillsDir = join15(ctx.projectRoot, ctx.outputPath);
6486
6878
  await mkdir4(skillsDir, { recursive: true });
6487
6879
  const filesWritten = [];
6880
+ const managedOutputs = [];
6488
6881
  for (const skill of bundle.skills) {
6489
- const skillDir = join13(skillsDir, skill.name);
6490
- await mkdir4(skillDir, { recursive: true });
6491
- const skillPath = join13(skillDir, "SKILL.md");
6882
+ const skillDir = join15(skillsDir, skill.name);
6883
+ await rm2(skillDir, { recursive: true, force: true });
6884
+ if (skill.sourcePath) {
6885
+ await cp2(skill.sourcePath, skillDir, { recursive: true });
6886
+ } else {
6887
+ await mkdir4(skillDir, { recursive: true });
6888
+ }
6889
+ const skillPath = join15(skillDir, "SKILL.md");
6492
6890
  const content = `---
6493
6891
  name: ${skill.name}
6494
6892
  description: "${skill.description}"
@@ -6496,16 +6894,43 @@ description: "${skill.description}"
6496
6894
 
6497
6895
  ${skill.instructions}`;
6498
6896
  await writeFile12(skillPath, content, "utf-8");
6499
- filesWritten.push(join13(ctx.outputPath, skill.name, "SKILL.md"));
6897
+ const relativePaths = skill.sourcePath ? await listRelativeFiles(skillDir) : ["SKILL.md"];
6898
+ for (const relativeFile of relativePaths) {
6899
+ filesWritten.push(join15(ctx.outputPath, skill.name, relativeFile));
6900
+ }
6901
+ const relativePath = join15(ctx.outputPath, skill.name, "SKILL.md");
6902
+ managedOutputs.push(createManagedOutput(relativePath, this.id, content, {
6903
+ cleanupStrategy: "delete-file-and-prune-empty-parents",
6904
+ pruneRoot: ctx.outputPath
6905
+ }));
6500
6906
  }
6501
6907
  return {
6502
- filesWritten
6908
+ filesWritten,
6909
+ managedOutputs
6503
6910
  };
6504
6911
  }
6505
6912
  };
6913
+ async function listRelativeFiles(basePath) {
6914
+ const files = [];
6915
+ const entries = await readdir3(basePath, { withFileTypes: true });
6916
+ for (const entry of entries) {
6917
+ const entryPath = join15(basePath, entry.name);
6918
+ if (entry.isDirectory()) {
6919
+ const nestedFiles = await listRelativeFiles(entryPath);
6920
+ for (const nestedFile of nestedFiles) {
6921
+ files.push(relative2(basePath, join15(entryPath, nestedFile)));
6922
+ }
6923
+ continue;
6924
+ }
6925
+ if (entry.isFile()) {
6926
+ files.push(entry.name);
6927
+ }
6928
+ }
6929
+ return files.sort((a, b) => a.localeCompare(b));
6930
+ }
6506
6931
  // ../adapters/src/writers/generic/commands-as-skills.ts
6507
6932
  import { mkdir as mkdir5, writeFile as writeFile13 } from "node:fs/promises";
6508
- import { join as join14 } from "node:path";
6933
+ import { join as join16 } from "node:path";
6509
6934
  function generateSkillFrontmatter(command) {
6510
6935
  const lines = ["---"];
6511
6936
  lines.push(`name: ${command.name}`);
@@ -6520,22 +6945,29 @@ function generateSkillFrontmatter(command) {
6520
6945
  var CommandsAsSkillsWriter = {
6521
6946
  id: "commands-as-skills",
6522
6947
  async write(bundle, ctx) {
6523
- const skillsDir = join14(ctx.projectRoot, ctx.outputPath);
6948
+ const skillsDir = join16(ctx.projectRoot, ctx.outputPath);
6524
6949
  await mkdir5(skillsDir, { recursive: true });
6525
6950
  const filesWritten = [];
6951
+ const managedOutputs = [];
6526
6952
  for (const command of bundle.commands) {
6527
- const commandSkillDir = join14(skillsDir, command.name);
6953
+ const commandSkillDir = join16(skillsDir, command.name);
6528
6954
  await mkdir5(commandSkillDir, { recursive: true });
6529
6955
  const frontmatter = generateSkillFrontmatter(command);
6530
6956
  const content = `${frontmatter}
6531
6957
 
6532
6958
  ${command.prompt}`;
6533
- const skillPath = join14(commandSkillDir, "SKILL.md");
6959
+ const skillPath = join16(commandSkillDir, "SKILL.md");
6534
6960
  await writeFile13(skillPath, content, "utf-8");
6535
- filesWritten.push(join14(ctx.outputPath, command.name, "SKILL.md"));
6961
+ const relativePath = join16(ctx.outputPath, command.name, "SKILL.md");
6962
+ filesWritten.push(relativePath);
6963
+ managedOutputs.push(createManagedOutput(relativePath, this.id, content, {
6964
+ cleanupStrategy: "delete-file-and-prune-empty-parents",
6965
+ pruneRoot: ctx.outputPath
6966
+ }));
6536
6967
  }
6537
6968
  return {
6538
- filesWritten
6969
+ filesWritten,
6970
+ managedOutputs
6539
6971
  };
6540
6972
  }
6541
6973
  };
@@ -6588,7 +7020,7 @@ function createProviderScopedBundle(bundle, providerId) {
6588
7020
 
6589
7021
  // ../adapters/src/writers/claude/agents.ts
6590
7022
  import { mkdir as mkdir6, writeFile as writeFile14 } from "node:fs/promises";
6591
- import { join as join15 } from "node:path";
7023
+ import { join as join17 } from "node:path";
6592
7024
  function generateFrontmatter(agent) {
6593
7025
  const lines = ["---"];
6594
7026
  lines.push(`name: ${agent.name}`);
@@ -6615,20 +7047,24 @@ function generateFrontmatter(agent) {
6615
7047
  var ClaudeAgentsWriter = {
6616
7048
  id: "claude-agents",
6617
7049
  async write(bundle, ctx) {
6618
- const agentsDir = join15(ctx.projectRoot, ctx.outputPath);
7050
+ const agentsDir = join17(ctx.projectRoot, ctx.outputPath);
6619
7051
  await mkdir6(agentsDir, { recursive: true });
6620
7052
  const filesWritten = [];
7053
+ const managedOutputs = [];
6621
7054
  for (const agent of bundle.subagents) {
6622
7055
  const frontmatter = generateFrontmatter(agent);
6623
7056
  const content = `${frontmatter}
6624
7057
 
6625
7058
  ${agent.systemPrompt}`;
6626
- const agentPath = join15(agentsDir, `${agent.name}.md`);
7059
+ const agentPath = join17(agentsDir, `${agent.name}.md`);
6627
7060
  await writeFile14(agentPath, content, "utf-8");
6628
- filesWritten.push(join15(ctx.outputPath, `${agent.name}.md`));
7061
+ const relativePath = join17(ctx.outputPath, `${agent.name}.md`);
7062
+ filesWritten.push(relativePath);
7063
+ managedOutputs.push(createManagedOutput(relativePath, this.id, content));
6629
7064
  }
6630
7065
  return {
6631
- filesWritten
7066
+ filesWritten,
7067
+ managedOutputs
6632
7068
  };
6633
7069
  }
6634
7070
  };
@@ -6655,18 +7091,19 @@ var claudeCodeAdapter = {
6655
7091
  const result = await executeWriters(this.writers, providerBundle, ctx.projectRoot, providerId);
6656
7092
  return {
6657
7093
  filesWritten: result.filesWritten,
6658
- filesDeleted: []
7094
+ filesDeleted: [],
7095
+ managedOutputs: result.managedOutputs
6659
7096
  };
6660
7097
  }
6661
7098
  };
6662
7099
  // ../adapters/src/codex/index.ts
6663
7100
  import { mkdirSync as mkdirSync6 } from "node:fs";
6664
- import { join as join17 } from "node:path";
7101
+ import { join as join19 } from "node:path";
6665
7102
 
6666
7103
  // ../adapters/src/writers/codex/toml.ts
6667
7104
  init_dist();
6668
7105
  import { mkdir as mkdir7, writeFile as writeFile15 } from "node:fs/promises";
6669
- import { dirname as dirname3, join as join16 } from "node:path";
7106
+ import { dirname as dirname4, join as join18 } from "node:path";
6670
7107
  var FILE_HEADER = `# Generated by OmniDev - DO NOT EDIT
6671
7108
  # Run \`omnidev sync\` to regenerate
6672
7109
 
@@ -6717,8 +7154,8 @@ var CodexTomlWriter = {
6717
7154
  if (mcps.size === 0) {
6718
7155
  return { filesWritten: [] };
6719
7156
  }
6720
- const configPath = join16(ctx.projectRoot, ctx.outputPath);
6721
- const parentDir = dirname3(configPath);
7157
+ const configPath = join18(ctx.projectRoot, ctx.outputPath);
7158
+ const parentDir = dirname4(configPath);
6722
7159
  await mkdir7(parentDir, { recursive: true });
6723
7160
  const mcpServers = {};
6724
7161
  for (const [id, mcp] of mcps) {
@@ -6736,7 +7173,8 @@ var CodexTomlWriter = {
6736
7173
  const tomlContent = FILE_HEADER + stringify(codexConfig);
6737
7174
  await writeFile15(configPath, tomlContent, "utf-8");
6738
7175
  return {
6739
- filesWritten: [ctx.outputPath]
7176
+ filesWritten: [ctx.outputPath],
7177
+ managedOutputs: [createManagedOutput(ctx.outputPath, this.id, tomlContent)]
6740
7178
  };
6741
7179
  }
6742
7180
  };
@@ -6751,7 +7189,7 @@ var codexAdapter = {
6751
7189
  { writer: CodexTomlWriter, outputPath: ".codex/config.toml" }
6752
7190
  ],
6753
7191
  async init(ctx) {
6754
- const codexDir = join17(ctx.projectRoot, ".codex");
7192
+ const codexDir = join19(ctx.projectRoot, ".codex");
6755
7193
  mkdirSync6(codexDir, { recursive: true });
6756
7194
  return {
6757
7195
  filesCreated: [".codex/"],
@@ -6764,17 +7202,18 @@ var codexAdapter = {
6764
7202
  const result = await executeWriters(this.writers, providerBundle, ctx.projectRoot, providerId);
6765
7203
  return {
6766
7204
  filesWritten: result.filesWritten,
6767
- filesDeleted: []
7205
+ filesDeleted: [],
7206
+ managedOutputs: result.managedOutputs
6768
7207
  };
6769
7208
  }
6770
7209
  };
6771
7210
  // ../adapters/src/cursor/index.ts
6772
7211
  import { mkdirSync as mkdirSync7 } from "node:fs";
6773
- import { join as join22 } from "node:path";
7212
+ import { join as join24 } from "node:path";
6774
7213
 
6775
7214
  // ../adapters/src/writers/cursor/agents.ts
6776
7215
  import { mkdir as mkdir8, writeFile as writeFile16 } from "node:fs/promises";
6777
- import { join as join18 } from "node:path";
7216
+ import { join as join20 } from "node:path";
6778
7217
  function mapModelToCursor(model) {
6779
7218
  if (!model || model === "inherit")
6780
7219
  return "inherit";
@@ -6806,50 +7245,58 @@ function generateFrontmatter2(agent) {
6806
7245
  var CursorAgentsWriter = {
6807
7246
  id: "cursor-agents",
6808
7247
  async write(bundle, ctx) {
6809
- const agentsDir = join18(ctx.projectRoot, ctx.outputPath);
7248
+ const agentsDir = join20(ctx.projectRoot, ctx.outputPath);
6810
7249
  await mkdir8(agentsDir, { recursive: true });
6811
7250
  const filesWritten = [];
7251
+ const managedOutputs = [];
6812
7252
  for (const agent of bundle.subagents) {
6813
7253
  const frontmatter = generateFrontmatter2(agent);
6814
7254
  const content = `${frontmatter}
6815
7255
 
6816
7256
  ${agent.systemPrompt}`;
6817
- const agentPath = join18(agentsDir, `${agent.name}.md`);
7257
+ const agentPath = join20(agentsDir, `${agent.name}.md`);
6818
7258
  await writeFile16(agentPath, content, "utf-8");
6819
- filesWritten.push(join18(ctx.outputPath, `${agent.name}.md`));
7259
+ const relativePath = join20(ctx.outputPath, `${agent.name}.md`);
7260
+ filesWritten.push(relativePath);
7261
+ managedOutputs.push(createManagedOutput(relativePath, this.id, content));
6820
7262
  }
6821
7263
  return {
6822
- filesWritten
7264
+ filesWritten,
7265
+ managedOutputs
6823
7266
  };
6824
7267
  }
6825
7268
  };
6826
7269
  // ../adapters/src/writers/cursor/commands.ts
6827
7270
  import { mkdir as mkdir9, writeFile as writeFile17 } from "node:fs/promises";
6828
- import { join as join19 } from "node:path";
7271
+ import { join as join21 } from "node:path";
6829
7272
  var CursorCommandsWriter = {
6830
7273
  id: "cursor-commands",
6831
7274
  async write(bundle, ctx) {
6832
- const commandsDir = join19(ctx.projectRoot, ctx.outputPath);
7275
+ const commandsDir = join21(ctx.projectRoot, ctx.outputPath);
6833
7276
  await mkdir9(commandsDir, { recursive: true });
6834
7277
  const filesWritten = [];
7278
+ const managedOutputs = [];
6835
7279
  for (const command of bundle.commands) {
6836
7280
  const content = `# ${command.name}
6837
7281
 
6838
7282
  ${command.description}
6839
7283
 
6840
7284
  ${command.prompt}`;
6841
- const commandPath = join19(commandsDir, `${command.name}.md`);
7285
+ const commandPath = join21(commandsDir, `${command.name}.md`);
6842
7286
  await writeFile17(commandPath, content, "utf-8");
6843
- filesWritten.push(join19(ctx.outputPath, `${command.name}.md`));
7287
+ const relativePath = join21(ctx.outputPath, `${command.name}.md`);
7288
+ filesWritten.push(relativePath);
7289
+ managedOutputs.push(createManagedOutput(relativePath, this.id, content));
6844
7290
  }
6845
7291
  return {
6846
- filesWritten
7292
+ filesWritten,
7293
+ managedOutputs
6847
7294
  };
6848
7295
  }
6849
7296
  };
6850
7297
  // ../adapters/src/writers/cursor/mcp-json.ts
6851
7298
  import { mkdir as mkdir10, writeFile as writeFile18 } from "node:fs/promises";
6852
- import { dirname as dirname4, join as join20 } from "node:path";
7299
+ import { dirname as dirname5, join as join22 } from "node:path";
6853
7300
  function buildCursorMcpConfig(mcp) {
6854
7301
  const transport = mcp.transport ?? "stdio";
6855
7302
  if (transport === "http" || transport === "sse") {
@@ -6894,8 +7341,8 @@ var CursorMcpJsonWriter = {
6894
7341
  if (mcps.size === 0) {
6895
7342
  return { filesWritten: [] };
6896
7343
  }
6897
- const configPath = join20(ctx.projectRoot, ctx.outputPath);
6898
- const parentDir = dirname4(configPath);
7344
+ const configPath = join22(ctx.projectRoot, ctx.outputPath);
7345
+ const parentDir = dirname5(configPath);
6899
7346
  await mkdir10(parentDir, { recursive: true });
6900
7347
  const mcpServers = {};
6901
7348
  for (const [id, mcp] of mcps) {
@@ -6910,29 +7357,35 @@ var CursorMcpJsonWriter = {
6910
7357
  const cursorMcpJson = {
6911
7358
  mcpServers
6912
7359
  };
6913
- await writeFile18(configPath, `${JSON.stringify(cursorMcpJson, null, 2)}
6914
- `, "utf-8");
7360
+ const content = `${JSON.stringify(cursorMcpJson, null, 2)}
7361
+ `;
7362
+ await writeFile18(configPath, content, "utf-8");
6915
7363
  return {
6916
- filesWritten: [ctx.outputPath]
7364
+ filesWritten: [ctx.outputPath],
7365
+ managedOutputs: [createManagedOutput(ctx.outputPath, this.id, content)]
6917
7366
  };
6918
7367
  }
6919
7368
  };
6920
7369
  // ../adapters/src/writers/cursor/rules.ts
6921
7370
  import { mkdir as mkdir11, writeFile as writeFile19 } from "node:fs/promises";
6922
- import { join as join21 } from "node:path";
7371
+ import { join as join23 } from "node:path";
6923
7372
  var CursorRulesWriter = {
6924
7373
  id: "cursor-rules",
6925
7374
  async write(bundle, ctx) {
6926
- const rulesDir = join21(ctx.projectRoot, ctx.outputPath);
7375
+ const rulesDir = join23(ctx.projectRoot, ctx.outputPath);
6927
7376
  await mkdir11(rulesDir, { recursive: true });
6928
7377
  const filesWritten = [];
7378
+ const managedOutputs = [];
6929
7379
  for (const rule of bundle.rules) {
6930
- const rulePath = join21(rulesDir, `omnidev-${rule.name}.mdc`);
7380
+ const rulePath = join23(rulesDir, `omnidev-${rule.name}.mdc`);
6931
7381
  await writeFile19(rulePath, rule.content, "utf-8");
6932
- filesWritten.push(join21(ctx.outputPath, `omnidev-${rule.name}.mdc`));
7382
+ const relativePath = join23(ctx.outputPath, `omnidev-${rule.name}.mdc`);
7383
+ filesWritten.push(relativePath);
7384
+ managedOutputs.push(createManagedOutput(relativePath, this.id, rule.content));
6933
7385
  }
6934
7386
  return {
6935
- filesWritten
7387
+ filesWritten,
7388
+ managedOutputs
6936
7389
  };
6937
7390
  }
6938
7391
  };
@@ -6949,7 +7402,7 @@ var cursorAdapter = {
6949
7402
  { writer: CursorMcpJsonWriter, outputPath: ".cursor/mcp.json" }
6950
7403
  ],
6951
7404
  async init(ctx) {
6952
- const rulesDir = join22(ctx.projectRoot, ".cursor", "rules");
7405
+ const rulesDir = join24(ctx.projectRoot, ".cursor", "rules");
6953
7406
  mkdirSync7(rulesDir, { recursive: true });
6954
7407
  return {
6955
7408
  filesCreated: [".cursor/rules/"],
@@ -6969,17 +7422,18 @@ var cursorAdapter = {
6969
7422
  filesWritten: [
6970
7423
  ...new Set([...instructionsResult.filesWritten, ...cursorResult.filesWritten])
6971
7424
  ],
6972
- filesDeleted: []
7425
+ filesDeleted: [],
7426
+ managedOutputs: [...instructionsResult.managedOutputs, ...cursorResult.managedOutputs]
6973
7427
  };
6974
7428
  }
6975
7429
  };
6976
7430
  // ../adapters/src/opencode/index.ts
6977
7431
  import { mkdirSync as mkdirSync8 } from "node:fs";
6978
- import { join as join25 } from "node:path";
7432
+ import { join as join27 } from "node:path";
6979
7433
 
6980
7434
  // ../adapters/src/writers/opencode/agents.ts
6981
7435
  import { mkdir as mkdir12, writeFile as writeFile20 } from "node:fs/promises";
6982
- import { join as join23 } from "node:path";
7436
+ import { join as join25 } from "node:path";
6983
7437
  function mapModelToOpenCode(model) {
6984
7438
  if (!model || model === "inherit")
6985
7439
  return;
@@ -7057,26 +7511,30 @@ function generateFrontmatter3(agent) {
7057
7511
  var OpenCodeAgentsWriter = {
7058
7512
  id: "opencode-agents",
7059
7513
  async write(bundle, ctx) {
7060
- const agentsDir = join23(ctx.projectRoot, ctx.outputPath);
7514
+ const agentsDir = join25(ctx.projectRoot, ctx.outputPath);
7061
7515
  await mkdir12(agentsDir, { recursive: true });
7062
7516
  const filesWritten = [];
7517
+ const managedOutputs = [];
7063
7518
  for (const agent of bundle.subagents) {
7064
7519
  const frontmatter = generateFrontmatter3(agent);
7065
7520
  const content = `${frontmatter}
7066
7521
 
7067
7522
  ${agent.systemPrompt}`;
7068
- const agentPath = join23(agentsDir, `${agent.name}.md`);
7523
+ const agentPath = join25(agentsDir, `${agent.name}.md`);
7069
7524
  await writeFile20(agentPath, content, "utf-8");
7070
- filesWritten.push(join23(ctx.outputPath, `${agent.name}.md`));
7525
+ const relativePath = join25(ctx.outputPath, `${agent.name}.md`);
7526
+ filesWritten.push(relativePath);
7527
+ managedOutputs.push(createManagedOutput(relativePath, this.id, content));
7071
7528
  }
7072
7529
  return {
7073
- filesWritten
7530
+ filesWritten,
7531
+ managedOutputs
7074
7532
  };
7075
7533
  }
7076
7534
  };
7077
7535
  // ../adapters/src/writers/opencode/commands.ts
7078
7536
  import { mkdir as mkdir13, writeFile as writeFile21 } from "node:fs/promises";
7079
- import { join as join24 } from "node:path";
7537
+ import { join as join26 } from "node:path";
7080
7538
  function generateFrontmatter4(command) {
7081
7539
  const lines = ["---"];
7082
7540
  lines.push(`description: "${command.description.replace(/"/g, "\\\"")}"`);
@@ -7093,20 +7551,24 @@ function generateFrontmatter4(command) {
7093
7551
  var OpenCodeCommandsWriter = {
7094
7552
  id: "opencode-commands",
7095
7553
  async write(bundle, ctx) {
7096
- const commandsDir = join24(ctx.projectRoot, ctx.outputPath);
7554
+ const commandsDir = join26(ctx.projectRoot, ctx.outputPath);
7097
7555
  await mkdir13(commandsDir, { recursive: true });
7098
7556
  const filesWritten = [];
7557
+ const managedOutputs = [];
7099
7558
  for (const command of bundle.commands) {
7100
7559
  const frontmatter = generateFrontmatter4(command);
7101
7560
  const content = `${frontmatter}
7102
7561
 
7103
7562
  ${command.prompt}`;
7104
- const commandPath = join24(commandsDir, `${command.name}.md`);
7563
+ const commandPath = join26(commandsDir, `${command.name}.md`);
7105
7564
  await writeFile21(commandPath, content, "utf-8");
7106
- filesWritten.push(join24(ctx.outputPath, `${command.name}.md`));
7565
+ const relativePath = join26(ctx.outputPath, `${command.name}.md`);
7566
+ filesWritten.push(relativePath);
7567
+ managedOutputs.push(createManagedOutput(relativePath, this.id, content));
7107
7568
  }
7108
7569
  return {
7109
- filesWritten
7570
+ filesWritten,
7571
+ managedOutputs
7110
7572
  };
7111
7573
  }
7112
7574
  };
@@ -7121,7 +7583,7 @@ var opencodeAdapter = {
7121
7583
  { writer: OpenCodeCommandsWriter, outputPath: ".opencode/commands/" }
7122
7584
  ],
7123
7585
  async init(ctx) {
7124
- const opencodeDir = join25(ctx.projectRoot, ".opencode");
7586
+ const opencodeDir = join27(ctx.projectRoot, ".opencode");
7125
7587
  mkdirSync8(opencodeDir, { recursive: true });
7126
7588
  return {
7127
7589
  filesCreated: [".opencode/"],
@@ -7134,7 +7596,8 @@ var opencodeAdapter = {
7134
7596
  const result = await executeWriters(this.writers, providerBundle, ctx.projectRoot, providerId);
7135
7597
  return {
7136
7598
  filesWritten: result.filesWritten,
7137
- filesDeleted: []
7599
+ filesDeleted: [],
7600
+ managedOutputs: result.managedOutputs
7138
7601
  };
7139
7602
  }
7140
7603
  };
@@ -7175,7 +7638,7 @@ async function inferCapabilityId(source, sourceType) {
7175
7638
  }
7176
7639
  async function runAddCap(flags, name) {
7177
7640
  try {
7178
- if (!existsSync23("omni.toml")) {
7641
+ if (!existsSync24("omni.toml")) {
7179
7642
  console.log("✗ No config file found");
7180
7643
  console.log(" Run: omnidev init");
7181
7644
  process.exit(1);
@@ -7198,7 +7661,7 @@ async function runAddCap(flags, name) {
7198
7661
  sourceType = "local";
7199
7662
  const localPath = flags.local.startsWith("file://") ? flags.local.slice(7) : flags.local;
7200
7663
  source = `file://${localPath}`;
7201
- if (!existsSync23(localPath)) {
7664
+ if (!existsSync24(localPath)) {
7202
7665
  console.error(`✗ Local path not found: ${localPath}`);
7203
7666
  process.exit(1);
7204
7667
  }
@@ -7290,7 +7753,7 @@ async function runAddCap(flags, name) {
7290
7753
  }
7291
7754
  async function runAddMcp(flags, name) {
7292
7755
  try {
7293
- if (!existsSync23("omni.toml")) {
7756
+ if (!existsSync24("omni.toml")) {
7294
7757
  console.log("✗ No config file found");
7295
7758
  console.log(" Run: omnidev init");
7296
7759
  process.exit(1);
@@ -7577,9 +8040,9 @@ var addRoutes = buildRouteMap({
7577
8040
  });
7578
8041
 
7579
8042
  // src/commands/capability.ts
7580
- import { existsSync as existsSync24, mkdirSync as mkdirSync9 } from "node:fs";
8043
+ import { existsSync as existsSync25, mkdirSync as mkdirSync9 } from "node:fs";
7581
8044
  import { writeFile as writeFile22 } from "node:fs/promises";
7582
- import { join as join26 } from "node:path";
8045
+ import { join as join28 } from "node:path";
7583
8046
  import { input } from "@inquirer/prompts";
7584
8047
  init_src();
7585
8048
  import { buildCommand as buildCommand2, buildRouteMap as buildRouteMap2 } from "@stricli/core";
@@ -7768,7 +8231,7 @@ function generateGitignore(programmatic) {
7768
8231
  }
7769
8232
  async function runCapabilityNew(flags, capabilityId) {
7770
8233
  try {
7771
- if (!existsSync24(".omni")) {
8234
+ if (!existsSync25(".omni")) {
7772
8235
  console.error("✗ OmniDev is not initialized in this directory.");
7773
8236
  console.log("");
7774
8237
  console.log(" Run: omnidev init");
@@ -7792,28 +8255,28 @@ async function runCapabilityNew(flags, capabilityId) {
7792
8255
  default: defaultPath
7793
8256
  });
7794
8257
  }
7795
- if (existsSync24(capabilityDir)) {
8258
+ if (existsSync25(capabilityDir)) {
7796
8259
  console.error(`✗ Directory already exists at ${capabilityDir}`);
7797
8260
  process.exit(1);
7798
8261
  }
7799
8262
  const name = toTitleCase(id);
7800
8263
  mkdirSync9(capabilityDir, { recursive: true });
7801
8264
  const capabilityToml = generateCapabilityToml2({ id, name });
7802
- await writeFile22(join26(capabilityDir, "capability.toml"), capabilityToml, "utf-8");
7803
- const skillDir = join26(capabilityDir, "skills", "getting-started");
8265
+ await writeFile22(join28(capabilityDir, "capability.toml"), capabilityToml, "utf-8");
8266
+ const skillDir = join28(capabilityDir, "skills", "getting-started");
7804
8267
  mkdirSync9(skillDir, { recursive: true });
7805
- await writeFile22(join26(skillDir, "SKILL.md"), generateSkillTemplate("getting-started"), "utf-8");
7806
- const rulesDir = join26(capabilityDir, "rules");
8268
+ await writeFile22(join28(skillDir, "SKILL.md"), generateSkillTemplate("getting-started"), "utf-8");
8269
+ const rulesDir = join28(capabilityDir, "rules");
7807
8270
  mkdirSync9(rulesDir, { recursive: true });
7808
- await writeFile22(join26(rulesDir, "coding-standards.md"), generateRuleTemplate("coding-standards"), "utf-8");
7809
- const hooksDir = join26(capabilityDir, "hooks");
8271
+ await writeFile22(join28(rulesDir, "coding-standards.md"), generateRuleTemplate("coding-standards"), "utf-8");
8272
+ const hooksDir = join28(capabilityDir, "hooks");
7810
8273
  mkdirSync9(hooksDir, { recursive: true });
7811
- await writeFile22(join26(hooksDir, "hooks.toml"), generateHooksTemplate(), "utf-8");
7812
- await writeFile22(join26(hooksDir, "example-hook.sh"), generateHookScript(), "utf-8");
7813
- await writeFile22(join26(capabilityDir, ".gitignore"), generateGitignore(Boolean(flags.programmatic)), "utf-8");
8274
+ await writeFile22(join28(hooksDir, "hooks.toml"), generateHooksTemplate(), "utf-8");
8275
+ await writeFile22(join28(hooksDir, "example-hook.sh"), generateHookScript(), "utf-8");
8276
+ await writeFile22(join28(capabilityDir, ".gitignore"), generateGitignore(Boolean(flags.programmatic)), "utf-8");
7814
8277
  if (flags.programmatic) {
7815
- await writeFile22(join26(capabilityDir, "package.json"), generatePackageJson(id), "utf-8");
7816
- await writeFile22(join26(capabilityDir, "index.ts"), generateIndexTs(id, name), "utf-8");
8278
+ await writeFile22(join28(capabilityDir, "package.json"), generatePackageJson(id), "utf-8");
8279
+ await writeFile22(join28(capabilityDir, "index.ts"), generateIndexTs(id, name), "utf-8");
7817
8280
  }
7818
8281
  console.log(`✓ Created capability: ${name}`);
7819
8282
  console.log(` Location: ${capabilityDir}`);
@@ -7969,7 +8432,7 @@ var capabilityRoutes = buildRouteMap2({
7969
8432
  });
7970
8433
 
7971
8434
  // src/commands/doctor.ts
7972
- import { existsSync as existsSync25 } from "node:fs";
8435
+ import { existsSync as existsSync26 } from "node:fs";
7973
8436
  import { execFile } from "node:child_process";
7974
8437
  import { readFile as readFile20 } from "node:fs/promises";
7975
8438
  import { promisify } from "node:util";
@@ -8016,50 +8479,52 @@ async function runDoctor() {
8016
8479
  async function checkPackageManager() {
8017
8480
  const execFileAsync = promisify(execFile);
8018
8481
  try {
8019
- const { stdout } = await execFileAsync("bun", ["--version"]);
8020
- const version2 = stdout.trim();
8021
- const firstPart = version2.split(".")[0];
8022
- if (!firstPart) {
8482
+ const { stdout: npmStdout } = await execFileAsync("npm", ["--version"]);
8483
+ const npmVersion = npmStdout.trim();
8484
+ try {
8485
+ const { stdout: bunStdout } = await execFileAsync("bun", ["--version"]);
8486
+ const bunVersion = bunStdout.trim();
8487
+ const firstPart = bunVersion.split(".")[0];
8488
+ if (!firstPart) {
8489
+ return {
8490
+ name: "Package Manager",
8491
+ passed: false,
8492
+ message: `Invalid Bun version format: ${bunVersion}`,
8493
+ fix: "Reinstall Bun: curl -fsSL https://bun.sh/install | bash"
8494
+ };
8495
+ }
8496
+ const major = Number.parseInt(firstPart, 10);
8497
+ if (major < 1) {
8498
+ return {
8499
+ name: "Package Manager",
8500
+ passed: false,
8501
+ message: `npm v${npmVersion}; bun v${bunVersion}`,
8502
+ fix: "Upgrade Bun: curl -fsSL https://bun.sh/install | bash"
8503
+ };
8504
+ }
8023
8505
  return {
8024
8506
  name: "Package Manager",
8025
- passed: false,
8026
- message: `Invalid Bun version format: ${version2}`,
8027
- fix: "Reinstall Bun: curl -fsSL https://bun.sh/install | bash"
8507
+ passed: true,
8508
+ message: `npm v${npmVersion}; bun v${bunVersion}`
8028
8509
  };
8029
- }
8030
- const major = Number.parseInt(firstPart, 10);
8031
- if (major < 1) {
8510
+ } catch {
8032
8511
  return {
8033
8512
  name: "Package Manager",
8034
- passed: false,
8035
- message: `bun v${version2}`,
8036
- fix: "Upgrade Bun: curl -fsSL https://bun.sh/install | bash"
8513
+ passed: true,
8514
+ message: `npm v${npmVersion}`
8037
8515
  };
8038
8516
  }
8039
- return {
8040
- name: "Package Manager",
8041
- passed: true,
8042
- message: `bun v${version2}`
8043
- };
8044
- } catch {}
8045
- try {
8046
- const { stdout } = await execFileAsync("npm", ["--version"]);
8047
- return {
8048
- name: "Package Manager",
8049
- passed: true,
8050
- message: `npm v${stdout.trim()}`
8051
- };
8052
8517
  } catch {
8053
8518
  return {
8054
8519
  name: "Package Manager",
8055
8520
  passed: false,
8056
- message: "Neither Bun nor npm is installed",
8057
- fix: "Install Bun (recommended): curl -fsSL https://bun.sh/install | bash"
8521
+ message: "npm is not installed",
8522
+ fix: "Install Node.js and npm: https://nodejs.org/"
8058
8523
  };
8059
8524
  }
8060
8525
  }
8061
8526
  async function checkOmniLocalDir() {
8062
- const exists = existsSync25(".omni");
8527
+ const exists = existsSync26(".omni");
8063
8528
  if (!exists) {
8064
8529
  return {
8065
8530
  name: ".omni/ directory",
@@ -8076,7 +8541,7 @@ async function checkOmniLocalDir() {
8076
8541
  }
8077
8542
  async function checkConfig() {
8078
8543
  const configPath = "omni.toml";
8079
- if (!existsSync25(configPath)) {
8544
+ if (!existsSync26(configPath)) {
8080
8545
  return {
8081
8546
  name: "Configuration",
8082
8547
  passed: false,
@@ -8103,7 +8568,7 @@ async function checkConfig() {
8103
8568
  }
8104
8569
  async function checkRootGitignore() {
8105
8570
  const gitignorePath = ".gitignore";
8106
- if (!existsSync25(gitignorePath)) {
8571
+ if (!existsSync26(gitignorePath)) {
8107
8572
  return {
8108
8573
  name: "Root .gitignore",
8109
8574
  passed: false,
@@ -8137,7 +8602,7 @@ async function checkRootGitignore() {
8137
8602
  }
8138
8603
  async function checkCapabilitiesDir() {
8139
8604
  const capabilitiesDirPath = ".omni/capabilities";
8140
- if (!existsSync25(capabilitiesDirPath)) {
8605
+ if (!existsSync26(capabilitiesDirPath)) {
8141
8606
  return {
8142
8607
  name: "Capabilities Directory",
8143
8608
  passed: true,
@@ -8153,7 +8618,7 @@ async function checkCapabilitiesDir() {
8153
8618
 
8154
8619
  // src/commands/init.ts
8155
8620
  import { exec } from "node:child_process";
8156
- import { existsSync as existsSync26, mkdirSync as mkdirSync10 } from "node:fs";
8621
+ import { existsSync as existsSync27, mkdirSync as mkdirSync10 } from "node:fs";
8157
8622
  import { readFile as readFile21, writeFile as writeFile23 } from "node:fs/promises";
8158
8623
  import { promisify as promisify2 } from "node:util";
8159
8624
  init_src();
@@ -8223,7 +8688,7 @@ async function runInit(_flags, providerArg) {
8223
8688
  }
8224
8689
  }
8225
8690
  await writeEnabledProviders(providerIds);
8226
- if (!existsSync26("omni.toml")) {
8691
+ if (!existsSync27("omni.toml")) {
8227
8692
  await writeConfig({
8228
8693
  profiles: {
8229
8694
  default: {
@@ -8239,7 +8704,7 @@ async function runInit(_flags, providerArg) {
8239
8704
  });
8240
8705
  await setActiveProfile("default");
8241
8706
  }
8242
- if (!existsSync26("OMNI.md")) {
8707
+ if (!existsSync27("OMNI.md")) {
8243
8708
  await writeFile23("OMNI.md", generateOmniMdTemplate(), "utf-8");
8244
8709
  }
8245
8710
  const config3 = await loadConfig();
@@ -8317,7 +8782,7 @@ async function addProviderFilesToGitignore(entries) {
8317
8782
  async function addToGitignore(entriesToAdd, sectionHeader) {
8318
8783
  const gitignorePath = ".gitignore";
8319
8784
  let content = "";
8320
- if (existsSync26(gitignorePath)) {
8785
+ if (existsSync27(gitignorePath)) {
8321
8786
  content = await readFile21(gitignorePath, "utf-8");
8322
8787
  }
8323
8788
  const lines = content.split(`
@@ -8349,7 +8814,7 @@ async function getTrackedProviderFiles(files) {
8349
8814
  }
8350
8815
 
8351
8816
  // src/commands/profile.ts
8352
- import { existsSync as existsSync27 } from "node:fs";
8817
+ import { existsSync as existsSync28 } from "node:fs";
8353
8818
  init_src();
8354
8819
  import { buildCommand as buildCommand5, buildRouteMap as buildRouteMap3 } from "@stricli/core";
8355
8820
  var listCommand2 = buildCommand5({
@@ -8393,7 +8858,7 @@ var profileRoutes = buildRouteMap3({
8393
8858
  });
8394
8859
  async function runProfileList() {
8395
8860
  try {
8396
- if (!existsSync27("omni.toml")) {
8861
+ if (!existsSync28("omni.toml")) {
8397
8862
  console.log("✗ No config file found");
8398
8863
  console.log(" Run: omnidev init");
8399
8864
  process.exit(1);
@@ -8433,7 +8898,7 @@ async function runProfileList() {
8433
8898
  }
8434
8899
  async function runProfileSet(profileName) {
8435
8900
  try {
8436
- if (!existsSync27("omni.toml")) {
8901
+ if (!existsSync28("omni.toml")) {
8437
8902
  console.log("✗ No config file found");
8438
8903
  console.log(" Run: omnidev init");
8439
8904
  process.exit(1);
@@ -8466,6 +8931,8 @@ async function runProfileSet(profileName) {
8466
8931
  }
8467
8932
 
8468
8933
  // src/commands/provider.ts
8934
+ import { existsSync as existsSync29 } from "node:fs";
8935
+ import { readFile as readFile22 } from "node:fs/promises";
8469
8936
  init_src();
8470
8937
  import { buildCommand as buildCommand6, buildRouteMap as buildRouteMap4 } from "@stricli/core";
8471
8938
  async function runProviderList() {
@@ -8499,6 +8966,7 @@ async function runProviderEnable(_flags, providerId) {
8499
8966
  }
8500
8967
  await enableProvider(providerId);
8501
8968
  console.log(`✓ Enabled provider: ${adapter.displayName}`);
8969
+ await maybeRemindAboutProviderGitignore(providerId);
8502
8970
  const enabledAdapters = await getEnabledAdapters();
8503
8971
  await syncAgentConfiguration({ silent: false, adapters: enabledAdapters });
8504
8972
  }
@@ -8579,10 +9047,33 @@ var providerRoutes = buildRouteMap4({
8579
9047
  brief: "Manage AI provider adapters"
8580
9048
  }
8581
9049
  });
9050
+ async function maybeRemindAboutProviderGitignore(providerId) {
9051
+ const missingEntries = await getMissingGitignoreEntries(getProviderGitignoreFiles([providerId]));
9052
+ if (missingEntries.length === 0) {
9053
+ return;
9054
+ }
9055
+ console.log("");
9056
+ console.log("Also update your .gitignore to ignore provider files if you do not want to commit them:");
9057
+ for (const entry of missingEntries) {
9058
+ console.log(` - ${entry}`);
9059
+ }
9060
+ }
9061
+ async function getMissingGitignoreEntries(entries) {
9062
+ if (entries.length === 0) {
9063
+ return [];
9064
+ }
9065
+ let content = "";
9066
+ if (existsSync29(".gitignore")) {
9067
+ content = await readFile22(".gitignore", "utf-8");
9068
+ }
9069
+ const lines = new Set(content.split(`
9070
+ `).map((line) => line.trim()));
9071
+ return [...new Set(entries)].filter((entry) => !lines.has(entry));
9072
+ }
8582
9073
 
8583
9074
  // src/commands/security.ts
8584
9075
  init_src();
8585
- import { existsSync as existsSync28 } from "node:fs";
9076
+ import { existsSync as existsSync30 } from "node:fs";
8586
9077
  import { buildCommand as buildCommand7, buildRouteMap as buildRouteMap5 } from "@stricli/core";
8587
9078
  var VALID_FINDING_TYPES = [
8588
9079
  "unicode_bidi",
@@ -8621,7 +9112,9 @@ async function filterAllowedFindings(summary, options = {}) {
8621
9112
  symlink_escape: 0,
8622
9113
  symlink_absolute: 0,
8623
9114
  suspicious_script: 0,
8624
- binary_file: 0
9115
+ binary_file: 0,
9116
+ hidden_command: 0,
9117
+ network_request: 0
8625
9118
  };
8626
9119
  const findingsBySeverity = {
8627
9120
  low: 0,
@@ -8699,7 +9192,7 @@ function formatFindingsWithHints(summary) {
8699
9192
  }
8700
9193
  async function runSecurityIssues(flags = {}) {
8701
9194
  try {
8702
- if (!existsSync28("omni.toml")) {
9195
+ if (!existsSync30("omni.toml")) {
8703
9196
  console.log("No config file found");
8704
9197
  console.log(" Run: omnidev init");
8705
9198
  process.exit(1);
@@ -8954,7 +9447,7 @@ var securityRoutes = buildRouteMap5({
8954
9447
  });
8955
9448
 
8956
9449
  // src/commands/sync.ts
8957
- import { existsSync as existsSync29 } from "node:fs";
9450
+ import { existsSync as existsSync31 } from "node:fs";
8958
9451
  init_src();
8959
9452
  import { buildCommand as buildCommand8 } from "@stricli/core";
8960
9453
  var PROVIDERS_STATE_PATH = ".omni/state/providers.json";
@@ -8972,7 +9465,7 @@ async function runSync() {
8972
9465
  const config3 = await loadConfig();
8973
9466
  const activeProfile = await getActiveProfile() ?? "default";
8974
9467
  let adapters = await getEnabledAdapters();
8975
- if (!existsSync29(PROVIDERS_STATE_PATH) || adapters.length === 0) {
9468
+ if (!existsSync31(PROVIDERS_STATE_PATH) || adapters.length === 0) {
8976
9469
  console.log("No providers configured yet. Select your provider(s):");
8977
9470
  const providerIds = await promptForProviders();
8978
9471
  await writeEnabledProviders(providerIds);
@@ -9182,9 +9675,9 @@ async function buildDynamicApp() {
9182
9675
  security: securityRoutes
9183
9676
  };
9184
9677
  debug("Core routes registered", Object.keys(routes));
9185
- const configPath = join27(process.cwd(), "omni.toml");
9186
- debug("Checking for config", { configPath, exists: existsSync30(configPath), cwd: process.cwd() });
9187
- if (existsSync30(configPath)) {
9678
+ const configPath = join29(process.cwd(), "omni.toml");
9679
+ debug("Checking for config", { configPath, exists: existsSync32(configPath), cwd: process.cwd() });
9680
+ if (existsSync32(configPath)) {
9188
9681
  try {
9189
9682
  debug("Loading capability commands...");
9190
9683
  const capabilityCommands = await loadCapabilityCommands();
@@ -9259,25 +9752,25 @@ async function loadCapabilityCommands() {
9259
9752
  return commands;
9260
9753
  }
9261
9754
  async function loadCapabilityExport(capability3) {
9262
- const capabilityPath = join27(process.cwd(), capability3.path);
9263
- const builtIndexPath = join27(capabilityPath, "dist", "index.js");
9264
- const jsIndexPath = join27(capabilityPath, "index.js");
9265
- const tsIndexPath = join27(capabilityPath, "index.ts");
9755
+ const capabilityPath = join29(process.cwd(), capability3.path);
9756
+ const builtIndexPath = join29(capabilityPath, "dist", "index.js");
9757
+ const jsIndexPath = join29(capabilityPath, "index.js");
9758
+ const tsIndexPath = join29(capabilityPath, "index.ts");
9266
9759
  debug(`Checking entry points for '${capability3.id}'`, {
9267
9760
  capabilityPath,
9268
9761
  builtIndexPath,
9269
- builtExists: existsSync30(builtIndexPath),
9762
+ builtExists: existsSync32(builtIndexPath),
9270
9763
  jsIndexPath,
9271
- jsExists: existsSync30(jsIndexPath),
9764
+ jsExists: existsSync32(jsIndexPath),
9272
9765
  tsIndexPath,
9273
- tsExists: existsSync30(tsIndexPath)
9766
+ tsExists: existsSync32(tsIndexPath)
9274
9767
  });
9275
9768
  let indexPath = null;
9276
- if (existsSync30(builtIndexPath)) {
9769
+ if (existsSync32(builtIndexPath)) {
9277
9770
  indexPath = builtIndexPath;
9278
- } else if (existsSync30(jsIndexPath)) {
9771
+ } else if (existsSync32(jsIndexPath)) {
9279
9772
  indexPath = jsIndexPath;
9280
- } else if (existsSync30(tsIndexPath)) {
9773
+ } else if (existsSync32(tsIndexPath)) {
9281
9774
  indexPath = tsIndexPath;
9282
9775
  }
9283
9776
  if (!indexPath) {