@omnidev-ai/cli 0.3.0 → 0.5.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.
@@ -1,339 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
- import { mkdirSync } from "node:fs";
3
- import { setupTestDir } from "@omnidev-ai/core/test-utils";
4
- import { runProfileList, runProfileSet } from "./profile";
5
-
6
- describe("profile commands", () => {
7
- setupTestDir("profile-test-", { chdir: true });
8
- let originalExit: typeof process.exit;
9
- let exitCode: number | undefined;
10
- let consoleOutput: string[];
11
- let consoleErrors: string[];
12
- let originalLog: typeof console.log;
13
- let originalError: typeof console.error;
14
-
15
- beforeEach(() => {
16
- // Mock process.exit
17
- exitCode = undefined;
18
- originalExit = process.exit;
19
- process.exit = ((code?: number) => {
20
- exitCode = code ?? 0;
21
- throw new Error(`process.exit(${code})`);
22
- }) as typeof process.exit;
23
-
24
- // Mock console
25
- consoleOutput = [];
26
- consoleErrors = [];
27
- originalLog = console.log;
28
- originalError = console.error;
29
- console.log = (...args: unknown[]) => {
30
- consoleOutput.push(args.join(" "));
31
- };
32
- console.error = (...args: unknown[]) => {
33
- consoleErrors.push(args.join(" "));
34
- };
35
- });
36
-
37
- afterEach(() => {
38
- process.exit = originalExit;
39
- console.log = originalLog;
40
- console.error = originalError;
41
- });
42
-
43
- describe("runProfileList", () => {
44
- test("should show error when config file does not exist", async () => {
45
- try {
46
- await runProfileList();
47
- } catch {
48
- // Expected to throw due to process.exit mock
49
- }
50
-
51
- expect(exitCode).toBe(1);
52
- expect(consoleOutput.join("\n")).toContain("No config file found");
53
- expect(consoleOutput.join("\n")).toContain("Run: omnidev init");
54
- });
55
-
56
- test("should show message when no profiles defined", async () => {
57
- // Create minimal config without profiles
58
- mkdirSync(".omni", { recursive: true });
59
- await Bun.write(
60
- "omni.toml",
61
- `project = "test-project"
62
- active_profile = "default"
63
- `,
64
- );
65
-
66
- await runProfileList();
67
-
68
- expect(exitCode).toBeUndefined();
69
- expect(consoleOutput.join("\n")).toContain("No profiles defined");
70
- expect(consoleOutput.join("\n")).toContain("Using default capabilities");
71
- });
72
-
73
- test("should list all profiles from config", async () => {
74
- // Create config with profiles
75
- mkdirSync(".omni", { recursive: true });
76
- await Bun.write(
77
- "omni.toml",
78
- `project = "test-project"
79
- active_profile = "default"
80
-
81
- [profiles.default]
82
- capabilities = []
83
-
84
- [profiles.planning]
85
- capabilities = ["tasks", "planner"]
86
-
87
- [profiles.coding]
88
- capabilities = []
89
- `,
90
- );
91
-
92
- await runProfileList();
93
-
94
- expect(exitCode).toBeUndefined();
95
- const output = consoleOutput.join("\n");
96
- expect(output).toContain("Available Profiles:");
97
- expect(output).toContain("default");
98
- expect(output).toContain("planning");
99
- expect(output).toContain("coding");
100
- });
101
-
102
- test("should show active profile with marker", async () => {
103
- // Create config with profiles
104
- mkdirSync(".omni", { recursive: true });
105
- await Bun.write(
106
- "omni.toml",
107
- `project = "test-project"
108
- active_profile = "planning"
109
-
110
- [profiles.default]
111
- capabilities = []
112
-
113
- [profiles.planning]
114
- capabilities = ["planner"]
115
- `,
116
- );
117
-
118
- await runProfileList();
119
-
120
- expect(exitCode).toBeUndefined();
121
- const output = consoleOutput.join("\n");
122
- expect(output).toContain("● planning (active)");
123
- expect(output).toContain("○ default");
124
- });
125
-
126
- test("should show profile capabilities", async () => {
127
- // Create config with profiles
128
- mkdirSync(".omni", { recursive: true });
129
- await Bun.write(
130
- "omni.toml",
131
- `project = "test-project"
132
- active_profile = "default"
133
-
134
- [profiles.default]
135
- capabilities = []
136
-
137
- [profiles.planning]
138
- capabilities = ["planner", "tasks"]
139
- `,
140
- );
141
-
142
- await runProfileList();
143
-
144
- expect(exitCode).toBeUndefined();
145
- const output = consoleOutput.join("\n");
146
- expect(output).toContain("Capabilities: planner, tasks");
147
- });
148
-
149
- test("should use default_profile when no active profile", async () => {
150
- // Create config with active_profile
151
- mkdirSync(".omni", { recursive: true });
152
- await Bun.write(
153
- "omni.toml",
154
- `project = "test-project"
155
- active_profile = "planning"
156
-
157
- [profiles.planning]
158
- capabilities = ["planner"]
159
- `,
160
- );
161
-
162
- await runProfileList();
163
-
164
- expect(exitCode).toBeUndefined();
165
- const output = consoleOutput.join("\n");
166
- expect(output).toContain("● planning (active)");
167
- });
168
-
169
- test("should handle invalid config gracefully", async () => {
170
- // Create invalid config
171
- mkdirSync(".omni", { recursive: true });
172
- await Bun.write("omni.toml", "invalid toml [[[");
173
-
174
- try {
175
- await runProfileList();
176
- } catch {
177
- // Expected to throw due to process.exit mock
178
- }
179
-
180
- expect(exitCode).toBe(1);
181
- expect(consoleErrors.join("\n")).toContain("Error loading profiles");
182
- });
183
- });
184
-
185
- describe("runProfileSet", () => {
186
- test("should show error when config file does not exist", async () => {
187
- try {
188
- await runProfileSet("planning");
189
- } catch {
190
- // Expected to throw due to process.exit mock
191
- }
192
-
193
- expect(exitCode).toBe(1);
194
- expect(consoleOutput.join("\n")).toContain("No config file found");
195
- expect(consoleOutput.join("\n")).toContain("Run: omnidev init");
196
- });
197
-
198
- test("should show error when profile does not exist", async () => {
199
- // Create config without the requested profile
200
- mkdirSync(".omni", { recursive: true });
201
- await Bun.write(
202
- "omni.toml",
203
- `project = "test-project"
204
-
205
- [profiles.default]
206
- capabilities = []
207
- `,
208
- );
209
-
210
- try {
211
- await runProfileSet("nonexistent");
212
- } catch {
213
- // Expected to throw due to process.exit mock
214
- }
215
-
216
- expect(exitCode).toBe(1);
217
- const output = consoleOutput.join("\n");
218
- expect(output).toContain('Profile "nonexistent" not found');
219
- expect(output).toContain("Available profiles:");
220
- expect(output).toContain("- default");
221
- });
222
-
223
- test("should set active profile", async () => {
224
- // Create config with profiles
225
- mkdirSync(".omni", { recursive: true });
226
- await Bun.write(
227
- "omni.toml",
228
- `project = "test-project"
229
-
230
- [profiles.default]
231
- capabilities = []
232
-
233
- [profiles.planning]
234
- capabilities = ["planner"]
235
- `,
236
- );
237
-
238
- await runProfileSet("planning");
239
-
240
- expect(exitCode).toBeUndefined();
241
- expect(consoleOutput.join("\n")).toContain("Active profile set to: planning");
242
-
243
- // Verify active_profile was written to state file (not config.toml)
244
- const stateContent = await Bun.file(".omni/state/active-profile").text();
245
- expect(stateContent).toBe("planning");
246
- });
247
-
248
- test("should trigger agents sync after setting profile", async () => {
249
- // Create config with profiles
250
- mkdirSync(".omni", { recursive: true });
251
- await Bun.write(
252
- "omni.toml",
253
- `project = "test-project"
254
- active_profile = "default"
255
-
256
- [profiles.default]
257
- capabilities = []
258
-
259
- [profiles.planning]
260
- capabilities = []
261
- `,
262
- );
263
-
264
- await runProfileSet("planning");
265
-
266
- expect(exitCode).toBeUndefined();
267
- const output = consoleOutput.join("\n");
268
- expect(output).toContain("Syncing agent configuration");
269
- });
270
-
271
- test("should show list of available profiles when profile not found", async () => {
272
- // Create config with multiple profiles
273
- mkdirSync(".omni", { recursive: true });
274
- await Bun.write(
275
- "omni.toml",
276
- `project = "test-project"
277
-
278
- [profiles.default]
279
- capabilities = []
280
-
281
- [profiles.planning]
282
- capabilities = []
283
-
284
- [profiles.coding]
285
- capabilities = []
286
- `,
287
- );
288
-
289
- try {
290
- await runProfileSet("nonexistent");
291
- } catch {
292
- // Expected to throw due to process.exit mock
293
- }
294
-
295
- expect(exitCode).toBe(1);
296
- const output = consoleOutput.join("\n");
297
- expect(output).toContain("Available profiles:");
298
- expect(output).toContain("- default");
299
- expect(output).toContain("- planning");
300
- expect(output).toContain("- coding");
301
- });
302
-
303
- test("should handle empty profiles config", async () => {
304
- // Create config without any profiles
305
- mkdirSync(".omni", { recursive: true });
306
- await Bun.write(
307
- "omni.toml",
308
- `project = "test-project"
309
- `,
310
- );
311
-
312
- try {
313
- await runProfileSet("default");
314
- } catch {
315
- // Expected to throw due to process.exit mock
316
- }
317
-
318
- expect(exitCode).toBe(1);
319
- const output = consoleOutput.join("\n");
320
- expect(output).toContain('Profile "default" not found');
321
- expect(output).toContain("(none defined)");
322
- });
323
-
324
- test("should handle invalid config gracefully", async () => {
325
- // Create invalid config
326
- mkdirSync(".omni", { recursive: true });
327
- await Bun.write("omni.toml", "invalid toml [[[");
328
-
329
- try {
330
- await runProfileSet("planning");
331
- } catch {
332
- // Expected to throw due to process.exit mock
333
- }
334
-
335
- expect(exitCode).toBe(1);
336
- expect(consoleErrors.join("\n")).toContain("Error setting profile");
337
- });
338
- });
339
- });
@@ -1,153 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { getEnabledAdapters } from "@omnidev-ai/adapters";
3
- import {
4
- getActiveProfile,
5
- loadConfig,
6
- resolveEnabledCapabilities,
7
- setActiveProfile,
8
- syncAgentConfiguration,
9
- } from "@omnidev-ai/core";
10
- import { buildCommand, buildRouteMap } from "@stricli/core";
11
-
12
- const listCommand = buildCommand({
13
- docs: {
14
- brief: "List available profiles",
15
- },
16
- parameters: {},
17
- async func() {
18
- await runProfileList();
19
- },
20
- });
21
-
22
- async function runSetCommand(_flags: Record<string, never>, profileName: string): Promise<void> {
23
- await runProfileSet(profileName);
24
- }
25
-
26
- const setCommand = buildCommand({
27
- docs: {
28
- brief: "Set the active profile",
29
- },
30
- parameters: {
31
- flags: {},
32
- positional: {
33
- kind: "tuple" as const,
34
- parameters: [
35
- {
36
- brief: "Profile name",
37
- parse: String,
38
- },
39
- ],
40
- },
41
- },
42
- func: runSetCommand,
43
- });
44
-
45
- export const profileRoutes = buildRouteMap({
46
- routes: {
47
- list: listCommand,
48
- set: setCommand,
49
- },
50
- docs: {
51
- brief: "Manage capability profiles",
52
- },
53
- });
54
-
55
- export async function runProfileList(): Promise<void> {
56
- try {
57
- // Check if omni.toml exists
58
- if (!existsSync("omni.toml")) {
59
- console.log("✗ No config file found");
60
- console.log(" Run: omnidev init");
61
- process.exit(1);
62
- }
63
-
64
- // Load config
65
- const config = await loadConfig();
66
-
67
- // Get active profile
68
- const activeProfile = (await getActiveProfile()) ?? config.active_profile ?? "default";
69
-
70
- // Check if profiles exist
71
- const profiles = config.profiles ?? {};
72
- const profileNames = Object.keys(profiles);
73
-
74
- if (profileNames.length === 0) {
75
- console.log("No profiles defined in omni.toml");
76
- console.log("");
77
- console.log("Using default capabilities from omni.toml");
78
- return;
79
- }
80
-
81
- // Display profiles
82
- console.log("Available Profiles:");
83
- console.log("");
84
-
85
- for (const name of profileNames) {
86
- const isActive = name === activeProfile;
87
- const icon = isActive ? "●" : "○";
88
- const profile = profiles[name];
89
-
90
- if (profile === undefined) {
91
- continue;
92
- }
93
-
94
- console.log(`${icon} ${name}${isActive ? " (active)" : ""}`);
95
-
96
- // Show capabilities (including always-enabled)
97
- const capabilities = resolveEnabledCapabilities(config, name);
98
- if (capabilities.length > 0) {
99
- console.log(` Capabilities: ${capabilities.join(", ")}`);
100
- } else {
101
- console.log(" Capabilities: none");
102
- }
103
- console.log("");
104
- }
105
- } catch (error) {
106
- console.error("✗ Error loading profiles:", error);
107
- process.exit(1);
108
- }
109
- }
110
-
111
- export async function runProfileSet(profileName: string): Promise<void> {
112
- try {
113
- // Check if omni.toml exists
114
- if (!existsSync("omni.toml")) {
115
- console.log("✗ No config file found");
116
- console.log(" Run: omnidev init");
117
- process.exit(1);
118
- }
119
-
120
- // Load config
121
- const config = await loadConfig();
122
-
123
- // Validate profile exists
124
- const profiles = config.profiles ?? {};
125
- if (!(profileName in profiles)) {
126
- console.log(`✗ Profile "${profileName}" not found in omni.toml`);
127
- console.log("");
128
- console.log("Available profiles:");
129
- const profileNames = Object.keys(profiles);
130
- if (profileNames.length === 0) {
131
- console.log(" (none defined)");
132
- } else {
133
- for (const name of profileNames) {
134
- console.log(` - ${name}`);
135
- }
136
- }
137
- process.exit(1);
138
- }
139
-
140
- // Set active profile
141
- await setActiveProfile(profileName);
142
-
143
- console.log(`✓ Active profile set to: ${profileName}`);
144
- console.log("");
145
-
146
- // Auto-sync agent configuration with enabled adapters
147
- const adapters = await getEnabledAdapters();
148
- await syncAgentConfiguration({ adapters });
149
- } catch (error) {
150
- console.error("✗ Error setting profile:", error);
151
- process.exit(1);
152
- }
153
- }
@@ -1,128 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
- import { existsSync, mkdirSync, rmSync } from "node:fs";
3
- import { captureConsole, tmpdir } from "@omnidev-ai/core/test-utils";
4
- import { runProviderList, runProviderEnable, runProviderDisable } from "./provider";
5
-
6
- // Import the functions we need to test
7
- async function getProviderFunctions() {
8
- const { readEnabledProviders, writeEnabledProviders } = await import("@omnidev-ai/core");
9
- return { readEnabledProviders, writeEnabledProviders };
10
- }
11
-
12
- describe("provider commands", () => {
13
- let testDir: string;
14
- let originalCwd: string;
15
-
16
- beforeEach(async () => {
17
- originalCwd = process.cwd();
18
- testDir = tmpdir("provider-test-");
19
- process.chdir(testDir);
20
-
21
- // Create basic OmniDev structure
22
- mkdirSync(".omni/state", { recursive: true });
23
- await Bun.write("omni.toml", 'project = "test"\n');
24
- });
25
-
26
- afterEach(() => {
27
- process.chdir(originalCwd);
28
- if (existsSync(testDir)) {
29
- rmSync(testDir, { recursive: true, force: true });
30
- }
31
- });
32
-
33
- describe("runProviderList", () => {
34
- test("lists all providers with status", async () => {
35
- const { writeEnabledProviders } = await getProviderFunctions();
36
- await writeEnabledProviders(["claude-code", "cursor"]);
37
-
38
- const { stdout } = await captureConsole(async () => {
39
- await runProviderList();
40
- });
41
-
42
- const output = stdout.join("\n");
43
- expect(output).toContain("Available providers:");
44
- expect(output).toContain("Claude Code");
45
- expect(output).toContain("Cursor");
46
- expect(output).toContain("Codex");
47
- expect(output).toContain("OpenCode");
48
- });
49
-
50
- test("shows enabled providers with marker", async () => {
51
- const { writeEnabledProviders } = await getProviderFunctions();
52
- await writeEnabledProviders(["claude-code"]);
53
-
54
- const { stdout } = await captureConsole(async () => {
55
- await runProviderList();
56
- });
57
-
58
- const output = stdout.join("\n");
59
- // Should have at least one enabled marker
60
- expect(output).toContain("●");
61
- });
62
-
63
- test("shows disabled providers with different marker", async () => {
64
- const { writeEnabledProviders } = await getProviderFunctions();
65
- await writeEnabledProviders(["claude-code"]);
66
-
67
- const { stdout } = await captureConsole(async () => {
68
- await runProviderList();
69
- });
70
-
71
- const output = stdout.join("\n");
72
- // Should have disabled markers for other providers
73
- expect(output).toContain("○");
74
- });
75
- });
76
-
77
- describe("runProviderEnable", () => {
78
- test("enables a provider", async () => {
79
- const { writeEnabledProviders, readEnabledProviders } = await getProviderFunctions();
80
- await writeEnabledProviders(["claude-code"]);
81
-
82
- await captureConsole(async () => {
83
- await runProviderEnable({}, "cursor");
84
- });
85
-
86
- const providers = await readEnabledProviders();
87
- expect(providers).toContain("cursor");
88
- });
89
-
90
- test("shows success message", async () => {
91
- const { writeEnabledProviders } = await getProviderFunctions();
92
- await writeEnabledProviders(["claude-code"]);
93
-
94
- const { stdout } = await captureConsole(async () => {
95
- await runProviderEnable({}, "cursor");
96
- });
97
-
98
- const output = stdout.join("\n");
99
- expect(output).toContain("✓ Enabled provider: Cursor");
100
- });
101
- });
102
-
103
- describe("runProviderDisable", () => {
104
- test("disables a provider", async () => {
105
- const { writeEnabledProviders, readEnabledProviders } = await getProviderFunctions();
106
- await writeEnabledProviders(["claude-code", "cursor"]);
107
-
108
- await captureConsole(async () => {
109
- await runProviderDisable({}, "cursor");
110
- });
111
-
112
- const providers = await readEnabledProviders();
113
- expect(providers).not.toContain("cursor");
114
- });
115
-
116
- test("shows success message", async () => {
117
- const { writeEnabledProviders } = await getProviderFunctions();
118
- await writeEnabledProviders(["claude-code", "cursor"]);
119
-
120
- const { stdout } = await captureConsole(async () => {
121
- await runProviderDisable({}, "cursor");
122
- });
123
-
124
- const output = stdout.join("\n");
125
- expect(output).toContain("✓ Disabled provider: Cursor");
126
- });
127
- });
128
- });