@omnidev-ai/core 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,439 +0,0 @@
1
- import { beforeEach, describe, expect, test } from "bun:test";
2
- import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
3
- import { join } from "node:path";
4
- import { setupTestDir } from "@omnidev-ai/core/test-utils";
5
- import {
6
- parseSourceConfig,
7
- sourceToGitUrl,
8
- getSourceCapabilityPath,
9
- getLockFilePath,
10
- loadLockFile,
11
- saveLockFile,
12
- } from "./sources";
13
- import type { CapabilitiesLockFile, GitCapabilitySourceConfig } from "../types/index.js";
14
-
15
- describe("parseSourceConfig", () => {
16
- test("parses simple github shorthand", () => {
17
- const config = parseSourceConfig("github:user/repo") as GitCapabilitySourceConfig;
18
-
19
- expect(config.source).toBe("github:user/repo");
20
- expect(config.ref).toBeUndefined();
21
- });
22
-
23
- test("parses github shorthand with ref", () => {
24
- const config = parseSourceConfig("github:user/repo#v1.0.0") as GitCapabilitySourceConfig;
25
-
26
- expect(config.source).toBe("github:user/repo");
27
- expect(config.ref).toBe("v1.0.0");
28
- });
29
-
30
- test("parses github shorthand with branch ref", () => {
31
- const config = parseSourceConfig("github:user/repo#main") as GitCapabilitySourceConfig;
32
-
33
- expect(config.source).toBe("github:user/repo");
34
- expect(config.ref).toBe("main");
35
- });
36
-
37
- test("parses github shorthand with commit ref", () => {
38
- const config = parseSourceConfig("github:user/repo#abc123def") as GitCapabilitySourceConfig;
39
-
40
- expect(config.source).toBe("github:user/repo");
41
- expect(config.ref).toBe("abc123def");
42
- });
43
-
44
- test("parses git ssh URL", () => {
45
- const config = parseSourceConfig("git@github.com:user/repo.git") as GitCapabilitySourceConfig;
46
-
47
- expect(config.source).toBe("git@github.com:user/repo.git");
48
- expect(config.ref).toBeUndefined();
49
- });
50
-
51
- test("parses https git URL", () => {
52
- const config = parseSourceConfig(
53
- "https://github.com/user/repo.git",
54
- ) as GitCapabilitySourceConfig;
55
-
56
- expect(config.source).toBe("https://github.com/user/repo.git");
57
- expect(config.ref).toBeUndefined();
58
- });
59
-
60
- test("passes through full config object", () => {
61
- const config = parseSourceConfig({
62
- source: "github:user/repo",
63
- ref: "v2.0.0",
64
- }) as GitCapabilitySourceConfig;
65
-
66
- expect(config.source).toBe("github:user/repo");
67
- expect(config.ref).toBe("v2.0.0");
68
- });
69
-
70
- test("passes through config with path", () => {
71
- const config = parseSourceConfig({
72
- source: "github:user/monorepo",
73
- path: "packages/my-cap",
74
- }) as GitCapabilitySourceConfig;
75
-
76
- expect(config.source).toBe("github:user/monorepo");
77
- expect(config.path).toBe("packages/my-cap");
78
- });
79
- });
80
-
81
- describe("sourceToGitUrl", () => {
82
- test("converts github shorthand to https URL", () => {
83
- const url = sourceToGitUrl("github:user/repo");
84
-
85
- expect(url).toBe("https://github.com/user/repo.git");
86
- });
87
-
88
- test("converts github shorthand with org to https URL", () => {
89
- const url = sourceToGitUrl("github:my-org/my-repo");
90
-
91
- expect(url).toBe("https://github.com/my-org/my-repo.git");
92
- });
93
-
94
- test("passes through https URL unchanged", () => {
95
- const url = sourceToGitUrl("https://github.com/user/repo.git");
96
-
97
- expect(url).toBe("https://github.com/user/repo.git");
98
- });
99
-
100
- test("passes through ssh URL unchanged", () => {
101
- const url = sourceToGitUrl("git@github.com:user/repo.git");
102
-
103
- expect(url).toBe("git@github.com:user/repo.git");
104
- });
105
-
106
- test("passes through gitlab https URL unchanged", () => {
107
- const url = sourceToGitUrl("https://gitlab.com/user/repo.git");
108
-
109
- expect(url).toBe("https://gitlab.com/user/repo.git");
110
- });
111
- });
112
-
113
- describe("getSourceCapabilityPath", () => {
114
- test("returns correct path for capability id", () => {
115
- const path = getSourceCapabilityPath("my-cap");
116
-
117
- expect(path).toBe(".omni/capabilities/my-cap");
118
- });
119
-
120
- test("handles capability id with hyphens", () => {
121
- const path = getSourceCapabilityPath("my-awesome-capability");
122
-
123
- expect(path).toBe(".omni/capabilities/my-awesome-capability");
124
- });
125
- });
126
-
127
- describe("getLockFilePath", () => {
128
- test("returns correct lock file path", () => {
129
- const path = getLockFilePath();
130
-
131
- expect(path).toBe("omni.lock.toml");
132
- });
133
- });
134
-
135
- describe("loadLockFile", () => {
136
- setupTestDir("test-lock-file-", { chdir: true, createOmniDir: true });
137
- test("returns empty capabilities when lock file does not exist", async () => {
138
- const lockFile = await loadLockFile();
139
-
140
- expect(lockFile.capabilities).toEqual({});
141
- });
142
-
143
- test("loads lock file with single capability", async () => {
144
- writeFileSync(
145
- "omni.lock.toml",
146
- `[capabilities.my-cap]
147
- source = "github:user/repo"
148
- version = "1.0.0"
149
- commit = "abc123def456"
150
- updated_at = "2026-01-01T00:00:00Z"
151
- `,
152
- );
153
-
154
- const lockFile = await loadLockFile();
155
-
156
- expect(lockFile.capabilities["my-cap"]).toBeDefined();
157
- expect(lockFile.capabilities["my-cap"]?.source).toBe("github:user/repo");
158
- expect(lockFile.capabilities["my-cap"]?.version).toBe("1.0.0");
159
- expect(lockFile.capabilities["my-cap"]?.commit).toBe("abc123def456");
160
- });
161
-
162
- test("loads lock file with multiple capabilities", async () => {
163
- writeFileSync(
164
- "omni.lock.toml",
165
- `[capabilities.cap1]
166
- source = "github:user/repo1"
167
- version = "1.0.0"
168
- commit = "abc123"
169
- updated_at = "2026-01-01T00:00:00Z"
170
-
171
- [capabilities.cap2]
172
- source = "github:user/repo2"
173
- version = "2.0.0"
174
- commit = "def456"
175
- ref = "v2.0.0"
176
- updated_at = "2026-01-02T00:00:00Z"
177
- `,
178
- );
179
-
180
- const lockFile = await loadLockFile();
181
-
182
- expect(Object.keys(lockFile.capabilities)).toHaveLength(2);
183
- expect(lockFile.capabilities["cap1"]?.version).toBe("1.0.0");
184
- expect(lockFile.capabilities["cap2"]?.version).toBe("2.0.0");
185
- expect(lockFile.capabilities["cap2"]?.ref).toBe("v2.0.0");
186
- });
187
-
188
- test("returns empty capabilities for invalid TOML", async () => {
189
- writeFileSync("omni.lock.toml", "invalid [[[toml");
190
-
191
- const lockFile = await loadLockFile();
192
-
193
- expect(lockFile.capabilities).toEqual({});
194
- });
195
- });
196
-
197
- describe("saveLockFile", () => {
198
- setupTestDir("test-save-lock-", { chdir: true, createOmniDir: true });
199
- test("creates lock file with single capability", async () => {
200
- const lockFile: CapabilitiesLockFile = {
201
- capabilities: {
202
- "my-cap": {
203
- source: "github:user/repo",
204
- version: "1.0.0",
205
- commit: "abc123",
206
- updated_at: "2026-01-01T00:00:00Z",
207
- },
208
- },
209
- };
210
-
211
- await saveLockFile(lockFile);
212
-
213
- expect(existsSync("omni.lock.toml")).toBe(true);
214
-
215
- const content = await Bun.file("omni.lock.toml").text();
216
- expect(content).toContain("[capabilities.my-cap]");
217
- expect(content).toContain('source = "github:user/repo"');
218
- expect(content).toContain('version = "1.0.0"');
219
- expect(content).toContain('commit = "abc123"');
220
- });
221
-
222
- test("creates lock file with ref when present", async () => {
223
- const lockFile: CapabilitiesLockFile = {
224
- capabilities: {
225
- "pinned-cap": {
226
- source: "github:user/repo",
227
- version: "2.0.0",
228
- commit: "def456",
229
- ref: "v2.0.0",
230
- updated_at: "2026-01-01T00:00:00Z",
231
- },
232
- },
233
- };
234
-
235
- await saveLockFile(lockFile);
236
-
237
- const content = await Bun.file("omni.lock.toml").text();
238
- expect(content).toContain('ref = "v2.0.0"');
239
- });
240
-
241
- test("creates capabilities directory if it does not exist", async () => {
242
- rmSync(".omni/capabilities", { recursive: true, force: true });
243
-
244
- const lockFile: CapabilitiesLockFile = {
245
- capabilities: {
246
- test: {
247
- source: "github:user/repo",
248
- version: "1.0.0",
249
- updated_at: "2026-01-01T00:00:00Z",
250
- },
251
- },
252
- };
253
-
254
- await saveLockFile(lockFile);
255
-
256
- expect(existsSync(".omni/capabilities")).toBe(true);
257
- expect(existsSync("omni.lock.toml")).toBe(true);
258
- });
259
-
260
- test("includes header comment in lock file", async () => {
261
- const lockFile: CapabilitiesLockFile = {
262
- capabilities: {},
263
- };
264
-
265
- await saveLockFile(lockFile);
266
-
267
- const content = await Bun.file("omni.lock.toml").text();
268
- expect(content).toContain("# Auto-generated by OmniDev");
269
- expect(content).toContain("DO NOT EDIT");
270
- });
271
-
272
- test("roundtrip: save and load produces same data", async () => {
273
- const original: CapabilitiesLockFile = {
274
- capabilities: {
275
- cap1: {
276
- source: "github:user/repo1",
277
- version: "1.0.0",
278
- commit: "abc123",
279
- updated_at: "2026-01-01T00:00:00Z",
280
- },
281
- cap2: {
282
- source: "github:user/repo2",
283
- version: "2.0.0",
284
- commit: "def456",
285
- ref: "v2.0.0",
286
- updated_at: "2026-01-02T00:00:00Z",
287
- },
288
- },
289
- };
290
-
291
- await saveLockFile(original);
292
- const loaded = await loadLockFile();
293
-
294
- expect(loaded.capabilities["cap1"]?.source).toBe(original.capabilities["cap1"]?.source);
295
- expect(loaded.capabilities["cap1"]?.version).toBe(original.capabilities["cap1"]?.version);
296
- expect(loaded.capabilities["cap1"]?.commit).toBe(original.capabilities["cap1"]?.commit);
297
- expect(loaded.capabilities["cap2"]?.ref).toBe(original.capabilities["cap2"]?.ref);
298
- });
299
- });
300
-
301
- describe("wrapping detection", () => {
302
- const testDir = setupTestDir("test-wrapping-", { chdir: true, createOmniDir: true });
303
-
304
- beforeEach(() => {
305
- mkdirSync(join(testDir.path, ".omni", "capabilities"), { recursive: true });
306
- });
307
- test("detects wrapping needed when .claude-plugin/plugin.json exists", () => {
308
- const capDir = join(testDir.path, ".omni", "capabilities", "test-cap");
309
- mkdirSync(join(capDir, ".claude-plugin"), { recursive: true });
310
-
311
- writeFileSync(
312
- join(capDir, ".claude-plugin", "plugin.json"),
313
- JSON.stringify({
314
- name: "test-capability",
315
- version: "1.0.0",
316
- description: "Test capability",
317
- author: {
318
- name: "Test Author",
319
- email: "test@example.com",
320
- },
321
- }),
322
- );
323
-
324
- // Should be detected as needing wrapping since no capability.toml exists
325
- expect(existsSync(join(capDir, "capability.toml"))).toBe(false);
326
- expect(existsSync(join(capDir, ".claude-plugin", "plugin.json"))).toBe(true);
327
- });
328
-
329
- test("detects wrapping needed when skills directory exists", () => {
330
- const capDir = join(testDir.path, ".omni", "capabilities", "test-cap");
331
- mkdirSync(join(capDir, "skills"), { recursive: true });
332
-
333
- writeFileSync(join(capDir, "skills", "example-skill.md"), "# Example Skill\n");
334
-
335
- expect(existsSync(join(capDir, "capability.toml"))).toBe(false);
336
- expect(existsSync(join(capDir, "skills"))).toBe(true);
337
- });
338
-
339
- test("detects wrapping needed when agents directory exists", () => {
340
- const capDir = join(testDir.path, ".omni", "capabilities", "test-cap");
341
- mkdirSync(join(capDir, "agents"), { recursive: true });
342
-
343
- writeFileSync(join(capDir, "agents", "example-agent.md"), "# Example Agent\n");
344
-
345
- expect(existsSync(join(capDir, "capability.toml"))).toBe(false);
346
- expect(existsSync(join(capDir, "agents"))).toBe(true);
347
- });
348
-
349
- test("detects wrapping needed when commands directory exists", () => {
350
- const capDir = join(testDir.path, ".omni", "capabilities", "test-cap");
351
- mkdirSync(join(capDir, "commands"), { recursive: true });
352
-
353
- writeFileSync(join(capDir, "commands", "example-command.md"), "# Example Command\n");
354
-
355
- expect(existsSync(join(capDir, "capability.toml"))).toBe(false);
356
- expect(existsSync(join(capDir, "commands"))).toBe(true);
357
- });
358
-
359
- test("detects wrapping needed when rules directory exists", () => {
360
- const capDir = join(testDir.path, ".omni", "capabilities", "test-cap");
361
- mkdirSync(join(capDir, "rules"), { recursive: true });
362
-
363
- writeFileSync(join(capDir, "rules", "example-rule.md"), "# Example Rule\n");
364
-
365
- expect(existsSync(join(capDir, "capability.toml"))).toBe(false);
366
- expect(existsSync(join(capDir, "rules"))).toBe(true);
367
- });
368
-
369
- test("detects wrapping needed when docs directory exists", () => {
370
- const capDir = join(testDir.path, ".omni", "capabilities", "test-cap");
371
- mkdirSync(join(capDir, "docs"), { recursive: true });
372
-
373
- writeFileSync(join(capDir, "docs", "getting-started.md"), "# Getting Started\n");
374
-
375
- expect(existsSync(join(capDir, "capability.toml"))).toBe(false);
376
- expect(existsSync(join(capDir, "docs"))).toBe(true);
377
- });
378
-
379
- test("does not wrap when capability.toml exists", () => {
380
- const capDir = join(testDir.path, ".omni", "capabilities", "test-cap");
381
- mkdirSync(join(capDir, "skills"), { recursive: true });
382
-
383
- writeFileSync(
384
- join(capDir, "capability.toml"),
385
- `[capability]
386
- id = "test-cap"
387
- name = "Test Capability"
388
- version = "1.0.0"
389
- description = "Test"
390
- `,
391
- );
392
-
393
- writeFileSync(join(capDir, "skills", "example-skill.md"), "# Example Skill\n");
394
-
395
- // Has capability.toml, so should NOT be wrapped even with skills dir
396
- expect(existsSync(join(capDir, "capability.toml"))).toBe(true);
397
- expect(existsSync(join(capDir, "skills"))).toBe(true);
398
- });
399
-
400
- test("does not wrap directory with no recognized structure", () => {
401
- const capDir = join(testDir.path, ".omni", "capabilities", "test-cap");
402
- mkdirSync(capDir, { recursive: true });
403
-
404
- // Create some random files/dirs that shouldn't trigger wrapping
405
- writeFileSync(join(capDir, "README.md"), "# Test\n");
406
- writeFileSync(join(capDir, "package.json"), "{}");
407
- mkdirSync(join(capDir, "src"));
408
-
409
- expect(existsSync(join(capDir, "capability.toml"))).toBe(false);
410
- expect(existsSync(join(capDir, ".claude-plugin"))).toBe(false);
411
- expect(existsSync(join(capDir, "skills"))).toBe(false);
412
- // Should not trigger wrapping
413
- });
414
-
415
- test("recognizes singular directory names", () => {
416
- const capDir = join(testDir.path, ".omni", "capabilities", "test-cap");
417
- mkdirSync(join(capDir, "skill"), { recursive: true });
418
-
419
- writeFileSync(join(capDir, "skill", "example.md"), "# Example\n");
420
-
421
- // Singular 'skill' should also be detected
422
- expect(existsSync(join(capDir, "skill"))).toBe(true);
423
- });
424
-
425
- test("recognizes alternative directory names", () => {
426
- const capDir = join(testDir.path, ".omni", "capabilities", "test-cap");
427
- mkdirSync(join(capDir, "subagents"), { recursive: true });
428
-
429
- writeFileSync(join(capDir, "subagents", "example.md"), "# Example\n");
430
-
431
- // 'subagents' should also be detected as agent dir
432
- expect(existsSync(join(capDir, "subagents"))).toBe(true);
433
- });
434
- });
435
-
436
- // Note: Tests for fetchCapabilitySource and fetchAllCapabilitySources with actual
437
- // git operations require network access. These should be tested via integration tests
438
- // or with mocked git commands. The manual test in /tmp/omni-test verified the
439
- // full flow works correctly with the real obsidian-skills repository.