@omnidev-ai/core 0.9.0 → 0.10.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.
package/dist/index.js CHANGED
@@ -118,58 +118,10 @@ async function loadDocs(capabilityPath, capabilityId) {
118
118
  return docs;
119
119
  }
120
120
  // src/capability/loader.ts
121
- import { existsSync as existsSync9, readdirSync as readdirSync6 } from "node:fs";
122
- import { readFile as readFile7 } from "node:fs/promises";
121
+ import { existsSync as existsSync8, readdirSync as readdirSync6 } from "node:fs";
122
+ import { readFile as readFile6 } from "node:fs/promises";
123
123
  import { join as join7 } from "node:path";
124
124
 
125
- // src/config/env.ts
126
- import { existsSync as existsSync3 } from "node:fs";
127
- import { readFile as readFile3 } from "node:fs/promises";
128
- var ENV_FILE = ".omni/.env";
129
- async function loadEnvironment() {
130
- const env = {};
131
- if (existsSync3(ENV_FILE)) {
132
- const content = await readFile3(ENV_FILE, "utf-8");
133
- for (const line of content.split(`
134
- `)) {
135
- const trimmed = line.trim();
136
- if (trimmed && !trimmed.startsWith("#")) {
137
- const eqIndex = trimmed.indexOf("=");
138
- if (eqIndex > 0) {
139
- const key = trimmed.slice(0, eqIndex).trim();
140
- const value = trimmed.slice(eqIndex + 1).trim();
141
- const unquotedValue = value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'") ? value.slice(1, -1) : value;
142
- env[key] = unquotedValue;
143
- }
144
- }
145
- }
146
- }
147
- const processEnv = {};
148
- for (const [key, value] of Object.entries(process.env)) {
149
- if (value !== undefined) {
150
- processEnv[key] = value;
151
- }
152
- }
153
- return { ...env, ...processEnv };
154
- }
155
- function validateEnv(declarations, env, capabilityId) {
156
- const missing = [];
157
- for (const [key, decl] of Object.entries(declarations)) {
158
- const declaration = decl;
159
- const value = env[key] ?? declaration.default;
160
- if (declaration.required && !value) {
161
- missing.push(key);
162
- }
163
- }
164
- if (missing.length > 0) {
165
- throw new Error(`Missing required environment variable${missing.length > 1 ? "s" : ""} for capability "${capabilityId}": ${missing.join(", ")}. ` + `Set ${missing.length > 1 ? "them" : "it"} in .omni/.env or as environment variable${missing.length > 1 ? "s" : ""}.`);
166
- }
167
- }
168
- function isSecretEnvVar(key, declarations) {
169
- const decl = declarations[key];
170
- return decl?.secret === true;
171
- }
172
-
173
125
  // src/config/parser.ts
174
126
  import { parse } from "smol-toml";
175
127
  function parseOmniConfig(tomlContent) {
@@ -208,7 +160,7 @@ function parseCapabilityConfig(tomlContent) {
208
160
  }
209
161
 
210
162
  // src/hooks/loader.ts
211
- import { existsSync as existsSync5, readFileSync } from "node:fs";
163
+ import { existsSync as existsSync4, readFileSync } from "node:fs";
212
164
  import { join as join3 } from "node:path";
213
165
  import { parse as parseToml } from "smol-toml";
214
166
 
@@ -274,7 +226,7 @@ var HOOKS_CONFIG_FILENAME = "hooks.toml";
274
226
  var HOOKS_DIRECTORY = "hooks";
275
227
 
276
228
  // src/hooks/validation.ts
277
- import { existsSync as existsSync4, statSync } from "node:fs";
229
+ import { existsSync as existsSync3, statSync } from "node:fs";
278
230
  import { resolve } from "node:path";
279
231
 
280
232
  // src/hooks/types.ts
@@ -590,7 +542,7 @@ function validateScriptInCommand(command, basePath, event, matcherIndex, hookInd
590
542
  const relativePath = match[1];
591
543
  if (relativePath) {
592
544
  const fullPath = resolve(basePath, relativePath);
593
- if (!existsSync4(fullPath)) {
545
+ if (!existsSync3(fullPath)) {
594
546
  issues.push({
595
547
  severity: "error",
596
548
  code: "HOOKS_SCRIPT_NOT_FOUND",
@@ -757,7 +709,7 @@ function loadHooksFromCapability(capabilityPath, options) {
757
709
  };
758
710
  const hooksDir = join3(capabilityPath, HOOKS_DIRECTORY);
759
711
  const configPath = join3(hooksDir, HOOKS_CONFIG_FILENAME);
760
- if (!existsSync5(configPath)) {
712
+ if (!existsSync4(configPath)) {
761
713
  return {
762
714
  config: createEmptyHooksConfig(),
763
715
  validation: createEmptyValidationResult(),
@@ -844,7 +796,7 @@ function loadCapabilityHooks(capabilityName, capabilityPath, options) {
844
796
  }
845
797
  function hasHooks(capabilityPath) {
846
798
  const configPath = join3(capabilityPath, HOOKS_DIRECTORY, HOOKS_CONFIG_FILENAME);
847
- return existsSync5(configPath);
799
+ return existsSync4(configPath);
848
800
  }
849
801
  function getHooksDirectory(capabilityPath) {
850
802
  return join3(capabilityPath, HOOKS_DIRECTORY);
@@ -854,12 +806,12 @@ function getHooksConfigPath(capabilityPath) {
854
806
  }
855
807
 
856
808
  // src/capability/rules.ts
857
- import { existsSync as existsSync6, readdirSync as readdirSync3 } from "node:fs";
858
- import { readFile as readFile4, writeFile } from "node:fs/promises";
809
+ import { existsSync as existsSync5, readdirSync as readdirSync3 } from "node:fs";
810
+ import { readFile as readFile3 } from "node:fs/promises";
859
811
  import { basename as basename2, join as join4 } from "node:path";
860
812
  async function loadRules(capabilityPath, capabilityId) {
861
813
  const rulesDir = join4(capabilityPath, "rules");
862
- if (!existsSync6(rulesDir)) {
814
+ if (!existsSync5(rulesDir)) {
863
815
  return [];
864
816
  }
865
817
  const rules = [];
@@ -867,7 +819,7 @@ async function loadRules(capabilityPath, capabilityId) {
867
819
  for (const entry of entries) {
868
820
  if (entry.isFile() && entry.name.endsWith(".md")) {
869
821
  const rulePath = join4(rulesDir, entry.name);
870
- const content = await readFile4(rulePath, "utf-8");
822
+ const content = await readFile3(rulePath, "utf-8");
871
823
  rules.push({
872
824
  name: basename2(entry.name, ".md"),
873
825
  content: content.trim(),
@@ -877,88 +829,14 @@ async function loadRules(capabilityPath, capabilityId) {
877
829
  }
878
830
  return rules;
879
831
  }
880
- async function writeRules(rules, docs = []) {
881
- const instructionsPath = ".omni/instructions.md";
882
- const rulesContent = generateRulesContent(rules, docs);
883
- let content;
884
- if (existsSync6(instructionsPath)) {
885
- content = await readFile4(instructionsPath, "utf-8");
886
- } else {
887
- content = `# OmniDev Instructions
888
-
889
- ## Project Description
890
- <!-- TODO: Add 2-3 sentences describing your project -->
891
- [Describe what this project does and its main purpose]
892
-
893
- <!-- BEGIN OMNIDEV GENERATED CONTENT - DO NOT EDIT BELOW THIS LINE -->
894
- <!-- END OMNIDEV GENERATED CONTENT -->
895
- `;
896
- }
897
- const beginMarker = "<!-- BEGIN OMNIDEV GENERATED CONTENT - DO NOT EDIT BELOW THIS LINE -->";
898
- const endMarker = "<!-- END OMNIDEV GENERATED CONTENT -->";
899
- const beginIndex = content.indexOf(beginMarker);
900
- const endIndex = content.indexOf(endMarker);
901
- if (beginIndex === -1 || endIndex === -1) {
902
- content += `
903
-
904
- ${beginMarker}
905
- ${rulesContent}
906
- ${endMarker}
907
- `;
908
- } else {
909
- content = content.substring(0, beginIndex + beginMarker.length) + `
910
- ` + rulesContent + `
911
- ` + content.substring(endIndex);
912
- }
913
- await writeFile(instructionsPath, content, "utf-8");
914
- }
915
- function generateRulesContent(rules, docs = []) {
916
- if (rules.length === 0 && docs.length === 0) {
917
- return `<!-- This section is automatically updated when capabilities change -->
918
-
919
- ## Capabilities
920
-
921
- No capabilities enabled yet. Run \`omnidev capability enable <name>\` to enable capabilities.`;
922
- }
923
- let content = `<!-- This section is automatically updated when capabilities change -->
924
-
925
- ## Capabilities
926
-
927
- `;
928
- if (docs.length > 0) {
929
- content += `### Documentation
930
-
931
- `;
932
- for (const doc of docs) {
933
- content += `#### ${doc.name} (from ${doc.capabilityId})
934
-
935
- ${doc.content}
936
-
937
- `;
938
- }
939
- }
940
- if (rules.length > 0) {
941
- content += `### Rules
942
-
943
- `;
944
- for (const rule of rules) {
945
- content += `#### ${rule.name} (from ${rule.capabilityId})
946
-
947
- ${rule.content}
948
-
949
- `;
950
- }
951
- }
952
- return content.trim();
953
- }
954
832
 
955
833
  // src/capability/skills.ts
956
- import { existsSync as existsSync7, readdirSync as readdirSync4 } from "node:fs";
957
- import { readFile as readFile5 } from "node:fs/promises";
834
+ import { existsSync as existsSync6, readdirSync as readdirSync4 } from "node:fs";
835
+ import { readFile as readFile4 } from "node:fs/promises";
958
836
  import { join as join5 } from "node:path";
959
837
  async function loadSkills(capabilityPath, capabilityId) {
960
838
  const skillsDir = join5(capabilityPath, "skills");
961
- if (!existsSync7(skillsDir)) {
839
+ if (!existsSync6(skillsDir)) {
962
840
  return [];
963
841
  }
964
842
  const skills = [];
@@ -966,7 +844,7 @@ async function loadSkills(capabilityPath, capabilityId) {
966
844
  for (const entry of entries) {
967
845
  if (entry.isDirectory()) {
968
846
  const skillPath = join5(skillsDir, entry.name, "SKILL.md");
969
- if (existsSync7(skillPath)) {
847
+ if (existsSync6(skillPath)) {
970
848
  const skill = await parseSkillFile(skillPath, capabilityId);
971
849
  skills.push(skill);
972
850
  }
@@ -975,7 +853,7 @@ async function loadSkills(capabilityPath, capabilityId) {
975
853
  return skills;
976
854
  }
977
855
  async function parseSkillFile(filePath, capabilityId) {
978
- const content = await readFile5(filePath, "utf-8");
856
+ const content = await readFile4(filePath, "utf-8");
979
857
  const parsed = parseFrontmatterWithMarkdown(content);
980
858
  if (!parsed) {
981
859
  throw new Error(`Invalid SKILL.md format at ${filePath}: missing YAML frontmatter`);
@@ -994,12 +872,12 @@ async function parseSkillFile(filePath, capabilityId) {
994
872
  }
995
873
 
996
874
  // src/capability/subagents.ts
997
- import { existsSync as existsSync8, readdirSync as readdirSync5 } from "node:fs";
998
- import { readFile as readFile6 } from "node:fs/promises";
875
+ import { existsSync as existsSync7, readdirSync as readdirSync5 } from "node:fs";
876
+ import { readFile as readFile5 } from "node:fs/promises";
999
877
  import { join as join6 } from "node:path";
1000
878
  async function loadSubagents(capabilityPath, capabilityId) {
1001
879
  const subagentsDir = join6(capabilityPath, "subagents");
1002
- if (!existsSync8(subagentsDir)) {
880
+ if (!existsSync7(subagentsDir)) {
1003
881
  return [];
1004
882
  }
1005
883
  const subagents = [];
@@ -1007,7 +885,7 @@ async function loadSubagents(capabilityPath, capabilityId) {
1007
885
  for (const entry of entries) {
1008
886
  if (entry.isDirectory()) {
1009
887
  const subagentPath = join6(subagentsDir, entry.name, "SUBAGENT.md");
1010
- if (existsSync8(subagentPath)) {
888
+ if (existsSync7(subagentPath)) {
1011
889
  const subagent = await parseSubagentFile(subagentPath, capabilityId);
1012
890
  subagents.push(subagent);
1013
891
  }
@@ -1016,7 +894,7 @@ async function loadSubagents(capabilityPath, capabilityId) {
1016
894
  return subagents;
1017
895
  }
1018
896
  async function parseSubagentFile(filePath, capabilityId) {
1019
- const content = await readFile6(filePath, "utf-8");
897
+ const content = await readFile5(filePath, "utf-8");
1020
898
  const parsed = parseFrontmatterWithMarkdown(content);
1021
899
  if (!parsed) {
1022
900
  throw new Error(`Invalid SUBAGENT.md format at ${filePath}: missing YAML frontmatter`);
@@ -1060,13 +938,13 @@ function parseCommaSeparatedList(value) {
1060
938
  var CAPABILITIES_DIR = ".omni/capabilities";
1061
939
  async function discoverCapabilities() {
1062
940
  const capabilities = [];
1063
- if (existsSync9(CAPABILITIES_DIR)) {
941
+ if (existsSync8(CAPABILITIES_DIR)) {
1064
942
  const entries = readdirSync6(CAPABILITIES_DIR, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
1065
943
  for (const entry of entries) {
1066
944
  if (entry.isDirectory()) {
1067
945
  const entryPath = join7(CAPABILITIES_DIR, entry.name);
1068
946
  const configPath = join7(entryPath, "capability.toml");
1069
- if (existsSync9(configPath)) {
947
+ if (existsSync8(configPath)) {
1070
948
  capabilities.push(entryPath);
1071
949
  }
1072
950
  }
@@ -1076,13 +954,13 @@ async function discoverCapabilities() {
1076
954
  }
1077
955
  async function loadCapabilityConfig(capabilityPath) {
1078
956
  const configPath = join7(capabilityPath, "capability.toml");
1079
- const content = await readFile7(configPath, "utf-8");
957
+ const content = await readFile6(configPath, "utf-8");
1080
958
  const config = parseCapabilityConfig(content);
1081
959
  return config;
1082
960
  }
1083
961
  async function importCapabilityExports(capabilityPath) {
1084
962
  const indexPath = join7(capabilityPath, "index.ts");
1085
- if (!existsSync9(indexPath)) {
963
+ if (!existsSync8(indexPath)) {
1086
964
  return {};
1087
965
  }
1088
966
  try {
@@ -1102,10 +980,10 @@ If this is a project-specific capability, install dependencies or remove it from
1102
980
  }
1103
981
  async function loadTypeDefinitions(capabilityPath) {
1104
982
  const typesPath = join7(capabilityPath, "types.d.ts");
1105
- if (!existsSync9(typesPath)) {
983
+ if (!existsSync8(typesPath)) {
1106
984
  return;
1107
985
  }
1108
- return readFile7(typesPath, "utf-8");
986
+ return readFile6(typesPath, "utf-8");
1109
987
  }
1110
988
  function convertSkillExports(skillExports, capabilityId) {
1111
989
  return skillExports.map((skillExport) => {
@@ -1282,12 +1160,9 @@ function convertCommandExports(commandExports, capabilityId) {
1282
1160
  return result;
1283
1161
  });
1284
1162
  }
1285
- async function loadCapability(capabilityPath, env) {
1163
+ async function loadCapability(capabilityPath) {
1286
1164
  const config = await loadCapabilityConfig(capabilityPath);
1287
1165
  const id = config.capability.id;
1288
- if (config.env) {
1289
- validateEnv(config.env, env, id);
1290
- }
1291
1166
  const exports = await importCapabilityExports(capabilityPath);
1292
1167
  const exportsAny = exports;
1293
1168
  const skills = "skills" in exports && Array.isArray(exportsAny.skills) ? convertSkillExports(exportsAny.skills, id) : await loadSkills(capabilityPath, id);
@@ -1322,13 +1197,12 @@ async function loadCapability(capabilityPath, env) {
1322
1197
  return result;
1323
1198
  }
1324
1199
  // src/config/config.ts
1325
- import { existsSync as existsSync10 } from "node:fs";
1326
- import { readFile as readFile8, writeFile as writeFile2 } from "node:fs/promises";
1200
+ import { existsSync as existsSync9 } from "node:fs";
1201
+ import { readFile as readFile7, writeFile } from "node:fs/promises";
1327
1202
  var CONFIG_PATH = "omni.toml";
1328
1203
  var LOCAL_CONFIG = "omni.local.toml";
1329
1204
  function mergeConfigs(base, override) {
1330
1205
  const merged = { ...base, ...override };
1331
- merged.env = { ...base.env, ...override.env };
1332
1206
  merged.profiles = { ...base.profiles };
1333
1207
  for (const [name, profile] of Object.entries(override.profiles || {})) {
1334
1208
  merged.profiles[name] = {
@@ -1342,8 +1216,8 @@ function mergeConfigs(base, override) {
1342
1216
  return merged;
1343
1217
  }
1344
1218
  async function loadBaseConfig() {
1345
- if (existsSync10(CONFIG_PATH)) {
1346
- const content = await readFile8(CONFIG_PATH, "utf-8");
1219
+ if (existsSync9(CONFIG_PATH)) {
1220
+ const content = await readFile7(CONFIG_PATH, "utf-8");
1347
1221
  return parseOmniConfig(content);
1348
1222
  }
1349
1223
  return {};
@@ -1351,34 +1225,18 @@ async function loadBaseConfig() {
1351
1225
  async function loadConfig() {
1352
1226
  const baseConfig = await loadBaseConfig();
1353
1227
  let localConfig = {};
1354
- if (existsSync10(LOCAL_CONFIG)) {
1355
- const content = await readFile8(LOCAL_CONFIG, "utf-8");
1228
+ if (existsSync9(LOCAL_CONFIG)) {
1229
+ const content = await readFile7(LOCAL_CONFIG, "utf-8");
1356
1230
  localConfig = parseOmniConfig(content);
1357
1231
  }
1358
1232
  return mergeConfigs(baseConfig, localConfig);
1359
1233
  }
1360
1234
  async function writeConfig(config) {
1361
1235
  const content = generateConfigToml(config);
1362
- await writeFile2(CONFIG_PATH, content, "utf-8");
1236
+ await writeFile(CONFIG_PATH, content, "utf-8");
1363
1237
  }
1364
1238
  function generateConfigToml(config) {
1365
1239
  const lines = [];
1366
- lines.push("# =============================================================================");
1367
- lines.push("# OmniDev Configuration");
1368
- lines.push("# =============================================================================");
1369
- lines.push("# This file defines your project's capabilities, profiles, and settings.");
1370
- lines.push("#");
1371
- lines.push("# Files:");
1372
- lines.push("# • omni.toml - Main config (commit to share with team)");
1373
- lines.push("# • omni.local.toml - Local overrides (add to .gitignore)");
1374
- lines.push("# • omni.lock.toml - Version lock file (commit for reproducibility)");
1375
- lines.push("#");
1376
- lines.push("# Quick start:");
1377
- lines.push("# 1. Add capability sources to [capabilities.sources]");
1378
- lines.push("# 2. Reference them in your profiles");
1379
- lines.push("# 3. Run: omnidev sync");
1380
- lines.push("# 4. Switch profiles: omnidev profile use <name>");
1381
- lines.push("");
1382
1240
  if (config.providers?.enabled && config.providers.enabled.length > 0) {
1383
1241
  lines.push("# AI providers to enable (claude, codex, or both)");
1384
1242
  lines.push("[providers]");
@@ -1386,23 +1244,6 @@ function generateConfigToml(config) {
1386
1244
  lines.push("");
1387
1245
  }
1388
1246
  lines.push("# =============================================================================");
1389
- lines.push("# Environment Variables");
1390
- lines.push("# =============================================================================");
1391
- lines.push("# Global environment variables available to all capabilities.");
1392
- lines.push("# Use ${VAR_NAME} syntax to reference shell environment variables.");
1393
- lines.push("#");
1394
- if (config.env && Object.keys(config.env).length > 0) {
1395
- lines.push("[env]");
1396
- for (const [key, value] of Object.entries(config.env)) {
1397
- lines.push(`${key} = "${value}"`);
1398
- }
1399
- } else {
1400
- lines.push("# [env]");
1401
- lines.push('# DATABASE_URL = "${DATABASE_URL}"');
1402
- lines.push('# API_KEY = "${MY_API_KEY}"');
1403
- }
1404
- lines.push("");
1405
- lines.push("# =============================================================================");
1406
1247
  lines.push("# Capability Sources");
1407
1248
  lines.push("# =============================================================================");
1408
1249
  lines.push("# Fetch capabilities from Git repositories. On sync, these are");
@@ -1414,7 +1255,7 @@ function generateConfigToml(config) {
1414
1255
  for (const [name, sourceConfig] of Object.entries(sources)) {
1415
1256
  if (typeof sourceConfig === "string") {
1416
1257
  lines.push(`${name} = "${sourceConfig}"`);
1417
- } else if (sourceConfig.path) {
1258
+ } else if ("path" in sourceConfig && sourceConfig.path) {
1418
1259
  lines.push(`${name} = { source = "${sourceConfig.source}", path = "${sourceConfig.path}" }`);
1419
1260
  } else {
1420
1261
  lines.push(`${name} = "${sourceConfig.source}"`);
@@ -1482,10 +1323,8 @@ function generateConfigToml(config) {
1482
1323
  lines.push(`url = "${mcpConfig.url}"`);
1483
1324
  }
1484
1325
  if (mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
1485
- lines.push(`[mcps.${name}.env]`);
1486
- for (const [key, value] of Object.entries(mcpConfig.env)) {
1487
- lines.push(`${key} = "${value}"`);
1488
- }
1326
+ const entries = Object.entries(mcpConfig.env).map(([key, value]) => `${key} = "${value}"`);
1327
+ lines.push(`env = { ${entries.join(", ")} }`);
1489
1328
  }
1490
1329
  if (mcpConfig.headers && Object.keys(mcpConfig.headers).length > 0) {
1491
1330
  lines.push(`[mcps.${name}.headers]`);
@@ -1505,8 +1344,7 @@ function generateConfigToml(config) {
1505
1344
  lines.push('# command = "node"');
1506
1345
  lines.push('# args = ["./servers/database.js"]');
1507
1346
  lines.push('# cwd = "./mcp-servers"');
1508
- lines.push("# [mcps.database.env]");
1509
- lines.push('# DB_URL = "${DATABASE_URL}"');
1347
+ lines.push('# env = { DB_URL = "${DATABASE_URL}" }');
1510
1348
  lines.push("");
1511
1349
  }
1512
1350
  lines.push("# =============================================================================");
@@ -1545,16 +1383,16 @@ function generateConfigToml(config) {
1545
1383
  }
1546
1384
 
1547
1385
  // src/state/active-profile.ts
1548
- import { existsSync as existsSync11, mkdirSync } from "node:fs";
1549
- import { readFile as readFile9, unlink, writeFile as writeFile3 } from "node:fs/promises";
1386
+ import { existsSync as existsSync10, mkdirSync } from "node:fs";
1387
+ import { readFile as readFile8, unlink, writeFile as writeFile2 } from "node:fs/promises";
1550
1388
  var STATE_DIR = ".omni/state";
1551
1389
  var ACTIVE_PROFILE_PATH = `${STATE_DIR}/active-profile`;
1552
1390
  async function readActiveProfileState() {
1553
- if (!existsSync11(ACTIVE_PROFILE_PATH)) {
1391
+ if (!existsSync10(ACTIVE_PROFILE_PATH)) {
1554
1392
  return null;
1555
1393
  }
1556
1394
  try {
1557
- const content = await readFile9(ACTIVE_PROFILE_PATH, "utf-8");
1395
+ const content = await readFile8(ACTIVE_PROFILE_PATH, "utf-8");
1558
1396
  const trimmed = content.trim();
1559
1397
  return trimmed || null;
1560
1398
  } catch {
@@ -1563,10 +1401,10 @@ async function readActiveProfileState() {
1563
1401
  }
1564
1402
  async function writeActiveProfileState(profileName) {
1565
1403
  mkdirSync(STATE_DIR, { recursive: true });
1566
- await writeFile3(ACTIVE_PROFILE_PATH, profileName, "utf-8");
1404
+ await writeFile2(ACTIVE_PROFILE_PATH, profileName, "utf-8");
1567
1405
  }
1568
1406
  async function clearActiveProfileState() {
1569
- if (existsSync11(ACTIVE_PROFILE_PATH)) {
1407
+ if (existsSync10(ACTIVE_PROFILE_PATH)) {
1570
1408
  await unlink(ACTIVE_PROFILE_PATH);
1571
1409
  }
1572
1410
  }
@@ -1740,13 +1578,12 @@ function getEventsWithHooks(config) {
1740
1578
 
1741
1579
  // src/capability/registry.ts
1742
1580
  async function buildCapabilityRegistry() {
1743
- const env = await loadEnvironment();
1744
1581
  const enabledIds = await getEnabledCapabilities();
1745
1582
  const capabilityPaths = await discoverCapabilities();
1746
1583
  const capabilities = new Map;
1747
1584
  for (const path of capabilityPaths) {
1748
1585
  try {
1749
- const cap = await loadCapability(path, env);
1586
+ const cap = await loadCapability(path);
1750
1587
  if (enabledIds.includes(cap.id)) {
1751
1588
  capabilities.set(cap.id, cap);
1752
1589
  }
@@ -1777,11 +1614,28 @@ async function buildCapabilityRegistry() {
1777
1614
  };
1778
1615
  }
1779
1616
  // src/capability/sources.ts
1780
- import { existsSync as existsSync12 } from "node:fs";
1617
+ import { existsSync as existsSync11 } from "node:fs";
1781
1618
  import { spawn } from "node:child_process";
1782
- import { cp, mkdir, readdir, readFile as readFile10, rename, rm, stat, writeFile as writeFile4 } from "node:fs/promises";
1619
+ import { cp, mkdir, readdir, readFile as readFile9, rename, rm, stat, writeFile as writeFile3 } from "node:fs/promises";
1783
1620
  import { join as join8 } from "node:path";
1784
1621
  import { parse as parseToml2 } from "smol-toml";
1622
+
1623
+ // src/types/index.ts
1624
+ function isFileSourceConfig(config) {
1625
+ if (typeof config === "string") {
1626
+ return config.startsWith("file://");
1627
+ }
1628
+ return config.source.startsWith("file://");
1629
+ }
1630
+ function getActiveProviders(config) {
1631
+ if (config.providers)
1632
+ return config.providers;
1633
+ if (config.provider)
1634
+ return [config.provider];
1635
+ return ["claude"];
1636
+ }
1637
+
1638
+ // src/capability/sources.ts
1785
1639
  var OMNI_LOCAL = ".omni";
1786
1640
  var SKILL_DIRS = ["skills", "skill"];
1787
1641
  var AGENT_DIRS = ["agents", "agent", "subagents", "subagent"];
@@ -1813,8 +1667,39 @@ async function spawnCapture(command, args, options) {
1813
1667
  });
1814
1668
  });
1815
1669
  }
1670
+ function isGitSource(source) {
1671
+ return source.startsWith("github:") || source.startsWith("git@") || source.startsWith("https://") || source.startsWith("http://");
1672
+ }
1673
+ function isFileSource(source) {
1674
+ return source.startsWith("file://");
1675
+ }
1676
+ function parseFileSourcePath(source) {
1677
+ if (!source.startsWith("file://")) {
1678
+ throw new Error(`Invalid file source: ${source}`);
1679
+ }
1680
+ return source.slice(7);
1681
+ }
1682
+ async function readCapabilityIdFromPath(capabilityPath) {
1683
+ const tomlPath = join8(capabilityPath, "capability.toml");
1684
+ if (existsSync11(tomlPath)) {
1685
+ try {
1686
+ const content = await readFile9(tomlPath, "utf-8");
1687
+ const parsed = parseToml2(content);
1688
+ const capability = parsed["capability"];
1689
+ if (capability?.["id"] && typeof capability["id"] === "string") {
1690
+ return capability["id"];
1691
+ }
1692
+ } catch {}
1693
+ }
1694
+ const parts = capabilityPath.replace(/\\/g, "/").split("/");
1695
+ const dirName = parts.pop() || parts.pop();
1696
+ return dirName || null;
1697
+ }
1816
1698
  function parseSourceConfig(source) {
1817
1699
  if (typeof source === "string") {
1700
+ if (isFileSource(source)) {
1701
+ return { source };
1702
+ }
1818
1703
  let sourceUrl = source;
1819
1704
  let ref;
1820
1705
  if (source.startsWith("github:") && source.includes("#")) {
@@ -1828,6 +1713,9 @@ function parseSourceConfig(source) {
1828
1713
  }
1829
1714
  return result;
1830
1715
  }
1716
+ if (isFileSourceConfig(source)) {
1717
+ return source;
1718
+ }
1831
1719
  return source;
1832
1720
  }
1833
1721
  function sourceToGitUrl(source) {
@@ -1845,11 +1733,11 @@ function getLockFilePath() {
1845
1733
  }
1846
1734
  async function loadLockFile() {
1847
1735
  const lockPath = getLockFilePath();
1848
- if (!existsSync12(lockPath)) {
1736
+ if (!existsSync11(lockPath)) {
1849
1737
  return { capabilities: {} };
1850
1738
  }
1851
1739
  try {
1852
- const content = await readFile10(lockPath, "utf-8");
1740
+ const content = await readFile9(lockPath, "utf-8");
1853
1741
  const parsed = parseToml2(content);
1854
1742
  const capabilities = parsed["capabilities"];
1855
1743
  return {
@@ -1886,7 +1774,7 @@ async function saveLockFile(lockFile) {
1886
1774
 
1887
1775
  `;
1888
1776
  const content = header + stringifyLockFile(lockFile);
1889
- await writeFile4(lockPath, content, "utf-8");
1777
+ await writeFile3(lockPath, content, "utf-8");
1890
1778
  }
1891
1779
  async function getRepoCommit(repoPath) {
1892
1780
  const { exitCode, stdout, stderr } = await spawnCapture("git", ["rev-parse", "HEAD"], {
@@ -1938,16 +1826,16 @@ async function fetchRepo(repoPath, ref) {
1938
1826
  return true;
1939
1827
  }
1940
1828
  function hasCapabilityToml(dirPath) {
1941
- return existsSync12(join8(dirPath, "capability.toml"));
1829
+ return existsSync11(join8(dirPath, "capability.toml"));
1942
1830
  }
1943
1831
  async function shouldWrapDirectory(dirPath) {
1944
- if (existsSync12(join8(dirPath, ".claude-plugin", "plugin.json"))) {
1832
+ if (existsSync11(join8(dirPath, ".claude-plugin", "plugin.json"))) {
1945
1833
  return true;
1946
1834
  }
1947
1835
  const allDirs = [...SKILL_DIRS, ...AGENT_DIRS, ...COMMAND_DIRS, ...RULE_DIRS, ...DOC_DIRS];
1948
1836
  for (const dirName of allDirs) {
1949
1837
  const checkPath = join8(dirPath, dirName);
1950
- if (existsSync12(checkPath)) {
1838
+ if (existsSync11(checkPath)) {
1951
1839
  const stats = await stat(checkPath);
1952
1840
  if (stats.isDirectory()) {
1953
1841
  return true;
@@ -1959,7 +1847,7 @@ async function shouldWrapDirectory(dirPath) {
1959
1847
  async function findMatchingDirs(basePath, names) {
1960
1848
  for (const name of names) {
1961
1849
  const dirPath = join8(basePath, name);
1962
- if (existsSync12(dirPath)) {
1850
+ if (existsSync11(dirPath)) {
1963
1851
  const stats = await stat(dirPath);
1964
1852
  if (stats.isDirectory()) {
1965
1853
  return dirPath;
@@ -1970,7 +1858,7 @@ async function findMatchingDirs(basePath, names) {
1970
1858
  }
1971
1859
  async function findContentItems(dirPath, filePatterns) {
1972
1860
  const items = [];
1973
- if (!existsSync12(dirPath)) {
1861
+ if (!existsSync11(dirPath)) {
1974
1862
  return items;
1975
1863
  }
1976
1864
  const entries = (await readdir(dirPath, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
@@ -1978,7 +1866,7 @@ async function findContentItems(dirPath, filePatterns) {
1978
1866
  const entryPath = join8(dirPath, entry.name);
1979
1867
  if (entry.isDirectory()) {
1980
1868
  for (const pattern of filePatterns) {
1981
- if (existsSync12(join8(entryPath, pattern))) {
1869
+ if (existsSync11(join8(entryPath, pattern))) {
1982
1870
  items.push({
1983
1871
  name: entry.name,
1984
1872
  path: entryPath,
@@ -2000,11 +1888,11 @@ async function findContentItems(dirPath, filePatterns) {
2000
1888
  }
2001
1889
  async function parsePluginJson(dirPath) {
2002
1890
  const pluginJsonPath = join8(dirPath, ".claude-plugin", "plugin.json");
2003
- if (!existsSync12(pluginJsonPath)) {
1891
+ if (!existsSync11(pluginJsonPath)) {
2004
1892
  return null;
2005
1893
  }
2006
1894
  try {
2007
- const content = await readFile10(pluginJsonPath, "utf-8");
1895
+ const content = await readFile9(pluginJsonPath, "utf-8");
2008
1896
  const data = JSON.parse(content);
2009
1897
  const result = {
2010
1898
  name: data.name,
@@ -2025,11 +1913,11 @@ async function parsePluginJson(dirPath) {
2025
1913
  }
2026
1914
  async function readReadmeDescription(dirPath) {
2027
1915
  const readmePath = join8(dirPath, "README.md");
2028
- if (!existsSync12(readmePath)) {
1916
+ if (!existsSync11(readmePath)) {
2029
1917
  return null;
2030
1918
  }
2031
1919
  try {
2032
- const content = await readFile10(readmePath, "utf-8");
1920
+ const content = await readFile9(readmePath, "utf-8");
2033
1921
  const lines = content.split(`
2034
1922
  `);
2035
1923
  let description = "";
@@ -2068,7 +1956,7 @@ async function normalizeFolderNames(repoPath) {
2068
1956
  for (const { from, to } of renameMappings) {
2069
1957
  const fromPath = join8(repoPath, from);
2070
1958
  const toPath = join8(repoPath, to);
2071
- if (existsSync12(fromPath) && !existsSync12(toPath)) {
1959
+ if (existsSync11(fromPath) && !existsSync11(toPath)) {
2072
1960
  try {
2073
1961
  const stats = await stat(fromPath);
2074
1962
  if (stats.isDirectory()) {
@@ -2157,7 +2045,7 @@ repository = "${repoUrl}"
2157
2045
  wrapped = true
2158
2046
  commit = "${commit}"
2159
2047
  `;
2160
- await writeFile4(join8(repoPath, "capability.toml"), tomlContent, "utf-8");
2048
+ await writeFile3(join8(repoPath, "capability.toml"), tomlContent, "utf-8");
2161
2049
  }
2162
2050
  async function fetchGitCapabilitySource(id, config, options) {
2163
2051
  const gitUrl = sourceToGitUrl(config.source);
@@ -2167,7 +2055,7 @@ async function fetchGitCapabilitySource(id, config, options) {
2167
2055
  let repoPath;
2168
2056
  if (config.path) {
2169
2057
  const tempPath = join8(OMNI_LOCAL, "_temp", `${id}-repo`);
2170
- if (existsSync12(join8(tempPath, ".git"))) {
2058
+ if (existsSync11(join8(tempPath, ".git"))) {
2171
2059
  if (!options?.silent) {
2172
2060
  console.log(` Checking ${id}...`);
2173
2061
  }
@@ -2183,17 +2071,17 @@ async function fetchGitCapabilitySource(id, config, options) {
2183
2071
  updated = true;
2184
2072
  }
2185
2073
  const sourcePath = join8(tempPath, config.path);
2186
- if (!existsSync12(sourcePath)) {
2074
+ if (!existsSync11(sourcePath)) {
2187
2075
  throw new Error(`Path not found in repository: ${config.path}`);
2188
2076
  }
2189
- if (existsSync12(targetPath)) {
2077
+ if (existsSync11(targetPath)) {
2190
2078
  await rm(targetPath, { recursive: true });
2191
2079
  }
2192
2080
  await mkdir(join8(targetPath, ".."), { recursive: true });
2193
2081
  await cp(sourcePath, targetPath, { recursive: true });
2194
2082
  repoPath = targetPath;
2195
2083
  } else {
2196
- if (existsSync12(join8(targetPath, ".git"))) {
2084
+ if (existsSync11(join8(targetPath, ".git"))) {
2197
2085
  if (!options?.silent) {
2198
2086
  console.log(` Checking ${id}...`);
2199
2087
  }
@@ -2232,9 +2120,9 @@ async function fetchGitCapabilitySource(id, config, options) {
2232
2120
  }
2233
2121
  let version = shortCommit(commit);
2234
2122
  const pkgJsonPath = join8(repoPath, "package.json");
2235
- if (existsSync12(pkgJsonPath)) {
2123
+ if (existsSync11(pkgJsonPath)) {
2236
2124
  try {
2237
- const pkgJson = JSON.parse(await readFile10(pkgJsonPath, "utf-8"));
2125
+ const pkgJson = JSON.parse(await readFile9(pkgJsonPath, "utf-8"));
2238
2126
  if (pkgJson.version) {
2239
2127
  version = pkgJson.version;
2240
2128
  }
@@ -2249,8 +2137,52 @@ async function fetchGitCapabilitySource(id, config, options) {
2249
2137
  wrapped: needsWrap
2250
2138
  };
2251
2139
  }
2140
+ async function fetchFileCapabilitySource(id, config, options) {
2141
+ const sourcePath = parseFileSourcePath(config.source);
2142
+ const targetPath = getSourceCapabilityPath(id);
2143
+ if (!existsSync11(sourcePath)) {
2144
+ throw new Error(`File source not found: ${sourcePath}`);
2145
+ }
2146
+ const sourceStats = await stat(sourcePath);
2147
+ if (!sourceStats.isDirectory()) {
2148
+ throw new Error(`File source must be a directory: ${sourcePath}`);
2149
+ }
2150
+ if (!existsSync11(join8(sourcePath, "capability.toml"))) {
2151
+ throw new Error(`No capability.toml found in: ${sourcePath}`);
2152
+ }
2153
+ if (!options?.silent) {
2154
+ console.log(` Copying ${id} from ${sourcePath}...`);
2155
+ }
2156
+ if (existsSync11(targetPath)) {
2157
+ await rm(targetPath, { recursive: true });
2158
+ }
2159
+ await mkdir(join8(targetPath, ".."), { recursive: true });
2160
+ await cp(sourcePath, targetPath, { recursive: true });
2161
+ let version = "local";
2162
+ const capTomlPath = join8(targetPath, "capability.toml");
2163
+ if (existsSync11(capTomlPath)) {
2164
+ try {
2165
+ const content = await readFile9(capTomlPath, "utf-8");
2166
+ const parsed = parseToml2(content);
2167
+ const capability = parsed["capability"];
2168
+ if (capability?.["version"] && typeof capability["version"] === "string") {
2169
+ version = capability["version"];
2170
+ }
2171
+ } catch {}
2172
+ }
2173
+ return {
2174
+ id,
2175
+ path: targetPath,
2176
+ version,
2177
+ updated: true,
2178
+ wrapped: false
2179
+ };
2180
+ }
2252
2181
  async function fetchCapabilitySource(id, sourceConfig, options) {
2253
2182
  const config = parseSourceConfig(sourceConfig);
2183
+ if (isFileSourceConfig(sourceConfig) || isFileSource(config.source)) {
2184
+ return fetchFileCapabilitySource(id, config, options);
2185
+ }
2254
2186
  return fetchGitCapabilitySource(id, config, options);
2255
2187
  }
2256
2188
  function generateMcpCapabilityTomlContent(id, mcpConfig) {
@@ -2317,16 +2249,16 @@ generated_from_omni_toml = true
2317
2249
  }
2318
2250
  async function generateMcpCapabilityToml(id, mcpConfig, targetPath) {
2319
2251
  const tomlContent = generateMcpCapabilityTomlContent(id, mcpConfig);
2320
- await writeFile4(join8(targetPath, "capability.toml"), tomlContent, "utf-8");
2252
+ await writeFile3(join8(targetPath, "capability.toml"), tomlContent, "utf-8");
2321
2253
  }
2322
2254
  async function isGeneratedMcpCapability(capabilityDir) {
2323
2255
  const tomlPath = join8(capabilityDir, "capability.toml");
2324
- if (!existsSync12(tomlPath)) {
2256
+ if (!existsSync11(tomlPath)) {
2325
2257
  console.warn("no capability.toml found in", capabilityDir);
2326
2258
  return false;
2327
2259
  }
2328
2260
  try {
2329
- const content = await readFile10(tomlPath, "utf-8");
2261
+ const content = await readFile9(tomlPath, "utf-8");
2330
2262
  const parsed = parseToml2(content);
2331
2263
  const capability = parsed["capability"];
2332
2264
  const metadata = capability?.["metadata"];
@@ -2337,7 +2269,7 @@ async function isGeneratedMcpCapability(capabilityDir) {
2337
2269
  }
2338
2270
  async function cleanupStaleMcpCapabilities(currentMcpIds) {
2339
2271
  const capabilitiesDir = join8(OMNI_LOCAL, "capabilities");
2340
- if (!existsSync12(capabilitiesDir)) {
2272
+ if (!existsSync11(capabilitiesDir)) {
2341
2273
  return;
2342
2274
  }
2343
2275
  const entries = await readdir(capabilitiesDir, { withFileTypes: true });
@@ -2387,12 +2319,14 @@ async function fetchAllCapabilitySources(config, options) {
2387
2319
  version: result.version,
2388
2320
  updated_at: new Date().toISOString()
2389
2321
  };
2390
- const gitConfig = parseSourceConfig(source);
2391
2322
  if (result.commit) {
2392
2323
  lockEntry.commit = result.commit;
2393
2324
  }
2394
- if (gitConfig.ref) {
2395
- lockEntry.ref = gitConfig.ref;
2325
+ if (!isFileSourceConfig(source)) {
2326
+ const gitConfig = parseSourceConfig(source);
2327
+ if (gitConfig.ref) {
2328
+ lockEntry.ref = gitConfig.ref;
2329
+ }
2396
2330
  }
2397
2331
  const existing = lockFile.capabilities[id];
2398
2332
  const hasChanged = !existing || existing.commit !== result.commit;
@@ -2432,8 +2366,18 @@ async function checkForUpdates(config) {
2432
2366
  const sourceConfig = parseSourceConfig(source);
2433
2367
  const targetPath = getSourceCapabilityPath(id);
2434
2368
  const existing = lockFile.capabilities[id];
2369
+ if (isFileSourceConfig(source) || isFileSource(sourceConfig.source)) {
2370
+ updates.push({
2371
+ id,
2372
+ source: sourceConfig.source,
2373
+ currentVersion: existing?.version || "local",
2374
+ latestVersion: "local",
2375
+ hasUpdate: false
2376
+ });
2377
+ continue;
2378
+ }
2435
2379
  const gitConfig = sourceConfig;
2436
- if (!existsSync12(join8(targetPath, ".git"))) {
2380
+ if (!existsSync11(join8(targetPath, ".git"))) {
2437
2381
  updates.push({
2438
2382
  id,
2439
2383
  source: gitConfig.source,
@@ -2469,15 +2413,15 @@ async function checkForUpdates(config) {
2469
2413
  return updates;
2470
2414
  }
2471
2415
  // src/config/provider.ts
2472
- import { existsSync as existsSync13 } from "node:fs";
2473
- import { readFile as readFile11, writeFile as writeFile5 } from "node:fs/promises";
2416
+ import { existsSync as existsSync12 } from "node:fs";
2417
+ import { readFile as readFile10, writeFile as writeFile4 } from "node:fs/promises";
2474
2418
  import { parse as parse2 } from "smol-toml";
2475
2419
  var PROVIDER_CONFIG_PATH = ".omni/provider.toml";
2476
2420
  async function loadProviderConfig() {
2477
- if (!existsSync13(PROVIDER_CONFIG_PATH)) {
2421
+ if (!existsSync12(PROVIDER_CONFIG_PATH)) {
2478
2422
  return { provider: "claude" };
2479
2423
  }
2480
- const content = await readFile11(PROVIDER_CONFIG_PATH, "utf-8");
2424
+ const content = await readFile10(PROVIDER_CONFIG_PATH, "utf-8");
2481
2425
  const parsed = parse2(content);
2482
2426
  return parsed;
2483
2427
  }
@@ -2504,7 +2448,7 @@ async function writeProviderConfig(config) {
2504
2448
  lines.push("# Default: Claude");
2505
2449
  lines.push('provider = "claude"');
2506
2450
  }
2507
- await writeFile5(PROVIDER_CONFIG_PATH, `${lines.join(`
2451
+ await writeFile4(PROVIDER_CONFIG_PATH, `${lines.join(`
2508
2452
  `)}
2509
2453
  `, "utf-8");
2510
2454
  }
@@ -2519,17 +2463,17 @@ function parseProviderFlag(flag) {
2519
2463
  throw new Error(`Invalid provider: ${flag}. Must be 'claude', 'codex', or 'both'.`);
2520
2464
  }
2521
2465
  // src/config/toml-patcher.ts
2522
- import { existsSync as existsSync14 } from "node:fs";
2523
- import { readFile as readFile12, writeFile as writeFile6 } from "node:fs/promises";
2466
+ import { existsSync as existsSync13 } from "node:fs";
2467
+ import { readFile as readFile11, writeFile as writeFile5 } from "node:fs/promises";
2524
2468
  var CONFIG_PATH2 = "omni.toml";
2525
2469
  async function readConfigFile() {
2526
- if (!existsSync14(CONFIG_PATH2)) {
2470
+ if (!existsSync13(CONFIG_PATH2)) {
2527
2471
  return "";
2528
2472
  }
2529
- return readFile12(CONFIG_PATH2, "utf-8");
2473
+ return readFile11(CONFIG_PATH2, "utf-8");
2530
2474
  }
2531
2475
  async function writeConfigFile(content) {
2532
- await writeFile6(CONFIG_PATH2, content, "utf-8");
2476
+ await writeFile5(CONFIG_PATH2, content, "utf-8");
2533
2477
  }
2534
2478
  function findSection(lines, sectionPattern) {
2535
2479
  return lines.findIndex((line) => sectionPattern.test(line.trim()));
@@ -2550,7 +2494,7 @@ function formatCapabilitySource(name, source) {
2550
2494
  if (typeof source === "string") {
2551
2495
  return `${name} = "${source}"`;
2552
2496
  }
2553
- if (source.path) {
2497
+ if ("path" in source && source.path) {
2554
2498
  return `${name} = { source = "${source.source}", path = "${source.path}" }`;
2555
2499
  }
2556
2500
  return `${name} = "${source.source}"`;
@@ -2616,10 +2560,8 @@ function formatMcpConfig(name, config) {
2616
2560
  lines.push(`url = "${config.url}"`);
2617
2561
  }
2618
2562
  if (config.env && Object.keys(config.env).length > 0) {
2619
- lines.push(`[mcps.${name}.env]`);
2620
- for (const [key, value] of Object.entries(config.env)) {
2621
- lines.push(`${key} = "${value}"`);
2622
- }
2563
+ const entries = Object.entries(config.env).map(([key, value]) => `${key} = "${value}"`);
2564
+ lines.push(`env = { ${entries.join(", ")} }`);
2623
2565
  }
2624
2566
  if (config.headers && Object.keys(config.headers).length > 0) {
2625
2567
  lines.push(`[mcps.${name}.headers]`);
@@ -2725,15 +2667,15 @@ function escapeRegExp(str) {
2725
2667
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2726
2668
  }
2727
2669
  // src/mcp-json/manager.ts
2728
- import { existsSync as existsSync15 } from "node:fs";
2729
- import { readFile as readFile13, writeFile as writeFile7 } from "node:fs/promises";
2670
+ import { existsSync as existsSync14 } from "node:fs";
2671
+ import { readFile as readFile12, writeFile as writeFile6 } from "node:fs/promises";
2730
2672
  var MCP_JSON_PATH = ".mcp.json";
2731
2673
  async function readMcpJson() {
2732
- if (!existsSync15(MCP_JSON_PATH)) {
2674
+ if (!existsSync14(MCP_JSON_PATH)) {
2733
2675
  return { mcpServers: {} };
2734
2676
  }
2735
2677
  try {
2736
- const content = await readFile13(MCP_JSON_PATH, "utf-8");
2678
+ const content = await readFile12(MCP_JSON_PATH, "utf-8");
2737
2679
  const parsed = JSON.parse(content);
2738
2680
  return {
2739
2681
  mcpServers: parsed.mcpServers || {}
@@ -2743,7 +2685,7 @@ async function readMcpJson() {
2743
2685
  }
2744
2686
  }
2745
2687
  async function writeMcpJson(config2) {
2746
- await writeFile7(MCP_JSON_PATH, `${JSON.stringify(config2, null, 2)}
2688
+ await writeFile6(MCP_JSON_PATH, `${JSON.stringify(config2, null, 2)}
2747
2689
  `, "utf-8");
2748
2690
  }
2749
2691
  function buildMcpServerConfig(mcp) {
@@ -2812,24 +2754,24 @@ async function syncMcpJson(capabilities2, previousManifest, options = {}) {
2812
2754
  }
2813
2755
  }
2814
2756
  // src/state/manifest.ts
2815
- import { existsSync as existsSync16, mkdirSync as mkdirSync2, rmSync } from "node:fs";
2816
- import { readFile as readFile14, writeFile as writeFile8 } from "node:fs/promises";
2757
+ import { existsSync as existsSync15, mkdirSync as mkdirSync2, rmSync } from "node:fs";
2758
+ import { readFile as readFile13, writeFile as writeFile7 } from "node:fs/promises";
2817
2759
  var MANIFEST_PATH = ".omni/state/manifest.json";
2818
2760
  var CURRENT_VERSION = 1;
2819
2761
  async function loadManifest() {
2820
- if (!existsSync16(MANIFEST_PATH)) {
2762
+ if (!existsSync15(MANIFEST_PATH)) {
2821
2763
  return {
2822
2764
  version: CURRENT_VERSION,
2823
2765
  syncedAt: new Date().toISOString(),
2824
2766
  capabilities: {}
2825
2767
  };
2826
2768
  }
2827
- const content = await readFile14(MANIFEST_PATH, "utf-8");
2769
+ const content = await readFile13(MANIFEST_PATH, "utf-8");
2828
2770
  return JSON.parse(content);
2829
2771
  }
2830
2772
  async function saveManifest(manifest) {
2831
2773
  mkdirSync2(".omni/state", { recursive: true });
2832
- await writeFile8(MANIFEST_PATH, `${JSON.stringify(manifest, null, 2)}
2774
+ await writeFile7(MANIFEST_PATH, `${JSON.stringify(manifest, null, 2)}
2833
2775
  `, "utf-8");
2834
2776
  }
2835
2777
  function buildManifestFromCapabilities(capabilities2) {
@@ -2864,14 +2806,14 @@ async function cleanupStaleResources(previousManifest, currentCapabilityIds) {
2864
2806
  }
2865
2807
  for (const skillName of resources.skills) {
2866
2808
  const skillDir = `.claude/skills/${skillName}`;
2867
- if (existsSync16(skillDir)) {
2809
+ if (existsSync15(skillDir)) {
2868
2810
  rmSync(skillDir, { recursive: true });
2869
2811
  result.deletedSkills.push(skillName);
2870
2812
  }
2871
2813
  }
2872
2814
  for (const ruleName of resources.rules) {
2873
2815
  const rulePath = `.cursor/rules/omnidev-${ruleName}.mdc`;
2874
- if (existsSync16(rulePath)) {
2816
+ if (existsSync15(rulePath)) {
2875
2817
  rmSync(rulePath);
2876
2818
  result.deletedRules.push(ruleName);
2877
2819
  }
@@ -2880,17 +2822,17 @@ async function cleanupStaleResources(previousManifest, currentCapabilityIds) {
2880
2822
  return result;
2881
2823
  }
2882
2824
  // src/state/providers.ts
2883
- import { existsSync as existsSync17, mkdirSync as mkdirSync3 } from "node:fs";
2884
- import { readFile as readFile15, writeFile as writeFile9 } from "node:fs/promises";
2825
+ import { existsSync as existsSync16, mkdirSync as mkdirSync3 } from "node:fs";
2826
+ import { readFile as readFile14, writeFile as writeFile8 } from "node:fs/promises";
2885
2827
  var STATE_DIR2 = ".omni/state";
2886
2828
  var PROVIDERS_PATH = `${STATE_DIR2}/providers.json`;
2887
2829
  var DEFAULT_PROVIDERS = ["claude-code"];
2888
2830
  async function readEnabledProviders() {
2889
- if (!existsSync17(PROVIDERS_PATH)) {
2831
+ if (!existsSync16(PROVIDERS_PATH)) {
2890
2832
  return DEFAULT_PROVIDERS;
2891
2833
  }
2892
2834
  try {
2893
- const content = await readFile15(PROVIDERS_PATH, "utf-8");
2835
+ const content = await readFile14(PROVIDERS_PATH, "utf-8");
2894
2836
  const state = JSON.parse(content);
2895
2837
  return state.enabled.length > 0 ? state.enabled : DEFAULT_PROVIDERS;
2896
2838
  } catch {
@@ -2900,7 +2842,7 @@ async function readEnabledProviders() {
2900
2842
  async function writeEnabledProviders(providers) {
2901
2843
  mkdirSync3(STATE_DIR2, { recursive: true });
2902
2844
  const state = { enabled: providers };
2903
- await writeFile9(PROVIDERS_PATH, `${JSON.stringify(state, null, 2)}
2845
+ await writeFile8(PROVIDERS_PATH, `${JSON.stringify(state, null, 2)}
2904
2846
  `, "utf-8");
2905
2847
  }
2906
2848
  async function enableProvider(providerId) {
@@ -2922,10 +2864,10 @@ async function isProviderEnabled(providerId) {
2922
2864
  import { spawn as spawn2 } from "node:child_process";
2923
2865
  import { mkdirSync as mkdirSync4 } from "node:fs";
2924
2866
  async function installCapabilityDependencies(silent) {
2925
- const { existsSync: existsSync18, readdirSync: readdirSync7 } = await import("node:fs");
2867
+ const { existsSync: existsSync17, readdirSync: readdirSync7 } = await import("node:fs");
2926
2868
  const { join: join9 } = await import("node:path");
2927
2869
  const capabilitiesDir = ".omni/capabilities";
2928
- if (!existsSync18(capabilitiesDir)) {
2870
+ if (!existsSync17(capabilitiesDir)) {
2929
2871
  return;
2930
2872
  }
2931
2873
  const entries = readdirSync7(capabilitiesDir, { withFileTypes: true });
@@ -2947,14 +2889,14 @@ async function installCapabilityDependencies(silent) {
2947
2889
  }
2948
2890
  const capabilityPath = join9(capabilitiesDir, entry.name);
2949
2891
  const packageJsonPath = join9(capabilityPath, "package.json");
2950
- if (!existsSync18(packageJsonPath)) {
2892
+ if (!existsSync17(packageJsonPath)) {
2951
2893
  continue;
2952
2894
  }
2953
2895
  if (!silent) {
2954
2896
  console.log(`Installing dependencies for ${capabilityPath}...`);
2955
2897
  }
2956
2898
  await new Promise((resolve2, reject) => {
2957
- const useNpmCi = hasNpm && existsSync18(join9(capabilityPath, "package-lock.json"));
2899
+ const useNpmCi = hasNpm && existsSync17(join9(capabilityPath, "package-lock.json"));
2958
2900
  const cmd = hasBun ? "bun" : "npm";
2959
2901
  const args = hasBun ? ["install"] : useNpmCi ? ["ci"] : ["install"];
2960
2902
  const proc = spawn2(cmd, args, {
@@ -2995,7 +2937,6 @@ async function buildSyncBundle(options) {
2995
2937
  docs,
2996
2938
  commands,
2997
2939
  subagents,
2998
- instructionsPath: ".omni/instructions.md",
2999
2940
  instructionsContent
3000
2941
  };
3001
2942
  if (hasAnyHooks(mergedHooks)) {
@@ -3044,7 +2985,6 @@ async function syncAgentConfiguration(options) {
3044
2985
  }
3045
2986
  }
3046
2987
  mkdirSync4(".omni", { recursive: true });
3047
- await writeRules(bundle.rules, bundle.docs);
3048
2988
  await syncMcpJson(capabilities2, previousManifest, { silent });
3049
2989
  const newManifest = buildManifestFromCapabilities(capabilities2);
3050
2990
  await saveManifest(newManifest);
@@ -3067,7 +3007,7 @@ async function syncAgentConfiguration(options) {
3067
3007
  }
3068
3008
  if (!silent) {
3069
3009
  console.log("✓ Synced:");
3070
- console.log(` - .omni/instructions.md (${bundle.docs.length} docs, ${bundle.rules.length} rules)`);
3010
+ console.log(` - ${bundle.docs.length} docs, ${bundle.rules.length} rules`);
3071
3011
  if (adapters.length > 0) {
3072
3012
  console.log(` - Provider adapters: ${adapters.map((a) => a.displayName).join(", ")}`);
3073
3013
  }
@@ -3122,56 +3062,150 @@ function generateAgentsTemplate() {
3122
3062
 
3123
3063
  ## OmniDev
3124
3064
 
3125
- @import .omni/instructions.md
3065
+ <!-- This section is populated during sync with capability rules and docs -->
3126
3066
  `;
3127
3067
  }
3128
- // src/templates/claude.ts
3129
- function generateClaudeTemplate() {
3130
- return `# Project Instructions
3068
+ // src/templates/capability.ts
3069
+ function generateCapabilityToml2(options) {
3070
+ const description = options.description || "TODO: Add a description for your capability";
3071
+ return `[capability]
3072
+ id = "${options.id}"
3073
+ name = "${options.name}"
3074
+ version = "0.1.0"
3075
+ description = "${description}"
3131
3076
 
3132
- <!-- Add your project-specific instructions here -->
3077
+ # Optional author information
3078
+ # [capability.author]
3079
+ # name = "Your Name"
3080
+ # email = "you@example.com"
3133
3081
 
3134
- ## OmniDev
3082
+ # Optional metadata
3083
+ # [capability.metadata]
3084
+ # repository = "https://github.com/user/repo"
3085
+ # license = "MIT"
3086
+ `;
3087
+ }
3088
+ function generateSkillTemplate(skillName) {
3089
+ return `---
3090
+ name: ${skillName}
3091
+ description: TODO: Add a description for this skill
3092
+ ---
3093
+
3094
+ ## What I do
3095
+
3096
+ <!-- Describe what this skill helps the AI agent accomplish -->
3097
+ - TODO: List the main capabilities of this skill
3098
+
3099
+ ## When to use me
3100
+
3101
+ <!-- Describe scenarios when this skill should be invoked -->
3102
+ Use this skill when you need to:
3103
+ - TODO: Add trigger conditions
3135
3104
 
3136
- @import .omni/instructions.md
3105
+ ## Implementation
3106
+
3107
+ <!-- Add detailed instructions for the AI agent -->
3108
+ ### Steps
3109
+
3110
+ 1. TODO: Add implementation steps
3111
+ 2. Validate inputs and outputs
3112
+ 3. Report results to the user
3113
+
3114
+ ## Examples
3115
+
3116
+ <!-- Optional: Add examples of how this skill should be used -->
3117
+ \`\`\`
3118
+ TODO: Add example usage
3119
+ \`\`\`
3137
3120
  `;
3138
3121
  }
3139
- function generateInstructionsTemplate() {
3140
- return `# OmniDev Instructions
3122
+ function generateRuleTemplate(ruleName) {
3123
+ return `# ${formatDisplayName(ruleName)}
3141
3124
 
3142
- ## Project Description
3143
- <!-- TODO: Add 2-3 sentences describing your project -->
3144
- [Describe what this project does and its main purpose]
3125
+ <!-- Rules are guidelines that the AI agent should follow when working in this project -->
3145
3126
 
3146
- ## How OmniDev Works
3127
+ ## Overview
3147
3128
 
3148
- OmniDev manages capability content for your project. Capabilities can provide:
3129
+ TODO: Describe what this rule enforces or guides.
3149
3130
 
3150
- - Skills (for agent workflows)
3151
- - Rules (for guardrails and conventions)
3152
- - Docs (reference material)
3153
- - Commands and subagents (optional)
3131
+ ## Guidelines
3154
3132
 
3155
- Enable capabilities with:
3133
+ - TODO: Add specific guidelines the AI should follow
3134
+ - Be specific and actionable
3135
+ - Include examples where helpful
3136
+
3137
+ ## Examples
3138
+
3139
+ ### Good
3156
3140
 
3157
3141
  \`\`\`
3158
- omnidev capability enable <capability-id>
3142
+ TODO: Add example of correct behavior
3159
3143
  \`\`\`
3160
3144
 
3161
- OmniDev will automatically sync enabled capabilities into your workspace. If you want to force a refresh:
3145
+ ### Bad
3162
3146
 
3163
3147
  \`\`\`
3164
- omnidev sync
3148
+ TODO: Add example of incorrect behavior
3165
3149
  \`\`\`
3150
+ `;
3151
+ }
3152
+ function generateHooksTemplate() {
3153
+ return `# Hook configuration for this capability
3154
+ # See: https://omnidev.dev/docs/advanced/hooks
3155
+
3156
+ # Example: Validate bash commands before execution
3157
+ # [[PreToolUse]]
3158
+ # matcher = "Bash"
3159
+ # [[PreToolUse.hooks]]
3160
+ # type = "command"
3161
+ # command = "\${OMNIDEV_CAPABILITY_ROOT}/hooks/validate-bash.sh"
3162
+ # timeout = 30
3163
+
3164
+ # Example: Run linter after file edits
3165
+ # [[PostToolUse]]
3166
+ # matcher = "Write|Edit"
3167
+ # [[PostToolUse.hooks]]
3168
+ # type = "command"
3169
+ # command = "\${OMNIDEV_CAPABILITY_ROOT}/hooks/run-linter.sh"
3170
+
3171
+ # Example: Load context at session start
3172
+ # [[SessionStart]]
3173
+ # matcher = "startup|resume"
3174
+ # [[SessionStart.hooks]]
3175
+ # type = "command"
3176
+ # command = "\${OMNIDEV_CAPABILITY_ROOT}/hooks/load-context.sh"
3177
+ `;
3178
+ }
3179
+ function generateHookScript() {
3180
+ return `#!/bin/bash
3181
+ # Sample hook script
3182
+ # This script receives JSON input via stdin
3166
3183
 
3167
- <!-- BEGIN OMNIDEV GENERATED CONTENT - DO NOT EDIT BELOW THIS LINE -->
3168
- <!-- This section is automatically updated by 'omnidev agents sync' -->
3184
+ # Read JSON input from stdin
3185
+ INPUT=$(cat)
3169
3186
 
3170
- ## Capabilities
3187
+ # Example: Extract tool information
3188
+ # TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
3189
+ # COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
3171
3190
 
3172
- No capabilities enabled yet. Run \`omnidev capability enable <name>\` to enable capabilities.
3191
+ # Add your validation logic here
3192
+ # Exit 0 to allow, exit 2 to block
3173
3193
 
3174
- <!-- END OMNIDEV GENERATED CONTENT -->
3194
+ exit 0
3195
+ `;
3196
+ }
3197
+ function formatDisplayName(kebabCase) {
3198
+ return kebabCase.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
3199
+ }
3200
+ // src/templates/claude.ts
3201
+ function generateClaudeTemplate() {
3202
+ return `# Project Instructions
3203
+
3204
+ <!-- Add your project-specific instructions here -->
3205
+
3206
+ ## OmniDev
3207
+
3208
+ <!-- This section is populated during sync with capability rules and docs -->
3175
3209
  `;
3176
3210
  }
3177
3211
  // src/templates/omni.ts
@@ -3195,14 +3229,6 @@ function generateOmniMdTemplate() {
3195
3229
  <!-- Describe your project's architecture and key components -->
3196
3230
  `;
3197
3231
  }
3198
- // src/types/index.ts
3199
- function getActiveProviders(config2) {
3200
- if (config2.providers)
3201
- return config2.providers;
3202
- if (config2.provider)
3203
- return [config2.provider];
3204
- return ["claude"];
3205
- }
3206
3232
  // src/debug.ts
3207
3233
  function debug(message, data) {
3208
3234
  if (process.env["OMNIDEV_DEBUG"] !== "1") {
@@ -3224,7 +3250,6 @@ function getVersion() {
3224
3250
  return version;
3225
3251
  }
3226
3252
  export {
3227
- writeRules,
3228
3253
  writeProviderConfig,
3229
3254
  writeMcpJson,
3230
3255
  writeEnabledProviders,
@@ -3233,7 +3258,6 @@ export {
3233
3258
  version,
3234
3259
  validateHooksConfig,
3235
3260
  validateHook,
3236
- validateEnv,
3237
3261
  transformToOmnidev,
3238
3262
  transformToClaude,
3239
3263
  transformHooksConfig,
@@ -3247,6 +3271,7 @@ export {
3247
3271
  resolveEnabledCapabilities,
3248
3272
  readMcpJson,
3249
3273
  readEnabledProviders,
3274
+ readCapabilityIdFromPath,
3250
3275
  readActiveProfileState,
3251
3276
  patchAddToProfile,
3252
3277
  patchAddMcp,
@@ -3254,6 +3279,7 @@ export {
3254
3279
  parseSourceConfig,
3255
3280
  parseProviderFlag,
3256
3281
  parseOmniConfig,
3282
+ parseFileSourcePath,
3257
3283
  parseCapabilityConfig,
3258
3284
  mergeHooksConfigs,
3259
3285
  mergeAndDeduplicateHooks,
@@ -3265,7 +3291,6 @@ export {
3265
3291
  loadManifest,
3266
3292
  loadLockFile,
3267
3293
  loadHooksFromCapability,
3268
- loadEnvironment,
3269
3294
  loadDocs,
3270
3295
  loadConfig,
3271
3296
  loadCommands,
@@ -3274,7 +3299,6 @@ export {
3274
3299
  loadCapability,
3275
3300
  loadBaseConfig,
3276
3301
  isValidMatcherPattern,
3277
- isSecretEnvVar,
3278
3302
  isProviderEnabled,
3279
3303
  isPromptHookEvent,
3280
3304
  isMatcherEvent,
@@ -3282,6 +3306,9 @@ export {
3282
3306
  isHookPrompt,
3283
3307
  isHookEvent,
3284
3308
  isHookCommand,
3309
+ isGitSource,
3310
+ isFileSourceConfig,
3311
+ isFileSource,
3285
3312
  installCapabilityDependencies,
3286
3313
  hasHooks,
3287
3314
  hasAnyHooks,
@@ -3294,9 +3321,13 @@ export {
3294
3321
  getEnabledCapabilities,
3295
3322
  getActiveProviders,
3296
3323
  getActiveProfile,
3324
+ generateSkillTemplate,
3325
+ generateRuleTemplate,
3297
3326
  generateOmniMdTemplate,
3298
- generateInstructionsTemplate,
3327
+ generateHooksTemplate,
3328
+ generateHookScript,
3299
3329
  generateClaudeTemplate,
3330
+ generateCapabilityToml2 as generateCapabilityToml,
3300
3331
  generateAgentsTemplate,
3301
3332
  findDuplicateCommands,
3302
3333
  fetchCapabilitySource,