@omnidev-ai/core 0.6.2 → 0.8.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 CHANGED
@@ -190,12 +190,37 @@ interface McpToolSchema {
190
190
  description: string;
191
191
  inputSchema: Record<string, unknown>;
192
192
  }
193
+ /**
194
+ * MCP server configuration supporting multiple transport types:
195
+ *
196
+ * - **stdio**: Local process using stdin/stdout (default)
197
+ * - Requires: command
198
+ * - Optional: args, env, cwd
199
+ *
200
+ * - **http**: Remote HTTP server (recommended for remote servers)
201
+ * - Requires: url
202
+ * - Optional: headers (for authentication)
203
+ *
204
+ * - **sse**: Server-Sent Events (deprecated, use http instead)
205
+ * - Requires: url
206
+ * - Optional: headers (for authentication)
207
+ */
193
208
  interface McpConfig {
194
- command: string;
209
+ /** Executable to run (required for stdio transport) */
210
+ command?: string;
211
+ /** Command arguments (stdio transport only) */
195
212
  args?: string[];
213
+ /** Environment variables (stdio transport only) */
196
214
  env?: Record<string, string>;
215
+ /** Working directory (stdio transport only) */
197
216
  cwd?: string;
217
+ /** Transport type: stdio (default), http, or sse */
198
218
  transport?: McpTransport;
219
+ /** URL for remote servers (required for http/sse transport) */
220
+ url?: string;
221
+ /** HTTP headers for authentication (http/sse transport only) */
222
+ headers?: Record<string, string>;
223
+ /** Tool schemas (optional, for documentation) */
199
224
  tools?: McpToolSchema[];
200
225
  }
201
226
  interface Skill {
@@ -585,6 +610,12 @@ declare function validateEnv(declarations: Record<string, EnvDeclaration | Recor
585
610
  */
586
611
  declare function isSecretEnvVar(key: string, declarations: Record<string, EnvDeclaration | Record<string, never>>): boolean;
587
612
  /**
613
+ * Load only the base config file (omni.toml) without merging local overrides.
614
+ * Use this when you need to modify and write back to omni.toml.
615
+ * @returns OmniConfig from omni.toml only
616
+ */
617
+ declare function loadBaseConfig(): Promise<OmniConfig>;
618
+ /**
588
619
  * Load and merge config and local configuration files
589
620
  * @returns Merged OmniConfig object
590
621
  *
@@ -697,14 +728,34 @@ declare function buildManifestFromCapabilities(capabilities: LoadedCapability[])
697
728
  */
698
729
  declare function cleanupStaleResources(previousManifest: ResourceManifest, currentCapabilityIds: Set<string>): Promise<CleanupResult>;
699
730
  /**
700
- * MCP server configuration in .mcp.json
731
+ * MCP server configuration in .mcp.json for stdio transport
701
732
  */
702
- interface McpServerConfig {
733
+ interface McpServerStdioConfig {
703
734
  command: string;
704
735
  args?: string[];
705
736
  env?: Record<string, string>;
706
737
  }
707
738
  /**
739
+ * MCP server configuration in .mcp.json for HTTP transport
740
+ */
741
+ interface McpServerHttpConfig {
742
+ type: "http";
743
+ url: string;
744
+ headers?: Record<string, string>;
745
+ }
746
+ /**
747
+ * MCP server configuration in .mcp.json for SSE transport
748
+ */
749
+ interface McpServerSseConfig {
750
+ type: "sse";
751
+ url: string;
752
+ headers?: Record<string, string>;
753
+ }
754
+ /**
755
+ * Union type for all MCP server configurations
756
+ */
757
+ type McpServerConfig = McpServerStdioConfig | McpServerHttpConfig | McpServerSseConfig;
758
+ /**
708
759
  * Structure of .mcp.json file
709
760
  */
710
761
  interface McpJsonConfig {
@@ -817,9 +868,15 @@ declare function generateClaudeTemplate(): string;
817
868
  */
818
869
  declare function generateInstructionsTemplate(): string;
819
870
  /**
871
+ * Template for OMNI.md - the user's project instructions file.
872
+ * This is the single source of truth that gets transformed into
873
+ * provider-specific files (CLAUDE.md, AGENTS.md, etc.) during sync.
874
+ */
875
+ declare function generateOmniMdTemplate(): string;
876
+ /**
820
877
  * Debug logger that writes to stdout when OMNIDEV_DEBUG=1
821
878
  */
822
879
  declare function debug(message: string, data?: unknown): void;
823
880
  declare const version = "0.1.0";
824
881
  declare function getVersion(): string;
825
- export { writeRules, writeProviderConfig, writeMcpJson, writeEnabledProviders, writeConfig, writeActiveProfileState, version, validateEnv, syncMcpJson, syncAgentConfiguration, sourceToGitUrl, setProfile, setActiveProfile, saveManifest, saveLockFile, resolveEnabledCapabilities, readMcpJson, readEnabledProviders, readActiveProfileState, parseSourceConfig, parseProviderFlag, parseOmniConfig, parseCapabilityConfig, loadSubagents, loadSkills, loadRules, loadProviderConfig, loadProfileConfig, loadManifest, loadLockFile, loadEnvironment, loadDocs, loadConfig, loadCommands, loadCapabilityConfig, loadCapability, isSecretEnvVar, isProviderEnabled, installCapabilityDependencies, getVersion, getSourceCapabilityPath, getLockFilePath, getEnabledCapabilities, getActiveProviders, getActiveProfile, generateInstructionsTemplate, generateClaudeTemplate, generateAgentsTemplate, fetchCapabilitySource, fetchAllCapabilitySources, enableProvider, enableCapability, discoverCapabilities, disableProvider, disableCapability, debug, clearActiveProfileState, cleanupStaleResources, checkForUpdates, buildSyncBundle, buildRouteMap, buildManifestFromCapabilities, buildCommand, buildCapabilityRegistry, SyncResult, SyncOptions, SyncConfig, SyncBundle, SubagentPermissionMode, SubagentModel, SubagentHooks, SubagentHookConfig, SubagentExport, Subagent, SourceUpdateInfo, SkillExport, Skill, Rule, ResourceManifest, ProvidersState, ProviderSyncResult, ProviderManifest, ProviderInitResult, ProviderId, ProviderContext, ProviderConfig, ProviderAdapter, Provider, ProfileConfig, OmniConfig, McpTransport, McpToolSchema, McpServerConfig, McpJsonConfig, McpConfig, LoadedCapability, GitCapabilitySourceConfig, FileContent, FetchResult, EnvDeclaration, DocExport, Doc, DiscoveredContent, CommandExport, Command, CliConfig, CleanupResult, CapabilitySourceType, CapabilitySourceConfig, CapabilitySource, CapabilityResources, CapabilityRegistry, CapabilityMetadata, CapabilityLockEntry, CapabilityExports, CapabilityExport, CapabilityConfig, CapabilitiesLockFile, CapabilitiesConfig };
882
+ export { writeRules, writeProviderConfig, writeMcpJson, writeEnabledProviders, writeConfig, writeActiveProfileState, version, validateEnv, syncMcpJson, syncAgentConfiguration, sourceToGitUrl, setProfile, setActiveProfile, saveManifest, saveLockFile, resolveEnabledCapabilities, readMcpJson, readEnabledProviders, readActiveProfileState, parseSourceConfig, parseProviderFlag, parseOmniConfig, parseCapabilityConfig, loadSubagents, loadSkills, loadRules, loadProviderConfig, loadProfileConfig, loadManifest, loadLockFile, loadEnvironment, loadDocs, loadConfig, loadCommands, loadCapabilityConfig, loadCapability, loadBaseConfig, isSecretEnvVar, isProviderEnabled, installCapabilityDependencies, getVersion, getSourceCapabilityPath, getLockFilePath, getEnabledCapabilities, getActiveProviders, getActiveProfile, generateOmniMdTemplate, generateInstructionsTemplate, generateClaudeTemplate, generateAgentsTemplate, fetchCapabilitySource, fetchAllCapabilitySources, enableProvider, enableCapability, discoverCapabilities, disableProvider, disableCapability, debug, clearActiveProfileState, cleanupStaleResources, checkForUpdates, buildSyncBundle, buildRouteMap, buildManifestFromCapabilities, buildCommand, buildCapabilityRegistry, SyncResult, SyncOptions, SyncConfig, SyncBundle, SubagentPermissionMode, SubagentModel, SubagentHooks, SubagentHookConfig, SubagentExport, Subagent, SourceUpdateInfo, SkillExport, Skill, Rule, ResourceManifest, ProvidersState, ProviderSyncResult, ProviderManifest, ProviderInitResult, ProviderId, ProviderContext, ProviderConfig, ProviderAdapter, Provider, ProfileConfig, OmniConfig, McpTransport, McpToolSchema, McpServerStdioConfig, McpServerSseConfig, McpServerHttpConfig, McpServerConfig, McpJsonConfig, McpConfig, LoadedCapability, GitCapabilitySourceConfig, FileContent, FetchResult, EnvDeclaration, DocExport, Doc, DiscoveredContent, CommandExport, Command, CliConfig, CleanupResult, CapabilitySourceType, CapabilitySourceConfig, CapabilitySource, CapabilityResources, CapabilityRegistry, CapabilityMetadata, CapabilityLockEntry, CapabilityExports, CapabilityExport, CapabilityConfig, CapabilitiesLockFile, CapabilitiesConfig };
package/dist/index.js CHANGED
@@ -691,13 +691,16 @@ function mergeConfigs(base, override) {
691
691
  }
692
692
  return merged;
693
693
  }
694
- async function loadConfig() {
695
- let baseConfig = {};
696
- let localConfig = {};
694
+ async function loadBaseConfig() {
697
695
  if (existsSync8(CONFIG_PATH)) {
698
696
  const content = await readFile8(CONFIG_PATH, "utf-8");
699
- baseConfig = parseOmniConfig(content);
697
+ return parseOmniConfig(content);
700
698
  }
699
+ return {};
700
+ }
701
+ async function loadConfig() {
702
+ const baseConfig = await loadBaseConfig();
703
+ let localConfig = {};
701
704
  if (existsSync8(LOCAL_CONFIG)) {
702
705
  const content = await readFile8(LOCAL_CONFIG, "utf-8");
703
706
  localConfig = parseOmniConfig(content);
@@ -726,10 +729,6 @@ function generateConfigToml(config) {
726
729
  lines.push("# 3. Run: omnidev sync");
727
730
  lines.push("# 4. Switch profiles: omnidev profile use <name>");
728
731
  lines.push("");
729
- if (config.project) {
730
- lines.push(`project = "${config.project}"`);
731
- lines.push("");
732
- }
733
732
  if (config.providers?.enabled && config.providers.enabled.length > 0) {
734
733
  lines.push("# AI providers to enable (claude, codex, or both)");
735
734
  lines.push("[providers]");
@@ -759,16 +758,30 @@ function generateConfigToml(config) {
759
758
  lines.push("# Fetch capabilities from Git repositories. On sync, these are");
760
759
  lines.push("# cloned/updated and made available to your profiles.");
761
760
  lines.push("#");
762
- lines.push("# [capabilities.sources]");
763
- lines.push("# # GitHub shorthand (uses latest commit)");
764
- lines.push('# tasks = "github:example-org/tasks-capability"');
765
- lines.push("#");
766
- lines.push("# # Version pinning (recommended for production)");
767
- lines.push('# ralph = { source = "github:example-org/ralph", ref = "v1.2.0" }');
768
- lines.push("#");
769
- lines.push("# # Other Git sources");
770
- lines.push('# private = "git@github.com:company/private-cap.git"');
771
- lines.push('# gitlab = "https://gitlab.com/user/capability.git"');
761
+ const sources = config.capabilities?.sources;
762
+ if (sources && Object.keys(sources).length > 0) {
763
+ lines.push("[capabilities.sources]");
764
+ for (const [name, sourceConfig] of Object.entries(sources)) {
765
+ if (typeof sourceConfig === "string") {
766
+ lines.push(`${name} = "${sourceConfig}"`);
767
+ } else if (sourceConfig.path) {
768
+ lines.push(`${name} = { source = "${sourceConfig.source}", path = "${sourceConfig.path}" }`);
769
+ } else {
770
+ lines.push(`${name} = "${sourceConfig.source}"`);
771
+ }
772
+ }
773
+ } else {
774
+ lines.push("# [capabilities.sources]");
775
+ lines.push("# # GitHub shorthand (uses latest commit)");
776
+ lines.push('# tasks = "github:example-org/tasks-capability"');
777
+ lines.push("#");
778
+ lines.push("# # With subdirectory path");
779
+ lines.push('# ralph = { source = "github:example-org/ralph", path = "plugins/my-cap" }');
780
+ lines.push("#");
781
+ lines.push("# # Other Git sources");
782
+ lines.push('# private = "git@github.com:company/private-cap.git"');
783
+ lines.push('# gitlab = "https://gitlab.com/user/capability.git"');
784
+ }
772
785
  lines.push("");
773
786
  lines.push("# =============================================================================");
774
787
  lines.push("# MCP Servers");
@@ -776,25 +789,66 @@ function generateConfigToml(config) {
776
789
  lines.push("# Define MCP servers that automatically become capabilities.");
777
790
  lines.push('# Reference in profiles using the MCP name directly, e.g. capabilities = ["filesystem"]');
778
791
  lines.push("#");
779
- lines.push("# [mcps.filesystem]");
780
- lines.push('# command = "npx"');
781
- lines.push('# args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]');
782
- lines.push('# transport = "stdio" # stdio (default), sse, or http');
783
- lines.push("#");
784
- lines.push("# [mcps.database]");
785
- lines.push('# command = "node"');
786
- lines.push('# args = ["./servers/database.js"]');
787
- lines.push('# cwd = "./mcp-servers"');
788
- lines.push("# [mcps.database.env]");
789
- lines.push('# DB_URL = "${DATABASE_URL}"');
790
- lines.push("");
792
+ const mcps = config.mcps;
793
+ if (mcps && Object.keys(mcps).length > 0) {
794
+ for (const [name, mcpConfig] of Object.entries(mcps)) {
795
+ lines.push(`[mcps.${name}]`);
796
+ if (mcpConfig.transport && mcpConfig.transport !== "stdio") {
797
+ lines.push(`transport = "${mcpConfig.transport}"`);
798
+ }
799
+ if (mcpConfig.command) {
800
+ lines.push(`command = "${mcpConfig.command}"`);
801
+ }
802
+ if (mcpConfig.args && mcpConfig.args.length > 0) {
803
+ const argsStr = mcpConfig.args.map((a) => `"${a}"`).join(", ");
804
+ lines.push(`args = [${argsStr}]`);
805
+ }
806
+ if (mcpConfig.cwd) {
807
+ lines.push(`cwd = "${mcpConfig.cwd}"`);
808
+ }
809
+ if (mcpConfig.url) {
810
+ lines.push(`url = "${mcpConfig.url}"`);
811
+ }
812
+ if (mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
813
+ lines.push(`[mcps.${name}.env]`);
814
+ for (const [key, value] of Object.entries(mcpConfig.env)) {
815
+ lines.push(`${key} = "${value}"`);
816
+ }
817
+ }
818
+ if (mcpConfig.headers && Object.keys(mcpConfig.headers).length > 0) {
819
+ lines.push(`[mcps.${name}.headers]`);
820
+ for (const [key, value] of Object.entries(mcpConfig.headers)) {
821
+ lines.push(`${key} = "${value}"`);
822
+ }
823
+ }
824
+ lines.push("");
825
+ }
826
+ } else {
827
+ lines.push("# [mcps.filesystem]");
828
+ lines.push('# command = "npx"');
829
+ lines.push('# args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]');
830
+ lines.push('# transport = "stdio" # stdio (default), sse, or http');
831
+ lines.push("#");
832
+ lines.push("# [mcps.database]");
833
+ lines.push('# command = "node"');
834
+ lines.push('# args = ["./servers/database.js"]');
835
+ lines.push('# cwd = "./mcp-servers"');
836
+ lines.push("# [mcps.database.env]");
837
+ lines.push('# DB_URL = "${DATABASE_URL}"');
838
+ lines.push("");
839
+ }
791
840
  lines.push("# =============================================================================");
792
841
  lines.push("# Always Enabled Capabilities");
793
842
  lines.push("# =============================================================================");
794
843
  lines.push("# Capabilities that load in ALL profiles, regardless of profile config.");
795
844
  lines.push("# Useful for essential tools needed everywhere.");
796
845
  lines.push("#");
797
- lines.push('# always_enabled_capabilities = ["git-tools", "linting"]');
846
+ if (config.always_enabled_capabilities && config.always_enabled_capabilities.length > 0) {
847
+ const caps = config.always_enabled_capabilities.map((c) => `"${c}"`).join(", ");
848
+ lines.push(`always_enabled_capabilities = [${caps}]`);
849
+ } else {
850
+ lines.push('# always_enabled_capabilities = ["git-tools", "linting"]');
851
+ }
798
852
  lines.push("");
799
853
  lines.push("# =============================================================================");
800
854
  lines.push("# Profiles");
@@ -883,7 +937,7 @@ async function getEnabledCapabilities() {
883
937
  return resolveEnabledCapabilities(config, activeProfile);
884
938
  }
885
939
  async function enableCapability(capabilityId) {
886
- const config = await loadConfig();
940
+ const config = await loadBaseConfig();
887
941
  const activeProfile = await getActiveProfile() ?? config.active_profile ?? "default";
888
942
  if (!config.profiles) {
889
943
  config.profiles = {};
@@ -897,7 +951,7 @@ async function enableCapability(capabilityId) {
897
951
  await writeConfig(config);
898
952
  }
899
953
  async function disableCapability(capabilityId) {
900
- const config = await loadConfig();
954
+ const config = await loadBaseConfig();
901
955
  const activeProfile = await getActiveProfile() ?? config.active_profile ?? "default";
902
956
  if (!config.profiles?.[activeProfile]) {
903
957
  return;
@@ -1389,6 +1443,8 @@ async function fetchCapabilitySource(id, sourceConfig, options) {
1389
1443
  return fetchGitCapabilitySource(id, config, options);
1390
1444
  }
1391
1445
  function generateMcpCapabilityTomlContent(id, mcpConfig) {
1446
+ const transport = mcpConfig.transport ?? "stdio";
1447
+ const isRemote = transport === "http" || transport === "sse";
1392
1448
  let tomlContent = `# Auto-generated by OmniDev from omni.toml [mcps] section - DO NOT EDIT
1393
1449
 
1394
1450
  [capability]
@@ -1402,21 +1458,42 @@ wrapped = true
1402
1458
  generated_from_omni_toml = true
1403
1459
 
1404
1460
  [mcp]
1405
- command = "${mcpConfig.command}"
1406
1461
  `;
1407
- if (mcpConfig.args && mcpConfig.args.length > 0) {
1408
- tomlContent += `args = ${JSON.stringify(mcpConfig.args)}
1462
+ if (isRemote) {
1463
+ tomlContent += `transport = "${transport}"
1409
1464
  `;
1410
- }
1411
- if (mcpConfig.transport) {
1412
- tomlContent += `transport = "${mcpConfig.transport}"
1465
+ if (mcpConfig.url) {
1466
+ tomlContent += `url = "${mcpConfig.url}"
1467
+ `;
1468
+ }
1469
+ } else {
1470
+ if (mcpConfig.command) {
1471
+ tomlContent += `command = "${mcpConfig.command}"
1472
+ `;
1473
+ }
1474
+ if (mcpConfig.args && mcpConfig.args.length > 0) {
1475
+ tomlContent += `args = ${JSON.stringify(mcpConfig.args)}
1476
+ `;
1477
+ }
1478
+ if (mcpConfig.transport) {
1479
+ tomlContent += `transport = "${mcpConfig.transport}"
1480
+ `;
1481
+ }
1482
+ if (mcpConfig.cwd) {
1483
+ tomlContent += `cwd = "${mcpConfig.cwd}"
1413
1484
  `;
1485
+ }
1414
1486
  }
1415
- if (mcpConfig.cwd) {
1416
- tomlContent += `cwd = "${mcpConfig.cwd}"
1487
+ if (isRemote && mcpConfig.headers && Object.keys(mcpConfig.headers).length > 0) {
1488
+ tomlContent += `
1489
+ [mcp.headers]
1417
1490
  `;
1491
+ for (const [key, value] of Object.entries(mcpConfig.headers)) {
1492
+ tomlContent += `"${key}" = "${value}"
1493
+ `;
1494
+ }
1418
1495
  }
1419
- if (mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
1496
+ if (!isRemote && mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
1420
1497
  tomlContent += `
1421
1498
  [mcp.env]
1422
1499
  `;
@@ -1653,6 +1730,36 @@ async function writeMcpJson(config) {
1653
1730
  `, "utf-8");
1654
1731
  }
1655
1732
  function buildMcpServerConfig(mcp) {
1733
+ const transport = mcp.transport ?? "stdio";
1734
+ if (transport === "http") {
1735
+ if (!mcp.url) {
1736
+ throw new Error("HTTP transport requires a URL");
1737
+ }
1738
+ const config2 = {
1739
+ type: "http",
1740
+ url: mcp.url
1741
+ };
1742
+ if (mcp.headers && Object.keys(mcp.headers).length > 0) {
1743
+ config2.headers = mcp.headers;
1744
+ }
1745
+ return config2;
1746
+ }
1747
+ if (transport === "sse") {
1748
+ if (!mcp.url) {
1749
+ throw new Error("SSE transport requires a URL");
1750
+ }
1751
+ const config2 = {
1752
+ type: "sse",
1753
+ url: mcp.url
1754
+ };
1755
+ if (mcp.headers && Object.keys(mcp.headers).length > 0) {
1756
+ config2.headers = mcp.headers;
1757
+ }
1758
+ return config2;
1759
+ }
1760
+ if (!mcp.command) {
1761
+ throw new Error("stdio transport requires a command");
1762
+ }
1656
1763
  const config = {
1657
1764
  command: mcp.command
1658
1765
  };
@@ -2046,6 +2153,27 @@ No capabilities enabled yet. Run \`omnidev capability enable <name>\` to enable
2046
2153
  <!-- END OMNIDEV GENERATED CONTENT -->
2047
2154
  `;
2048
2155
  }
2156
+ // src/templates/omni.ts
2157
+ function generateOmniMdTemplate() {
2158
+ return `# Project Instructions
2159
+
2160
+ <!-- This file is your project's instruction manifest for AI agents. -->
2161
+ <!-- It will be combined with capability-generated content during sync. -->
2162
+
2163
+ ## Project Description
2164
+
2165
+ <!-- TODO: Add 2-3 sentences describing your project -->
2166
+ [Describe what this project does and its main purpose]
2167
+
2168
+ ## Conventions
2169
+
2170
+ <!-- Add your project conventions, coding standards, and guidelines here -->
2171
+
2172
+ ## Architecture
2173
+
2174
+ <!-- Describe your project's architecture and key components -->
2175
+ `;
2176
+ }
2049
2177
  // src/types/index.ts
2050
2178
  function getActiveProviders(config) {
2051
2179
  if (config.providers)
@@ -2111,6 +2239,7 @@ export {
2111
2239
  loadCommands,
2112
2240
  loadCapabilityConfig,
2113
2241
  loadCapability,
2242
+ loadBaseConfig,
2114
2243
  isSecretEnvVar,
2115
2244
  isProviderEnabled,
2116
2245
  installCapabilityDependencies,
@@ -2120,6 +2249,7 @@ export {
2120
2249
  getEnabledCapabilities,
2121
2250
  getActiveProviders,
2122
2251
  getActiveProfile,
2252
+ generateOmniMdTemplate,
2123
2253
  generateInstructionsTemplate,
2124
2254
  generateClaudeTemplate,
2125
2255
  generateAgentsTemplate,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnidev-ai/core",
3
- "version": "0.6.2",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -752,6 +752,9 @@ function generateMcpCapabilityTomlContent(
752
752
  id: string,
753
753
  mcpConfig: import("../types/index.js").McpConfig,
754
754
  ): string {
755
+ const transport = mcpConfig.transport ?? "stdio";
756
+ const isRemote = transport === "http" || transport === "sse";
757
+
755
758
  let tomlContent = `# Auto-generated by OmniDev from omni.toml [mcps] section - DO NOT EDIT
756
759
 
757
760
  [capability]
@@ -765,22 +768,43 @@ wrapped = true
765
768
  generated_from_omni_toml = true
766
769
 
767
770
  [mcp]
768
- command = "${mcpConfig.command}"
769
771
  `;
770
772
 
771
- if (mcpConfig.args && mcpConfig.args.length > 0) {
772
- tomlContent += `args = ${JSON.stringify(mcpConfig.args)}\n`;
773
- }
773
+ // For remote transports (http/sse), url is required
774
+ if (isRemote) {
775
+ tomlContent += `transport = "${transport}"\n`;
776
+ if (mcpConfig.url) {
777
+ tomlContent += `url = "${mcpConfig.url}"\n`;
778
+ }
779
+ } else {
780
+ // For stdio transport, command is required
781
+ if (mcpConfig.command) {
782
+ tomlContent += `command = "${mcpConfig.command}"\n`;
783
+ }
774
784
 
775
- if (mcpConfig.transport) {
776
- tomlContent += `transport = "${mcpConfig.transport}"\n`;
785
+ if (mcpConfig.args && mcpConfig.args.length > 0) {
786
+ tomlContent += `args = ${JSON.stringify(mcpConfig.args)}\n`;
787
+ }
788
+
789
+ if (mcpConfig.transport) {
790
+ tomlContent += `transport = "${mcpConfig.transport}"\n`;
791
+ }
792
+
793
+ if (mcpConfig.cwd) {
794
+ tomlContent += `cwd = "${mcpConfig.cwd}"\n`;
795
+ }
777
796
  }
778
797
 
779
- if (mcpConfig.cwd) {
780
- tomlContent += `cwd = "${mcpConfig.cwd}"\n`;
798
+ // Headers for remote transports
799
+ if (isRemote && mcpConfig.headers && Object.keys(mcpConfig.headers).length > 0) {
800
+ tomlContent += `\n[mcp.headers]\n`;
801
+ for (const [key, value] of Object.entries(mcpConfig.headers)) {
802
+ tomlContent += `"${key}" = "${value}"\n`;
803
+ }
781
804
  }
782
805
 
783
- if (mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
806
+ // Env variables for stdio transport
807
+ if (!isRemote && mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
784
808
  tomlContent += `\n[mcp.env]\n`;
785
809
  for (const [key, value] of Object.entries(mcpConfig.env)) {
786
810
  tomlContent += `${key} = "${value}"\n`;
@@ -1,4 +1,4 @@
1
- import { loadConfig, writeConfig } from "./loader.js";
1
+ import { loadBaseConfig, loadConfig, writeConfig } from "./loader.js";
2
2
  import { getActiveProfile, resolveEnabledCapabilities } from "./profiles.js";
3
3
 
4
4
  /**
@@ -17,7 +17,8 @@ export async function getEnabledCapabilities(): Promise<string[]> {
17
17
  * @param capabilityId - The ID of the capability to enable
18
18
  */
19
19
  export async function enableCapability(capabilityId: string): Promise<void> {
20
- const config = await loadConfig();
20
+ // Use loadBaseConfig to avoid writing local overrides to omni.toml
21
+ const config = await loadBaseConfig();
21
22
  const activeProfile = (await getActiveProfile()) ?? config.active_profile ?? "default";
22
23
 
23
24
  if (!config.profiles) {
@@ -39,7 +40,8 @@ export async function enableCapability(capabilityId: string): Promise<void> {
39
40
  * @param capabilityId - The ID of the capability to disable
40
41
  */
41
42
  export async function disableCapability(capabilityId: string): Promise<void> {
42
- const config = await loadConfig();
43
+ // Use loadBaseConfig to avoid writing local overrides to omni.toml
44
+ const config = await loadBaseConfig();
43
45
  const activeProfile = (await getActiveProfile()) ?? config.active_profile ?? "default";
44
46
 
45
47
  if (!config.profiles?.[activeProfile]) {
@@ -35,6 +35,19 @@ function mergeConfigs(base: OmniConfig, override: OmniConfig): OmniConfig {
35
35
  return merged;
36
36
  }
37
37
 
38
+ /**
39
+ * Load only the base config file (omni.toml) without merging local overrides.
40
+ * Use this when you need to modify and write back to omni.toml.
41
+ * @returns OmniConfig from omni.toml only
42
+ */
43
+ export async function loadBaseConfig(): Promise<OmniConfig> {
44
+ if (existsSync(CONFIG_PATH)) {
45
+ const content = await readFile(CONFIG_PATH, "utf-8");
46
+ return parseOmniConfig(content);
47
+ }
48
+ return {};
49
+ }
50
+
38
51
  /**
39
52
  * Load and merge config and local configuration files
40
53
  * @returns Merged OmniConfig object
@@ -43,14 +56,9 @@ function mergeConfigs(base: OmniConfig, override: OmniConfig): OmniConfig {
43
56
  * Local config takes precedence over main config. Missing files are treated as empty configs.
44
57
  */
45
58
  export async function loadConfig(): Promise<OmniConfig> {
46
- let baseConfig: OmniConfig = {};
59
+ const baseConfig = await loadBaseConfig();
47
60
  let localConfig: OmniConfig = {};
48
61
 
49
- if (existsSync(CONFIG_PATH)) {
50
- const content = await readFile(CONFIG_PATH, "utf-8");
51
- baseConfig = parseOmniConfig(content);
52
- }
53
-
54
62
  if (existsSync(LOCAL_CONFIG)) {
55
63
  const content = await readFile(LOCAL_CONFIG, "utf-8");
56
64
  localConfig = parseOmniConfig(content);
@@ -93,12 +101,6 @@ function generateConfigToml(config: OmniConfig): string {
93
101
  lines.push("# 4. Switch profiles: omnidev profile use <name>");
94
102
  lines.push("");
95
103
 
96
- // Project name
97
- if (config.project) {
98
- lines.push(`project = "${config.project}"`);
99
- lines.push("");
100
- }
101
-
102
104
  // Note: active_profile is stored in .omni/state/active-profile, not in config.toml
103
105
  // We still read it from config.toml for backwards compatibility, but don't write it here
104
106
 
@@ -132,26 +134,46 @@ function generateConfigToml(config: OmniConfig): string {
132
134
  }
133
135
  lines.push("");
134
136
 
135
- // Capability sources (commented examples)
137
+ // Capability sources
136
138
  lines.push("# =============================================================================");
137
139
  lines.push("# Capability Sources");
138
140
  lines.push("# =============================================================================");
139
141
  lines.push("# Fetch capabilities from Git repositories. On sync, these are");
140
142
  lines.push("# cloned/updated and made available to your profiles.");
141
143
  lines.push("#");
142
- lines.push("# [capabilities.sources]");
143
- lines.push("# # GitHub shorthand (uses latest commit)");
144
- lines.push('# tasks = "github:example-org/tasks-capability"');
145
- lines.push("#");
146
- lines.push("# # Version pinning (recommended for production)");
147
- lines.push('# ralph = { source = "github:example-org/ralph", ref = "v1.2.0" }');
148
- lines.push("#");
149
- lines.push("# # Other Git sources");
150
- lines.push('# private = "git@github.com:company/private-cap.git"');
151
- lines.push('# gitlab = "https://gitlab.com/user/capability.git"');
144
+
145
+ const sources = config.capabilities?.sources;
146
+ if (sources && Object.keys(sources).length > 0) {
147
+ lines.push("[capabilities.sources]");
148
+ for (const [name, sourceConfig] of Object.entries(sources)) {
149
+ if (typeof sourceConfig === "string") {
150
+ // Simple string source
151
+ lines.push(`${name} = "${sourceConfig}"`);
152
+ } else if (sourceConfig.path) {
153
+ // Full config object with path
154
+ lines.push(
155
+ `${name} = { source = "${sourceConfig.source}", path = "${sourceConfig.path}" }`,
156
+ );
157
+ } else {
158
+ // Full config object without path - just write the source
159
+ lines.push(`${name} = "${sourceConfig.source}"`);
160
+ }
161
+ }
162
+ } else {
163
+ lines.push("# [capabilities.sources]");
164
+ lines.push("# # GitHub shorthand (uses latest commit)");
165
+ lines.push('# tasks = "github:example-org/tasks-capability"');
166
+ lines.push("#");
167
+ lines.push("# # With subdirectory path");
168
+ lines.push('# ralph = { source = "github:example-org/ralph", path = "plugins/my-cap" }');
169
+ lines.push("#");
170
+ lines.push("# # Other Git sources");
171
+ lines.push('# private = "git@github.com:company/private-cap.git"');
172
+ lines.push('# gitlab = "https://gitlab.com/user/capability.git"');
173
+ }
152
174
  lines.push("");
153
175
 
154
- // MCP servers (commented examples)
176
+ // MCP servers
155
177
  lines.push("# =============================================================================");
156
178
  lines.push("# MCP Servers");
157
179
  lines.push("# =============================================================================");
@@ -160,19 +182,67 @@ function generateConfigToml(config: OmniConfig): string {
160
182
  '# Reference in profiles using the MCP name directly, e.g. capabilities = ["filesystem"]',
161
183
  );
162
184
  lines.push("#");
163
- lines.push("# [mcps.filesystem]");
164
- lines.push('# command = "npx"');
165
- lines.push('# args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]');
166
- lines.push('# transport = "stdio" # stdio (default), sse, or http');
167
- lines.push("#");
168
- lines.push("# [mcps.database]");
169
- lines.push('# command = "node"');
170
- lines.push('# args = ["./servers/database.js"]');
171
- lines.push('# cwd = "./mcp-servers"');
172
- lines.push("# [mcps.database.env]");
173
- // biome-ignore lint/suspicious/noTemplateCurlyInString: Example of env var syntax
174
- lines.push('# DB_URL = "${DATABASE_URL}"');
175
- lines.push("");
185
+
186
+ const mcps = config.mcps;
187
+ if (mcps && Object.keys(mcps).length > 0) {
188
+ for (const [name, mcpConfig] of Object.entries(mcps)) {
189
+ lines.push(`[mcps.${name}]`);
190
+
191
+ // Transport type (default is stdio)
192
+ if (mcpConfig.transport && mcpConfig.transport !== "stdio") {
193
+ lines.push(`transport = "${mcpConfig.transport}"`);
194
+ }
195
+
196
+ // For stdio transport
197
+ if (mcpConfig.command) {
198
+ lines.push(`command = "${mcpConfig.command}"`);
199
+ }
200
+ if (mcpConfig.args && mcpConfig.args.length > 0) {
201
+ const argsStr = mcpConfig.args.map((a) => `"${a}"`).join(", ");
202
+ lines.push(`args = [${argsStr}]`);
203
+ }
204
+ if (mcpConfig.cwd) {
205
+ lines.push(`cwd = "${mcpConfig.cwd}"`);
206
+ }
207
+
208
+ // For http/sse transport
209
+ if (mcpConfig.url) {
210
+ lines.push(`url = "${mcpConfig.url}"`);
211
+ }
212
+
213
+ // Environment variables (sub-table)
214
+ if (mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
215
+ lines.push(`[mcps.${name}.env]`);
216
+ for (const [key, value] of Object.entries(mcpConfig.env)) {
217
+ lines.push(`${key} = "${value}"`);
218
+ }
219
+ }
220
+
221
+ // Headers (sub-table)
222
+ if (mcpConfig.headers && Object.keys(mcpConfig.headers).length > 0) {
223
+ lines.push(`[mcps.${name}.headers]`);
224
+ for (const [key, value] of Object.entries(mcpConfig.headers)) {
225
+ lines.push(`${key} = "${value}"`);
226
+ }
227
+ }
228
+
229
+ lines.push("");
230
+ }
231
+ } else {
232
+ lines.push("# [mcps.filesystem]");
233
+ lines.push('# command = "npx"');
234
+ lines.push('# args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]');
235
+ lines.push('# transport = "stdio" # stdio (default), sse, or http');
236
+ lines.push("#");
237
+ lines.push("# [mcps.database]");
238
+ lines.push('# command = "node"');
239
+ lines.push('# args = ["./servers/database.js"]');
240
+ lines.push('# cwd = "./mcp-servers"');
241
+ lines.push("# [mcps.database.env]");
242
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: Example of env var syntax
243
+ lines.push('# DB_URL = "${DATABASE_URL}"');
244
+ lines.push("");
245
+ }
176
246
 
177
247
  // Always enabled capabilities
178
248
  lines.push("# =============================================================================");
@@ -181,7 +251,12 @@ function generateConfigToml(config: OmniConfig): string {
181
251
  lines.push("# Capabilities that load in ALL profiles, regardless of profile config.");
182
252
  lines.push("# Useful for essential tools needed everywhere.");
183
253
  lines.push("#");
184
- lines.push('# always_enabled_capabilities = ["git-tools", "linting"]');
254
+ if (config.always_enabled_capabilities && config.always_enabled_capabilities.length > 0) {
255
+ const caps = config.always_enabled_capabilities.map((c) => `"${c}"`).join(", ");
256
+ lines.push(`always_enabled_capabilities = [${caps}]`);
257
+ } else {
258
+ lines.push('# always_enabled_capabilities = ["git-tools", "linting"]');
259
+ }
185
260
  lines.push("");
186
261
 
187
262
  // Profiles
package/src/index.ts CHANGED
@@ -30,6 +30,7 @@ export * from "./sync";
30
30
  // Export templates
31
31
  export * from "./templates/agents";
32
32
  export * from "./templates/claude";
33
+ export * from "./templates/omni";
33
34
  // Export core types
34
35
  export * from "./types";
35
36
 
@@ -4,14 +4,37 @@ import type { LoadedCapability, McpConfig } from "../types";
4
4
  import type { ResourceManifest } from "../state/manifest";
5
5
 
6
6
  /**
7
- * MCP server configuration in .mcp.json
7
+ * MCP server configuration in .mcp.json for stdio transport
8
8
  */
9
- export interface McpServerConfig {
9
+ export interface McpServerStdioConfig {
10
10
  command: string;
11
11
  args?: string[];
12
12
  env?: Record<string, string>;
13
13
  }
14
14
 
15
+ /**
16
+ * MCP server configuration in .mcp.json for HTTP transport
17
+ */
18
+ export interface McpServerHttpConfig {
19
+ type: "http";
20
+ url: string;
21
+ headers?: Record<string, string>;
22
+ }
23
+
24
+ /**
25
+ * MCP server configuration in .mcp.json for SSE transport
26
+ */
27
+ export interface McpServerSseConfig {
28
+ type: "sse";
29
+ url: string;
30
+ headers?: Record<string, string>;
31
+ }
32
+
33
+ /**
34
+ * Union type for all MCP server configurations
35
+ */
36
+ export type McpServerConfig = McpServerStdioConfig | McpServerHttpConfig | McpServerSseConfig;
37
+
15
38
  /**
16
39
  * Structure of .mcp.json file
17
40
  */
@@ -52,7 +75,43 @@ export async function writeMcpJson(config: McpJsonConfig): Promise<void> {
52
75
  * Build MCP server config from capability's mcp section
53
76
  */
54
77
  function buildMcpServerConfig(mcp: McpConfig): McpServerConfig {
55
- const config: McpServerConfig = {
78
+ const transport = mcp.transport ?? "stdio";
79
+
80
+ // HTTP transport - remote server
81
+ if (transport === "http") {
82
+ if (!mcp.url) {
83
+ throw new Error("HTTP transport requires a URL");
84
+ }
85
+ const config: McpServerHttpConfig = {
86
+ type: "http",
87
+ url: mcp.url,
88
+ };
89
+ if (mcp.headers && Object.keys(mcp.headers).length > 0) {
90
+ config.headers = mcp.headers;
91
+ }
92
+ return config;
93
+ }
94
+
95
+ // SSE transport - remote server (deprecated)
96
+ if (transport === "sse") {
97
+ if (!mcp.url) {
98
+ throw new Error("SSE transport requires a URL");
99
+ }
100
+ const config: McpServerSseConfig = {
101
+ type: "sse",
102
+ url: mcp.url,
103
+ };
104
+ if (mcp.headers && Object.keys(mcp.headers).length > 0) {
105
+ config.headers = mcp.headers;
106
+ }
107
+ return config;
108
+ }
109
+
110
+ // stdio transport - local process (default)
111
+ if (!mcp.command) {
112
+ throw new Error("stdio transport requires a command");
113
+ }
114
+ const config: McpServerStdioConfig = {
56
115
  command: mcp.command,
57
116
  };
58
117
  if (mcp.args) {
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Template for OMNI.md - the user's project instructions file.
3
+ * This is the single source of truth that gets transformed into
4
+ * provider-specific files (CLAUDE.md, AGENTS.md, etc.) during sync.
5
+ */
6
+ export function generateOmniMdTemplate(): string {
7
+ return `# Project Instructions
8
+
9
+ <!-- This file is your project's instruction manifest for AI agents. -->
10
+ <!-- It will be combined with capability-generated content during sync. -->
11
+
12
+ ## Project Description
13
+
14
+ <!-- TODO: Add 2-3 sentences describing your project -->
15
+ [Describe what this project does and its main purpose]
16
+
17
+ ## Conventions
18
+
19
+ <!-- Add your project conventions, coding standards, and guidelines here -->
20
+
21
+ ## Architecture
22
+
23
+ <!-- Describe your project's architecture and key components -->
24
+ `;
25
+ }
@@ -62,12 +62,37 @@ export interface McpToolSchema {
62
62
  inputSchema: Record<string, unknown>;
63
63
  }
64
64
 
65
+ /**
66
+ * MCP server configuration supporting multiple transport types:
67
+ *
68
+ * - **stdio**: Local process using stdin/stdout (default)
69
+ * - Requires: command
70
+ * - Optional: args, env, cwd
71
+ *
72
+ * - **http**: Remote HTTP server (recommended for remote servers)
73
+ * - Requires: url
74
+ * - Optional: headers (for authentication)
75
+ *
76
+ * - **sse**: Server-Sent Events (deprecated, use http instead)
77
+ * - Requires: url
78
+ * - Optional: headers (for authentication)
79
+ */
65
80
  export interface McpConfig {
66
- command: string;
81
+ /** Executable to run (required for stdio transport) */
82
+ command?: string;
83
+ /** Command arguments (stdio transport only) */
67
84
  args?: string[];
85
+ /** Environment variables (stdio transport only) */
68
86
  env?: Record<string, string>;
87
+ /** Working directory (stdio transport only) */
69
88
  cwd?: string;
89
+ /** Transport type: stdio (default), http, or sse */
70
90
  transport?: McpTransport;
91
+ /** URL for remote servers (required for http/sse transport) */
92
+ url?: string;
93
+ /** HTTP headers for authentication (http/sse transport only) */
94
+ headers?: Record<string, string>;
95
+ /** Tool schemas (optional, for documentation) */
71
96
  tools?: McpToolSchema[];
72
97
  }
73
98