@omnidev-ai/core 0.3.0 → 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.
Files changed (64) hide show
  1. package/dist/index.d.ts +889 -0
  2. package/dist/index.js +2173 -0
  3. package/dist/test-utils/index.d.ts +142 -0
  4. package/dist/test-utils/index.js +261 -0
  5. package/package.json +16 -6
  6. package/src/capability/AGENTS.md +0 -58
  7. package/src/capability/commands.test.ts +0 -410
  8. package/src/capability/commands.ts +0 -72
  9. package/src/capability/docs.test.ts +0 -192
  10. package/src/capability/docs.ts +0 -48
  11. package/src/capability/index.ts +0 -20
  12. package/src/capability/loader.test.ts +0 -668
  13. package/src/capability/loader.ts +0 -431
  14. package/src/capability/registry.test.ts +0 -455
  15. package/src/capability/registry.ts +0 -55
  16. package/src/capability/rules.test.ts +0 -135
  17. package/src/capability/rules.ts +0 -135
  18. package/src/capability/skills.test.ts +0 -312
  19. package/src/capability/skills.ts +0 -58
  20. package/src/capability/sources.test.ts +0 -439
  21. package/src/capability/sources.ts +0 -998
  22. package/src/capability/subagents.test.ts +0 -474
  23. package/src/capability/subagents.ts +0 -105
  24. package/src/capability/wrapping-integration.test.ts +0 -412
  25. package/src/capability/yaml-parser.ts +0 -81
  26. package/src/config/AGENTS.md +0 -46
  27. package/src/config/capabilities.ts +0 -54
  28. package/src/config/env.test.ts +0 -270
  29. package/src/config/env.ts +0 -96
  30. package/src/config/index.ts +0 -6
  31. package/src/config/loader.test.ts +0 -198
  32. package/src/config/loader.ts +0 -207
  33. package/src/config/parser.test.ts +0 -256
  34. package/src/config/parser.ts +0 -55
  35. package/src/config/profiles.test.ts +0 -222
  36. package/src/config/profiles.ts +0 -75
  37. package/src/config/provider.test.ts +0 -66
  38. package/src/config/provider.ts +0 -55
  39. package/src/debug.ts +0 -20
  40. package/src/index.test.ts +0 -26
  41. package/src/index.ts +0 -37
  42. package/src/mcp-json/index.ts +0 -1
  43. package/src/mcp-json/manager.test.ts +0 -310
  44. package/src/mcp-json/manager.ts +0 -106
  45. package/src/state/active-profile.test.ts +0 -117
  46. package/src/state/active-profile.ts +0 -41
  47. package/src/state/index.ts +0 -3
  48. package/src/state/manifest.test.ts +0 -411
  49. package/src/state/manifest.ts +0 -137
  50. package/src/state/providers.test.ts +0 -125
  51. package/src/state/providers.ts +0 -69
  52. package/src/sync.ts +0 -288
  53. package/src/templates/agents.test.ts +0 -23
  54. package/src/templates/agents.ts +0 -14
  55. package/src/templates/claude.test.ts +0 -48
  56. package/src/templates/claude.ts +0 -57
  57. package/src/test-utils/helpers.test.ts +0 -214
  58. package/src/test-utils/helpers.ts +0 -284
  59. package/src/test-utils/index.ts +0 -34
  60. package/src/test-utils/mocks.test.ts +0 -83
  61. package/src/test-utils/mocks.ts +0 -101
  62. package/src/types/capability-export.ts +0 -157
  63. package/src/types/index.test.ts +0 -28
  64. package/src/types/index.ts +0 -314
@@ -0,0 +1,142 @@
1
+ //#region src/test-utils/helpers.d.ts
2
+ /**
3
+ * Helper functions for testing
4
+ */
5
+ /**
6
+ * Expects an async function to throw an error
7
+ * @param fn - Async function that should throw
8
+ * @param errorMatch - Optional string or regex to match against error message
9
+ * @throws If the function doesn't throw
10
+ */
11
+ declare function expectToThrowAsync(fn: () => Promise<unknown>, errorMatch?: string | RegExp): Promise<void>;
12
+ /**
13
+ * Waits for a condition to be true
14
+ * @param condition - Function that returns true when condition is met
15
+ * @param timeout - Maximum time to wait in milliseconds (default: 1000)
16
+ * @param interval - Check interval in milliseconds (default: 50)
17
+ * @throws If timeout is reached before condition is met
18
+ */
19
+ declare function waitForCondition(condition: () => boolean | Promise<boolean>, timeout?: number, interval?: number): Promise<void>;
20
+ /**
21
+ * Delays execution for a specified amount of time
22
+ * @param ms - Milliseconds to delay
23
+ */
24
+ declare function delay(ms: number): Promise<void>;
25
+ /**
26
+ * Creates a spy function that records calls and arguments
27
+ * @returns Spy function with call tracking
28
+ */
29
+ declare function createSpy<TArgs extends unknown[], TReturn>(implementation?: (...args: TArgs) => TReturn): {
30
+ (...args: TArgs): TReturn;
31
+ calls: TArgs[];
32
+ callCount: number;
33
+ reset: () => void;
34
+ };
35
+ /**
36
+ * Creates a mock function that returns predefined values
37
+ * @param returnValues - Array of values to return on consecutive calls
38
+ * @returns Mock function
39
+ */
40
+ declare function createMockFn<T>(...returnValues: T[]): () => T;
41
+ /**
42
+ * Creates a mock promise that can be resolved or rejected manually
43
+ * @returns Object with promise and resolve/reject functions
44
+ */
45
+ declare function createDeferredPromise<T>(): {
46
+ promise: Promise<T>;
47
+ resolve: (value: T) => void;
48
+ reject: (reason?: unknown) => void;
49
+ };
50
+ /**
51
+ * Captures console output during test execution
52
+ * @param fn - Function to execute while capturing output
53
+ * @returns Object with stdout and stderr arrays
54
+ */
55
+ declare function captureConsole<T>(fn: () => Promise<T> | T): Promise<{
56
+ stdout: string[];
57
+ stderr: string[];
58
+ result: T;
59
+ }>;
60
+ /**
61
+ * Creates a unique temporary directory for tests in /tmp
62
+ * @param prefix - Optional prefix for the directory name (default: "omnidev-test-")
63
+ * @returns Path to the created temporary directory
64
+ */
65
+ declare function tmpdir(prefix?: string): string;
66
+ type TestDirOptions = {
67
+ chdir?: boolean;
68
+ createOmniDir?: boolean;
69
+ };
70
+ type TestDirController = {
71
+ readonly path: string;
72
+ readonly originalCwd: string;
73
+ setPath: (path: string, options?: TestDirOptions) => void;
74
+ reset: (prefix?: string, options?: TestDirOptions & {
75
+ cleanupPrevious?: boolean;
76
+ }) => string;
77
+ };
78
+ /**
79
+ * Sets up a temporary directory for each test and cleans it up automatically.
80
+ * Registers beforeEach/afterEach hooks on call.
81
+ */
82
+ declare function setupTestDir(prefix?: string, options?: TestDirOptions): TestDirController;
83
+ //#endregion
84
+ //#region src/test-utils/mocks.d.ts
85
+ /**
86
+ * Mock factories for creating test data
87
+ */
88
+ interface MockCapability {
89
+ id: string;
90
+ name: string;
91
+ version: string;
92
+ enabled?: boolean;
93
+ metadata?: Record<string, unknown>;
94
+ }
95
+ interface MockConfig {
96
+ project: string;
97
+ capabilities: {
98
+ enable: string[];
99
+ disable?: string[];
100
+ };
101
+ profiles?: Record<string, unknown>;
102
+ env?: Record<string, string>;
103
+ }
104
+ interface MockSkill {
105
+ id: string;
106
+ name: string;
107
+ description: string;
108
+ instructions: string;
109
+ triggers?: string[];
110
+ }
111
+ interface MockRule {
112
+ id: string;
113
+ name: string;
114
+ content: string;
115
+ priority?: number;
116
+ }
117
+ /**
118
+ * Creates a mock capability with default values
119
+ * @param overrides - Partial capability object to override defaults
120
+ * @returns Mock capability object
121
+ */
122
+ declare function createMockCapability(overrides?: Partial<MockCapability>): MockCapability;
123
+ /**
124
+ * Creates a mock config with default values
125
+ * @param overrides - Partial config object to override defaults
126
+ * @returns Mock config object
127
+ */
128
+ declare function createMockConfig(overrides?: Partial<MockConfig>): MockConfig;
129
+ /**
130
+ * Creates a mock skill with default values
131
+ * @param overrides - Partial skill object to override defaults
132
+ * @returns Mock skill object
133
+ */
134
+ declare function createMockSkill(overrides?: Partial<MockSkill>): MockSkill;
135
+ /**
136
+ * Creates a mock rule with default values
137
+ * @param overrides - Partial rule object to override defaults
138
+ * @returns Mock rule object
139
+ */
140
+ declare function createMockRule(overrides?: Partial<MockRule>): MockRule;
141
+ //#endregion
142
+ export { type MockCapability, type MockConfig, type MockRule, type MockSkill, type TestDirController, type TestDirOptions, captureConsole, createDeferredPromise, createMockCapability, createMockConfig, createMockFn, createMockRule, createMockSkill, createSpy, delay, expectToThrowAsync, setupTestDir, tmpdir, waitForCondition };
@@ -0,0 +1,261 @@
1
+ import { existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { afterEach, beforeEach, expect } from "bun:test";
4
+ import { tmpdir as tmpdir$1 } from "node:os";
5
+
6
+ //#region src/test-utils/helpers.ts
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
+ async function expectToThrowAsync(fn, errorMatch) {
14
+ let threw = false;
15
+ let caughtError;
16
+ try {
17
+ await fn();
18
+ } catch (e) {
19
+ threw = true;
20
+ caughtError = e;
21
+ }
22
+ expect(threw).toBe(true);
23
+ if (errorMatch && caughtError) if (typeof errorMatch === "string") expect(caughtError.message).toContain(errorMatch);
24
+ else expect(caughtError.message).toMatch(errorMatch);
25
+ }
26
+ /**
27
+ * Waits for a condition to be true
28
+ * @param condition - Function that returns true when condition is met
29
+ * @param timeout - Maximum time to wait in milliseconds (default: 1000)
30
+ * @param interval - Check interval in milliseconds (default: 50)
31
+ * @throws If timeout is reached before condition is met
32
+ */
33
+ async function waitForCondition(condition, timeout = 1e3, interval = 50) {
34
+ const startTime = Date.now();
35
+ while (Date.now() - startTime < timeout) {
36
+ const result = await condition();
37
+ if (result) return;
38
+ await delay(interval);
39
+ }
40
+ throw new Error(`Condition not met within ${timeout}ms`);
41
+ }
42
+ /**
43
+ * Delays execution for a specified amount of time
44
+ * @param ms - Milliseconds to delay
45
+ */
46
+ function delay(ms) {
47
+ return new Promise((resolve) => setTimeout(resolve, ms));
48
+ }
49
+ /**
50
+ * Creates a spy function that records calls and arguments
51
+ * @returns Spy function with call tracking
52
+ */
53
+ function createSpy(implementation) {
54
+ const calls = [];
55
+ const spy = (...args) => {
56
+ calls.push(args);
57
+ if (implementation) return implementation(...args);
58
+ return void 0;
59
+ };
60
+ Object.defineProperty(spy, "calls", { get: () => calls });
61
+ Object.defineProperty(spy, "callCount", { get: () => calls.length });
62
+ spy.reset = () => {
63
+ calls.length = 0;
64
+ };
65
+ return spy;
66
+ }
67
+ /**
68
+ * Creates a mock function that returns predefined values
69
+ * @param returnValues - Array of values to return on consecutive calls
70
+ * @returns Mock function
71
+ */
72
+ function createMockFn(...returnValues) {
73
+ let callIndex = 0;
74
+ return () => {
75
+ if (callIndex >= returnValues.length) throw new Error("Mock function called more times than return values provided");
76
+ const value = returnValues[callIndex++];
77
+ if (value === void 0) throw new Error("Mock function returned undefined");
78
+ return value;
79
+ };
80
+ }
81
+ /**
82
+ * Creates a mock promise that can be resolved or rejected manually
83
+ * @returns Object with promise and resolve/reject functions
84
+ */
85
+ function createDeferredPromise() {
86
+ let resolveRef;
87
+ let rejectRef;
88
+ const promise = new Promise((res, rej) => {
89
+ resolveRef = res;
90
+ rejectRef = rej;
91
+ });
92
+ if (!resolveRef || !rejectRef) throw new Error("Promise executor did not initialize resolve/reject");
93
+ return {
94
+ promise,
95
+ resolve: resolveRef,
96
+ reject: rejectRef
97
+ };
98
+ }
99
+ /**
100
+ * Captures console output during test execution
101
+ * @param fn - Function to execute while capturing output
102
+ * @returns Object with stdout and stderr arrays
103
+ */
104
+ async function captureConsole(fn) {
105
+ const stdout = [];
106
+ const stderr = [];
107
+ const originalLog = console.log;
108
+ const originalError = console.error;
109
+ const originalWarn = console.warn;
110
+ console.log = (...args) => {
111
+ stdout.push(args.map(String).join(" "));
112
+ };
113
+ console.error = (...args) => {
114
+ stderr.push(args.map(String).join(" "));
115
+ };
116
+ console.warn = (...args) => {
117
+ stderr.push(args.map(String).join(" "));
118
+ };
119
+ try {
120
+ const result = await fn();
121
+ return {
122
+ stdout,
123
+ stderr,
124
+ result
125
+ };
126
+ } finally {
127
+ console.log = originalLog;
128
+ console.error = originalError;
129
+ console.warn = originalWarn;
130
+ }
131
+ }
132
+ /**
133
+ * Creates a unique temporary directory for tests in /tmp
134
+ * @param prefix - Optional prefix for the directory name (default: "omnidev-test-")
135
+ * @returns Path to the created temporary directory
136
+ */
137
+ function tmpdir(prefix = "omnidev-test-") {
138
+ return mkdtempSync(join(tmpdir$1(), prefix));
139
+ }
140
+ /**
141
+ * Sets up a temporary directory for each test and cleans it up automatically.
142
+ * Registers beforeEach/afterEach hooks on call.
143
+ */
144
+ function setupTestDir(prefix = "omnidev-test-", options = {}) {
145
+ let currentDir = "";
146
+ let originalCwd = "";
147
+ let shouldChdir = options.chdir ?? false;
148
+ let shouldCreateOmniDir = options.createOmniDir ?? false;
149
+ const applyOptions = (dir, nextOptions) => {
150
+ if (nextOptions) {
151
+ if (typeof nextOptions.chdir === "boolean") shouldChdir = nextOptions.chdir;
152
+ if (typeof nextOptions.createOmniDir === "boolean") shouldCreateOmniDir = nextOptions.createOmniDir;
153
+ }
154
+ if (shouldCreateOmniDir) mkdirSync(join(dir, ".omni"), { recursive: true });
155
+ if (shouldChdir) process.chdir(dir);
156
+ };
157
+ beforeEach(() => {
158
+ originalCwd = process.cwd();
159
+ currentDir = tmpdir(prefix);
160
+ applyOptions(currentDir);
161
+ });
162
+ afterEach(() => {
163
+ if (shouldChdir) process.chdir(originalCwd);
164
+ if (currentDir && existsSync(currentDir)) rmSync(currentDir, {
165
+ recursive: true,
166
+ force: true
167
+ });
168
+ });
169
+ return {
170
+ get path() {
171
+ return currentDir;
172
+ },
173
+ get originalCwd() {
174
+ return originalCwd;
175
+ },
176
+ setPath(path, nextOptions) {
177
+ currentDir = path;
178
+ applyOptions(currentDir, nextOptions);
179
+ },
180
+ reset(nextPrefix = prefix, nextOptions) {
181
+ const cleanupPrevious = nextOptions?.cleanupPrevious ?? true;
182
+ if (cleanupPrevious && currentDir && existsSync(currentDir)) {
183
+ if (shouldChdir) process.chdir(originalCwd);
184
+ rmSync(currentDir, {
185
+ recursive: true,
186
+ force: true
187
+ });
188
+ }
189
+ currentDir = tmpdir(nextPrefix);
190
+ applyOptions(currentDir, nextOptions);
191
+ return currentDir;
192
+ }
193
+ };
194
+ }
195
+
196
+ //#endregion
197
+ //#region src/test-utils/mocks.ts
198
+ /**
199
+ * Creates a mock capability with default values
200
+ * @param overrides - Partial capability object to override defaults
201
+ * @returns Mock capability object
202
+ */
203
+ function createMockCapability(overrides = {}) {
204
+ return {
205
+ id: "test-capability",
206
+ name: "Test Capability",
207
+ version: "1.0.0",
208
+ enabled: true,
209
+ metadata: {},
210
+ ...overrides
211
+ };
212
+ }
213
+ /**
214
+ * Creates a mock config with default values
215
+ * @param overrides - Partial config object to override defaults
216
+ * @returns Mock config object
217
+ */
218
+ function createMockConfig(overrides = {}) {
219
+ return {
220
+ project: "test-project",
221
+ capabilities: {
222
+ enable: [],
223
+ disable: []
224
+ },
225
+ profiles: {},
226
+ env: {},
227
+ ...overrides
228
+ };
229
+ }
230
+ /**
231
+ * Creates a mock skill with default values
232
+ * @param overrides - Partial skill object to override defaults
233
+ * @returns Mock skill object
234
+ */
235
+ function createMockSkill(overrides = {}) {
236
+ return {
237
+ id: "test-skill",
238
+ name: "Test Skill",
239
+ description: "A test skill for unit testing",
240
+ instructions: "Test instructions",
241
+ triggers: [],
242
+ ...overrides
243
+ };
244
+ }
245
+ /**
246
+ * Creates a mock rule with default values
247
+ * @param overrides - Partial rule object to override defaults
248
+ * @returns Mock rule object
249
+ */
250
+ function createMockRule(overrides = {}) {
251
+ return {
252
+ id: "test-rule",
253
+ name: "Test Rule",
254
+ content: "# Test Rule\n\nTest rule content",
255
+ priority: 1,
256
+ ...overrides
257
+ };
258
+ }
259
+
260
+ //#endregion
261
+ export { captureConsole, createDeferredPromise, createMockCapability, createMockConfig, createMockFn, createMockRule, createMockSkill, createSpy, delay, expectToThrowAsync, setupTestDir, tmpdir, waitForCondition };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnidev-ai/core",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -9,11 +9,19 @@
9
9
  "directory": "packages/core"
10
10
  },
11
11
  "exports": {
12
- ".": "./src/index.ts",
13
- "./test-utils": "./src/test-utils/index.ts"
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "bun": "./src/index.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "./test-utils": {
18
+ "types": "./dist/test-utils/index.d.ts",
19
+ "bun": "./src/test-utils/index.ts",
20
+ "default": "./dist/test-utils/index.js"
21
+ }
14
22
  },
15
23
  "files": [
16
- "src",
24
+ "dist",
17
25
  "README.md"
18
26
  ],
19
27
  "publishConfig": {
@@ -22,11 +30,13 @@
22
30
  },
23
31
  "scripts": {
24
32
  "typecheck": "tsc --noEmit",
25
- "build": "echo 'Build not needed for Bun runtime'"
33
+ "build": "tsdown"
26
34
  },
27
35
  "dependencies": {
28
36
  "@stricli/core": "^1.2.5",
29
37
  "smol-toml": "^1.6.0"
30
38
  },
31
- "devDependencies": {}
39
+ "devDependencies": {
40
+ "tsdown": "^0.12.5"
41
+ }
32
42
  }
@@ -1,58 +0,0 @@
1
- # PROJECT KNOWLEDGE BASE
2
-
3
- **Generated:** 2026-01-12
4
- **Commit:** (not specified)
5
- **Branch:** (not specified)
6
-
7
- ## OVERVIEW
8
- Core capability loading system: discovers capabilities from .omni/capabilities/ & capabilities/, validates TOML configs, loads TypeScript exports (skills/rules/docs/commands/subagents), and builds runtime registry.
9
-
10
- ## WHERE TO LOOK
11
- | Task | Location | Notes |
12
- |------|----------|-------|
13
- | Capability discovery | loader.ts | Scans .omni/capabilities/ & capabilities/ for capability.toml |
14
- | Load TOML config | loader.ts | Validates required fields, checks reserved names |
15
- | Dynamic imports | loader.ts | Imports index.ts exports, handles missing deps gracefully |
16
- | Registry build | registry.ts | Filters by enabled caps, aggregates all content types |
17
- | Skills loading | skills.ts | Parses SKILL.md with YAML frontmatter |
18
- | Rules loading | rules.ts | Loads *.md from rules/ directory |
19
- | Docs loading | docs.ts | Loads definition.md + docs/*.md |
20
- | Commands loading | commands.ts | Parses COMMAND.md with YAML frontmatter |
21
- | Subagents loading | subagents.ts | Parses SUBAGENT.md, supports tools/skills/models |
22
- | Remote sources | sources.ts | Git clone/fetch, wrap external repos, lock file mgmt |
23
-
24
- ## CONVENTIONS
25
-
26
- **Capability Structure:**
27
- - Must have capability.toml in root
28
- - Optional: index.ts (exports: skills/rules/docs/commands/subagents/gitignore)
29
- - Optional directories: skills/, rules/, docs/, commands/, subagents/
30
- - Optional types.d.ts (for LLM type hints)
31
-
32
- **YAML Frontmatter Format:**
33
- - Skills/Commands: name, description (required)
34
- - Subagents: name, description + optional tools/disallowedTools/model/permissionMode/skills
35
- - Supports kebab-case keys (converted to camelCase)
36
-
37
- **Content Loading Priority:**
38
- - Programmatic exports (index.ts) take precedence over file-based content
39
- - Loader converts both old and new export formats automatically
40
-
41
- **Reserved Names:**
42
- - Node builtins (fs, path, http, crypto, os, etc.)
43
- - Common libs (react, vue, lodash, axios, express, typescript)
44
- - Prevents import conflicts with capability modules
45
-
46
- **Remote Capability Sources:**
47
- - Shorthand: "github:user/repo#ref"
48
- - Lock file: .omni/capabilities.lock.toml (tracks versions/commits)
49
- - Wrap mode: discovers skills/agents/commands in repo without capability.toml
50
- - Version: from package.json if available, else short commit hash
51
-
52
- ## ANTI-PATTERNS (THIS MODULE)
53
-
54
- - **NEVER** use reserved capability names (fs, path, react, typescript, etc.)
55
- - **NEVER** commit capabilities.lock.toml modifications - auto-generated
56
- - **NEVER** skip YAML frontmatter validation - name/description required
57
- - **NEVER** modify generated capability.toml in wrapped repos
58
- - **NEVER** assume index.ts exists - loader returns empty object if missing