@omnidev-ai/core 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +9 -46
- package/dist/index.js +134 -276
- package/package.json +1 -1
- package/src/capability/loader.ts +2 -14
- package/src/capability/registry.ts +1 -3
- package/src/config/capabilities.ts +3 -3
- package/src/config/config.ts +22 -67
- package/src/config/index.ts +0 -1
- package/src/config/profiles.ts +5 -16
- package/src/config/toml-patcher.ts +3 -5
- package/src/sync.ts +10 -31
- package/src/templates/capability.ts +4 -24
- package/src/templates/claude.ts +0 -4
- package/src/types/index.ts +4 -13
- package/src/capability/AGENTS.md +0 -58
- package/src/config/AGENTS.md +0 -46
- package/src/config/env.ts +0 -97
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
|
|
122
|
-
import { readFile as
|
|
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
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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
|
|
858
|
-
import { readFile as
|
|
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 (!
|
|
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
|
|
822
|
+
const content = await readFile3(rulePath, "utf-8");
|
|
871
823
|
rules.push({
|
|
872
824
|
name: basename2(entry.name, ".md"),
|
|
873
825
|
content: content.trim(),
|
|
@@ -879,12 +831,12 @@ async function loadRules(capabilityPath, capabilityId) {
|
|
|
879
831
|
}
|
|
880
832
|
|
|
881
833
|
// src/capability/skills.ts
|
|
882
|
-
import { existsSync as
|
|
883
|
-
import { readFile as
|
|
834
|
+
import { existsSync as existsSync6, readdirSync as readdirSync4 } from "node:fs";
|
|
835
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
884
836
|
import { join as join5 } from "node:path";
|
|
885
837
|
async function loadSkills(capabilityPath, capabilityId) {
|
|
886
838
|
const skillsDir = join5(capabilityPath, "skills");
|
|
887
|
-
if (!
|
|
839
|
+
if (!existsSync6(skillsDir)) {
|
|
888
840
|
return [];
|
|
889
841
|
}
|
|
890
842
|
const skills = [];
|
|
@@ -892,7 +844,7 @@ async function loadSkills(capabilityPath, capabilityId) {
|
|
|
892
844
|
for (const entry of entries) {
|
|
893
845
|
if (entry.isDirectory()) {
|
|
894
846
|
const skillPath = join5(skillsDir, entry.name, "SKILL.md");
|
|
895
|
-
if (
|
|
847
|
+
if (existsSync6(skillPath)) {
|
|
896
848
|
const skill = await parseSkillFile(skillPath, capabilityId);
|
|
897
849
|
skills.push(skill);
|
|
898
850
|
}
|
|
@@ -901,7 +853,7 @@ async function loadSkills(capabilityPath, capabilityId) {
|
|
|
901
853
|
return skills;
|
|
902
854
|
}
|
|
903
855
|
async function parseSkillFile(filePath, capabilityId) {
|
|
904
|
-
const content = await
|
|
856
|
+
const content = await readFile4(filePath, "utf-8");
|
|
905
857
|
const parsed = parseFrontmatterWithMarkdown(content);
|
|
906
858
|
if (!parsed) {
|
|
907
859
|
throw new Error(`Invalid SKILL.md format at ${filePath}: missing YAML frontmatter`);
|
|
@@ -920,12 +872,12 @@ async function parseSkillFile(filePath, capabilityId) {
|
|
|
920
872
|
}
|
|
921
873
|
|
|
922
874
|
// src/capability/subagents.ts
|
|
923
|
-
import { existsSync as
|
|
924
|
-
import { readFile as
|
|
875
|
+
import { existsSync as existsSync7, readdirSync as readdirSync5 } from "node:fs";
|
|
876
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
925
877
|
import { join as join6 } from "node:path";
|
|
926
878
|
async function loadSubagents(capabilityPath, capabilityId) {
|
|
927
879
|
const subagentsDir = join6(capabilityPath, "subagents");
|
|
928
|
-
if (!
|
|
880
|
+
if (!existsSync7(subagentsDir)) {
|
|
929
881
|
return [];
|
|
930
882
|
}
|
|
931
883
|
const subagents = [];
|
|
@@ -933,7 +885,7 @@ async function loadSubagents(capabilityPath, capabilityId) {
|
|
|
933
885
|
for (const entry of entries) {
|
|
934
886
|
if (entry.isDirectory()) {
|
|
935
887
|
const subagentPath = join6(subagentsDir, entry.name, "SUBAGENT.md");
|
|
936
|
-
if (
|
|
888
|
+
if (existsSync7(subagentPath)) {
|
|
937
889
|
const subagent = await parseSubagentFile(subagentPath, capabilityId);
|
|
938
890
|
subagents.push(subagent);
|
|
939
891
|
}
|
|
@@ -942,7 +894,7 @@ async function loadSubagents(capabilityPath, capabilityId) {
|
|
|
942
894
|
return subagents;
|
|
943
895
|
}
|
|
944
896
|
async function parseSubagentFile(filePath, capabilityId) {
|
|
945
|
-
const content = await
|
|
897
|
+
const content = await readFile5(filePath, "utf-8");
|
|
946
898
|
const parsed = parseFrontmatterWithMarkdown(content);
|
|
947
899
|
if (!parsed) {
|
|
948
900
|
throw new Error(`Invalid SUBAGENT.md format at ${filePath}: missing YAML frontmatter`);
|
|
@@ -986,13 +938,13 @@ function parseCommaSeparatedList(value) {
|
|
|
986
938
|
var CAPABILITIES_DIR = ".omni/capabilities";
|
|
987
939
|
async function discoverCapabilities() {
|
|
988
940
|
const capabilities = [];
|
|
989
|
-
if (
|
|
941
|
+
if (existsSync8(CAPABILITIES_DIR)) {
|
|
990
942
|
const entries = readdirSync6(CAPABILITIES_DIR, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
991
943
|
for (const entry of entries) {
|
|
992
944
|
if (entry.isDirectory()) {
|
|
993
945
|
const entryPath = join7(CAPABILITIES_DIR, entry.name);
|
|
994
946
|
const configPath = join7(entryPath, "capability.toml");
|
|
995
|
-
if (
|
|
947
|
+
if (existsSync8(configPath)) {
|
|
996
948
|
capabilities.push(entryPath);
|
|
997
949
|
}
|
|
998
950
|
}
|
|
@@ -1002,13 +954,13 @@ async function discoverCapabilities() {
|
|
|
1002
954
|
}
|
|
1003
955
|
async function loadCapabilityConfig(capabilityPath) {
|
|
1004
956
|
const configPath = join7(capabilityPath, "capability.toml");
|
|
1005
|
-
const content = await
|
|
957
|
+
const content = await readFile6(configPath, "utf-8");
|
|
1006
958
|
const config = parseCapabilityConfig(content);
|
|
1007
959
|
return config;
|
|
1008
960
|
}
|
|
1009
961
|
async function importCapabilityExports(capabilityPath) {
|
|
1010
962
|
const indexPath = join7(capabilityPath, "index.ts");
|
|
1011
|
-
if (!
|
|
963
|
+
if (!existsSync8(indexPath)) {
|
|
1012
964
|
return {};
|
|
1013
965
|
}
|
|
1014
966
|
try {
|
|
@@ -1028,10 +980,10 @@ If this is a project-specific capability, install dependencies or remove it from
|
|
|
1028
980
|
}
|
|
1029
981
|
async function loadTypeDefinitions(capabilityPath) {
|
|
1030
982
|
const typesPath = join7(capabilityPath, "types.d.ts");
|
|
1031
|
-
if (!
|
|
983
|
+
if (!existsSync8(typesPath)) {
|
|
1032
984
|
return;
|
|
1033
985
|
}
|
|
1034
|
-
return
|
|
986
|
+
return readFile6(typesPath, "utf-8");
|
|
1035
987
|
}
|
|
1036
988
|
function convertSkillExports(skillExports, capabilityId) {
|
|
1037
989
|
return skillExports.map((skillExport) => {
|
|
@@ -1208,12 +1160,9 @@ function convertCommandExports(commandExports, capabilityId) {
|
|
|
1208
1160
|
return result;
|
|
1209
1161
|
});
|
|
1210
1162
|
}
|
|
1211
|
-
async function loadCapability(capabilityPath
|
|
1163
|
+
async function loadCapability(capabilityPath) {
|
|
1212
1164
|
const config = await loadCapabilityConfig(capabilityPath);
|
|
1213
1165
|
const id = config.capability.id;
|
|
1214
|
-
if (config.env) {
|
|
1215
|
-
validateEnv(config.env, env, id);
|
|
1216
|
-
}
|
|
1217
1166
|
const exports = await importCapabilityExports(capabilityPath);
|
|
1218
1167
|
const exportsAny = exports;
|
|
1219
1168
|
const skills = "skills" in exports && Array.isArray(exportsAny.skills) ? convertSkillExports(exportsAny.skills, id) : await loadSkills(capabilityPath, id);
|
|
@@ -1248,13 +1197,12 @@ async function loadCapability(capabilityPath, env) {
|
|
|
1248
1197
|
return result;
|
|
1249
1198
|
}
|
|
1250
1199
|
// src/config/config.ts
|
|
1251
|
-
import { existsSync as
|
|
1252
|
-
import { readFile as
|
|
1200
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
1201
|
+
import { readFile as readFile7, writeFile } from "node:fs/promises";
|
|
1253
1202
|
var CONFIG_PATH = "omni.toml";
|
|
1254
1203
|
var LOCAL_CONFIG = "omni.local.toml";
|
|
1255
1204
|
function mergeConfigs(base, override) {
|
|
1256
1205
|
const merged = { ...base, ...override };
|
|
1257
|
-
merged.env = { ...base.env, ...override.env };
|
|
1258
1206
|
merged.profiles = { ...base.profiles };
|
|
1259
1207
|
for (const [name, profile] of Object.entries(override.profiles || {})) {
|
|
1260
1208
|
merged.profiles[name] = {
|
|
@@ -1268,8 +1216,8 @@ function mergeConfigs(base, override) {
|
|
|
1268
1216
|
return merged;
|
|
1269
1217
|
}
|
|
1270
1218
|
async function loadBaseConfig() {
|
|
1271
|
-
if (
|
|
1272
|
-
const content = await
|
|
1219
|
+
if (existsSync9(CONFIG_PATH)) {
|
|
1220
|
+
const content = await readFile7(CONFIG_PATH, "utf-8");
|
|
1273
1221
|
return parseOmniConfig(content);
|
|
1274
1222
|
}
|
|
1275
1223
|
return {};
|
|
@@ -1277,8 +1225,8 @@ async function loadBaseConfig() {
|
|
|
1277
1225
|
async function loadConfig() {
|
|
1278
1226
|
const baseConfig = await loadBaseConfig();
|
|
1279
1227
|
let localConfig = {};
|
|
1280
|
-
if (
|
|
1281
|
-
const content = await
|
|
1228
|
+
if (existsSync9(LOCAL_CONFIG)) {
|
|
1229
|
+
const content = await readFile7(LOCAL_CONFIG, "utf-8");
|
|
1282
1230
|
localConfig = parseOmniConfig(content);
|
|
1283
1231
|
}
|
|
1284
1232
|
return mergeConfigs(baseConfig, localConfig);
|
|
@@ -1289,22 +1237,6 @@ async function writeConfig(config) {
|
|
|
1289
1237
|
}
|
|
1290
1238
|
function generateConfigToml(config) {
|
|
1291
1239
|
const lines = [];
|
|
1292
|
-
lines.push("# =============================================================================");
|
|
1293
|
-
lines.push("# OmniDev Configuration");
|
|
1294
|
-
lines.push("# =============================================================================");
|
|
1295
|
-
lines.push("# This file defines your project's capabilities, profiles, and settings.");
|
|
1296
|
-
lines.push("#");
|
|
1297
|
-
lines.push("# Files:");
|
|
1298
|
-
lines.push("# • omni.toml - Main config (commit to share with team)");
|
|
1299
|
-
lines.push("# • omni.local.toml - Local overrides (add to .gitignore)");
|
|
1300
|
-
lines.push("# • omni.lock.toml - Version lock file (commit for reproducibility)");
|
|
1301
|
-
lines.push("#");
|
|
1302
|
-
lines.push("# Quick start:");
|
|
1303
|
-
lines.push("# 1. Add capability sources to [capabilities.sources]");
|
|
1304
|
-
lines.push("# 2. Reference them in your profiles");
|
|
1305
|
-
lines.push("# 3. Run: omnidev sync");
|
|
1306
|
-
lines.push("# 4. Switch profiles: omnidev profile use <name>");
|
|
1307
|
-
lines.push("");
|
|
1308
1240
|
if (config.providers?.enabled && config.providers.enabled.length > 0) {
|
|
1309
1241
|
lines.push("# AI providers to enable (claude, codex, or both)");
|
|
1310
1242
|
lines.push("[providers]");
|
|
@@ -1312,23 +1244,6 @@ function generateConfigToml(config) {
|
|
|
1312
1244
|
lines.push("");
|
|
1313
1245
|
}
|
|
1314
1246
|
lines.push("# =============================================================================");
|
|
1315
|
-
lines.push("# Environment Variables");
|
|
1316
|
-
lines.push("# =============================================================================");
|
|
1317
|
-
lines.push("# Global environment variables available to all capabilities.");
|
|
1318
|
-
lines.push("# Use ${VAR_NAME} syntax to reference shell environment variables.");
|
|
1319
|
-
lines.push("#");
|
|
1320
|
-
if (config.env && Object.keys(config.env).length > 0) {
|
|
1321
|
-
lines.push("[env]");
|
|
1322
|
-
for (const [key, value] of Object.entries(config.env)) {
|
|
1323
|
-
lines.push(`${key} = "${value}"`);
|
|
1324
|
-
}
|
|
1325
|
-
} else {
|
|
1326
|
-
lines.push("# [env]");
|
|
1327
|
-
lines.push('# DATABASE_URL = "${DATABASE_URL}"');
|
|
1328
|
-
lines.push('# API_KEY = "${MY_API_KEY}"');
|
|
1329
|
-
}
|
|
1330
|
-
lines.push("");
|
|
1331
|
-
lines.push("# =============================================================================");
|
|
1332
1247
|
lines.push("# Capability Sources");
|
|
1333
1248
|
lines.push("# =============================================================================");
|
|
1334
1249
|
lines.push("# Fetch capabilities from Git repositories. On sync, these are");
|
|
@@ -1382,6 +1297,22 @@ function generateConfigToml(config) {
|
|
|
1382
1297
|
}
|
|
1383
1298
|
lines.push("");
|
|
1384
1299
|
lines.push("# =============================================================================");
|
|
1300
|
+
lines.push("# Always Enabled Capabilities");
|
|
1301
|
+
lines.push("# =============================================================================");
|
|
1302
|
+
lines.push("# Capabilities that load in ALL profiles, regardless of profile config.");
|
|
1303
|
+
lines.push("# Useful for essential tools needed everywhere.");
|
|
1304
|
+
lines.push("#");
|
|
1305
|
+
const alwaysEnabled = config.capabilities?.always_enabled;
|
|
1306
|
+
if (alwaysEnabled && alwaysEnabled.length > 0) {
|
|
1307
|
+
const caps = alwaysEnabled.map((c) => `"${c}"`).join(", ");
|
|
1308
|
+
lines.push(`[capabilities]`);
|
|
1309
|
+
lines.push(`always_enabled = [${caps}]`);
|
|
1310
|
+
} else {
|
|
1311
|
+
lines.push("# [capabilities]");
|
|
1312
|
+
lines.push('# always_enabled = ["git-tools", "linting"]');
|
|
1313
|
+
}
|
|
1314
|
+
lines.push("");
|
|
1315
|
+
lines.push("# =============================================================================");
|
|
1385
1316
|
lines.push("# MCP Servers");
|
|
1386
1317
|
lines.push("# =============================================================================");
|
|
1387
1318
|
lines.push("# Define MCP servers that automatically become capabilities.");
|
|
@@ -1408,10 +1339,8 @@ function generateConfigToml(config) {
|
|
|
1408
1339
|
lines.push(`url = "${mcpConfig.url}"`);
|
|
1409
1340
|
}
|
|
1410
1341
|
if (mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
lines.push(`${key} = "${value}"`);
|
|
1414
|
-
}
|
|
1342
|
+
const entries = Object.entries(mcpConfig.env).map(([key, value]) => `${key} = "${value}"`);
|
|
1343
|
+
lines.push(`env = { ${entries.join(", ")} }`);
|
|
1415
1344
|
}
|
|
1416
1345
|
if (mcpConfig.headers && Object.keys(mcpConfig.headers).length > 0) {
|
|
1417
1346
|
lines.push(`[mcps.${name}.headers]`);
|
|
@@ -1431,24 +1360,10 @@ function generateConfigToml(config) {
|
|
|
1431
1360
|
lines.push('# command = "node"');
|
|
1432
1361
|
lines.push('# args = ["./servers/database.js"]');
|
|
1433
1362
|
lines.push('# cwd = "./mcp-servers"');
|
|
1434
|
-
lines.push(
|
|
1435
|
-
lines.push('# DB_URL = "${DATABASE_URL}"');
|
|
1363
|
+
lines.push('# env = { DB_URL = "${DATABASE_URL}" }');
|
|
1436
1364
|
lines.push("");
|
|
1437
1365
|
}
|
|
1438
1366
|
lines.push("# =============================================================================");
|
|
1439
|
-
lines.push("# Always Enabled Capabilities");
|
|
1440
|
-
lines.push("# =============================================================================");
|
|
1441
|
-
lines.push("# Capabilities that load in ALL profiles, regardless of profile config.");
|
|
1442
|
-
lines.push("# Useful for essential tools needed everywhere.");
|
|
1443
|
-
lines.push("#");
|
|
1444
|
-
if (config.always_enabled_capabilities && config.always_enabled_capabilities.length > 0) {
|
|
1445
|
-
const caps = config.always_enabled_capabilities.map((c) => `"${c}"`).join(", ");
|
|
1446
|
-
lines.push(`always_enabled_capabilities = [${caps}]`);
|
|
1447
|
-
} else {
|
|
1448
|
-
lines.push('# always_enabled_capabilities = ["git-tools", "linting"]');
|
|
1449
|
-
}
|
|
1450
|
-
lines.push("");
|
|
1451
|
-
lines.push("# =============================================================================");
|
|
1452
1367
|
lines.push("# Profiles");
|
|
1453
1368
|
lines.push("# =============================================================================");
|
|
1454
1369
|
lines.push("# Define different capability sets for different workflows.");
|
|
@@ -1471,16 +1386,16 @@ function generateConfigToml(config) {
|
|
|
1471
1386
|
}
|
|
1472
1387
|
|
|
1473
1388
|
// src/state/active-profile.ts
|
|
1474
|
-
import { existsSync as
|
|
1475
|
-
import { readFile as
|
|
1389
|
+
import { existsSync as existsSync10, mkdirSync } from "node:fs";
|
|
1390
|
+
import { readFile as readFile8, unlink, writeFile as writeFile2 } from "node:fs/promises";
|
|
1476
1391
|
var STATE_DIR = ".omni/state";
|
|
1477
1392
|
var ACTIVE_PROFILE_PATH = `${STATE_DIR}/active-profile`;
|
|
1478
1393
|
async function readActiveProfileState() {
|
|
1479
|
-
if (!
|
|
1394
|
+
if (!existsSync10(ACTIVE_PROFILE_PATH)) {
|
|
1480
1395
|
return null;
|
|
1481
1396
|
}
|
|
1482
1397
|
try {
|
|
1483
|
-
const content = await
|
|
1398
|
+
const content = await readFile8(ACTIVE_PROFILE_PATH, "utf-8");
|
|
1484
1399
|
const trimmed = content.trim();
|
|
1485
1400
|
return trimmed || null;
|
|
1486
1401
|
} catch {
|
|
@@ -1492,27 +1407,22 @@ async function writeActiveProfileState(profileName) {
|
|
|
1492
1407
|
await writeFile2(ACTIVE_PROFILE_PATH, profileName, "utf-8");
|
|
1493
1408
|
}
|
|
1494
1409
|
async function clearActiveProfileState() {
|
|
1495
|
-
if (
|
|
1410
|
+
if (existsSync10(ACTIVE_PROFILE_PATH)) {
|
|
1496
1411
|
await unlink(ACTIVE_PROFILE_PATH);
|
|
1497
1412
|
}
|
|
1498
1413
|
}
|
|
1499
1414
|
|
|
1500
1415
|
// src/config/profiles.ts
|
|
1501
1416
|
async function getActiveProfile() {
|
|
1502
|
-
|
|
1503
|
-
if (stateProfile) {
|
|
1504
|
-
return stateProfile;
|
|
1505
|
-
}
|
|
1506
|
-
const config = await loadConfig();
|
|
1507
|
-
return config.active_profile ?? null;
|
|
1417
|
+
return await readActiveProfileState();
|
|
1508
1418
|
}
|
|
1509
1419
|
async function setActiveProfile(name) {
|
|
1510
1420
|
await writeActiveProfileState(name);
|
|
1511
1421
|
}
|
|
1512
1422
|
function resolveEnabledCapabilities(config, profileName) {
|
|
1513
|
-
const profile = profileName ? config.profiles?.[profileName] : config.profiles?.[
|
|
1423
|
+
const profile = profileName ? config.profiles?.[profileName] : config.profiles?.["default"];
|
|
1514
1424
|
const profileCapabilities = profile?.capabilities ?? [];
|
|
1515
|
-
const alwaysEnabled = config.
|
|
1425
|
+
const alwaysEnabled = config.capabilities?.always_enabled ?? [];
|
|
1516
1426
|
const groups = config.capabilities?.groups ?? {};
|
|
1517
1427
|
const expandCapabilities = (caps) => {
|
|
1518
1428
|
return caps.flatMap((cap) => {
|
|
@@ -1548,12 +1458,12 @@ async function setProfile(profileName, profileConfig) {
|
|
|
1548
1458
|
// src/config/capabilities.ts
|
|
1549
1459
|
async function getEnabledCapabilities() {
|
|
1550
1460
|
const config = await loadConfig();
|
|
1551
|
-
const activeProfile = await getActiveProfile() ??
|
|
1461
|
+
const activeProfile = await getActiveProfile() ?? "default";
|
|
1552
1462
|
return resolveEnabledCapabilities(config, activeProfile);
|
|
1553
1463
|
}
|
|
1554
1464
|
async function enableCapability(capabilityId) {
|
|
1555
1465
|
const config = await loadBaseConfig();
|
|
1556
|
-
const activeProfile = await getActiveProfile() ??
|
|
1466
|
+
const activeProfile = await getActiveProfile() ?? "default";
|
|
1557
1467
|
if (!config.profiles) {
|
|
1558
1468
|
config.profiles = {};
|
|
1559
1469
|
}
|
|
@@ -1567,7 +1477,7 @@ async function enableCapability(capabilityId) {
|
|
|
1567
1477
|
}
|
|
1568
1478
|
async function disableCapability(capabilityId) {
|
|
1569
1479
|
const config = await loadBaseConfig();
|
|
1570
|
-
const activeProfile = await getActiveProfile() ??
|
|
1480
|
+
const activeProfile = await getActiveProfile() ?? "default";
|
|
1571
1481
|
if (!config.profiles?.[activeProfile]) {
|
|
1572
1482
|
return;
|
|
1573
1483
|
}
|
|
@@ -1666,13 +1576,12 @@ function getEventsWithHooks(config) {
|
|
|
1666
1576
|
|
|
1667
1577
|
// src/capability/registry.ts
|
|
1668
1578
|
async function buildCapabilityRegistry() {
|
|
1669
|
-
const env = await loadEnvironment();
|
|
1670
1579
|
const enabledIds = await getEnabledCapabilities();
|
|
1671
1580
|
const capabilityPaths = await discoverCapabilities();
|
|
1672
1581
|
const capabilities = new Map;
|
|
1673
1582
|
for (const path of capabilityPaths) {
|
|
1674
1583
|
try {
|
|
1675
|
-
const cap = await loadCapability(path
|
|
1584
|
+
const cap = await loadCapability(path);
|
|
1676
1585
|
if (enabledIds.includes(cap.id)) {
|
|
1677
1586
|
capabilities.set(cap.id, cap);
|
|
1678
1587
|
}
|
|
@@ -1703,9 +1612,9 @@ async function buildCapabilityRegistry() {
|
|
|
1703
1612
|
};
|
|
1704
1613
|
}
|
|
1705
1614
|
// src/capability/sources.ts
|
|
1706
|
-
import { existsSync as
|
|
1615
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
1707
1616
|
import { spawn } from "node:child_process";
|
|
1708
|
-
import { cp, mkdir, readdir, readFile as
|
|
1617
|
+
import { cp, mkdir, readdir, readFile as readFile9, rename, rm, stat, writeFile as writeFile3 } from "node:fs/promises";
|
|
1709
1618
|
import { join as join8 } from "node:path";
|
|
1710
1619
|
import { parse as parseToml2 } from "smol-toml";
|
|
1711
1620
|
|
|
@@ -1770,9 +1679,9 @@ function parseFileSourcePath(source) {
|
|
|
1770
1679
|
}
|
|
1771
1680
|
async function readCapabilityIdFromPath(capabilityPath) {
|
|
1772
1681
|
const tomlPath = join8(capabilityPath, "capability.toml");
|
|
1773
|
-
if (
|
|
1682
|
+
if (existsSync11(tomlPath)) {
|
|
1774
1683
|
try {
|
|
1775
|
-
const content = await
|
|
1684
|
+
const content = await readFile9(tomlPath, "utf-8");
|
|
1776
1685
|
const parsed = parseToml2(content);
|
|
1777
1686
|
const capability = parsed["capability"];
|
|
1778
1687
|
if (capability?.["id"] && typeof capability["id"] === "string") {
|
|
@@ -1822,11 +1731,11 @@ function getLockFilePath() {
|
|
|
1822
1731
|
}
|
|
1823
1732
|
async function loadLockFile() {
|
|
1824
1733
|
const lockPath = getLockFilePath();
|
|
1825
|
-
if (!
|
|
1734
|
+
if (!existsSync11(lockPath)) {
|
|
1826
1735
|
return { capabilities: {} };
|
|
1827
1736
|
}
|
|
1828
1737
|
try {
|
|
1829
|
-
const content = await
|
|
1738
|
+
const content = await readFile9(lockPath, "utf-8");
|
|
1830
1739
|
const parsed = parseToml2(content);
|
|
1831
1740
|
const capabilities = parsed["capabilities"];
|
|
1832
1741
|
return {
|
|
@@ -1915,16 +1824,16 @@ async function fetchRepo(repoPath, ref) {
|
|
|
1915
1824
|
return true;
|
|
1916
1825
|
}
|
|
1917
1826
|
function hasCapabilityToml(dirPath) {
|
|
1918
|
-
return
|
|
1827
|
+
return existsSync11(join8(dirPath, "capability.toml"));
|
|
1919
1828
|
}
|
|
1920
1829
|
async function shouldWrapDirectory(dirPath) {
|
|
1921
|
-
if (
|
|
1830
|
+
if (existsSync11(join8(dirPath, ".claude-plugin", "plugin.json"))) {
|
|
1922
1831
|
return true;
|
|
1923
1832
|
}
|
|
1924
1833
|
const allDirs = [...SKILL_DIRS, ...AGENT_DIRS, ...COMMAND_DIRS, ...RULE_DIRS, ...DOC_DIRS];
|
|
1925
1834
|
for (const dirName of allDirs) {
|
|
1926
1835
|
const checkPath = join8(dirPath, dirName);
|
|
1927
|
-
if (
|
|
1836
|
+
if (existsSync11(checkPath)) {
|
|
1928
1837
|
const stats = await stat(checkPath);
|
|
1929
1838
|
if (stats.isDirectory()) {
|
|
1930
1839
|
return true;
|
|
@@ -1936,7 +1845,7 @@ async function shouldWrapDirectory(dirPath) {
|
|
|
1936
1845
|
async function findMatchingDirs(basePath, names) {
|
|
1937
1846
|
for (const name of names) {
|
|
1938
1847
|
const dirPath = join8(basePath, name);
|
|
1939
|
-
if (
|
|
1848
|
+
if (existsSync11(dirPath)) {
|
|
1940
1849
|
const stats = await stat(dirPath);
|
|
1941
1850
|
if (stats.isDirectory()) {
|
|
1942
1851
|
return dirPath;
|
|
@@ -1947,7 +1856,7 @@ async function findMatchingDirs(basePath, names) {
|
|
|
1947
1856
|
}
|
|
1948
1857
|
async function findContentItems(dirPath, filePatterns) {
|
|
1949
1858
|
const items = [];
|
|
1950
|
-
if (!
|
|
1859
|
+
if (!existsSync11(dirPath)) {
|
|
1951
1860
|
return items;
|
|
1952
1861
|
}
|
|
1953
1862
|
const entries = (await readdir(dirPath, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -1955,7 +1864,7 @@ async function findContentItems(dirPath, filePatterns) {
|
|
|
1955
1864
|
const entryPath = join8(dirPath, entry.name);
|
|
1956
1865
|
if (entry.isDirectory()) {
|
|
1957
1866
|
for (const pattern of filePatterns) {
|
|
1958
|
-
if (
|
|
1867
|
+
if (existsSync11(join8(entryPath, pattern))) {
|
|
1959
1868
|
items.push({
|
|
1960
1869
|
name: entry.name,
|
|
1961
1870
|
path: entryPath,
|
|
@@ -1977,11 +1886,11 @@ async function findContentItems(dirPath, filePatterns) {
|
|
|
1977
1886
|
}
|
|
1978
1887
|
async function parsePluginJson(dirPath) {
|
|
1979
1888
|
const pluginJsonPath = join8(dirPath, ".claude-plugin", "plugin.json");
|
|
1980
|
-
if (!
|
|
1889
|
+
if (!existsSync11(pluginJsonPath)) {
|
|
1981
1890
|
return null;
|
|
1982
1891
|
}
|
|
1983
1892
|
try {
|
|
1984
|
-
const content = await
|
|
1893
|
+
const content = await readFile9(pluginJsonPath, "utf-8");
|
|
1985
1894
|
const data = JSON.parse(content);
|
|
1986
1895
|
const result = {
|
|
1987
1896
|
name: data.name,
|
|
@@ -2002,11 +1911,11 @@ async function parsePluginJson(dirPath) {
|
|
|
2002
1911
|
}
|
|
2003
1912
|
async function readReadmeDescription(dirPath) {
|
|
2004
1913
|
const readmePath = join8(dirPath, "README.md");
|
|
2005
|
-
if (!
|
|
1914
|
+
if (!existsSync11(readmePath)) {
|
|
2006
1915
|
return null;
|
|
2007
1916
|
}
|
|
2008
1917
|
try {
|
|
2009
|
-
const content = await
|
|
1918
|
+
const content = await readFile9(readmePath, "utf-8");
|
|
2010
1919
|
const lines = content.split(`
|
|
2011
1920
|
`);
|
|
2012
1921
|
let description = "";
|
|
@@ -2045,7 +1954,7 @@ async function normalizeFolderNames(repoPath) {
|
|
|
2045
1954
|
for (const { from, to } of renameMappings) {
|
|
2046
1955
|
const fromPath = join8(repoPath, from);
|
|
2047
1956
|
const toPath = join8(repoPath, to);
|
|
2048
|
-
if (
|
|
1957
|
+
if (existsSync11(fromPath) && !existsSync11(toPath)) {
|
|
2049
1958
|
try {
|
|
2050
1959
|
const stats = await stat(fromPath);
|
|
2051
1960
|
if (stats.isDirectory()) {
|
|
@@ -2144,7 +2053,7 @@ async function fetchGitCapabilitySource(id, config, options) {
|
|
|
2144
2053
|
let repoPath;
|
|
2145
2054
|
if (config.path) {
|
|
2146
2055
|
const tempPath = join8(OMNI_LOCAL, "_temp", `${id}-repo`);
|
|
2147
|
-
if (
|
|
2056
|
+
if (existsSync11(join8(tempPath, ".git"))) {
|
|
2148
2057
|
if (!options?.silent) {
|
|
2149
2058
|
console.log(` Checking ${id}...`);
|
|
2150
2059
|
}
|
|
@@ -2160,17 +2069,17 @@ async function fetchGitCapabilitySource(id, config, options) {
|
|
|
2160
2069
|
updated = true;
|
|
2161
2070
|
}
|
|
2162
2071
|
const sourcePath = join8(tempPath, config.path);
|
|
2163
|
-
if (!
|
|
2072
|
+
if (!existsSync11(sourcePath)) {
|
|
2164
2073
|
throw new Error(`Path not found in repository: ${config.path}`);
|
|
2165
2074
|
}
|
|
2166
|
-
if (
|
|
2075
|
+
if (existsSync11(targetPath)) {
|
|
2167
2076
|
await rm(targetPath, { recursive: true });
|
|
2168
2077
|
}
|
|
2169
2078
|
await mkdir(join8(targetPath, ".."), { recursive: true });
|
|
2170
2079
|
await cp(sourcePath, targetPath, { recursive: true });
|
|
2171
2080
|
repoPath = targetPath;
|
|
2172
2081
|
} else {
|
|
2173
|
-
if (
|
|
2082
|
+
if (existsSync11(join8(targetPath, ".git"))) {
|
|
2174
2083
|
if (!options?.silent) {
|
|
2175
2084
|
console.log(` Checking ${id}...`);
|
|
2176
2085
|
}
|
|
@@ -2209,9 +2118,9 @@ async function fetchGitCapabilitySource(id, config, options) {
|
|
|
2209
2118
|
}
|
|
2210
2119
|
let version = shortCommit(commit);
|
|
2211
2120
|
const pkgJsonPath = join8(repoPath, "package.json");
|
|
2212
|
-
if (
|
|
2121
|
+
if (existsSync11(pkgJsonPath)) {
|
|
2213
2122
|
try {
|
|
2214
|
-
const pkgJson = JSON.parse(await
|
|
2123
|
+
const pkgJson = JSON.parse(await readFile9(pkgJsonPath, "utf-8"));
|
|
2215
2124
|
if (pkgJson.version) {
|
|
2216
2125
|
version = pkgJson.version;
|
|
2217
2126
|
}
|
|
@@ -2229,29 +2138,29 @@ async function fetchGitCapabilitySource(id, config, options) {
|
|
|
2229
2138
|
async function fetchFileCapabilitySource(id, config, options) {
|
|
2230
2139
|
const sourcePath = parseFileSourcePath(config.source);
|
|
2231
2140
|
const targetPath = getSourceCapabilityPath(id);
|
|
2232
|
-
if (!
|
|
2141
|
+
if (!existsSync11(sourcePath)) {
|
|
2233
2142
|
throw new Error(`File source not found: ${sourcePath}`);
|
|
2234
2143
|
}
|
|
2235
2144
|
const sourceStats = await stat(sourcePath);
|
|
2236
2145
|
if (!sourceStats.isDirectory()) {
|
|
2237
2146
|
throw new Error(`File source must be a directory: ${sourcePath}`);
|
|
2238
2147
|
}
|
|
2239
|
-
if (!
|
|
2148
|
+
if (!existsSync11(join8(sourcePath, "capability.toml"))) {
|
|
2240
2149
|
throw new Error(`No capability.toml found in: ${sourcePath}`);
|
|
2241
2150
|
}
|
|
2242
2151
|
if (!options?.silent) {
|
|
2243
2152
|
console.log(` Copying ${id} from ${sourcePath}...`);
|
|
2244
2153
|
}
|
|
2245
|
-
if (
|
|
2154
|
+
if (existsSync11(targetPath)) {
|
|
2246
2155
|
await rm(targetPath, { recursive: true });
|
|
2247
2156
|
}
|
|
2248
2157
|
await mkdir(join8(targetPath, ".."), { recursive: true });
|
|
2249
2158
|
await cp(sourcePath, targetPath, { recursive: true });
|
|
2250
2159
|
let version = "local";
|
|
2251
2160
|
const capTomlPath = join8(targetPath, "capability.toml");
|
|
2252
|
-
if (
|
|
2161
|
+
if (existsSync11(capTomlPath)) {
|
|
2253
2162
|
try {
|
|
2254
|
-
const content = await
|
|
2163
|
+
const content = await readFile9(capTomlPath, "utf-8");
|
|
2255
2164
|
const parsed = parseToml2(content);
|
|
2256
2165
|
const capability = parsed["capability"];
|
|
2257
2166
|
if (capability?.["version"] && typeof capability["version"] === "string") {
|
|
@@ -2342,12 +2251,12 @@ async function generateMcpCapabilityToml(id, mcpConfig, targetPath) {
|
|
|
2342
2251
|
}
|
|
2343
2252
|
async function isGeneratedMcpCapability(capabilityDir) {
|
|
2344
2253
|
const tomlPath = join8(capabilityDir, "capability.toml");
|
|
2345
|
-
if (!
|
|
2254
|
+
if (!existsSync11(tomlPath)) {
|
|
2346
2255
|
console.warn("no capability.toml found in", capabilityDir);
|
|
2347
2256
|
return false;
|
|
2348
2257
|
}
|
|
2349
2258
|
try {
|
|
2350
|
-
const content = await
|
|
2259
|
+
const content = await readFile9(tomlPath, "utf-8");
|
|
2351
2260
|
const parsed = parseToml2(content);
|
|
2352
2261
|
const capability = parsed["capability"];
|
|
2353
2262
|
const metadata = capability?.["metadata"];
|
|
@@ -2358,7 +2267,7 @@ async function isGeneratedMcpCapability(capabilityDir) {
|
|
|
2358
2267
|
}
|
|
2359
2268
|
async function cleanupStaleMcpCapabilities(currentMcpIds) {
|
|
2360
2269
|
const capabilitiesDir = join8(OMNI_LOCAL, "capabilities");
|
|
2361
|
-
if (!
|
|
2270
|
+
if (!existsSync11(capabilitiesDir)) {
|
|
2362
2271
|
return;
|
|
2363
2272
|
}
|
|
2364
2273
|
const entries = await readdir(capabilitiesDir, { withFileTypes: true });
|
|
@@ -2466,7 +2375,7 @@ async function checkForUpdates(config) {
|
|
|
2466
2375
|
continue;
|
|
2467
2376
|
}
|
|
2468
2377
|
const gitConfig = sourceConfig;
|
|
2469
|
-
if (!
|
|
2378
|
+
if (!existsSync11(join8(targetPath, ".git"))) {
|
|
2470
2379
|
updates.push({
|
|
2471
2380
|
id,
|
|
2472
2381
|
source: gitConfig.source,
|
|
@@ -2502,15 +2411,15 @@ async function checkForUpdates(config) {
|
|
|
2502
2411
|
return updates;
|
|
2503
2412
|
}
|
|
2504
2413
|
// src/config/provider.ts
|
|
2505
|
-
import { existsSync as
|
|
2506
|
-
import { readFile as
|
|
2414
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
2415
|
+
import { readFile as readFile10, writeFile as writeFile4 } from "node:fs/promises";
|
|
2507
2416
|
import { parse as parse2 } from "smol-toml";
|
|
2508
2417
|
var PROVIDER_CONFIG_PATH = ".omni/provider.toml";
|
|
2509
2418
|
async function loadProviderConfig() {
|
|
2510
|
-
if (!
|
|
2419
|
+
if (!existsSync12(PROVIDER_CONFIG_PATH)) {
|
|
2511
2420
|
return { provider: "claude" };
|
|
2512
2421
|
}
|
|
2513
|
-
const content = await
|
|
2422
|
+
const content = await readFile10(PROVIDER_CONFIG_PATH, "utf-8");
|
|
2514
2423
|
const parsed = parse2(content);
|
|
2515
2424
|
return parsed;
|
|
2516
2425
|
}
|
|
@@ -2552,14 +2461,14 @@ function parseProviderFlag(flag) {
|
|
|
2552
2461
|
throw new Error(`Invalid provider: ${flag}. Must be 'claude', 'codex', or 'both'.`);
|
|
2553
2462
|
}
|
|
2554
2463
|
// src/config/toml-patcher.ts
|
|
2555
|
-
import { existsSync as
|
|
2556
|
-
import { readFile as
|
|
2464
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
2465
|
+
import { readFile as readFile11, writeFile as writeFile5 } from "node:fs/promises";
|
|
2557
2466
|
var CONFIG_PATH2 = "omni.toml";
|
|
2558
2467
|
async function readConfigFile() {
|
|
2559
|
-
if (!
|
|
2468
|
+
if (!existsSync13(CONFIG_PATH2)) {
|
|
2560
2469
|
return "";
|
|
2561
2470
|
}
|
|
2562
|
-
return
|
|
2471
|
+
return readFile11(CONFIG_PATH2, "utf-8");
|
|
2563
2472
|
}
|
|
2564
2473
|
async function writeConfigFile(content) {
|
|
2565
2474
|
await writeFile5(CONFIG_PATH2, content, "utf-8");
|
|
@@ -2649,10 +2558,8 @@ function formatMcpConfig(name, config) {
|
|
|
2649
2558
|
lines.push(`url = "${config.url}"`);
|
|
2650
2559
|
}
|
|
2651
2560
|
if (config.env && Object.keys(config.env).length > 0) {
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
lines.push(`${key} = "${value}"`);
|
|
2655
|
-
}
|
|
2561
|
+
const entries = Object.entries(config.env).map(([key, value]) => `${key} = "${value}"`);
|
|
2562
|
+
lines.push(`env = { ${entries.join(", ")} }`);
|
|
2656
2563
|
}
|
|
2657
2564
|
if (config.headers && Object.keys(config.headers).length > 0) {
|
|
2658
2565
|
lines.push(`[mcps.${name}.headers]`);
|
|
@@ -2758,15 +2665,15 @@ function escapeRegExp(str) {
|
|
|
2758
2665
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2759
2666
|
}
|
|
2760
2667
|
// src/mcp-json/manager.ts
|
|
2761
|
-
import { existsSync as
|
|
2762
|
-
import { readFile as
|
|
2668
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
2669
|
+
import { readFile as readFile12, writeFile as writeFile6 } from "node:fs/promises";
|
|
2763
2670
|
var MCP_JSON_PATH = ".mcp.json";
|
|
2764
2671
|
async function readMcpJson() {
|
|
2765
|
-
if (!
|
|
2672
|
+
if (!existsSync14(MCP_JSON_PATH)) {
|
|
2766
2673
|
return { mcpServers: {} };
|
|
2767
2674
|
}
|
|
2768
2675
|
try {
|
|
2769
|
-
const content = await
|
|
2676
|
+
const content = await readFile12(MCP_JSON_PATH, "utf-8");
|
|
2770
2677
|
const parsed = JSON.parse(content);
|
|
2771
2678
|
return {
|
|
2772
2679
|
mcpServers: parsed.mcpServers || {}
|
|
@@ -2845,19 +2752,19 @@ async function syncMcpJson(capabilities2, previousManifest, options = {}) {
|
|
|
2845
2752
|
}
|
|
2846
2753
|
}
|
|
2847
2754
|
// src/state/manifest.ts
|
|
2848
|
-
import { existsSync as
|
|
2849
|
-
import { readFile as
|
|
2755
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync2, rmSync } from "node:fs";
|
|
2756
|
+
import { readFile as readFile13, writeFile as writeFile7 } from "node:fs/promises";
|
|
2850
2757
|
var MANIFEST_PATH = ".omni/state/manifest.json";
|
|
2851
2758
|
var CURRENT_VERSION = 1;
|
|
2852
2759
|
async function loadManifest() {
|
|
2853
|
-
if (!
|
|
2760
|
+
if (!existsSync15(MANIFEST_PATH)) {
|
|
2854
2761
|
return {
|
|
2855
2762
|
version: CURRENT_VERSION,
|
|
2856
2763
|
syncedAt: new Date().toISOString(),
|
|
2857
2764
|
capabilities: {}
|
|
2858
2765
|
};
|
|
2859
2766
|
}
|
|
2860
|
-
const content = await
|
|
2767
|
+
const content = await readFile13(MANIFEST_PATH, "utf-8");
|
|
2861
2768
|
return JSON.parse(content);
|
|
2862
2769
|
}
|
|
2863
2770
|
async function saveManifest(manifest) {
|
|
@@ -2897,14 +2804,14 @@ async function cleanupStaleResources(previousManifest, currentCapabilityIds) {
|
|
|
2897
2804
|
}
|
|
2898
2805
|
for (const skillName of resources.skills) {
|
|
2899
2806
|
const skillDir = `.claude/skills/${skillName}`;
|
|
2900
|
-
if (
|
|
2807
|
+
if (existsSync15(skillDir)) {
|
|
2901
2808
|
rmSync(skillDir, { recursive: true });
|
|
2902
2809
|
result.deletedSkills.push(skillName);
|
|
2903
2810
|
}
|
|
2904
2811
|
}
|
|
2905
2812
|
for (const ruleName of resources.rules) {
|
|
2906
2813
|
const rulePath = `.cursor/rules/omnidev-${ruleName}.mdc`;
|
|
2907
|
-
if (
|
|
2814
|
+
if (existsSync15(rulePath)) {
|
|
2908
2815
|
rmSync(rulePath);
|
|
2909
2816
|
result.deletedRules.push(ruleName);
|
|
2910
2817
|
}
|
|
@@ -2913,17 +2820,17 @@ async function cleanupStaleResources(previousManifest, currentCapabilityIds) {
|
|
|
2913
2820
|
return result;
|
|
2914
2821
|
}
|
|
2915
2822
|
// src/state/providers.ts
|
|
2916
|
-
import { existsSync as
|
|
2917
|
-
import { readFile as
|
|
2823
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync3 } from "node:fs";
|
|
2824
|
+
import { readFile as readFile14, writeFile as writeFile8 } from "node:fs/promises";
|
|
2918
2825
|
var STATE_DIR2 = ".omni/state";
|
|
2919
2826
|
var PROVIDERS_PATH = `${STATE_DIR2}/providers.json`;
|
|
2920
2827
|
var DEFAULT_PROVIDERS = ["claude-code"];
|
|
2921
2828
|
async function readEnabledProviders() {
|
|
2922
|
-
if (!
|
|
2829
|
+
if (!existsSync16(PROVIDERS_PATH)) {
|
|
2923
2830
|
return DEFAULT_PROVIDERS;
|
|
2924
2831
|
}
|
|
2925
2832
|
try {
|
|
2926
|
-
const content = await
|
|
2833
|
+
const content = await readFile14(PROVIDERS_PATH, "utf-8");
|
|
2927
2834
|
const state = JSON.parse(content);
|
|
2928
2835
|
return state.enabled.length > 0 ? state.enabled : DEFAULT_PROVIDERS;
|
|
2929
2836
|
} catch {
|
|
@@ -2955,10 +2862,10 @@ async function isProviderEnabled(providerId) {
|
|
|
2955
2862
|
import { spawn as spawn2 } from "node:child_process";
|
|
2956
2863
|
import { mkdirSync as mkdirSync4 } from "node:fs";
|
|
2957
2864
|
async function installCapabilityDependencies(silent) {
|
|
2958
|
-
const { existsSync:
|
|
2865
|
+
const { existsSync: existsSync17, readdirSync: readdirSync7 } = await import("node:fs");
|
|
2959
2866
|
const { join: join9 } = await import("node:path");
|
|
2960
2867
|
const capabilitiesDir = ".omni/capabilities";
|
|
2961
|
-
if (!
|
|
2868
|
+
if (!existsSync17(capabilitiesDir)) {
|
|
2962
2869
|
return;
|
|
2963
2870
|
}
|
|
2964
2871
|
const entries = readdirSync7(capabilitiesDir, { withFileTypes: true });
|
|
@@ -2980,14 +2887,14 @@ async function installCapabilityDependencies(silent) {
|
|
|
2980
2887
|
}
|
|
2981
2888
|
const capabilityPath = join9(capabilitiesDir, entry.name);
|
|
2982
2889
|
const packageJsonPath = join9(capabilityPath, "package.json");
|
|
2983
|
-
if (!
|
|
2890
|
+
if (!existsSync17(packageJsonPath)) {
|
|
2984
2891
|
continue;
|
|
2985
2892
|
}
|
|
2986
2893
|
if (!silent) {
|
|
2987
2894
|
console.log(`Installing dependencies for ${capabilityPath}...`);
|
|
2988
2895
|
}
|
|
2989
2896
|
await new Promise((resolve2, reject) => {
|
|
2990
|
-
const useNpmCi = hasNpm &&
|
|
2897
|
+
const useNpmCi = hasNpm && existsSync17(join9(capabilityPath, "package-lock.json"));
|
|
2991
2898
|
const cmd = hasBun ? "bun" : "npm";
|
|
2992
2899
|
const args = hasBun ? ["install"] : useNpmCi ? ["ci"] : ["install"];
|
|
2993
2900
|
const proc = spawn2(cmd, args, {
|
|
@@ -3110,38 +3017,17 @@ async function syncAgentConfiguration(options) {
|
|
|
3110
3017
|
docCount: bundle.docs.length
|
|
3111
3018
|
};
|
|
3112
3019
|
}
|
|
3113
|
-
function generateInstructionsContent(rules,
|
|
3114
|
-
if (rules.length === 0
|
|
3115
|
-
return
|
|
3116
|
-
|
|
3117
|
-
No capabilities enabled yet. Run \`omnidev capability enable <name>\` to enable capabilities.`;
|
|
3118
|
-
}
|
|
3119
|
-
let content = `## Capabilities
|
|
3120
|
-
|
|
3121
|
-
`;
|
|
3122
|
-
if (docs.length > 0) {
|
|
3123
|
-
content += `### Documentation
|
|
3124
|
-
|
|
3125
|
-
`;
|
|
3126
|
-
for (const doc of docs) {
|
|
3127
|
-
content += `#### ${doc.name} (from ${doc.capabilityId})
|
|
3128
|
-
|
|
3129
|
-
${doc.content}
|
|
3130
|
-
|
|
3131
|
-
`;
|
|
3132
|
-
}
|
|
3020
|
+
function generateInstructionsContent(rules, _docs) {
|
|
3021
|
+
if (rules.length === 0) {
|
|
3022
|
+
return "";
|
|
3133
3023
|
}
|
|
3134
|
-
|
|
3135
|
-
content += `### Rules
|
|
3024
|
+
let content = `## Rules
|
|
3136
3025
|
|
|
3137
3026
|
`;
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
${rule.content}
|
|
3027
|
+
for (const rule of rules) {
|
|
3028
|
+
content += `${rule.content}
|
|
3142
3029
|
|
|
3143
3030
|
`;
|
|
3144
|
-
}
|
|
3145
3031
|
}
|
|
3146
3032
|
return content.trim();
|
|
3147
3033
|
}
|
|
@@ -3211,33 +3097,12 @@ TODO: Add example usage
|
|
|
3211
3097
|
`;
|
|
3212
3098
|
}
|
|
3213
3099
|
function generateRuleTemplate(ruleName) {
|
|
3214
|
-
return
|
|
3100
|
+
return `### ${formatDisplayName(ruleName)}
|
|
3215
3101
|
|
|
3216
3102
|
<!-- Rules are guidelines that the AI agent should follow when working in this project -->
|
|
3103
|
+
<!-- Each rule should start with a ### header -->
|
|
3217
3104
|
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
TODO: Describe what this rule enforces or guides.
|
|
3221
|
-
|
|
3222
|
-
## Guidelines
|
|
3223
|
-
|
|
3224
|
-
- TODO: Add specific guidelines the AI should follow
|
|
3225
|
-
- Be specific and actionable
|
|
3226
|
-
- Include examples where helpful
|
|
3227
|
-
|
|
3228
|
-
## Examples
|
|
3229
|
-
|
|
3230
|
-
### Good
|
|
3231
|
-
|
|
3232
|
-
\`\`\`
|
|
3233
|
-
TODO: Add example of correct behavior
|
|
3234
|
-
\`\`\`
|
|
3235
|
-
|
|
3236
|
-
### Bad
|
|
3237
|
-
|
|
3238
|
-
\`\`\`
|
|
3239
|
-
TODO: Add example of incorrect behavior
|
|
3240
|
-
\`\`\`
|
|
3105
|
+
TODO: Add specific guidelines the AI should follow. Be specific and actionable.
|
|
3241
3106
|
`;
|
|
3242
3107
|
}
|
|
3243
3108
|
function generateHooksTemplate() {
|
|
@@ -3293,10 +3158,6 @@ function generateClaudeTemplate() {
|
|
|
3293
3158
|
return `# Project Instructions
|
|
3294
3159
|
|
|
3295
3160
|
<!-- Add your project-specific instructions here -->
|
|
3296
|
-
|
|
3297
|
-
## OmniDev
|
|
3298
|
-
|
|
3299
|
-
<!-- This section is populated during sync with capability rules and docs -->
|
|
3300
3161
|
`;
|
|
3301
3162
|
}
|
|
3302
3163
|
// src/templates/omni.ts
|
|
@@ -3349,7 +3210,6 @@ export {
|
|
|
3349
3210
|
version,
|
|
3350
3211
|
validateHooksConfig,
|
|
3351
3212
|
validateHook,
|
|
3352
|
-
validateEnv,
|
|
3353
3213
|
transformToOmnidev,
|
|
3354
3214
|
transformToClaude,
|
|
3355
3215
|
transformHooksConfig,
|
|
@@ -3383,7 +3243,6 @@ export {
|
|
|
3383
3243
|
loadManifest,
|
|
3384
3244
|
loadLockFile,
|
|
3385
3245
|
loadHooksFromCapability,
|
|
3386
|
-
loadEnvironment,
|
|
3387
3246
|
loadDocs,
|
|
3388
3247
|
loadConfig,
|
|
3389
3248
|
loadCommands,
|
|
@@ -3392,7 +3251,6 @@ export {
|
|
|
3392
3251
|
loadCapability,
|
|
3393
3252
|
loadBaseConfig,
|
|
3394
3253
|
isValidMatcherPattern,
|
|
3395
|
-
isSecretEnvVar,
|
|
3396
3254
|
isProviderEnabled,
|
|
3397
3255
|
isPromptHookEvent,
|
|
3398
3256
|
isMatcherEvent,
|