@omnidev-ai/cli 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.
Files changed (2) hide show
  1. package/dist/index.js +510 -132
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -22,33 +22,34 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
22
22
  import { run } from "@stricli/core";
23
23
 
24
24
  // src/lib/dynamic-app.ts
25
- import { existsSync as existsSync7 } from "node:fs";
25
+ import { existsSync as existsSync8 } from "node:fs";
26
26
  import { createRequire as createRequire2 } from "node:module";
27
27
  import { join as join5 } from "node:path";
28
- import { buildApplication, buildRouteMap as buildRouteMap4 } from "@stricli/core";
28
+ import { buildApplication, buildRouteMap as buildRouteMap5 } from "@stricli/core";
29
+
30
+ // src/commands/add.ts
31
+ import { existsSync as existsSync4 } from "node:fs";
29
32
 
30
33
  // ../adapters/src/claude-code/index.ts
31
34
  import { existsSync, mkdirSync } from "node:fs";
32
- import { writeFile } from "node:fs/promises";
35
+ import { readFile, writeFile } from "node:fs/promises";
33
36
  import { join } from "node:path";
34
37
  var claudeCodeAdapter = {
35
38
  id: "claude-code",
36
39
  displayName: "Claude Code",
37
- async init(ctx) {
38
- const claudeMdPath = join(ctx.projectRoot, "CLAUDE.md");
39
- const filesCreated = [];
40
- if (!existsSync(claudeMdPath)) {
41
- await writeFile(claudeMdPath, generateClaudeTemplate(), "utf-8");
42
- filesCreated.push("CLAUDE.md");
43
- }
40
+ async init(_ctx) {
44
41
  return {
45
- filesCreated,
46
- message: filesCreated.length > 0 ? `Created ${filesCreated.join(", ")}` : "CLAUDE.md already exists"
42
+ filesCreated: [],
43
+ message: "Claude Code adapter initialized"
47
44
  };
48
45
  },
49
46
  async sync(bundle, ctx) {
50
47
  const filesWritten = [];
51
48
  const filesDeleted = [];
49
+ const claudeMdPath = join(ctx.projectRoot, "CLAUDE.md");
50
+ const claudeMdContent = await generateClaudeMdContent(ctx.projectRoot);
51
+ await writeFile(claudeMdPath, claudeMdContent, "utf-8");
52
+ filesWritten.push("CLAUDE.md");
52
53
  const skillsDir = join(ctx.projectRoot, ".claude", "skills");
53
54
  mkdirSync(skillsDir, { recursive: true });
54
55
  for (const skill of bundle.skills) {
@@ -70,51 +71,61 @@ ${skill.instructions}`;
70
71
  };
71
72
  }
72
73
  };
73
- function generateClaudeTemplate() {
74
- return `# Project Instructions
75
-
76
- <!-- Add your project-specific instructions here -->
74
+ async function generateClaudeMdContent(projectRoot) {
75
+ const omniMdPath = join(projectRoot, "OMNI.md");
76
+ let omniMdContent = "";
77
+ if (existsSync(omniMdPath)) {
78
+ omniMdContent = await readFile(omniMdPath, "utf-8");
79
+ }
80
+ let content = omniMdContent;
81
+ content += `
77
82
 
78
83
  ## OmniDev
79
84
 
80
85
  @import .omni/instructions.md
81
86
  `;
87
+ return content;
82
88
  }
83
89
  // ../adapters/src/codex/index.ts
84
90
  import { existsSync as existsSync2 } from "node:fs";
85
- import { writeFile as writeFile2 } from "node:fs/promises";
91
+ import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
86
92
  import { join as join2 } from "node:path";
87
93
  var codexAdapter = {
88
94
  id: "codex",
89
95
  displayName: "Codex",
90
- async init(ctx) {
91
- const agentsMdPath = join2(ctx.projectRoot, "AGENTS.md");
92
- const filesCreated = [];
93
- if (!existsSync2(agentsMdPath)) {
94
- await writeFile2(agentsMdPath, generateAgentsTemplate(), "utf-8");
95
- filesCreated.push("AGENTS.md");
96
- }
96
+ async init(_ctx) {
97
97
  return {
98
- filesCreated,
99
- message: filesCreated.length > 0 ? `Created ${filesCreated.join(", ")}` : "AGENTS.md already exists"
98
+ filesCreated: [],
99
+ message: "Codex adapter initialized"
100
100
  };
101
101
  },
102
- async sync(_bundle, _ctx) {
102
+ async sync(_bundle, ctx) {
103
+ const filesWritten = [];
104
+ const filesDeleted = [];
105
+ const agentsMdPath = join2(ctx.projectRoot, "AGENTS.md");
106
+ const agentsMdContent = await generateAgentsMdContent(ctx.projectRoot);
107
+ await writeFile2(agentsMdPath, agentsMdContent, "utf-8");
108
+ filesWritten.push("AGENTS.md");
103
109
  return {
104
- filesWritten: [],
105
- filesDeleted: []
110
+ filesWritten,
111
+ filesDeleted
106
112
  };
107
113
  }
108
114
  };
109
- function generateAgentsTemplate() {
110
- return `# Project Instructions
111
-
112
- <!-- Add your project-specific instructions here -->
115
+ async function generateAgentsMdContent(projectRoot) {
116
+ const omniMdPath = join2(projectRoot, "OMNI.md");
117
+ let omniMdContent = "";
118
+ if (existsSync2(omniMdPath)) {
119
+ omniMdContent = await readFile2(omniMdPath, "utf-8");
120
+ }
121
+ let content = omniMdContent;
122
+ content += `
113
123
 
114
124
  ## OmniDev
115
125
 
116
126
  @import .omni/instructions.md
117
127
  `;
128
+ return content;
118
129
  }
119
130
  // ../adapters/src/cursor/index.ts
120
131
  import { mkdirSync as mkdirSync2 } from "node:fs";
@@ -149,7 +160,7 @@ var cursorAdapter = {
149
160
  };
150
161
  // ../adapters/src/opencode/index.ts
151
162
  import { existsSync as existsSync3, mkdirSync as mkdirSync3 } from "node:fs";
152
- import { writeFile as writeFile4 } from "node:fs/promises";
163
+ import { readFile as readFile3, writeFile as writeFile4 } from "node:fs/promises";
153
164
  import { join as join4 } from "node:path";
154
165
  var opencodeAdapter = {
155
166
  id: "opencode",
@@ -157,33 +168,40 @@ var opencodeAdapter = {
157
168
  async init(ctx) {
158
169
  const opencodeDir = join4(ctx.projectRoot, ".opencode");
159
170
  mkdirSync3(opencodeDir, { recursive: true });
160
- const instructionsPath = join4(opencodeDir, "instructions.md");
161
- const filesCreated = [];
162
- if (!existsSync3(instructionsPath)) {
163
- await writeFile4(instructionsPath, generateOpencodeTemplate(), "utf-8");
164
- filesCreated.push(".opencode/instructions.md");
165
- }
166
171
  return {
167
- filesCreated,
168
- message: filesCreated.length > 0 ? `Created ${filesCreated.join(", ")}` : ".opencode/instructions.md already exists"
172
+ filesCreated: [".opencode/"],
173
+ message: "OpenCode adapter initialized"
169
174
  };
170
175
  },
171
- async sync(_bundle, _ctx) {
176
+ async sync(_bundle, ctx) {
177
+ const filesWritten = [];
178
+ const filesDeleted = [];
179
+ const opencodeDir = join4(ctx.projectRoot, ".opencode");
180
+ mkdirSync3(opencodeDir, { recursive: true });
181
+ const instructionsPath = join4(opencodeDir, "instructions.md");
182
+ const instructionsContent = await generateOpencodeInstructionsContent(ctx.projectRoot);
183
+ await writeFile4(instructionsPath, instructionsContent, "utf-8");
184
+ filesWritten.push(".opencode/instructions.md");
172
185
  return {
173
- filesWritten: [],
174
- filesDeleted: []
186
+ filesWritten,
187
+ filesDeleted
175
188
  };
176
189
  }
177
190
  };
178
- function generateOpencodeTemplate() {
179
- return `# OpenCode Instructions
180
-
181
- <!-- Add your project-specific instructions here -->
191
+ async function generateOpencodeInstructionsContent(projectRoot) {
192
+ const omniMdPath = join4(projectRoot, "OMNI.md");
193
+ let omniMdContent = "";
194
+ if (existsSync3(omniMdPath)) {
195
+ omniMdContent = await readFile3(omniMdPath, "utf-8");
196
+ }
197
+ let content = omniMdContent;
198
+ content += `
182
199
 
183
200
  ## OmniDev
184
201
 
185
202
  @import ../.omni/instructions.md
186
203
  `;
204
+ return content;
187
205
  }
188
206
  // ../adapters/src/registry.ts
189
207
  import { readEnabledProviders } from "@omnidev-ai/core";
@@ -201,6 +219,328 @@ async function getEnabledAdapters() {
201
219
  const enabledIds = await readEnabledProviders();
202
220
  return enabledIds.map((id) => adapterMap.get(id)).filter((a) => a != null);
203
221
  }
222
+ // src/commands/add.ts
223
+ import {
224
+ getActiveProfile,
225
+ loadBaseConfig,
226
+ syncAgentConfiguration,
227
+ writeConfig
228
+ } from "@omnidev-ai/core";
229
+ import { buildCommand, buildRouteMap } from "@stricli/core";
230
+ function addToActiveProfile(config, activeProfile, capabilityName) {
231
+ if (!config.profiles) {
232
+ config.profiles = {};
233
+ }
234
+ if (!config.profiles[activeProfile]) {
235
+ config.profiles[activeProfile] = { capabilities: [] };
236
+ }
237
+ if (!config.profiles[activeProfile].capabilities) {
238
+ config.profiles[activeProfile].capabilities = [];
239
+ }
240
+ if (!config.profiles[activeProfile].capabilities.includes(capabilityName)) {
241
+ config.profiles[activeProfile].capabilities.push(capabilityName);
242
+ }
243
+ }
244
+ async function runAddCap(flags, name) {
245
+ try {
246
+ if (!existsSync4("omni.toml")) {
247
+ console.log("✗ No config file found");
248
+ console.log(" Run: omnidev init");
249
+ process.exit(1);
250
+ }
251
+ if (!flags.github.includes("/")) {
252
+ console.error("✗ Invalid GitHub repository format");
253
+ console.log(" Expected format: user/repo");
254
+ console.log(" Example: omnidev add cap my-cap --github expo/skills");
255
+ process.exit(1);
256
+ }
257
+ const config = await loadBaseConfig();
258
+ const activeProfile = await getActiveProfile() ?? config.active_profile ?? "default";
259
+ if (!config.capabilities) {
260
+ config.capabilities = {};
261
+ }
262
+ if (!config.capabilities.sources) {
263
+ config.capabilities.sources = {};
264
+ }
265
+ if (config.capabilities.sources[name]) {
266
+ console.error(`✗ Capability source "${name}" already exists`);
267
+ console.log(" Use a different name or remove the existing source first");
268
+ process.exit(1);
269
+ }
270
+ const source = `github:${flags.github}`;
271
+ if (flags.path) {
272
+ config.capabilities.sources[name] = { source, path: flags.path };
273
+ } else {
274
+ config.capabilities.sources[name] = source;
275
+ }
276
+ addToActiveProfile(config, activeProfile, name);
277
+ await writeConfig(config);
278
+ console.log(`✓ Added capability source: ${name}`);
279
+ console.log(` Source: ${source}`);
280
+ if (flags.path) {
281
+ console.log(` Path: ${flags.path}`);
282
+ }
283
+ console.log(` Enabled in profile: ${activeProfile}`);
284
+ console.log("");
285
+ const adapters = await getEnabledAdapters();
286
+ await syncAgentConfiguration({ adapters });
287
+ console.log("✓ Sync completed");
288
+ } catch (error) {
289
+ console.error("✗ Error adding capability:", error);
290
+ process.exit(1);
291
+ }
292
+ }
293
+ async function runAddMcp(flags, name) {
294
+ try {
295
+ if (!existsSync4("omni.toml")) {
296
+ console.log("✗ No config file found");
297
+ console.log(" Run: omnidev init");
298
+ process.exit(1);
299
+ }
300
+ const config = await loadBaseConfig();
301
+ const activeProfile = await getActiveProfile() ?? config.active_profile ?? "default";
302
+ if (!config.mcps) {
303
+ config.mcps = {};
304
+ }
305
+ if (config.mcps[name]) {
306
+ console.error(`✗ MCP "${name}" already exists`);
307
+ console.log(" Use a different name or remove the existing MCP first");
308
+ process.exit(1);
309
+ }
310
+ const transport = flags.transport ?? "stdio";
311
+ const mcpConfig = {};
312
+ if (transport === "http" || transport === "sse") {
313
+ if (!flags.url) {
314
+ console.error("✗ --url is required for http/sse transport");
315
+ console.log(" Example: omnidev add mcp notion --transport http --url https://mcp.notion.com/mcp");
316
+ process.exit(1);
317
+ }
318
+ mcpConfig.transport = transport;
319
+ mcpConfig.url = flags.url;
320
+ if (flags.header && flags.header.length > 0) {
321
+ mcpConfig.headers = {};
322
+ for (const header of flags.header) {
323
+ const colonIndex = header.indexOf(":");
324
+ if (colonIndex === -1) {
325
+ console.error(`✗ Invalid header format: ${header}`);
326
+ console.log(" Expected format: Name: Value");
327
+ process.exit(1);
328
+ }
329
+ const headerName = header.slice(0, colonIndex).trim();
330
+ const headerValue = header.slice(colonIndex + 1).trim();
331
+ mcpConfig.headers[headerName] = headerValue;
332
+ }
333
+ }
334
+ } else {
335
+ if (!flags.command) {
336
+ console.error("✗ --command is required for stdio transport");
337
+ console.log(" Example: omnidev add mcp filesystem --command npx --args '-y @modelcontextprotocol/server-filesystem /path'");
338
+ process.exit(1);
339
+ }
340
+ mcpConfig.command = flags.command;
341
+ if (flags.args) {
342
+ mcpConfig.args = parseArgs(flags.args);
343
+ }
344
+ if (flags.env && flags.env.length > 0) {
345
+ mcpConfig.env = {};
346
+ for (const envVar of flags.env) {
347
+ const eqIndex = envVar.indexOf("=");
348
+ if (eqIndex === -1) {
349
+ console.error(`✗ Invalid env format: ${envVar}`);
350
+ console.log(" Expected format: KEY=value");
351
+ process.exit(1);
352
+ }
353
+ const key = envVar.slice(0, eqIndex);
354
+ const value = envVar.slice(eqIndex + 1);
355
+ mcpConfig.env[key] = value;
356
+ }
357
+ }
358
+ }
359
+ config.mcps[name] = mcpConfig;
360
+ addToActiveProfile(config, activeProfile, name);
361
+ await writeConfig(config);
362
+ console.log(`✓ Added MCP: ${name}`);
363
+ console.log(` Transport: ${transport}`);
364
+ if (mcpConfig.url) {
365
+ console.log(` URL: ${mcpConfig.url}`);
366
+ }
367
+ if (mcpConfig.command) {
368
+ console.log(` Command: ${mcpConfig.command}`);
369
+ if (mcpConfig.args) {
370
+ console.log(` Args: ${mcpConfig.args.join(" ")}`);
371
+ }
372
+ }
373
+ console.log(` Enabled in profile: ${activeProfile}`);
374
+ console.log("");
375
+ const adapters = await getEnabledAdapters();
376
+ await syncAgentConfiguration({ adapters });
377
+ console.log("✓ Sync completed");
378
+ } catch (error) {
379
+ console.error("✗ Error adding MCP:", error);
380
+ process.exit(1);
381
+ }
382
+ }
383
+ function parseArgs(argsString) {
384
+ const args = [];
385
+ let current = "";
386
+ let inQuote = false;
387
+ let quoteChar = "";
388
+ for (let i = 0;i < argsString.length; i++) {
389
+ const char = argsString[i];
390
+ if ((char === '"' || char === "'") && !inQuote) {
391
+ inQuote = true;
392
+ quoteChar = char;
393
+ } else if (char === quoteChar && inQuote) {
394
+ inQuote = false;
395
+ quoteChar = "";
396
+ } else if (char === " " && !inQuote) {
397
+ if (current) {
398
+ args.push(current);
399
+ current = "";
400
+ }
401
+ } else {
402
+ current += char;
403
+ }
404
+ }
405
+ if (current) {
406
+ args.push(current);
407
+ }
408
+ return args;
409
+ }
410
+ async function runAddCapWrapper(flags, name) {
411
+ await runAddCap({ github: flags.github, path: flags.path }, name);
412
+ }
413
+ var addCapCommand = buildCommand({
414
+ docs: {
415
+ brief: "Add a capability source from GitHub",
416
+ fullDescription: "Add a capability source from a GitHub repository. The capability will be auto-enabled in the active profile."
417
+ },
418
+ parameters: {
419
+ flags: {
420
+ github: {
421
+ kind: "parsed",
422
+ brief: "GitHub repository in user/repo format",
423
+ parse: String
424
+ },
425
+ path: {
426
+ kind: "parsed",
427
+ brief: "Subdirectory within the repo containing the capability",
428
+ parse: String,
429
+ optional: true
430
+ }
431
+ },
432
+ positional: {
433
+ kind: "tuple",
434
+ parameters: [
435
+ {
436
+ brief: "Capability name",
437
+ parse: String
438
+ }
439
+ ]
440
+ }
441
+ },
442
+ func: runAddCapWrapper
443
+ });
444
+ async function runAddMcpWrapper(flags, name) {
445
+ await runAddMcp({
446
+ transport: flags.transport,
447
+ url: flags.url,
448
+ command: flags.command,
449
+ args: flags.args,
450
+ header: flags.header,
451
+ env: flags.env
452
+ }, name);
453
+ }
454
+ var addMcpCommand = buildCommand({
455
+ docs: {
456
+ brief: "Add an MCP server",
457
+ fullDescription: `Add an MCP server to the configuration. Supports three transport types:
458
+
459
+ HTTP remote server:
460
+ omnidev add mcp <name> --transport http --url <url> [--header "Header: value"]
461
+
462
+ SSE remote server (deprecated):
463
+ omnidev add mcp <name> --transport sse --url <url> [--header "Header: value"]
464
+
465
+ Stdio local process (default):
466
+ omnidev add mcp <name> --command <cmd> [--args "arg1 arg2"] [--env KEY=value]
467
+
468
+ Examples:
469
+ omnidev add mcp notion --transport http --url https://mcp.notion.com/mcp
470
+ omnidev add mcp secure-api --transport http --url https://api.example.com/mcp --header "Authorization: Bearer token"
471
+ omnidev add mcp filesystem --command npx --args "-y @modelcontextprotocol/server-filesystem /path"
472
+ omnidev add mcp database --command node --args "./servers/db.js" --env DB_URL=postgres://localhost`
473
+ },
474
+ parameters: {
475
+ flags: {
476
+ transport: {
477
+ kind: "parsed",
478
+ brief: "Transport type: stdio (default), http, or sse",
479
+ parse: String,
480
+ optional: true
481
+ },
482
+ url: {
483
+ kind: "parsed",
484
+ brief: "URL for http/sse transport",
485
+ parse: String,
486
+ optional: true
487
+ },
488
+ command: {
489
+ kind: "parsed",
490
+ brief: "Command to run for stdio transport",
491
+ parse: String,
492
+ optional: true
493
+ },
494
+ args: {
495
+ kind: "parsed",
496
+ brief: "Arguments for the command (space-separated, use quotes for args with spaces)",
497
+ parse: String,
498
+ optional: true
499
+ },
500
+ header: {
501
+ kind: "parsed",
502
+ brief: "HTTP header in 'Name: Value' format (repeatable)",
503
+ parse: String,
504
+ optional: true,
505
+ variadic: true
506
+ },
507
+ env: {
508
+ kind: "parsed",
509
+ brief: "Environment variable in KEY=value format (repeatable)",
510
+ parse: String,
511
+ optional: true,
512
+ variadic: true
513
+ }
514
+ },
515
+ positional: {
516
+ kind: "tuple",
517
+ parameters: [
518
+ {
519
+ brief: "MCP name",
520
+ parse: String
521
+ }
522
+ ]
523
+ },
524
+ aliases: {
525
+ t: "transport",
526
+ u: "url",
527
+ c: "command",
528
+ a: "args",
529
+ e: "env"
530
+ }
531
+ },
532
+ func: runAddMcpWrapper
533
+ });
534
+ var addRoutes = buildRouteMap({
535
+ routes: {
536
+ cap: addCapCommand,
537
+ mcp: addMcpCommand
538
+ },
539
+ docs: {
540
+ brief: "Add capabilities or MCP servers"
541
+ }
542
+ });
543
+
204
544
  // src/commands/capability.ts
205
545
  import {
206
546
  disableCapability,
@@ -208,9 +548,9 @@ import {
208
548
  enableCapability,
209
549
  getEnabledCapabilities,
210
550
  loadCapabilityConfig,
211
- syncAgentConfiguration
551
+ syncAgentConfiguration as syncAgentConfiguration2
212
552
  } from "@omnidev-ai/core";
213
- import { buildCommand, buildRouteMap } from "@stricli/core";
553
+ import { buildCommand as buildCommand2, buildRouteMap as buildRouteMap2 } from "@stricli/core";
214
554
  async function runCapabilityList() {
215
555
  try {
216
556
  const enabledIds = await getEnabledCapabilities();
@@ -261,7 +601,7 @@ async function runCapabilityEnable(_flags, name) {
261
601
  console.log(`✓ Enabled capability: ${name}`);
262
602
  console.log("");
263
603
  const adapters = await getEnabledAdapters();
264
- await syncAgentConfiguration({ adapters });
604
+ await syncAgentConfiguration2({ adapters });
265
605
  } catch (error) {
266
606
  console.error("Error enabling capability:", error);
267
607
  process.exit(1);
@@ -273,13 +613,13 @@ async function runCapabilityDisable(_flags, name) {
273
613
  console.log(`✓ Disabled capability: ${name}`);
274
614
  console.log("");
275
615
  const adapters = await getEnabledAdapters();
276
- await syncAgentConfiguration({ adapters });
616
+ await syncAgentConfiguration2({ adapters });
277
617
  } catch (error) {
278
618
  console.error("Error disabling capability:", error);
279
619
  process.exit(1);
280
620
  }
281
621
  }
282
- var listCommand = buildCommand({
622
+ var listCommand = buildCommand2({
283
623
  docs: {
284
624
  brief: "List all discovered capabilities"
285
625
  },
@@ -288,7 +628,7 @@ var listCommand = buildCommand({
288
628
  await runCapabilityList();
289
629
  }
290
630
  });
291
- var enableCommand = buildCommand({
631
+ var enableCommand = buildCommand2({
292
632
  docs: {
293
633
  brief: "Enable a capability"
294
634
  },
@@ -306,7 +646,7 @@ var enableCommand = buildCommand({
306
646
  },
307
647
  func: runCapabilityEnable
308
648
  });
309
- var disableCommand = buildCommand({
649
+ var disableCommand = buildCommand2({
310
650
  docs: {
311
651
  brief: "Disable a capability"
312
652
  },
@@ -324,7 +664,7 @@ var disableCommand = buildCommand({
324
664
  },
325
665
  func: runCapabilityDisable
326
666
  });
327
- var capabilityRoutes = buildRouteMap({
667
+ var capabilityRoutes = buildRouteMap2({
328
668
  routes: {
329
669
  list: listCommand,
330
670
  enable: enableCommand,
@@ -336,12 +676,12 @@ var capabilityRoutes = buildRouteMap({
336
676
  });
337
677
 
338
678
  // src/commands/doctor.ts
339
- import { existsSync as existsSync4 } from "node:fs";
679
+ import { existsSync as existsSync5 } from "node:fs";
340
680
  import { execFile } from "node:child_process";
341
- import { readFile } from "node:fs/promises";
681
+ import { readFile as readFile4 } from "node:fs/promises";
342
682
  import { promisify } from "node:util";
343
- import { buildCommand as buildCommand2 } from "@stricli/core";
344
- var doctorCommand = buildCommand2({
683
+ import { buildCommand as buildCommand3 } from "@stricli/core";
684
+ var doctorCommand = buildCommand3({
345
685
  docs: {
346
686
  brief: "Check OmniDev setup and dependencies"
347
687
  },
@@ -426,7 +766,7 @@ async function checkPackageManager() {
426
766
  }
427
767
  }
428
768
  async function checkOmniLocalDir() {
429
- const exists = existsSync4(".omni");
769
+ const exists = existsSync5(".omni");
430
770
  if (!exists) {
431
771
  return {
432
772
  name: ".omni/ directory",
@@ -443,7 +783,7 @@ async function checkOmniLocalDir() {
443
783
  }
444
784
  async function checkConfig() {
445
785
  const configPath = "omni.toml";
446
- if (!existsSync4(configPath)) {
786
+ if (!existsSync5(configPath)) {
447
787
  return {
448
788
  name: "Configuration",
449
789
  passed: false,
@@ -470,7 +810,7 @@ async function checkConfig() {
470
810
  }
471
811
  async function checkRootGitignore() {
472
812
  const gitignorePath = ".gitignore";
473
- if (!existsSync4(gitignorePath)) {
813
+ if (!existsSync5(gitignorePath)) {
474
814
  return {
475
815
  name: "Root .gitignore",
476
816
  passed: false,
@@ -478,7 +818,7 @@ async function checkRootGitignore() {
478
818
  fix: "Run: omnidev init"
479
819
  };
480
820
  }
481
- const content = await readFile(gitignorePath, "utf-8");
821
+ const content = await readFile4(gitignorePath, "utf-8");
482
822
  const lines = content.split(`
483
823
  `).map((line) => line.trim());
484
824
  const hasOmniDir = lines.includes(".omni/");
@@ -504,7 +844,7 @@ async function checkRootGitignore() {
504
844
  }
505
845
  async function checkCapabilitiesDir() {
506
846
  const capabilitiesDirPath = ".omni/capabilities";
507
- if (!existsSync4(capabilitiesDirPath)) {
847
+ if (!existsSync5(capabilitiesDirPath)) {
508
848
  return {
509
849
  name: "Capabilities Directory",
510
850
  passed: true,
@@ -519,20 +859,32 @@ async function checkCapabilitiesDir() {
519
859
  }
520
860
 
521
861
  // src/commands/init.ts
522
- import { existsSync as existsSync5, mkdirSync as mkdirSync4 } from "node:fs";
523
- import { readFile as readFile2, writeFile as writeFile5 } from "node:fs/promises";
862
+ import { exec } from "node:child_process";
863
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4 } from "node:fs";
864
+ import { readFile as readFile5, writeFile as writeFile5 } from "node:fs/promises";
865
+ import { promisify as promisify2 } from "node:util";
524
866
  import {
525
- generateInstructionsTemplate,
867
+ generateOmniMdTemplate,
526
868
  loadConfig,
527
869
  setActiveProfile,
528
- syncAgentConfiguration as syncAgentConfiguration2,
529
- writeConfig,
870
+ syncAgentConfiguration as syncAgentConfiguration3,
871
+ writeConfig as writeConfig2,
530
872
  writeEnabledProviders
531
873
  } from "@omnidev-ai/core";
532
- import { buildCommand as buildCommand3 } from "@stricli/core";
874
+ import { buildCommand as buildCommand4 } from "@stricli/core";
533
875
 
534
876
  // src/prompts/provider.ts
535
- import { checkbox } from "@inquirer/prompts";
877
+ import { checkbox, confirm } from "@inquirer/prompts";
878
+ var PROVIDER_GITIGNORE_FILES = {
879
+ claude: ["CLAUDE.md", ".claude/"],
880
+ "claude-code": ["CLAUDE.md", ".claude/"],
881
+ cursor: [".cursor/"],
882
+ codex: ["AGENTS.md", ".codex/"],
883
+ opencode: [".opencode/"]
884
+ };
885
+ function getProviderGitignoreFiles(providers) {
886
+ return providers.flatMap((p) => PROVIDER_GITIGNORE_FILES[p] ?? []);
887
+ }
536
888
  async function promptForProviders() {
537
889
  const answers = await checkbox({
538
890
  message: "Select your AI provider(s):",
@@ -546,8 +898,16 @@ async function promptForProviders() {
546
898
  });
547
899
  return answers;
548
900
  }
901
+ async function promptForGitignoreProviderFiles(selectedProviders) {
902
+ const filesToIgnore = getProviderGitignoreFiles(selectedProviders);
903
+ return confirm({
904
+ message: `Add provider files to .gitignore? (${filesToIgnore.join(", ")})`,
905
+ default: false
906
+ });
907
+ }
549
908
 
550
909
  // src/commands/init.ts
910
+ var execAsync = promisify2(exec);
551
911
  async function runInit(_flags, providerArg) {
552
912
  console.log("Initializing OmniDev...");
553
913
  mkdirSync4(".omni", { recursive: true });
@@ -555,15 +915,31 @@ async function runInit(_flags, providerArg) {
555
915
  mkdirSync4(".omni/state", { recursive: true });
556
916
  await updateRootGitignore();
557
917
  let providerIds;
918
+ const isInteractive = !providerArg;
558
919
  if (providerArg) {
559
920
  providerIds = parseProviderArg(providerArg);
560
921
  } else {
561
922
  providerIds = await promptForProviders();
562
923
  }
924
+ if (isInteractive) {
925
+ const shouldIgnoreProviderFiles = await promptForGitignoreProviderFiles(providerIds);
926
+ if (shouldIgnoreProviderFiles) {
927
+ const filesToIgnore = getProviderGitignoreFiles(providerIds);
928
+ await addProviderFilesToGitignore(filesToIgnore);
929
+ const trackedFiles = await getTrackedProviderFiles(filesToIgnore);
930
+ if (trackedFiles.length > 0) {
931
+ console.log("");
932
+ console.log("⚠️ Some provider files are already tracked in git.");
933
+ console.log(" Run the following to stop tracking them:");
934
+ console.log("");
935
+ console.log(` git rm --cached ${trackedFiles.join(" ")}`);
936
+ console.log("");
937
+ }
938
+ }
939
+ }
563
940
  await writeEnabledProviders(providerIds);
564
- if (!existsSync5("omni.toml")) {
565
- await writeConfig({
566
- project: "my-project",
941
+ if (!existsSync6("omni.toml")) {
942
+ await writeConfig2({
567
943
  profiles: {
568
944
  default: {
569
945
  capabilities: []
@@ -578,8 +954,8 @@ async function runInit(_flags, providerArg) {
578
954
  });
579
955
  await setActiveProfile("default");
580
956
  }
581
- if (!existsSync5(".omni/instructions.md")) {
582
- await writeFile5(".omni/instructions.md", generateInstructionsTemplate(), "utf-8");
957
+ if (!existsSync6("OMNI.md")) {
958
+ await writeFile5("OMNI.md", generateOmniMdTemplate(), "utf-8");
583
959
  }
584
960
  const config = await loadConfig();
585
961
  const ctx = {
@@ -588,39 +964,22 @@ async function runInit(_flags, providerArg) {
588
964
  };
589
965
  const allAdapters = getAllAdapters();
590
966
  const selectedAdapters = allAdapters.filter((a) => providerIds.includes(a.id));
591
- const filesCreated = [];
592
- const filesExisting = [];
593
967
  for (const adapter of selectedAdapters) {
594
968
  if (adapter.init) {
595
- const result = await adapter.init(ctx);
596
- if (result.filesCreated) {
597
- filesCreated.push(...result.filesCreated);
598
- }
969
+ await adapter.init(ctx);
599
970
  }
600
971
  }
601
972
  const enabledAdapters = await getEnabledAdapters();
602
- await syncAgentConfiguration2({ silent: true, adapters: enabledAdapters });
973
+ await syncAgentConfiguration3({ silent: true, adapters: enabledAdapters });
603
974
  console.log("");
604
975
  console.log(`✓ OmniDev initialized for ${selectedAdapters.map((a) => a.displayName).join(" and ")}!`);
605
976
  console.log("");
606
- if (filesCreated.length > 0) {
607
- console.log("\uD83D\uDCDD Don't forget to add your project description to:");
608
- console.log(" • .omni/instructions.md");
609
- }
610
- if (filesExisting.length > 0) {
611
- console.log("\uD83D\uDCDD Add this line to your existing file(s):");
612
- for (const file of filesExisting) {
613
- console.log(` • ${file}: @import .omni/instructions.md`);
614
- }
615
- }
977
+ console.log("\uD83D\uDCDD Add your project description and instructions to OMNI.md");
978
+ console.log(" This will be transformed into provider-specific files (CLAUDE.md, AGENTS.md, etc.)");
616
979
  console.log("");
617
- console.log("\uD83D\uDCA1 Recommendation:");
618
- console.log(" Add provider-specific files to .gitignore:");
619
- console.log(" CLAUDE.md, .claude/, AGENTS.md, .cursor/, .mcp.json");
620
- console.log("");
621
- console.log(" Run 'omnidev capability list' to see available capabilities.");
980
+ console.log("\uD83D\uDCA1 Run 'omnidev capability list' to see available capabilities.");
622
981
  }
623
- var initCommand = buildCommand3({
982
+ var initCommand = buildCommand4({
624
983
  parameters: {
625
984
  flags: {},
626
985
  positional: {
@@ -660,11 +1019,17 @@ function parseProviderArg(arg) {
660
1019
  return result;
661
1020
  }
662
1021
  async function updateRootGitignore() {
663
- const gitignorePath = ".gitignore";
664
1022
  const entriesToAdd = [".omni/", "omni.local.toml"];
1023
+ await addToGitignore(entriesToAdd, "OmniDev");
1024
+ }
1025
+ async function addProviderFilesToGitignore(entries) {
1026
+ await addToGitignore(entries, "OmniDev Provider Files");
1027
+ }
1028
+ async function addToGitignore(entriesToAdd, sectionHeader) {
1029
+ const gitignorePath = ".gitignore";
665
1030
  let content = "";
666
- if (existsSync5(gitignorePath)) {
667
- content = await readFile2(gitignorePath, "utf-8");
1031
+ if (existsSync6(gitignorePath)) {
1032
+ content = await readFile5(gitignorePath, "utf-8");
668
1033
  }
669
1034
  const lines = content.split(`
670
1035
  `);
@@ -675,24 +1040,36 @@ async function updateRootGitignore() {
675
1040
  const needsNewline = content.length > 0 && !content.endsWith(`
676
1041
  `);
677
1042
  const section = `${needsNewline ? `
678
- ` : ""}# OmniDev
1043
+ ` : ""}# ${sectionHeader}
679
1044
  ${missingEntries.join(`
680
1045
  `)}
681
1046
  `;
682
1047
  await writeFile5(gitignorePath, content + section, "utf-8");
683
1048
  }
1049
+ async function getTrackedProviderFiles(files) {
1050
+ const tracked = [];
1051
+ for (const file of files) {
1052
+ try {
1053
+ const { stdout } = await execAsync(`git ls-files "${file}"`);
1054
+ if (stdout.trim()) {
1055
+ tracked.push(file);
1056
+ }
1057
+ } catch {}
1058
+ }
1059
+ return tracked;
1060
+ }
684
1061
 
685
1062
  // src/commands/profile.ts
686
- import { existsSync as existsSync6 } from "node:fs";
1063
+ import { existsSync as existsSync7 } from "node:fs";
687
1064
  import {
688
- getActiveProfile,
1065
+ getActiveProfile as getActiveProfile2,
689
1066
  loadConfig as loadConfig2,
690
1067
  resolveEnabledCapabilities,
691
1068
  setActiveProfile as setActiveProfile2,
692
- syncAgentConfiguration as syncAgentConfiguration3
1069
+ syncAgentConfiguration as syncAgentConfiguration4
693
1070
  } from "@omnidev-ai/core";
694
- import { buildCommand as buildCommand4, buildRouteMap as buildRouteMap2 } from "@stricli/core";
695
- var listCommand2 = buildCommand4({
1071
+ import { buildCommand as buildCommand5, buildRouteMap as buildRouteMap3 } from "@stricli/core";
1072
+ var listCommand2 = buildCommand5({
696
1073
  docs: {
697
1074
  brief: "List available profiles"
698
1075
  },
@@ -704,7 +1081,7 @@ var listCommand2 = buildCommand4({
704
1081
  async function runSetCommand(_flags, profileName) {
705
1082
  await runProfileSet(profileName);
706
1083
  }
707
- var setCommand = buildCommand4({
1084
+ var setCommand = buildCommand5({
708
1085
  docs: {
709
1086
  brief: "Set the active profile"
710
1087
  },
@@ -722,7 +1099,7 @@ var setCommand = buildCommand4({
722
1099
  },
723
1100
  func: runSetCommand
724
1101
  });
725
- var profileRoutes = buildRouteMap2({
1102
+ var profileRoutes = buildRouteMap3({
726
1103
  routes: {
727
1104
  list: listCommand2,
728
1105
  set: setCommand
@@ -733,13 +1110,13 @@ var profileRoutes = buildRouteMap2({
733
1110
  });
734
1111
  async function runProfileList() {
735
1112
  try {
736
- if (!existsSync6("omni.toml")) {
1113
+ if (!existsSync7("omni.toml")) {
737
1114
  console.log("✗ No config file found");
738
1115
  console.log(" Run: omnidev init");
739
1116
  process.exit(1);
740
1117
  }
741
1118
  const config = await loadConfig2();
742
- const activeProfile = await getActiveProfile() ?? config.active_profile ?? "default";
1119
+ const activeProfile = await getActiveProfile2() ?? config.active_profile ?? "default";
743
1120
  const profiles = config.profiles ?? {};
744
1121
  const profileNames = Object.keys(profiles);
745
1122
  if (profileNames.length === 0) {
@@ -773,7 +1150,7 @@ async function runProfileList() {
773
1150
  }
774
1151
  async function runProfileSet(profileName) {
775
1152
  try {
776
- if (!existsSync6("omni.toml")) {
1153
+ if (!existsSync7("omni.toml")) {
777
1154
  console.log("✗ No config file found");
778
1155
  console.log(" Run: omnidev init");
779
1156
  process.exit(1);
@@ -798,7 +1175,7 @@ async function runProfileSet(profileName) {
798
1175
  console.log(`✓ Active profile set to: ${profileName}`);
799
1176
  console.log("");
800
1177
  const adapters = await getEnabledAdapters();
801
- await syncAgentConfiguration3({ adapters });
1178
+ await syncAgentConfiguration4({ adapters });
802
1179
  } catch (error) {
803
1180
  console.error("✗ Error setting profile:", error);
804
1181
  process.exit(1);
@@ -810,9 +1187,9 @@ import {
810
1187
  disableProvider,
811
1188
  enableProvider,
812
1189
  readEnabledProviders as readEnabledProviders2,
813
- syncAgentConfiguration as syncAgentConfiguration4
1190
+ syncAgentConfiguration as syncAgentConfiguration5
814
1191
  } from "@omnidev-ai/core";
815
- import { buildCommand as buildCommand5, buildRouteMap as buildRouteMap3 } from "@stricli/core";
1192
+ import { buildCommand as buildCommand6, buildRouteMap as buildRouteMap4 } from "@stricli/core";
816
1193
  async function runProviderList() {
817
1194
  const enabled = await readEnabledProviders2();
818
1195
  const allAdapters = getAllAdapters();
@@ -845,7 +1222,7 @@ async function runProviderEnable(_flags, providerId) {
845
1222
  await enableProvider(providerId);
846
1223
  console.log(`✓ Enabled provider: ${adapter.displayName}`);
847
1224
  const enabledAdapters = await getEnabledAdapters();
848
- await syncAgentConfiguration4({ silent: false, adapters: enabledAdapters });
1225
+ await syncAgentConfiguration5({ silent: false, adapters: enabledAdapters });
849
1226
  }
850
1227
  async function runProviderDisable(_flags, providerId) {
851
1228
  if (!providerId) {
@@ -866,7 +1243,7 @@ async function runProviderDisable(_flags, providerId) {
866
1243
  await disableProvider(providerId);
867
1244
  console.log(`✓ Disabled provider: ${adapter.displayName}`);
868
1245
  }
869
- var listCommand3 = buildCommand5({
1246
+ var listCommand3 = buildCommand6({
870
1247
  parameters: {
871
1248
  flags: {},
872
1249
  positional: { kind: "tuple", parameters: [] }
@@ -876,7 +1253,7 @@ var listCommand3 = buildCommand5({
876
1253
  },
877
1254
  func: runProviderList
878
1255
  });
879
- var enableCommand2 = buildCommand5({
1256
+ var enableCommand2 = buildCommand6({
880
1257
  parameters: {
881
1258
  flags: {},
882
1259
  positional: {
@@ -895,7 +1272,7 @@ var enableCommand2 = buildCommand5({
895
1272
  },
896
1273
  func: runProviderEnable
897
1274
  });
898
- var disableCommand2 = buildCommand5({
1275
+ var disableCommand2 = buildCommand6({
899
1276
  parameters: {
900
1277
  flags: {},
901
1278
  positional: {
@@ -914,7 +1291,7 @@ var disableCommand2 = buildCommand5({
914
1291
  },
915
1292
  func: runProviderDisable
916
1293
  });
917
- var providerRoutes = buildRouteMap3({
1294
+ var providerRoutes = buildRouteMap4({
918
1295
  routes: {
919
1296
  list: listCommand3,
920
1297
  enable: enableCommand2,
@@ -926,9 +1303,9 @@ var providerRoutes = buildRouteMap3({
926
1303
  });
927
1304
 
928
1305
  // src/commands/sync.ts
929
- import { getActiveProfile as getActiveProfile2, loadConfig as loadConfig3, syncAgentConfiguration as syncAgentConfiguration5 } from "@omnidev-ai/core";
930
- import { buildCommand as buildCommand6 } from "@stricli/core";
931
- var syncCommand = buildCommand6({
1306
+ import { getActiveProfile as getActiveProfile3, loadConfig as loadConfig3, syncAgentConfiguration as syncAgentConfiguration6 } from "@omnidev-ai/core";
1307
+ import { buildCommand as buildCommand7 } from "@stricli/core";
1308
+ var syncCommand = buildCommand7({
932
1309
  docs: {
933
1310
  brief: "Manually sync all capabilities, roles, and instructions"
934
1311
  },
@@ -942,9 +1319,9 @@ async function runSync() {
942
1319
  console.log("");
943
1320
  try {
944
1321
  const config = await loadConfig3();
945
- const activeProfile = await getActiveProfile2() ?? config.active_profile ?? "default";
1322
+ const activeProfile = await getActiveProfile3() ?? config.active_profile ?? "default";
946
1323
  const adapters = await getEnabledAdapters();
947
- const result = await syncAgentConfiguration5({ silent: false, adapters });
1324
+ const result = await syncAgentConfiguration6({ silent: false, adapters });
948
1325
  console.log("");
949
1326
  console.log("✓ Sync completed successfully!");
950
1327
  console.log("");
@@ -985,12 +1362,13 @@ async function buildDynamicApp() {
985
1362
  init: initCommand,
986
1363
  doctor: doctorCommand,
987
1364
  sync: syncCommand,
1365
+ add: addRoutes,
988
1366
  capability: capabilityRoutes,
989
1367
  profile: profileRoutes,
990
1368
  provider: providerRoutes
991
1369
  };
992
1370
  debug("Core routes registered", Object.keys(routes));
993
- if (existsSync7(".omni/config.toml")) {
1371
+ if (existsSync8(".omni/config.toml")) {
994
1372
  try {
995
1373
  const capabilityCommands = await loadCapabilityCommands();
996
1374
  debug("Capability commands loaded", {
@@ -1011,7 +1389,7 @@ async function buildDynamicApp() {
1011
1389
  }
1012
1390
  }
1013
1391
  debug("Final routes", Object.keys(routes));
1014
- const app = buildApplication(buildRouteMap4({
1392
+ const app = buildApplication(buildRouteMap5({
1015
1393
  routes,
1016
1394
  docs: {
1017
1395
  brief: "OmniDev commands"
@@ -1061,9 +1439,9 @@ async function loadCapabilityCommands() {
1061
1439
  async function loadCapabilityExport(capability) {
1062
1440
  const capabilityPath = join5(process.cwd(), capability.path);
1063
1441
  const indexPath = join5(capabilityPath, "index.ts");
1064
- if (!existsSync7(indexPath)) {
1442
+ if (!existsSync8(indexPath)) {
1065
1443
  const jsIndexPath = join5(capabilityPath, "index.js");
1066
- if (!existsSync7(jsIndexPath)) {
1444
+ if (!existsSync8(jsIndexPath)) {
1067
1445
  return null;
1068
1446
  }
1069
1447
  const module2 = await import(jsIndexPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnidev-ai/cli",
3
- "version": "0.6.2",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@inquirer/prompts": "^8.1.0",
31
- "@omnidev-ai/core": "0.6.2",
31
+ "@omnidev-ai/core": "0.8.0",
32
32
  "@stricli/core": "^1.2.5"
33
33
  },
34
34
  "devDependencies": {