@omnidev-ai/core 0.1.1 → 0.3.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 +1 -1
- package/src/capability/commands.test.ts +6 -10
- package/src/capability/docs.test.ts +39 -46
- package/src/capability/docs.ts +3 -1
- package/src/capability/loader.test.ts +10 -157
- package/src/capability/loader.ts +8 -69
- package/src/capability/registry.test.ts +9 -27
- package/src/capability/rules.test.ts +25 -35
- package/src/capability/rules.ts +3 -1
- package/src/capability/skills.test.ts +6 -10
- package/src/capability/sources.test.ts +142 -41
- package/src/capability/sources.ts +377 -345
- package/src/capability/subagents.test.ts +7 -11
- package/src/capability/subagents.ts +3 -1
- package/src/capability/wrapping-integration.test.ts +412 -0
- package/src/config/capabilities.ts +0 -28
- package/src/config/env.test.ts +4 -18
- package/src/config/loader.test.ts +4 -86
- package/src/config/loader.ts +88 -18
- package/src/config/parser.test.ts +0 -25
- package/src/config/profiles.test.ts +5 -39
- package/src/config/provider.test.ts +5 -19
- package/src/index.ts +1 -3
- package/src/mcp-json/manager.test.ts +77 -182
- package/src/mcp-json/manager.ts +22 -34
- package/src/state/active-profile.test.ts +4 -18
- package/src/state/index.ts +1 -0
- package/src/state/manifest.test.ts +25 -162
- package/src/state/manifest.ts +4 -31
- package/src/state/providers.test.ts +125 -0
- package/src/state/providers.ts +69 -0
- package/src/sync.ts +128 -53
- package/src/templates/claude.ts +9 -74
- package/src/test-utils/helpers.test.ts +18 -0
- package/src/test-utils/helpers.ts +87 -2
- package/src/test-utils/index.ts +3 -0
- package/src/types/capability-export.ts +0 -77
- package/src/types/index.ts +66 -22
- package/src/gitignore/manager.test.ts +0 -216
- package/src/gitignore/manager.ts +0 -167
|
@@ -1,39 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mkdirSync,
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { setupTestDir } from "@omnidev-ai/core/test-utils";
|
|
4
5
|
import { loadRules } from "./rules";
|
|
5
6
|
|
|
6
7
|
describe("loadRules", () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
testDir = join(process.cwd(), "test-capability-rules");
|
|
11
|
-
mkdirSync(testDir, { recursive: true });
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
afterEach(() => {
|
|
15
|
-
if (testDir) {
|
|
16
|
-
rmSync(testDir, { recursive: true, force: true });
|
|
17
|
-
}
|
|
18
|
-
});
|
|
8
|
+
const testDir = setupTestDir("capability-rules-test-");
|
|
19
9
|
|
|
20
10
|
test("returns empty array when rules directory does not exist", async () => {
|
|
21
|
-
const rules = await loadRules(testDir, "test-cap");
|
|
11
|
+
const rules = await loadRules(testDir.path, "test-cap");
|
|
22
12
|
expect(rules).toEqual([]);
|
|
23
13
|
});
|
|
24
14
|
|
|
25
15
|
test("returns empty array when rules directory is empty", async () => {
|
|
26
|
-
mkdirSync(join(testDir, "rules"));
|
|
27
|
-
const rules = await loadRules(testDir, "test-cap");
|
|
16
|
+
mkdirSync(join(testDir.path, "rules"));
|
|
17
|
+
const rules = await loadRules(testDir.path, "test-cap");
|
|
28
18
|
expect(rules).toEqual([]);
|
|
29
19
|
});
|
|
30
20
|
|
|
31
21
|
test("loads single rule from rules directory", async () => {
|
|
32
|
-
const rulesDir = join(testDir, "rules");
|
|
22
|
+
const rulesDir = join(testDir.path, "rules");
|
|
33
23
|
mkdirSync(rulesDir);
|
|
34
24
|
writeFileSync(join(rulesDir, "test-rule.md"), "# Test Rule\n\nThis is a test rule.");
|
|
35
25
|
|
|
36
|
-
const rules = await loadRules(testDir, "test-cap");
|
|
26
|
+
const rules = await loadRules(testDir.path, "test-cap");
|
|
37
27
|
expect(rules).toHaveLength(1);
|
|
38
28
|
expect(rules[0]?.name).toBe("test-rule");
|
|
39
29
|
expect(rules[0]?.content).toBe("# Test Rule\n\nThis is a test rule.");
|
|
@@ -41,13 +31,13 @@ describe("loadRules", () => {
|
|
|
41
31
|
});
|
|
42
32
|
|
|
43
33
|
test("loads multiple rules from rules directory", async () => {
|
|
44
|
-
const rulesDir = join(testDir, "rules");
|
|
34
|
+
const rulesDir = join(testDir.path, "rules");
|
|
45
35
|
mkdirSync(rulesDir);
|
|
46
36
|
writeFileSync(join(rulesDir, "rule-one.md"), "# Rule One");
|
|
47
37
|
writeFileSync(join(rulesDir, "rule-two.md"), "# Rule Two");
|
|
48
38
|
writeFileSync(join(rulesDir, "rule-three.md"), "# Rule Three");
|
|
49
39
|
|
|
50
|
-
const rules = await loadRules(testDir, "test-cap");
|
|
40
|
+
const rules = await loadRules(testDir.path, "test-cap");
|
|
51
41
|
expect(rules).toHaveLength(3);
|
|
52
42
|
|
|
53
43
|
const names = rules.map((r) => r.name).sort();
|
|
@@ -55,40 +45,40 @@ describe("loadRules", () => {
|
|
|
55
45
|
});
|
|
56
46
|
|
|
57
47
|
test("trims whitespace from rule content", async () => {
|
|
58
|
-
const rulesDir = join(testDir, "rules");
|
|
48
|
+
const rulesDir = join(testDir.path, "rules");
|
|
59
49
|
mkdirSync(rulesDir);
|
|
60
50
|
writeFileSync(join(rulesDir, "trimmed.md"), "\n\n # Trimmed Rule\n\nContent here.\n\n ");
|
|
61
51
|
|
|
62
|
-
const rules = await loadRules(testDir, "test-cap");
|
|
52
|
+
const rules = await loadRules(testDir.path, "test-cap");
|
|
63
53
|
expect(rules).toHaveLength(1);
|
|
64
54
|
expect(rules[0]?.content).toBe("# Trimmed Rule\n\nContent here.");
|
|
65
55
|
});
|
|
66
56
|
|
|
67
57
|
test("ignores non-markdown files in rules directory", async () => {
|
|
68
|
-
const rulesDir = join(testDir, "rules");
|
|
58
|
+
const rulesDir = join(testDir.path, "rules");
|
|
69
59
|
mkdirSync(rulesDir);
|
|
70
60
|
writeFileSync(join(rulesDir, "rule.md"), "# Rule");
|
|
71
61
|
writeFileSync(join(rulesDir, "readme.txt"), "Not a markdown file");
|
|
72
62
|
writeFileSync(join(rulesDir, "config.json"), "{}");
|
|
73
63
|
|
|
74
|
-
const rules = await loadRules(testDir, "test-cap");
|
|
64
|
+
const rules = await loadRules(testDir.path, "test-cap");
|
|
75
65
|
expect(rules).toHaveLength(1);
|
|
76
66
|
expect(rules[0]?.name).toBe("rule");
|
|
77
67
|
});
|
|
78
68
|
|
|
79
69
|
test("ignores directories in rules directory", async () => {
|
|
80
|
-
const rulesDir = join(testDir, "rules");
|
|
70
|
+
const rulesDir = join(testDir.path, "rules");
|
|
81
71
|
mkdirSync(rulesDir);
|
|
82
72
|
mkdirSync(join(rulesDir, "subdir"));
|
|
83
73
|
writeFileSync(join(rulesDir, "rule.md"), "# Rule");
|
|
84
74
|
|
|
85
|
-
const rules = await loadRules(testDir, "test-cap");
|
|
75
|
+
const rules = await loadRules(testDir.path, "test-cap");
|
|
86
76
|
expect(rules).toHaveLength(1);
|
|
87
77
|
expect(rules[0]?.name).toBe("rule");
|
|
88
78
|
});
|
|
89
79
|
|
|
90
80
|
test("handles rules with complex markdown formatting", async () => {
|
|
91
|
-
const rulesDir = join(testDir, "rules");
|
|
81
|
+
const rulesDir = join(testDir.path, "rules");
|
|
92
82
|
mkdirSync(rulesDir);
|
|
93
83
|
const complexMarkdown = `# Complex Rule
|
|
94
84
|
|
|
@@ -105,38 +95,38 @@ const code = "example";
|
|
|
105
95
|
|
|
106
96
|
writeFileSync(join(rulesDir, "complex.md"), complexMarkdown);
|
|
107
97
|
|
|
108
|
-
const rules = await loadRules(testDir, "test-cap");
|
|
98
|
+
const rules = await loadRules(testDir.path, "test-cap");
|
|
109
99
|
expect(rules).toHaveLength(1);
|
|
110
100
|
expect(rules[0]?.content).toBe(complexMarkdown);
|
|
111
101
|
});
|
|
112
102
|
|
|
113
103
|
test("handles empty rule files", async () => {
|
|
114
|
-
const rulesDir = join(testDir, "rules");
|
|
104
|
+
const rulesDir = join(testDir.path, "rules");
|
|
115
105
|
mkdirSync(rulesDir);
|
|
116
106
|
writeFileSync(join(rulesDir, "empty.md"), "");
|
|
117
107
|
|
|
118
|
-
const rules = await loadRules(testDir, "test-cap");
|
|
108
|
+
const rules = await loadRules(testDir.path, "test-cap");
|
|
119
109
|
expect(rules).toHaveLength(1);
|
|
120
110
|
expect(rules[0]?.content).toBe("");
|
|
121
111
|
});
|
|
122
112
|
|
|
123
113
|
test("handles rules with only whitespace", async () => {
|
|
124
|
-
const rulesDir = join(testDir, "rules");
|
|
114
|
+
const rulesDir = join(testDir.path, "rules");
|
|
125
115
|
mkdirSync(rulesDir);
|
|
126
116
|
writeFileSync(join(rulesDir, "whitespace.md"), " \n\n ");
|
|
127
117
|
|
|
128
|
-
const rules = await loadRules(testDir, "test-cap");
|
|
118
|
+
const rules = await loadRules(testDir.path, "test-cap");
|
|
129
119
|
expect(rules).toHaveLength(1);
|
|
130
120
|
expect(rules[0]?.content).toBe("");
|
|
131
121
|
});
|
|
132
122
|
|
|
133
123
|
test("handles rule names with hyphens and underscores", async () => {
|
|
134
|
-
const rulesDir = join(testDir, "rules");
|
|
124
|
+
const rulesDir = join(testDir.path, "rules");
|
|
135
125
|
mkdirSync(rulesDir);
|
|
136
126
|
writeFileSync(join(rulesDir, "my-rule-name.md"), "Content");
|
|
137
127
|
writeFileSync(join(rulesDir, "another_rule_name.md"), "Content");
|
|
138
128
|
|
|
139
|
-
const rules = await loadRules(testDir, "test-cap");
|
|
129
|
+
const rules = await loadRules(testDir.path, "test-cap");
|
|
140
130
|
expect(rules).toHaveLength(2);
|
|
141
131
|
|
|
142
132
|
const names = rules.map((r) => r.name).sort();
|
package/src/capability/rules.ts
CHANGED
|
@@ -16,7 +16,9 @@ export async function loadRules(capabilityPath: string, capabilityId: string): P
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const rules: Rule[] = [];
|
|
19
|
-
const entries = readdirSync(rulesDir, { withFileTypes: true })
|
|
19
|
+
const entries = readdirSync(rulesDir, { withFileTypes: true }).sort((a, b) =>
|
|
20
|
+
a.name.localeCompare(b.name),
|
|
21
|
+
);
|
|
20
22
|
|
|
21
23
|
for (const entry of entries) {
|
|
22
24
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mkdirSync,
|
|
1
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { setupTestDir } from "@omnidev-ai/core/test-utils";
|
|
4
5
|
import { loadSkills } from "./skills";
|
|
5
6
|
|
|
6
7
|
describe("loadSkills", () => {
|
|
7
|
-
const testDir =
|
|
8
|
-
|
|
8
|
+
const testDir = setupTestDir("capability-skills-test-");
|
|
9
|
+
let capabilityPath: string;
|
|
9
10
|
|
|
10
11
|
beforeEach(() => {
|
|
12
|
+
capabilityPath = join(testDir.path, "test-capability");
|
|
11
13
|
mkdirSync(capabilityPath, { recursive: true });
|
|
12
14
|
});
|
|
13
15
|
|
|
14
|
-
afterEach(() => {
|
|
15
|
-
if (testDir) {
|
|
16
|
-
rmSync(testDir, { recursive: true, force: true });
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
|
|
20
16
|
test("returns empty array when skills directory does not exist", async () => {
|
|
21
17
|
const skills = await loadSkills(capabilityPath, "test-cap");
|
|
22
18
|
expect(skills).toEqual([]);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { existsSync, mkdirSync,
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
1
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
3
|
import { join } from "node:path";
|
|
4
|
+
import { setupTestDir } from "@omnidev-ai/core/test-utils";
|
|
5
5
|
import {
|
|
6
6
|
parseSourceConfig,
|
|
7
7
|
sourceToGitUrl,
|
|
@@ -61,12 +61,10 @@ describe("parseSourceConfig", () => {
|
|
|
61
61
|
const config = parseSourceConfig({
|
|
62
62
|
source: "github:user/repo",
|
|
63
63
|
ref: "v2.0.0",
|
|
64
|
-
type: "wrap",
|
|
65
64
|
}) as GitCapabilitySourceConfig;
|
|
66
65
|
|
|
67
66
|
expect(config.source).toBe("github:user/repo");
|
|
68
67
|
expect(config.ref).toBe("v2.0.0");
|
|
69
|
-
expect(config.type).toBe("wrap");
|
|
70
68
|
});
|
|
71
69
|
|
|
72
70
|
test("passes through config with path", () => {
|
|
@@ -135,23 +133,7 @@ describe("getLockFilePath", () => {
|
|
|
135
133
|
});
|
|
136
134
|
|
|
137
135
|
describe("loadLockFile", () => {
|
|
138
|
-
|
|
139
|
-
let originalCwd: string;
|
|
140
|
-
|
|
141
|
-
beforeEach(() => {
|
|
142
|
-
originalCwd = process.cwd();
|
|
143
|
-
testDir = mkdtempSync(join(tmpdir(), "test-lock-file-"));
|
|
144
|
-
mkdirSync(join(testDir, ".omni"), { recursive: true });
|
|
145
|
-
process.chdir(testDir);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
afterEach(() => {
|
|
149
|
-
process.chdir(originalCwd);
|
|
150
|
-
if (existsSync(testDir)) {
|
|
151
|
-
rmSync(testDir, { recursive: true, force: true });
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
136
|
+
setupTestDir("test-lock-file-", { chdir: true, createOmniDir: true });
|
|
155
137
|
test("returns empty capabilities when lock file does not exist", async () => {
|
|
156
138
|
const lockFile = await loadLockFile();
|
|
157
139
|
|
|
@@ -213,23 +195,7 @@ updated_at = "2026-01-02T00:00:00Z"
|
|
|
213
195
|
});
|
|
214
196
|
|
|
215
197
|
describe("saveLockFile", () => {
|
|
216
|
-
|
|
217
|
-
let originalCwd: string;
|
|
218
|
-
|
|
219
|
-
beforeEach(() => {
|
|
220
|
-
originalCwd = process.cwd();
|
|
221
|
-
testDir = mkdtempSync(join(tmpdir(), "test-save-lock-"));
|
|
222
|
-
mkdirSync(join(testDir, ".omni"), { recursive: true });
|
|
223
|
-
process.chdir(testDir);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
afterEach(() => {
|
|
227
|
-
process.chdir(originalCwd);
|
|
228
|
-
if (existsSync(testDir)) {
|
|
229
|
-
rmSync(testDir, { recursive: true, force: true });
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
|
|
198
|
+
setupTestDir("test-save-lock-", { chdir: true, createOmniDir: true });
|
|
233
199
|
test("creates lock file with single capability", async () => {
|
|
234
200
|
const lockFile: CapabilitiesLockFile = {
|
|
235
201
|
capabilities: {
|
|
@@ -332,7 +298,142 @@ describe("saveLockFile", () => {
|
|
|
332
298
|
});
|
|
333
299
|
});
|
|
334
300
|
|
|
335
|
-
|
|
336
|
-
|
|
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
|
|
337
438
|
// or with mocked git commands. The manual test in /tmp/omni-test verified the
|
|
338
439
|
// full flow works correctly with the real obsidian-skills repository.
|