@omnidev-ai/cli 0.1.1 → 0.4.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.js +772 -0
- package/package.json +10 -7
- package/src/commands/AGENTS.md +0 -43
- package/src/commands/capability.test.ts +0 -483
- package/src/commands/capability.ts +0 -163
- package/src/commands/doctor.test.ts +0 -190
- package/src/commands/doctor.ts +0 -164
- package/src/commands/init.test.ts +0 -264
- package/src/commands/init.ts +0 -192
- package/src/commands/mcp.ts +0 -113
- package/src/commands/profile.test.ts +0 -351
- package/src/commands/profile.ts +0 -151
- package/src/commands/serve.test.ts +0 -181
- package/src/commands/serve.ts +0 -63
- package/src/commands/sync.ts +0 -43
- package/src/index.ts +0 -55
- package/src/lib/debug.ts +0 -4
- package/src/lib/dynamic-app.ts +0 -182
- package/src/prompts/provider.ts +0 -15
package/dist/index.js
ADDED
|
@@ -0,0 +1,772 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { buildApplication, buildCommand, buildRouteMap, run } from "@stricli/core";
|
|
3
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { getAllAdapters, getEnabledAdapters } from "@omnidev-ai/adapters";
|
|
6
|
+
import { debug, disableCapability, disableProvider, discoverCapabilities, enableCapability, enableProvider, generateInstructionsTemplate, getActiveProfile, getEnabledCapabilities, loadCapabilityConfig, loadConfig, readEnabledProviders, resolveEnabledCapabilities, setActiveProfile, syncAgentConfiguration, writeConfig, writeEnabledProviders } from "@omnidev-ai/core";
|
|
7
|
+
import { checkbox } from "@inquirer/prompts";
|
|
8
|
+
|
|
9
|
+
//#region src/commands/capability.ts
|
|
10
|
+
/**
|
|
11
|
+
* Run the capability list command.
|
|
12
|
+
*/
|
|
13
|
+
async function runCapabilityList() {
|
|
14
|
+
try {
|
|
15
|
+
const enabledIds = await getEnabledCapabilities();
|
|
16
|
+
const capabilityPaths = await discoverCapabilities();
|
|
17
|
+
if (capabilityPaths.length === 0) {
|
|
18
|
+
console.log("No capabilities found.");
|
|
19
|
+
console.log("");
|
|
20
|
+
console.log("To add capabilities, create directories in omni/capabilities/");
|
|
21
|
+
console.log("Each capability must have a capability.toml file.");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
console.log("Capabilities:");
|
|
25
|
+
console.log("");
|
|
26
|
+
for (const path of capabilityPaths) try {
|
|
27
|
+
const capConfig = await loadCapabilityConfig(path);
|
|
28
|
+
const isEnabled = enabledIds.includes(capConfig.capability.id);
|
|
29
|
+
const status = isEnabled ? "✓ enabled" : "✗ disabled";
|
|
30
|
+
const { id, name, version } = capConfig.capability;
|
|
31
|
+
console.log(` ${status} ${name}`);
|
|
32
|
+
console.log(` ID: ${id}`);
|
|
33
|
+
console.log(` Version: ${version}`);
|
|
34
|
+
console.log("");
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(` ✗ Failed to load capability at ${path}:`, error);
|
|
37
|
+
console.log("");
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error("Error listing capabilities:", error);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Run the capability enable command.
|
|
46
|
+
*/
|
|
47
|
+
async function runCapabilityEnable(_flags, name) {
|
|
48
|
+
try {
|
|
49
|
+
const capabilityPaths = await discoverCapabilities();
|
|
50
|
+
const capabilityExists = capabilityPaths.some(async (path) => {
|
|
51
|
+
const config = await loadCapabilityConfig(path);
|
|
52
|
+
return config.capability.id === name;
|
|
53
|
+
});
|
|
54
|
+
if (!capabilityExists) {
|
|
55
|
+
console.error(`Error: Capability '${name}' not found`);
|
|
56
|
+
console.log("");
|
|
57
|
+
console.log("Run 'dev capability list' to see available capabilities");
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
await enableCapability(name);
|
|
61
|
+
console.log(`✓ Enabled capability: ${name}`);
|
|
62
|
+
console.log("");
|
|
63
|
+
const adapters = await getEnabledAdapters();
|
|
64
|
+
await syncAgentConfiguration({ adapters });
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error("Error enabling capability:", error);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Run the capability disable command.
|
|
72
|
+
*/
|
|
73
|
+
async function runCapabilityDisable(_flags, name) {
|
|
74
|
+
try {
|
|
75
|
+
await disableCapability(name);
|
|
76
|
+
console.log(`✓ Disabled capability: ${name}`);
|
|
77
|
+
console.log("");
|
|
78
|
+
const adapters = await getEnabledAdapters();
|
|
79
|
+
await syncAgentConfiguration({ adapters });
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error("Error disabling capability:", error);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const listCommand$2 = buildCommand({
|
|
86
|
+
docs: { brief: "List all discovered capabilities" },
|
|
87
|
+
parameters: {},
|
|
88
|
+
async func() {
|
|
89
|
+
await runCapabilityList();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const enableCommand$1 = buildCommand({
|
|
93
|
+
docs: { brief: "Enable a capability" },
|
|
94
|
+
parameters: {
|
|
95
|
+
flags: {},
|
|
96
|
+
positional: {
|
|
97
|
+
kind: "tuple",
|
|
98
|
+
parameters: [{
|
|
99
|
+
brief: "Capability name to enable",
|
|
100
|
+
parse: String
|
|
101
|
+
}]
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
func: runCapabilityEnable
|
|
105
|
+
});
|
|
106
|
+
const disableCommand$1 = buildCommand({
|
|
107
|
+
docs: { brief: "Disable a capability" },
|
|
108
|
+
parameters: {
|
|
109
|
+
flags: {},
|
|
110
|
+
positional: {
|
|
111
|
+
kind: "tuple",
|
|
112
|
+
parameters: [{
|
|
113
|
+
brief: "Capability name to disable",
|
|
114
|
+
parse: String
|
|
115
|
+
}]
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
func: runCapabilityDisable
|
|
119
|
+
});
|
|
120
|
+
const capabilityRoutes = buildRouteMap({
|
|
121
|
+
routes: {
|
|
122
|
+
list: listCommand$2,
|
|
123
|
+
enable: enableCommand$1,
|
|
124
|
+
disable: disableCommand$1
|
|
125
|
+
},
|
|
126
|
+
docs: { brief: "Manage capabilities" }
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/commands/doctor.ts
|
|
131
|
+
const doctorCommand = buildCommand({
|
|
132
|
+
docs: { brief: "Check OmniDev setup and dependencies" },
|
|
133
|
+
parameters: {},
|
|
134
|
+
async func() {
|
|
135
|
+
return await runDoctor();
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
async function runDoctor() {
|
|
139
|
+
console.log("OmniDev Doctor");
|
|
140
|
+
console.log("==============");
|
|
141
|
+
console.log("");
|
|
142
|
+
const checks = [
|
|
143
|
+
checkBunVersion(),
|
|
144
|
+
checkOmniLocalDir(),
|
|
145
|
+
checkConfig(),
|
|
146
|
+
checkRootGitignore(),
|
|
147
|
+
checkCapabilitiesDir()
|
|
148
|
+
];
|
|
149
|
+
let allPassed = true;
|
|
150
|
+
for (const check of checks) {
|
|
151
|
+
const { name, passed, message, fix } = await check;
|
|
152
|
+
const icon = passed ? "✓" : "✗";
|
|
153
|
+
console.log(`${icon} ${name}: ${message}`);
|
|
154
|
+
if (!passed && fix) console.log(` Fix: ${fix}`);
|
|
155
|
+
if (!passed) allPassed = false;
|
|
156
|
+
}
|
|
157
|
+
console.log("");
|
|
158
|
+
if (allPassed) console.log("All checks passed!");
|
|
159
|
+
else {
|
|
160
|
+
console.log("Some checks failed. Please fix the issues above.");
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async function checkBunVersion() {
|
|
165
|
+
const version = Bun.version;
|
|
166
|
+
const parts = version.split(".");
|
|
167
|
+
const firstPart = parts[0];
|
|
168
|
+
if (!firstPart) return {
|
|
169
|
+
name: "Bun Version",
|
|
170
|
+
passed: false,
|
|
171
|
+
message: `Invalid version format: ${version}`,
|
|
172
|
+
fix: "Reinstall Bun: curl -fsSL https://bun.sh/install | bash"
|
|
173
|
+
};
|
|
174
|
+
const major = Number.parseInt(firstPart, 10);
|
|
175
|
+
if (major < 1) return {
|
|
176
|
+
name: "Bun Version",
|
|
177
|
+
passed: false,
|
|
178
|
+
message: `v${version}`,
|
|
179
|
+
fix: "Upgrade Bun: curl -fsSL https://bun.sh/install | bash"
|
|
180
|
+
};
|
|
181
|
+
return {
|
|
182
|
+
name: "Bun Version",
|
|
183
|
+
passed: true,
|
|
184
|
+
message: `v${version}`
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
async function checkOmniLocalDir() {
|
|
188
|
+
const exists = existsSync(".omni");
|
|
189
|
+
if (!exists) return {
|
|
190
|
+
name: ".omni/ directory",
|
|
191
|
+
passed: false,
|
|
192
|
+
message: "Not found",
|
|
193
|
+
fix: "Run: omnidev init"
|
|
194
|
+
};
|
|
195
|
+
return {
|
|
196
|
+
name: ".omni/ directory",
|
|
197
|
+
passed: true,
|
|
198
|
+
message: "Found"
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
async function checkConfig() {
|
|
202
|
+
const configPath = "omni.toml";
|
|
203
|
+
if (!existsSync(configPath)) return {
|
|
204
|
+
name: "Configuration",
|
|
205
|
+
passed: false,
|
|
206
|
+
message: "omni.toml not found",
|
|
207
|
+
fix: "Run: omnidev init"
|
|
208
|
+
};
|
|
209
|
+
try {
|
|
210
|
+
const { loadConfig: loadConfig$1 } = await import("@omnidev-ai/core");
|
|
211
|
+
await loadConfig$1();
|
|
212
|
+
return {
|
|
213
|
+
name: "Configuration",
|
|
214
|
+
passed: true,
|
|
215
|
+
message: "Valid"
|
|
216
|
+
};
|
|
217
|
+
} catch (error) {
|
|
218
|
+
return {
|
|
219
|
+
name: "Configuration",
|
|
220
|
+
passed: false,
|
|
221
|
+
message: `Invalid: ${error instanceof Error ? error.message : String(error)}`,
|
|
222
|
+
fix: "Check omni.toml syntax"
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
async function checkRootGitignore() {
|
|
227
|
+
const gitignorePath = ".gitignore";
|
|
228
|
+
if (!existsSync(gitignorePath)) return {
|
|
229
|
+
name: "Root .gitignore",
|
|
230
|
+
passed: false,
|
|
231
|
+
message: ".gitignore not found",
|
|
232
|
+
fix: "Run: omnidev init"
|
|
233
|
+
};
|
|
234
|
+
const content = await Bun.file(gitignorePath).text();
|
|
235
|
+
const lines = content.split("\n").map((line) => line.trim());
|
|
236
|
+
const hasOmniDir = lines.includes(".omni/");
|
|
237
|
+
const hasLocalToml = lines.includes("omni.local.toml");
|
|
238
|
+
if (!hasOmniDir || !hasLocalToml) {
|
|
239
|
+
const missing = [];
|
|
240
|
+
if (!hasOmniDir) missing.push(".omni/");
|
|
241
|
+
if (!hasLocalToml) missing.push("omni.local.toml");
|
|
242
|
+
return {
|
|
243
|
+
name: "Root .gitignore",
|
|
244
|
+
passed: false,
|
|
245
|
+
message: `Missing entries: ${missing.join(", ")}`,
|
|
246
|
+
fix: "Run: omnidev init"
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
name: "Root .gitignore",
|
|
251
|
+
passed: true,
|
|
252
|
+
message: "Found with OmniDev entries"
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
async function checkCapabilitiesDir() {
|
|
256
|
+
const capabilitiesDirPath = ".omni/capabilities";
|
|
257
|
+
if (!existsSync(capabilitiesDirPath)) return {
|
|
258
|
+
name: "Capabilities Directory",
|
|
259
|
+
passed: true,
|
|
260
|
+
message: "Not found (no custom capabilities)"
|
|
261
|
+
};
|
|
262
|
+
return {
|
|
263
|
+
name: "Capabilities Directory",
|
|
264
|
+
passed: true,
|
|
265
|
+
message: "Found"
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
//#endregion
|
|
270
|
+
//#region src/prompts/provider.ts
|
|
271
|
+
async function promptForProviders() {
|
|
272
|
+
const answers = await checkbox({
|
|
273
|
+
message: "Select your AI provider(s):",
|
|
274
|
+
choices: [
|
|
275
|
+
{
|
|
276
|
+
name: "Claude Code (Claude CLI)",
|
|
277
|
+
value: "claude-code",
|
|
278
|
+
checked: true
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
name: "Cursor",
|
|
282
|
+
value: "cursor",
|
|
283
|
+
checked: false
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
name: "Codex",
|
|
287
|
+
value: "codex",
|
|
288
|
+
checked: false
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
name: "OpenCode",
|
|
292
|
+
value: "opencode",
|
|
293
|
+
checked: false
|
|
294
|
+
}
|
|
295
|
+
],
|
|
296
|
+
required: true
|
|
297
|
+
});
|
|
298
|
+
return answers;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
//#endregion
|
|
302
|
+
//#region src/commands/init.ts
|
|
303
|
+
async function runInit(_flags, providerArg) {
|
|
304
|
+
console.log("Initializing OmniDev...");
|
|
305
|
+
mkdirSync(".omni", { recursive: true });
|
|
306
|
+
mkdirSync(".omni/capabilities", { recursive: true });
|
|
307
|
+
mkdirSync(".omni/state", { recursive: true });
|
|
308
|
+
await updateRootGitignore();
|
|
309
|
+
let providerIds;
|
|
310
|
+
if (providerArg) providerIds = parseProviderArg(providerArg);
|
|
311
|
+
else providerIds = await promptForProviders();
|
|
312
|
+
await writeEnabledProviders(providerIds);
|
|
313
|
+
if (!existsSync("omni.toml")) {
|
|
314
|
+
await writeConfig({
|
|
315
|
+
project: "my-project",
|
|
316
|
+
profiles: {
|
|
317
|
+
default: { capabilities: [] },
|
|
318
|
+
planning: { capabilities: [] },
|
|
319
|
+
coding: { capabilities: [] }
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
await setActiveProfile("default");
|
|
323
|
+
}
|
|
324
|
+
if (!existsSync(".omni/instructions.md")) await Bun.write(".omni/instructions.md", generateInstructionsTemplate());
|
|
325
|
+
const config = await loadConfig();
|
|
326
|
+
const ctx = {
|
|
327
|
+
projectRoot: process.cwd(),
|
|
328
|
+
config
|
|
329
|
+
};
|
|
330
|
+
const allAdapters = getAllAdapters();
|
|
331
|
+
const selectedAdapters = allAdapters.filter((a) => providerIds.includes(a.id));
|
|
332
|
+
const filesCreated = [];
|
|
333
|
+
const filesExisting = [];
|
|
334
|
+
for (const adapter of selectedAdapters) if (adapter.init) {
|
|
335
|
+
const result = await adapter.init(ctx);
|
|
336
|
+
if (result.filesCreated) filesCreated.push(...result.filesCreated);
|
|
337
|
+
}
|
|
338
|
+
const enabledAdapters = await getEnabledAdapters();
|
|
339
|
+
await syncAgentConfiguration({
|
|
340
|
+
silent: true,
|
|
341
|
+
adapters: enabledAdapters
|
|
342
|
+
});
|
|
343
|
+
console.log("");
|
|
344
|
+
console.log(`✓ OmniDev initialized for ${selectedAdapters.map((a) => a.displayName).join(" and ")}!`);
|
|
345
|
+
console.log("");
|
|
346
|
+
if (filesCreated.length > 0) {
|
|
347
|
+
console.log("📝 Don't forget to add your project description to:");
|
|
348
|
+
console.log(" • .omni/instructions.md");
|
|
349
|
+
}
|
|
350
|
+
if (filesExisting.length > 0) {
|
|
351
|
+
console.log("📝 Add this line to your existing file(s):");
|
|
352
|
+
for (const file of filesExisting) console.log(` • ${file}: @import .omni/instructions.md`);
|
|
353
|
+
}
|
|
354
|
+
console.log("");
|
|
355
|
+
console.log("💡 Recommendation:");
|
|
356
|
+
console.log(" Add provider-specific files to .gitignore:");
|
|
357
|
+
console.log(" CLAUDE.md, .claude/, AGENTS.md, .cursor/, .mcp.json");
|
|
358
|
+
console.log("");
|
|
359
|
+
console.log(" Run 'omnidev capability list' to see available capabilities.");
|
|
360
|
+
}
|
|
361
|
+
const initCommand = buildCommand({
|
|
362
|
+
parameters: {
|
|
363
|
+
flags: {},
|
|
364
|
+
positional: {
|
|
365
|
+
kind: "tuple",
|
|
366
|
+
parameters: [{
|
|
367
|
+
brief: "AI provider(s): claude-code, cursor, codex, opencode, or comma-separated",
|
|
368
|
+
parse: String,
|
|
369
|
+
optional: true
|
|
370
|
+
}]
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
docs: { brief: "Initialize OmniDev in the current project" },
|
|
374
|
+
func: runInit
|
|
375
|
+
});
|
|
376
|
+
function parseProviderArg(arg) {
|
|
377
|
+
const allAdapters = getAllAdapters();
|
|
378
|
+
const validIds = new Set(allAdapters.map((a) => a.id));
|
|
379
|
+
if (arg.toLowerCase() === "both") return ["claude-code", "cursor"];
|
|
380
|
+
const parts = arg.split(",").map((p) => p.trim().toLowerCase());
|
|
381
|
+
const result = [];
|
|
382
|
+
for (const part of parts) {
|
|
383
|
+
let id = part;
|
|
384
|
+
if (id === "claude") id = "claude-code";
|
|
385
|
+
if (!validIds.has(id)) throw new Error(`Invalid provider: ${part}. Valid providers: ${[...validIds].join(", ")}`);
|
|
386
|
+
result.push(id);
|
|
387
|
+
}
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
async function updateRootGitignore() {
|
|
391
|
+
const gitignorePath = ".gitignore";
|
|
392
|
+
const entriesToAdd = [".omni/", "omni.local.toml"];
|
|
393
|
+
let content = "";
|
|
394
|
+
if (existsSync(gitignorePath)) content = await Bun.file(gitignorePath).text();
|
|
395
|
+
const lines = content.split("\n");
|
|
396
|
+
const missingEntries = entriesToAdd.filter((entry) => !lines.some((line) => line.trim() === entry));
|
|
397
|
+
if (missingEntries.length === 0) return;
|
|
398
|
+
const needsNewline = content.length > 0 && !content.endsWith("\n");
|
|
399
|
+
const section = `${needsNewline ? "\n" : ""}# OmniDev\n${missingEntries.join("\n")}\n`;
|
|
400
|
+
await Bun.write(gitignorePath, content + section);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
//#endregion
|
|
404
|
+
//#region src/commands/profile.ts
|
|
405
|
+
const listCommand$1 = buildCommand({
|
|
406
|
+
docs: { brief: "List available profiles" },
|
|
407
|
+
parameters: {},
|
|
408
|
+
async func() {
|
|
409
|
+
await runProfileList();
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
async function runSetCommand(_flags, profileName) {
|
|
413
|
+
await runProfileSet(profileName);
|
|
414
|
+
}
|
|
415
|
+
const setCommand = buildCommand({
|
|
416
|
+
docs: { brief: "Set the active profile" },
|
|
417
|
+
parameters: {
|
|
418
|
+
flags: {},
|
|
419
|
+
positional: {
|
|
420
|
+
kind: "tuple",
|
|
421
|
+
parameters: [{
|
|
422
|
+
brief: "Profile name",
|
|
423
|
+
parse: String
|
|
424
|
+
}]
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
func: runSetCommand
|
|
428
|
+
});
|
|
429
|
+
const profileRoutes = buildRouteMap({
|
|
430
|
+
routes: {
|
|
431
|
+
list: listCommand$1,
|
|
432
|
+
set: setCommand
|
|
433
|
+
},
|
|
434
|
+
docs: { brief: "Manage capability profiles" }
|
|
435
|
+
});
|
|
436
|
+
async function runProfileList() {
|
|
437
|
+
try {
|
|
438
|
+
if (!existsSync("omni.toml")) {
|
|
439
|
+
console.log("✗ No config file found");
|
|
440
|
+
console.log(" Run: omnidev init");
|
|
441
|
+
process.exit(1);
|
|
442
|
+
}
|
|
443
|
+
const config = await loadConfig();
|
|
444
|
+
const activeProfile = await getActiveProfile() ?? config.active_profile ?? "default";
|
|
445
|
+
const profiles = config.profiles ?? {};
|
|
446
|
+
const profileNames = Object.keys(profiles);
|
|
447
|
+
if (profileNames.length === 0) {
|
|
448
|
+
console.log("No profiles defined in omni.toml");
|
|
449
|
+
console.log("");
|
|
450
|
+
console.log("Using default capabilities from omni.toml");
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
console.log("Available Profiles:");
|
|
454
|
+
console.log("");
|
|
455
|
+
for (const name of profileNames) {
|
|
456
|
+
const isActive = name === activeProfile;
|
|
457
|
+
const icon = isActive ? "●" : "○";
|
|
458
|
+
const profile = profiles[name];
|
|
459
|
+
if (profile === void 0) continue;
|
|
460
|
+
console.log(`${icon} ${name}${isActive ? " (active)" : ""}`);
|
|
461
|
+
const capabilities = resolveEnabledCapabilities(config, name);
|
|
462
|
+
if (capabilities.length > 0) console.log(` Capabilities: ${capabilities.join(", ")}`);
|
|
463
|
+
else console.log(" Capabilities: none");
|
|
464
|
+
console.log("");
|
|
465
|
+
}
|
|
466
|
+
} catch (error) {
|
|
467
|
+
console.error("✗ Error loading profiles:", error);
|
|
468
|
+
process.exit(1);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async function runProfileSet(profileName) {
|
|
472
|
+
try {
|
|
473
|
+
if (!existsSync("omni.toml")) {
|
|
474
|
+
console.log("✗ No config file found");
|
|
475
|
+
console.log(" Run: omnidev init");
|
|
476
|
+
process.exit(1);
|
|
477
|
+
}
|
|
478
|
+
const config = await loadConfig();
|
|
479
|
+
const profiles = config.profiles ?? {};
|
|
480
|
+
if (!(profileName in profiles)) {
|
|
481
|
+
console.log(`✗ Profile "${profileName}" not found in omni.toml`);
|
|
482
|
+
console.log("");
|
|
483
|
+
console.log("Available profiles:");
|
|
484
|
+
const profileNames = Object.keys(profiles);
|
|
485
|
+
if (profileNames.length === 0) console.log(" (none defined)");
|
|
486
|
+
else for (const name of profileNames) console.log(` - ${name}`);
|
|
487
|
+
process.exit(1);
|
|
488
|
+
}
|
|
489
|
+
await setActiveProfile(profileName);
|
|
490
|
+
console.log(`✓ Active profile set to: ${profileName}`);
|
|
491
|
+
console.log("");
|
|
492
|
+
const adapters = await getEnabledAdapters();
|
|
493
|
+
await syncAgentConfiguration({ adapters });
|
|
494
|
+
} catch (error) {
|
|
495
|
+
console.error("✗ Error setting profile:", error);
|
|
496
|
+
process.exit(1);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
//#endregion
|
|
501
|
+
//#region src/commands/provider.ts
|
|
502
|
+
async function runProviderList() {
|
|
503
|
+
const enabled = await readEnabledProviders();
|
|
504
|
+
const allAdapters = getAllAdapters();
|
|
505
|
+
console.log("Available providers:");
|
|
506
|
+
console.log("");
|
|
507
|
+
for (const adapter of allAdapters) {
|
|
508
|
+
const isEnabled = enabled.includes(adapter.id);
|
|
509
|
+
const marker = isEnabled ? "●" : "○";
|
|
510
|
+
console.log(` ${marker} ${adapter.displayName} (${adapter.id})`);
|
|
511
|
+
}
|
|
512
|
+
console.log("");
|
|
513
|
+
console.log("Legend: ● enabled, ○ disabled");
|
|
514
|
+
}
|
|
515
|
+
async function runProviderEnable(_flags, providerId) {
|
|
516
|
+
if (!providerId) {
|
|
517
|
+
console.error("Error: Provider ID is required");
|
|
518
|
+
console.error("Usage: omnidev provider enable <provider-id>");
|
|
519
|
+
process.exit(1);
|
|
520
|
+
}
|
|
521
|
+
const allAdapters = getAllAdapters();
|
|
522
|
+
const adapter = allAdapters.find((a) => a.id === providerId);
|
|
523
|
+
if (!adapter) {
|
|
524
|
+
console.error(`Error: Unknown provider "${providerId}"`);
|
|
525
|
+
console.error("Available providers:");
|
|
526
|
+
for (const a of allAdapters) console.error(` - ${a.id}`);
|
|
527
|
+
process.exit(1);
|
|
528
|
+
}
|
|
529
|
+
await enableProvider(providerId);
|
|
530
|
+
console.log(`✓ Enabled provider: ${adapter.displayName}`);
|
|
531
|
+
const enabledAdapters = await getEnabledAdapters();
|
|
532
|
+
await syncAgentConfiguration({
|
|
533
|
+
silent: false,
|
|
534
|
+
adapters: enabledAdapters
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
async function runProviderDisable(_flags, providerId) {
|
|
538
|
+
if (!providerId) {
|
|
539
|
+
console.error("Error: Provider ID is required");
|
|
540
|
+
console.error("Usage: omnidev provider disable <provider-id>");
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
const allAdapters = getAllAdapters();
|
|
544
|
+
const adapter = allAdapters.find((a) => a.id === providerId);
|
|
545
|
+
if (!adapter) {
|
|
546
|
+
console.error(`Error: Unknown provider "${providerId}"`);
|
|
547
|
+
console.error("Available providers:");
|
|
548
|
+
for (const a of allAdapters) console.error(` - ${a.id}`);
|
|
549
|
+
process.exit(1);
|
|
550
|
+
}
|
|
551
|
+
await disableProvider(providerId);
|
|
552
|
+
console.log(`✓ Disabled provider: ${adapter.displayName}`);
|
|
553
|
+
}
|
|
554
|
+
const listCommand = buildCommand({
|
|
555
|
+
parameters: {
|
|
556
|
+
flags: {},
|
|
557
|
+
positional: {
|
|
558
|
+
kind: "tuple",
|
|
559
|
+
parameters: []
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
docs: { brief: "List all providers and their status" },
|
|
563
|
+
func: runProviderList
|
|
564
|
+
});
|
|
565
|
+
const enableCommand = buildCommand({
|
|
566
|
+
parameters: {
|
|
567
|
+
flags: {},
|
|
568
|
+
positional: {
|
|
569
|
+
kind: "tuple",
|
|
570
|
+
parameters: [{
|
|
571
|
+
brief: "Provider ID to enable",
|
|
572
|
+
parse: String,
|
|
573
|
+
optional: true
|
|
574
|
+
}]
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
docs: { brief: "Enable a provider" },
|
|
578
|
+
func: runProviderEnable
|
|
579
|
+
});
|
|
580
|
+
const disableCommand = buildCommand({
|
|
581
|
+
parameters: {
|
|
582
|
+
flags: {},
|
|
583
|
+
positional: {
|
|
584
|
+
kind: "tuple",
|
|
585
|
+
parameters: [{
|
|
586
|
+
brief: "Provider ID to disable",
|
|
587
|
+
parse: String,
|
|
588
|
+
optional: true
|
|
589
|
+
}]
|
|
590
|
+
}
|
|
591
|
+
},
|
|
592
|
+
docs: { brief: "Disable a provider" },
|
|
593
|
+
func: runProviderDisable
|
|
594
|
+
});
|
|
595
|
+
const providerRoutes = buildRouteMap({
|
|
596
|
+
routes: {
|
|
597
|
+
list: listCommand,
|
|
598
|
+
enable: enableCommand,
|
|
599
|
+
disable: disableCommand
|
|
600
|
+
},
|
|
601
|
+
docs: { brief: "Manage AI provider adapters" }
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
//#endregion
|
|
605
|
+
//#region src/commands/sync.ts
|
|
606
|
+
const syncCommand = buildCommand({
|
|
607
|
+
docs: { brief: "Manually sync all capabilities, roles, and instructions" },
|
|
608
|
+
parameters: {},
|
|
609
|
+
async func() {
|
|
610
|
+
return await runSync();
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
async function runSync() {
|
|
614
|
+
console.log("Syncing OmniDev configuration...");
|
|
615
|
+
console.log("");
|
|
616
|
+
try {
|
|
617
|
+
const config = await loadConfig();
|
|
618
|
+
const activeProfile = await getActiveProfile() ?? config.active_profile ?? "default";
|
|
619
|
+
const adapters = await getEnabledAdapters();
|
|
620
|
+
const result = await syncAgentConfiguration({
|
|
621
|
+
silent: false,
|
|
622
|
+
adapters
|
|
623
|
+
});
|
|
624
|
+
console.log("");
|
|
625
|
+
console.log("✓ Sync completed successfully!");
|
|
626
|
+
console.log("");
|
|
627
|
+
console.log(`Profile: ${activeProfile}`);
|
|
628
|
+
console.log(`Capabilities: ${result.capabilities.join(", ") || "none"}`);
|
|
629
|
+
console.log(`Providers: ${adapters.map((a) => a.displayName).join(", ") || "none"}`);
|
|
630
|
+
console.log("");
|
|
631
|
+
console.log("Synced components:");
|
|
632
|
+
console.log(" • Capability registry");
|
|
633
|
+
console.log(" • Capability sync hooks");
|
|
634
|
+
console.log(" • .omni/.gitignore");
|
|
635
|
+
console.log(" • .omni/instructions.md");
|
|
636
|
+
if (adapters.length > 0) console.log(" • Provider-specific files");
|
|
637
|
+
} catch (error) {
|
|
638
|
+
console.error("");
|
|
639
|
+
console.error("✗ Sync failed:");
|
|
640
|
+
console.error(` ${error instanceof Error ? error.message : String(error)}`);
|
|
641
|
+
process.exit(1);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
//#endregion
|
|
646
|
+
//#region src/lib/dynamic-app.ts
|
|
647
|
+
/**
|
|
648
|
+
* Build CLI app with dynamically loaded capability commands
|
|
649
|
+
*/
|
|
650
|
+
async function buildDynamicApp() {
|
|
651
|
+
const routes = {
|
|
652
|
+
init: initCommand,
|
|
653
|
+
doctor: doctorCommand,
|
|
654
|
+
sync: syncCommand,
|
|
655
|
+
capability: capabilityRoutes,
|
|
656
|
+
profile: profileRoutes,
|
|
657
|
+
provider: providerRoutes
|
|
658
|
+
};
|
|
659
|
+
debug("Core routes registered", Object.keys(routes));
|
|
660
|
+
if (existsSync(".omni/config.toml")) try {
|
|
661
|
+
const capabilityCommands = await loadCapabilityCommands();
|
|
662
|
+
debug("Capability commands loaded", {
|
|
663
|
+
commands: Object.keys(capabilityCommands),
|
|
664
|
+
details: Object.entries(capabilityCommands).map(([name, cmd]) => ({
|
|
665
|
+
name,
|
|
666
|
+
type: typeof cmd,
|
|
667
|
+
constructor: cmd?.constructor?.name,
|
|
668
|
+
keys: Object.keys(cmd),
|
|
669
|
+
hasGetRoutingTargetForInput: typeof cmd?.getRoutingTargetForInput
|
|
670
|
+
}))
|
|
671
|
+
});
|
|
672
|
+
Object.assign(routes, capabilityCommands);
|
|
673
|
+
} catch (error) {
|
|
674
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
675
|
+
console.warn(`Warning: Failed to load capability commands: ${errorMessage}`);
|
|
676
|
+
debug("Full error loading capabilities", error);
|
|
677
|
+
}
|
|
678
|
+
debug("Final routes", Object.keys(routes));
|
|
679
|
+
const app$1 = buildApplication(buildRouteMap({
|
|
680
|
+
routes,
|
|
681
|
+
docs: { brief: "OmniDev commands" }
|
|
682
|
+
}), {
|
|
683
|
+
name: "omnidev",
|
|
684
|
+
versionInfo: { currentVersion: "0.1.0" }
|
|
685
|
+
});
|
|
686
|
+
debug("App built successfully");
|
|
687
|
+
return app$1;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Load CLI commands from enabled capabilities
|
|
691
|
+
*/
|
|
692
|
+
async function loadCapabilityCommands() {
|
|
693
|
+
const { buildCapabilityRegistry, installCapabilityDependencies } = await import("@omnidev-ai/core");
|
|
694
|
+
await installCapabilityDependencies(true);
|
|
695
|
+
const registry = await buildCapabilityRegistry();
|
|
696
|
+
const capabilities = registry.getAllCapabilities();
|
|
697
|
+
const commands = {};
|
|
698
|
+
for (const capability of capabilities) try {
|
|
699
|
+
debug(`Loading capability '${capability.id}'`, { path: capability.path });
|
|
700
|
+
const capabilityExport = await loadCapabilityExport(capability);
|
|
701
|
+
debug(`Capability '${capability.id}' export`, {
|
|
702
|
+
found: !!capabilityExport,
|
|
703
|
+
hasCLICommands: !!capabilityExport?.cliCommands,
|
|
704
|
+
cliCommands: capabilityExport?.cliCommands ? Object.keys(capabilityExport.cliCommands) : []
|
|
705
|
+
});
|
|
706
|
+
if (capabilityExport?.cliCommands) for (const [commandName, command] of Object.entries(capabilityExport.cliCommands)) {
|
|
707
|
+
if (commands[commandName]) console.warn(`Command '${commandName}' from capability '${capability.id}' conflicts with existing command. Using '${capability.id}' version.`);
|
|
708
|
+
commands[commandName] = command;
|
|
709
|
+
debug(`Registered command '${commandName}' from '${capability.id}'`, {
|
|
710
|
+
type: typeof command,
|
|
711
|
+
constructor: command?.constructor?.name
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
} catch (error) {
|
|
715
|
+
console.error(`Failed to load capability '${capability.id}':`, error);
|
|
716
|
+
}
|
|
717
|
+
return commands;
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Load the default export from a capability
|
|
721
|
+
*/
|
|
722
|
+
async function loadCapabilityExport(capability) {
|
|
723
|
+
const capabilityPath = join(process.cwd(), capability.path);
|
|
724
|
+
const indexPath = join(capabilityPath, "index.ts");
|
|
725
|
+
if (!existsSync(indexPath)) {
|
|
726
|
+
const jsIndexPath = join(capabilityPath, "index.js");
|
|
727
|
+
if (!existsSync(jsIndexPath)) return null;
|
|
728
|
+
const module$1 = await import(jsIndexPath);
|
|
729
|
+
if (!module$1.default) return null;
|
|
730
|
+
return module$1.default;
|
|
731
|
+
}
|
|
732
|
+
const module = await import(indexPath);
|
|
733
|
+
if (!module.default) return null;
|
|
734
|
+
const capExport = module.default;
|
|
735
|
+
if (capExport.cliCommands) for (const [name, cmd] of Object.entries(capExport.cliCommands)) debug(`CLI command '${name}' structure`, {
|
|
736
|
+
type: typeof cmd,
|
|
737
|
+
constructor: cmd?.constructor?.name,
|
|
738
|
+
keys: Object.keys(cmd),
|
|
739
|
+
hasGetRoutingTargetForInput: typeof cmd?.getRoutingTargetForInput,
|
|
740
|
+
routesKeys: cmd.routes ? Object.keys(cmd.routes) : void 0
|
|
741
|
+
});
|
|
742
|
+
return capExport;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
//#endregion
|
|
746
|
+
//#region src/index.ts
|
|
747
|
+
const app = await buildDynamicApp();
|
|
748
|
+
debug("CLI startup", {
|
|
749
|
+
arguments: process.argv.slice(2),
|
|
750
|
+
cwd: process.cwd()
|
|
751
|
+
});
|
|
752
|
+
try {
|
|
753
|
+
run(app, process.argv.slice(2), { process });
|
|
754
|
+
} catch (error) {
|
|
755
|
+
if (error instanceof Error) if (error.message.includes("getRoutingTargetForInput") || error.stack?.includes("@stricli/core")) {
|
|
756
|
+
const args = process.argv.slice(2);
|
|
757
|
+
console.error(`\nError: Command not found or invalid usage.`);
|
|
758
|
+
if (args.length > 0) {
|
|
759
|
+
console.error(`\nYou tried to run: omnidev ${args.join(" ")}`);
|
|
760
|
+
console.error("\nThis could mean:");
|
|
761
|
+
console.error(" 1. The command doesn't exist");
|
|
762
|
+
console.error(" 2. A required capability is not enabled");
|
|
763
|
+
console.error(" 3. Invalid command syntax\n");
|
|
764
|
+
}
|
|
765
|
+
console.error("Run 'omnidev --help' to see available commands");
|
|
766
|
+
console.error("\nTo enable capabilities, run: omnidev capability enable <name>");
|
|
767
|
+
console.error("To see enabled capabilities: omnidev capability list");
|
|
768
|
+
process.exit(1);
|
|
769
|
+
} else throw error;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
//#endregion
|