@omnidev-ai/core 0.1.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/package.json +31 -0
- package/src/capability/AGENTS.md +58 -0
- package/src/capability/commands.test.ts +414 -0
- package/src/capability/commands.ts +70 -0
- package/src/capability/docs.test.ts +199 -0
- package/src/capability/docs.ts +46 -0
- package/src/capability/index.ts +20 -0
- package/src/capability/loader.test.ts +815 -0
- package/src/capability/loader.ts +492 -0
- package/src/capability/registry.test.ts +473 -0
- package/src/capability/registry.ts +55 -0
- package/src/capability/rules.test.ts +145 -0
- package/src/capability/rules.ts +133 -0
- package/src/capability/skills.test.ts +316 -0
- package/src/capability/skills.ts +56 -0
- package/src/capability/sources.test.ts +338 -0
- package/src/capability/sources.ts +966 -0
- package/src/capability/subagents.test.ts +478 -0
- package/src/capability/subagents.ts +103 -0
- package/src/capability/yaml-parser.ts +81 -0
- package/src/config/AGENTS.md +46 -0
- package/src/config/capabilities.ts +82 -0
- package/src/config/env.test.ts +286 -0
- package/src/config/env.ts +96 -0
- package/src/config/index.ts +6 -0
- package/src/config/loader.test.ts +282 -0
- package/src/config/loader.ts +137 -0
- package/src/config/parser.test.ts +281 -0
- package/src/config/parser.ts +55 -0
- package/src/config/profiles.test.ts +259 -0
- package/src/config/profiles.ts +75 -0
- package/src/config/provider.test.ts +79 -0
- package/src/config/provider.ts +55 -0
- package/src/debug.ts +20 -0
- package/src/gitignore/manager.test.ts +219 -0
- package/src/gitignore/manager.ts +167 -0
- package/src/index.test.ts +26 -0
- package/src/index.ts +39 -0
- package/src/mcp-json/index.ts +1 -0
- package/src/mcp-json/manager.test.ts +415 -0
- package/src/mcp-json/manager.ts +118 -0
- package/src/state/active-profile.test.ts +131 -0
- package/src/state/active-profile.ts +41 -0
- package/src/state/index.ts +2 -0
- package/src/state/manifest.test.ts +548 -0
- package/src/state/manifest.ts +164 -0
- package/src/sync.ts +213 -0
- package/src/templates/agents.test.ts +23 -0
- package/src/templates/agents.ts +14 -0
- package/src/templates/claude.test.ts +48 -0
- package/src/templates/claude.ts +122 -0
- package/src/test-utils/helpers.test.ts +196 -0
- package/src/test-utils/helpers.ts +187 -0
- package/src/test-utils/index.ts +30 -0
- package/src/test-utils/mocks.test.ts +83 -0
- package/src/test-utils/mocks.ts +101 -0
- package/src/types/capability-export.ts +234 -0
- package/src/types/index.test.ts +28 -0
- package/src/types/index.ts +270 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for testing
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { expect } from "bun:test";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Expects an async function to throw an error
|
|
9
|
+
* @param fn - Async function that should throw
|
|
10
|
+
* @param errorMatch - Optional string or regex to match against error message
|
|
11
|
+
* @throws If the function doesn't throw
|
|
12
|
+
*/
|
|
13
|
+
export async function expectToThrowAsync(
|
|
14
|
+
fn: () => Promise<unknown>,
|
|
15
|
+
errorMatch?: string | RegExp,
|
|
16
|
+
): Promise<void> {
|
|
17
|
+
let threw = false;
|
|
18
|
+
let caughtError: Error | undefined;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
await fn();
|
|
22
|
+
} catch (e) {
|
|
23
|
+
threw = true;
|
|
24
|
+
caughtError = e as Error;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
expect(threw).toBe(true);
|
|
28
|
+
|
|
29
|
+
if (errorMatch && caughtError) {
|
|
30
|
+
if (typeof errorMatch === "string") {
|
|
31
|
+
expect(caughtError.message).toContain(errorMatch);
|
|
32
|
+
} else {
|
|
33
|
+
expect(caughtError.message).toMatch(errorMatch);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Waits for a condition to be true
|
|
40
|
+
* @param condition - Function that returns true when condition is met
|
|
41
|
+
* @param timeout - Maximum time to wait in milliseconds (default: 1000)
|
|
42
|
+
* @param interval - Check interval in milliseconds (default: 50)
|
|
43
|
+
* @throws If timeout is reached before condition is met
|
|
44
|
+
*/
|
|
45
|
+
export async function waitForCondition(
|
|
46
|
+
condition: () => boolean | Promise<boolean>,
|
|
47
|
+
timeout = 1000,
|
|
48
|
+
interval = 50,
|
|
49
|
+
): Promise<void> {
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
|
|
52
|
+
while (Date.now() - startTime < timeout) {
|
|
53
|
+
const result = await condition();
|
|
54
|
+
if (result) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
await delay(interval);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
throw new Error(`Condition not met within ${timeout}ms`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Delays execution for a specified amount of time
|
|
65
|
+
* @param ms - Milliseconds to delay
|
|
66
|
+
*/
|
|
67
|
+
export function delay(ms: number): Promise<void> {
|
|
68
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Creates a spy function that records calls and arguments
|
|
73
|
+
* @returns Spy function with call tracking
|
|
74
|
+
*/
|
|
75
|
+
export function createSpy<TArgs extends unknown[], TReturn>(
|
|
76
|
+
implementation?: (...args: TArgs) => TReturn,
|
|
77
|
+
) {
|
|
78
|
+
const calls: TArgs[] = [];
|
|
79
|
+
|
|
80
|
+
const spy = ((...args: TArgs) => {
|
|
81
|
+
calls.push(args);
|
|
82
|
+
if (implementation) {
|
|
83
|
+
return implementation(...args);
|
|
84
|
+
}
|
|
85
|
+
return undefined as TReturn;
|
|
86
|
+
}) as {
|
|
87
|
+
(...args: TArgs): TReturn;
|
|
88
|
+
calls: TArgs[];
|
|
89
|
+
callCount: number;
|
|
90
|
+
reset: () => void;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
Object.defineProperty(spy, "calls", {
|
|
94
|
+
get: () => calls,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
Object.defineProperty(spy, "callCount", {
|
|
98
|
+
get: () => calls.length,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
spy.reset = () => {
|
|
102
|
+
calls.length = 0;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return spy;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Creates a mock function that returns predefined values
|
|
110
|
+
* @param returnValues - Array of values to return on consecutive calls
|
|
111
|
+
* @returns Mock function
|
|
112
|
+
*/
|
|
113
|
+
export function createMockFn<T>(...returnValues: T[]): () => T {
|
|
114
|
+
let callIndex = 0;
|
|
115
|
+
|
|
116
|
+
return () => {
|
|
117
|
+
if (callIndex >= returnValues.length) {
|
|
118
|
+
throw new Error("Mock function called more times than return values provided");
|
|
119
|
+
}
|
|
120
|
+
const value = returnValues[callIndex++];
|
|
121
|
+
if (value === undefined) {
|
|
122
|
+
throw new Error("Mock function returned undefined");
|
|
123
|
+
}
|
|
124
|
+
return value;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Creates a mock promise that can be resolved or rejected manually
|
|
130
|
+
* @returns Object with promise and resolve/reject functions
|
|
131
|
+
*/
|
|
132
|
+
export function createDeferredPromise<T>() {
|
|
133
|
+
let resolveRef: ((value: T) => void) | undefined;
|
|
134
|
+
let rejectRef: ((reason?: unknown) => void) | undefined;
|
|
135
|
+
|
|
136
|
+
const promise = new Promise<T>((res, rej) => {
|
|
137
|
+
resolveRef = res;
|
|
138
|
+
rejectRef = rej;
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (!resolveRef || !rejectRef) {
|
|
142
|
+
throw new Error("Promise executor did not initialize resolve/reject");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
promise,
|
|
147
|
+
resolve: resolveRef,
|
|
148
|
+
reject: rejectRef,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Captures console output during test execution
|
|
154
|
+
* @param fn - Function to execute while capturing output
|
|
155
|
+
* @returns Object with stdout and stderr arrays
|
|
156
|
+
*/
|
|
157
|
+
export async function captureConsole<T>(
|
|
158
|
+
fn: () => Promise<T> | T,
|
|
159
|
+
): Promise<{ stdout: string[]; stderr: string[]; result: T }> {
|
|
160
|
+
const stdout: string[] = [];
|
|
161
|
+
const stderr: string[] = [];
|
|
162
|
+
|
|
163
|
+
const originalLog = console.log;
|
|
164
|
+
const originalError = console.error;
|
|
165
|
+
const originalWarn = console.warn;
|
|
166
|
+
|
|
167
|
+
console.log = (...args: unknown[]) => {
|
|
168
|
+
stdout.push(args.map(String).join(" "));
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
console.error = (...args: unknown[]) => {
|
|
172
|
+
stderr.push(args.map(String).join(" "));
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
console.warn = (...args: unknown[]) => {
|
|
176
|
+
stderr.push(args.map(String).join(" "));
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const result = await fn();
|
|
181
|
+
return { stdout, stderr, result };
|
|
182
|
+
} finally {
|
|
183
|
+
console.log = originalLog;
|
|
184
|
+
console.error = originalError;
|
|
185
|
+
console.warn = originalWarn;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test utilities for OmniDev
|
|
3
|
+
*
|
|
4
|
+
* This module provides shared test utilities including:
|
|
5
|
+
* - Mock factories for creating test data
|
|
6
|
+
* - Helper functions for async testing
|
|
7
|
+
* - Spy and mock function utilities
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Re-export all helper functions
|
|
11
|
+
export {
|
|
12
|
+
captureConsole,
|
|
13
|
+
createDeferredPromise,
|
|
14
|
+
createMockFn,
|
|
15
|
+
createSpy,
|
|
16
|
+
delay,
|
|
17
|
+
expectToThrowAsync,
|
|
18
|
+
waitForCondition,
|
|
19
|
+
} from "./helpers";
|
|
20
|
+
// Re-export all mock factories
|
|
21
|
+
export {
|
|
22
|
+
createMockCapability,
|
|
23
|
+
createMockConfig,
|
|
24
|
+
createMockRule,
|
|
25
|
+
createMockSkill,
|
|
26
|
+
type MockCapability,
|
|
27
|
+
type MockConfig,
|
|
28
|
+
type MockRule,
|
|
29
|
+
type MockSkill,
|
|
30
|
+
} from "./mocks";
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { createMockCapability, createMockConfig, createMockRule, createMockSkill } from "./mocks";
|
|
3
|
+
|
|
4
|
+
describe("createMockCapability", () => {
|
|
5
|
+
test("should create a mock capability with default values", () => {
|
|
6
|
+
const capability = createMockCapability();
|
|
7
|
+
expect(capability.id).toBe("test-capability");
|
|
8
|
+
expect(capability.name).toBe("Test Capability");
|
|
9
|
+
expect(capability.version).toBe("1.0.0");
|
|
10
|
+
expect(capability.enabled).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("should allow overriding default values", () => {
|
|
14
|
+
const capability = createMockCapability({
|
|
15
|
+
id: "custom-id",
|
|
16
|
+
name: "Custom Name",
|
|
17
|
+
version: "2.0.0",
|
|
18
|
+
enabled: false,
|
|
19
|
+
});
|
|
20
|
+
expect(capability.id).toBe("custom-id");
|
|
21
|
+
expect(capability.name).toBe("Custom Name");
|
|
22
|
+
expect(capability.version).toBe("2.0.0");
|
|
23
|
+
expect(capability.enabled).toBe(false);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe("createMockConfig", () => {
|
|
28
|
+
test("should create a mock config with default values", () => {
|
|
29
|
+
const config = createMockConfig();
|
|
30
|
+
expect(config.project).toBe("test-project");
|
|
31
|
+
expect(config.capabilities.enable).toEqual([]);
|
|
32
|
+
expect(config.capabilities.disable).toEqual([]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("should allow overriding default values", () => {
|
|
36
|
+
const config = createMockConfig({
|
|
37
|
+
project: "custom-project",
|
|
38
|
+
capabilities: {
|
|
39
|
+
enable: ["cap1", "cap2"],
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
expect(config.project).toBe("custom-project");
|
|
43
|
+
expect(config.capabilities.enable).toEqual(["cap1", "cap2"]);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("createMockSkill", () => {
|
|
48
|
+
test("should create a mock skill with default values", () => {
|
|
49
|
+
const skill = createMockSkill();
|
|
50
|
+
expect(skill.id).toBe("test-skill");
|
|
51
|
+
expect(skill.name).toBe("Test Skill");
|
|
52
|
+
expect(skill.description).toBe("A test skill for unit testing");
|
|
53
|
+
expect(skill.instructions).toBe("Test instructions");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("should allow overriding default values", () => {
|
|
57
|
+
const skill = createMockSkill({
|
|
58
|
+
id: "custom-skill",
|
|
59
|
+
triggers: ["trigger1", "trigger2"],
|
|
60
|
+
});
|
|
61
|
+
expect(skill.id).toBe("custom-skill");
|
|
62
|
+
expect(skill.triggers).toEqual(["trigger1", "trigger2"]);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("createMockRule", () => {
|
|
67
|
+
test("should create a mock rule with default values", () => {
|
|
68
|
+
const rule = createMockRule();
|
|
69
|
+
expect(rule.id).toBe("test-rule");
|
|
70
|
+
expect(rule.name).toBe("Test Rule");
|
|
71
|
+
expect(rule.content).toBe("# Test Rule\n\nTest rule content");
|
|
72
|
+
expect(rule.priority).toBe(1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("should allow overriding default values", () => {
|
|
76
|
+
const rule = createMockRule({
|
|
77
|
+
id: "custom-rule",
|
|
78
|
+
priority: 10,
|
|
79
|
+
});
|
|
80
|
+
expect(rule.id).toBe("custom-rule");
|
|
81
|
+
expect(rule.priority).toBe(10);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock factories for creating test data
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface MockCapability {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
version: string;
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
metadata?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface MockConfig {
|
|
14
|
+
project: string;
|
|
15
|
+
capabilities: {
|
|
16
|
+
enable: string[];
|
|
17
|
+
disable?: string[];
|
|
18
|
+
};
|
|
19
|
+
profiles?: Record<string, unknown>;
|
|
20
|
+
env?: Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface MockSkill {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
description: string;
|
|
27
|
+
instructions: string;
|
|
28
|
+
triggers?: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface MockRule {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
content: string;
|
|
35
|
+
priority?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Creates a mock capability with default values
|
|
40
|
+
* @param overrides - Partial capability object to override defaults
|
|
41
|
+
* @returns Mock capability object
|
|
42
|
+
*/
|
|
43
|
+
export function createMockCapability(overrides: Partial<MockCapability> = {}): MockCapability {
|
|
44
|
+
return {
|
|
45
|
+
id: "test-capability",
|
|
46
|
+
name: "Test Capability",
|
|
47
|
+
version: "1.0.0",
|
|
48
|
+
enabled: true,
|
|
49
|
+
metadata: {},
|
|
50
|
+
...overrides,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Creates a mock config with default values
|
|
56
|
+
* @param overrides - Partial config object to override defaults
|
|
57
|
+
* @returns Mock config object
|
|
58
|
+
*/
|
|
59
|
+
export function createMockConfig(overrides: Partial<MockConfig> = {}): MockConfig {
|
|
60
|
+
return {
|
|
61
|
+
project: "test-project",
|
|
62
|
+
capabilities: {
|
|
63
|
+
enable: [],
|
|
64
|
+
disable: [],
|
|
65
|
+
},
|
|
66
|
+
profiles: {},
|
|
67
|
+
env: {},
|
|
68
|
+
...overrides,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a mock skill with default values
|
|
74
|
+
* @param overrides - Partial skill object to override defaults
|
|
75
|
+
* @returns Mock skill object
|
|
76
|
+
*/
|
|
77
|
+
export function createMockSkill(overrides: Partial<MockSkill> = {}): MockSkill {
|
|
78
|
+
return {
|
|
79
|
+
id: "test-skill",
|
|
80
|
+
name: "Test Skill",
|
|
81
|
+
description: "A test skill for unit testing",
|
|
82
|
+
instructions: "Test instructions",
|
|
83
|
+
triggers: [],
|
|
84
|
+
...overrides,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Creates a mock rule with default values
|
|
90
|
+
* @param overrides - Partial rule object to override defaults
|
|
91
|
+
* @returns Mock rule object
|
|
92
|
+
*/
|
|
93
|
+
export function createMockRule(overrides: Partial<MockRule> = {}): MockRule {
|
|
94
|
+
return {
|
|
95
|
+
id: "test-rule",
|
|
96
|
+
name: "Test Rule",
|
|
97
|
+
content: "# Test Rule\n\nTest rule content",
|
|
98
|
+
priority: 1,
|
|
99
|
+
...overrides,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Export Types
|
|
3
|
+
*
|
|
4
|
+
* These types define the structure that capabilities use to export their features.
|
|
5
|
+
* Capabilities should import these types from @omnidev-ai/core and use them in their index.ts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* File content structure for programmatic file creation
|
|
10
|
+
*/
|
|
11
|
+
export interface FileContent {
|
|
12
|
+
/** File name (relative path within capability) */
|
|
13
|
+
name: string;
|
|
14
|
+
|
|
15
|
+
/** File content */
|
|
16
|
+
content: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Documentation export structure
|
|
21
|
+
*/
|
|
22
|
+
export interface DocExport {
|
|
23
|
+
/** Document title */
|
|
24
|
+
title: string;
|
|
25
|
+
|
|
26
|
+
/** Markdown content */
|
|
27
|
+
content: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Skill export structure
|
|
32
|
+
*/
|
|
33
|
+
export interface SkillExport {
|
|
34
|
+
/** SKILL.md content (markdown with YAML frontmatter) */
|
|
35
|
+
skillMd: string;
|
|
36
|
+
|
|
37
|
+
/** Optional: Reference files to create (files the skill needs access to) */
|
|
38
|
+
references?: FileContent[];
|
|
39
|
+
|
|
40
|
+
/** Optional: Additional files to create (templates, examples, etc.) */
|
|
41
|
+
additionalFiles?: FileContent[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* JSON Schema type for tool parameters
|
|
46
|
+
*/
|
|
47
|
+
export interface JSONSchema {
|
|
48
|
+
type?: string | string[];
|
|
49
|
+
properties?: Record<string, JSONSchema>;
|
|
50
|
+
required?: string[];
|
|
51
|
+
items?: JSONSchema;
|
|
52
|
+
enum?: unknown[];
|
|
53
|
+
description?: string;
|
|
54
|
+
default?: unknown;
|
|
55
|
+
[key: string]: unknown;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Subagent export structure
|
|
60
|
+
*
|
|
61
|
+
* Defines a subagent that Claude can delegate tasks to.
|
|
62
|
+
* Uses YAML frontmatter in markdown format for configuration.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const codeReviewer: SubagentExport = {
|
|
67
|
+
* subagentMd: `---
|
|
68
|
+
* name: code-reviewer
|
|
69
|
+
* description: Reviews code for quality and best practices
|
|
70
|
+
* tools: Read, Glob, Grep
|
|
71
|
+
* model: sonnet
|
|
72
|
+
* ---
|
|
73
|
+
*
|
|
74
|
+
* You are a code reviewer. When invoked, analyze the code and provide
|
|
75
|
+
* specific, actionable feedback on quality, security, and best practices.`
|
|
76
|
+
* };
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export interface SubagentExport {
|
|
80
|
+
/** SUBAGENT.md content (markdown with YAML frontmatter) */
|
|
81
|
+
subagentMd: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Slash command export structure
|
|
86
|
+
*
|
|
87
|
+
* Defines a slash command that can be invoked in Claude Code.
|
|
88
|
+
* Uses YAML frontmatter in markdown format for configuration.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* const fixIssue: CommandExport = {
|
|
93
|
+
* commandMd: `---
|
|
94
|
+
* name: fix-issue
|
|
95
|
+
* description: Fix a GitHub issue following coding standards
|
|
96
|
+
* allowed-tools: Bash(git add:*), Bash(git commit:*)
|
|
97
|
+
* ---
|
|
98
|
+
*
|
|
99
|
+
* Fix issue #$ARGUMENTS following our coding standards.
|
|
100
|
+
*
|
|
101
|
+
* 1. Read the issue details
|
|
102
|
+
* 2. Implement the fix
|
|
103
|
+
* 3. Write tests
|
|
104
|
+
* 4. Create a commit`
|
|
105
|
+
* };
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export interface CommandExport {
|
|
109
|
+
/** COMMAND.md content (markdown with YAML frontmatter) */
|
|
110
|
+
commandMd: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Sandbox tool export structure
|
|
115
|
+
*
|
|
116
|
+
* Defines a tool that can be called from sandbox code via omni_execute.
|
|
117
|
+
* Full schema is required for proper introspection and type generation.
|
|
118
|
+
*
|
|
119
|
+
* For MCP wrapper capabilities, these are auto-discovered from the child MCP.
|
|
120
|
+
* For custom capabilities, these must be explicitly defined with full schemas.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* const createTask: SandboxToolExport = {
|
|
125
|
+
* name: "createTask",
|
|
126
|
+
* description: "Create a new task",
|
|
127
|
+
* inputSchema: {
|
|
128
|
+
* type: "object",
|
|
129
|
+
* properties: {
|
|
130
|
+
* title: { type: "string", description: "Task title" },
|
|
131
|
+
* priority: { type: "string", enum: ["low", "medium", "high"] }
|
|
132
|
+
* },
|
|
133
|
+
* required: ["title"]
|
|
134
|
+
* },
|
|
135
|
+
* outputSchema: {
|
|
136
|
+
* type: "object",
|
|
137
|
+
* properties: {
|
|
138
|
+
* id: { type: "string" },
|
|
139
|
+
* title: { type: "string" },
|
|
140
|
+
* createdAt: { type: "string" }
|
|
141
|
+
* }
|
|
142
|
+
* },
|
|
143
|
+
* specification: `/**
|
|
144
|
+
* * Create a new task in the task management system.
|
|
145
|
+
* * @param input.title - The title of the task (required)
|
|
146
|
+
* * @param input.priority - Priority level (default: "medium")
|
|
147
|
+
* * @returns The created task object with generated ID
|
|
148
|
+
* *\/`
|
|
149
|
+
* };
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
export interface SandboxToolExport {
|
|
153
|
+
/** Tool name (used as function name in sandbox) */
|
|
154
|
+
name: string;
|
|
155
|
+
|
|
156
|
+
/** Short description for overview listings */
|
|
157
|
+
description: string;
|
|
158
|
+
|
|
159
|
+
/** JSON Schema for input parameters (required for proper introspection) */
|
|
160
|
+
inputSchema: JSONSchema;
|
|
161
|
+
|
|
162
|
+
/** JSON Schema for output/return value */
|
|
163
|
+
outputSchema?: JSONSchema;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Full specification/documentation for the tool.
|
|
167
|
+
* Can include JSDoc, examples, detailed behavior notes.
|
|
168
|
+
* This is shown when requesting full details for a specific tool.
|
|
169
|
+
*/
|
|
170
|
+
specification?: string;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Complete capability export structure
|
|
175
|
+
*
|
|
176
|
+
* Capabilities export this as their default export from index.ts.
|
|
177
|
+
* All content fields are OPTIONAL and PROGRAMMATIC.
|
|
178
|
+
* Capabilities can also provide content via static files in their directory.
|
|
179
|
+
* Both approaches are supported and will be merged during sync.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* // Static files approach - just export CLI commands
|
|
184
|
+
* export default {
|
|
185
|
+
* cliCommands: { mycap: myRoutes },
|
|
186
|
+
* gitignore: ["mycap/"],
|
|
187
|
+
* sync
|
|
188
|
+
* } satisfies CapabilityExport;
|
|
189
|
+
* ```
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```typescript
|
|
193
|
+
* // Programmatic approach - generate content dynamically
|
|
194
|
+
* export default {
|
|
195
|
+
* cliCommands: { mycap: myRoutes },
|
|
196
|
+
* docs: [{ title: "Guide", content: "# Guide\n..." }],
|
|
197
|
+
* rules: ["# Rule content..."],
|
|
198
|
+
* skills: [{ skillMd: "...", references: [...] }],
|
|
199
|
+
* gitignore: ["mycap/"],
|
|
200
|
+
* sync
|
|
201
|
+
* } satisfies CapabilityExport;
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export interface CapabilityExport {
|
|
205
|
+
/** CLI commands provided by this capability */
|
|
206
|
+
cliCommands?: Record<string, unknown>; // stricli Command type
|
|
207
|
+
|
|
208
|
+
/** Sandbox tools provided by this capability (callable from omni_execute) */
|
|
209
|
+
sandboxTools?: Record<string, SandboxToolExport>;
|
|
210
|
+
|
|
211
|
+
/** Documentation (programmatic - optional, can also use docs/ directory) */
|
|
212
|
+
docs?: DocExport[];
|
|
213
|
+
|
|
214
|
+
/** Rules (programmatic - optional, can also use rules/ directory) */
|
|
215
|
+
rules?: string[]; // Array of markdown content strings
|
|
216
|
+
|
|
217
|
+
/** Skills (programmatic - optional, can also use skills/ directory) */
|
|
218
|
+
skills?: SkillExport[];
|
|
219
|
+
|
|
220
|
+
/** Subagents (programmatic - optional, can also use subagents/ directory) */
|
|
221
|
+
subagents?: SubagentExport[];
|
|
222
|
+
|
|
223
|
+
/** Commands (programmatic - optional, can also use commands/ directory) */
|
|
224
|
+
commands?: CommandExport[];
|
|
225
|
+
|
|
226
|
+
/** Gitignore patterns */
|
|
227
|
+
gitignore?: string[];
|
|
228
|
+
|
|
229
|
+
/** Custom sync hook function */
|
|
230
|
+
sync?: () => Promise<void>;
|
|
231
|
+
|
|
232
|
+
/** Additional exports for extensibility */
|
|
233
|
+
[key: string]: unknown;
|
|
234
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type { ProviderConfig } from "./index.js";
|
|
3
|
+
import { getActiveProviders } from "./index.js";
|
|
4
|
+
|
|
5
|
+
describe("getActiveProviders", () => {
|
|
6
|
+
test("returns providers array when present", () => {
|
|
7
|
+
const config: ProviderConfig = { providers: ["claude", "codex"] };
|
|
8
|
+
expect(getActiveProviders(config)).toEqual(["claude", "codex"]);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("returns single provider as array when present", () => {
|
|
12
|
+
const config: ProviderConfig = { provider: "claude" };
|
|
13
|
+
expect(getActiveProviders(config)).toEqual(["claude"]);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("prefers providers array over single provider", () => {
|
|
17
|
+
const config: ProviderConfig = {
|
|
18
|
+
provider: "claude",
|
|
19
|
+
providers: ["codex"],
|
|
20
|
+
};
|
|
21
|
+
expect(getActiveProviders(config)).toEqual(["codex"]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("returns claude as default when no provider specified", () => {
|
|
25
|
+
const config: ProviderConfig = {};
|
|
26
|
+
expect(getActiveProviders(config)).toEqual(["claude"]);
|
|
27
|
+
});
|
|
28
|
+
});
|