@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.
Files changed (59) hide show
  1. package/package.json +31 -0
  2. package/src/capability/AGENTS.md +58 -0
  3. package/src/capability/commands.test.ts +414 -0
  4. package/src/capability/commands.ts +70 -0
  5. package/src/capability/docs.test.ts +199 -0
  6. package/src/capability/docs.ts +46 -0
  7. package/src/capability/index.ts +20 -0
  8. package/src/capability/loader.test.ts +815 -0
  9. package/src/capability/loader.ts +492 -0
  10. package/src/capability/registry.test.ts +473 -0
  11. package/src/capability/registry.ts +55 -0
  12. package/src/capability/rules.test.ts +145 -0
  13. package/src/capability/rules.ts +133 -0
  14. package/src/capability/skills.test.ts +316 -0
  15. package/src/capability/skills.ts +56 -0
  16. package/src/capability/sources.test.ts +338 -0
  17. package/src/capability/sources.ts +966 -0
  18. package/src/capability/subagents.test.ts +478 -0
  19. package/src/capability/subagents.ts +103 -0
  20. package/src/capability/yaml-parser.ts +81 -0
  21. package/src/config/AGENTS.md +46 -0
  22. package/src/config/capabilities.ts +82 -0
  23. package/src/config/env.test.ts +286 -0
  24. package/src/config/env.ts +96 -0
  25. package/src/config/index.ts +6 -0
  26. package/src/config/loader.test.ts +282 -0
  27. package/src/config/loader.ts +137 -0
  28. package/src/config/parser.test.ts +281 -0
  29. package/src/config/parser.ts +55 -0
  30. package/src/config/profiles.test.ts +259 -0
  31. package/src/config/profiles.ts +75 -0
  32. package/src/config/provider.test.ts +79 -0
  33. package/src/config/provider.ts +55 -0
  34. package/src/debug.ts +20 -0
  35. package/src/gitignore/manager.test.ts +219 -0
  36. package/src/gitignore/manager.ts +167 -0
  37. package/src/index.test.ts +26 -0
  38. package/src/index.ts +39 -0
  39. package/src/mcp-json/index.ts +1 -0
  40. package/src/mcp-json/manager.test.ts +415 -0
  41. package/src/mcp-json/manager.ts +118 -0
  42. package/src/state/active-profile.test.ts +131 -0
  43. package/src/state/active-profile.ts +41 -0
  44. package/src/state/index.ts +2 -0
  45. package/src/state/manifest.test.ts +548 -0
  46. package/src/state/manifest.ts +164 -0
  47. package/src/sync.ts +213 -0
  48. package/src/templates/agents.test.ts +23 -0
  49. package/src/templates/agents.ts +14 -0
  50. package/src/templates/claude.test.ts +48 -0
  51. package/src/templates/claude.ts +122 -0
  52. package/src/test-utils/helpers.test.ts +196 -0
  53. package/src/test-utils/helpers.ts +187 -0
  54. package/src/test-utils/index.ts +30 -0
  55. package/src/test-utils/mocks.test.ts +83 -0
  56. package/src/test-utils/mocks.ts +101 -0
  57. package/src/types/capability-export.ts +234 -0
  58. package/src/types/index.test.ts +28 -0
  59. package/src/types/index.ts +270 -0
@@ -0,0 +1,199 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { loadDocs } from "./docs";
5
+
6
+ describe("loadDocs", () => {
7
+ let testDir: string;
8
+
9
+ beforeEach(() => {
10
+ testDir = join(process.cwd(), "test-capability-docs");
11
+ mkdirSync(testDir, { recursive: true });
12
+ });
13
+
14
+ afterEach(() => {
15
+ if (testDir) {
16
+ rmSync(testDir, { recursive: true, force: true });
17
+ }
18
+ });
19
+
20
+ test("returns empty array when no docs exist", async () => {
21
+ const docs = await loadDocs(testDir, "test-cap");
22
+ expect(docs).toEqual([]);
23
+ });
24
+
25
+ test("loads only definition.md when docs directory does not exist", async () => {
26
+ writeFileSync(join(testDir, "definition.md"), "# Test Capability\n\nThis is the definition.");
27
+
28
+ const docs = await loadDocs(testDir, "test-cap");
29
+ expect(docs).toHaveLength(1);
30
+ expect(docs[0]?.name).toBe("definition");
31
+ expect(docs[0]?.content).toBe("# Test Capability\n\nThis is the definition.");
32
+ expect(docs[0]?.capabilityId).toBe("test-cap");
33
+ });
34
+
35
+ test("loads only docs from docs directory when definition.md does not exist", async () => {
36
+ const docsDir = join(testDir, "docs");
37
+ mkdirSync(docsDir);
38
+ writeFileSync(join(docsDir, "guide.md"), "# Guide\n\nGuide content.");
39
+
40
+ const docs = await loadDocs(testDir, "test-cap");
41
+ expect(docs).toHaveLength(1);
42
+ expect(docs[0]?.name).toBe("guide");
43
+ expect(docs[0]?.content).toBe("# Guide\n\nGuide content.");
44
+ expect(docs[0]?.capabilityId).toBe("test-cap");
45
+ });
46
+
47
+ test("loads both definition.md and docs from docs directory", async () => {
48
+ writeFileSync(join(testDir, "definition.md"), "# Definition");
49
+
50
+ const docsDir = join(testDir, "docs");
51
+ mkdirSync(docsDir);
52
+ writeFileSync(join(docsDir, "guide.md"), "# Guide");
53
+ writeFileSync(join(docsDir, "examples.md"), "# Examples");
54
+
55
+ const docs = await loadDocs(testDir, "test-cap");
56
+ expect(docs).toHaveLength(3);
57
+
58
+ const names = docs.map((d) => d.name).sort();
59
+ expect(names).toEqual(["definition", "examples", "guide"]);
60
+ });
61
+
62
+ test("trims whitespace from doc content", async () => {
63
+ writeFileSync(join(testDir, "definition.md"), "\n\n # Definition\n\nContent.\n\n ");
64
+
65
+ const docsDir = join(testDir, "docs");
66
+ mkdirSync(docsDir);
67
+ writeFileSync(join(docsDir, "guide.md"), " \n# Guide\n\nGuide content.\n ");
68
+
69
+ const docs = await loadDocs(testDir, "test-cap");
70
+ expect(docs).toHaveLength(2);
71
+ expect(docs[0]?.content).toBe("# Definition\n\nContent.");
72
+ expect(docs[1]?.content).toBe("# Guide\n\nGuide content.");
73
+ });
74
+
75
+ test("ignores non-markdown files in docs directory", async () => {
76
+ const docsDir = join(testDir, "docs");
77
+ mkdirSync(docsDir);
78
+ writeFileSync(join(docsDir, "guide.md"), "# Guide");
79
+ writeFileSync(join(docsDir, "readme.txt"), "Not markdown");
80
+ writeFileSync(join(docsDir, "config.json"), "{}");
81
+
82
+ const docs = await loadDocs(testDir, "test-cap");
83
+ expect(docs).toHaveLength(1);
84
+ expect(docs[0]?.name).toBe("guide");
85
+ });
86
+
87
+ test("ignores directories in docs directory", async () => {
88
+ const docsDir = join(testDir, "docs");
89
+ mkdirSync(docsDir);
90
+ mkdirSync(join(docsDir, "subdir"));
91
+ writeFileSync(join(docsDir, "guide.md"), "# Guide");
92
+
93
+ const docs = await loadDocs(testDir, "test-cap");
94
+ expect(docs).toHaveLength(1);
95
+ expect(docs[0]?.name).toBe("guide");
96
+ });
97
+
98
+ test("handles docs with complex markdown formatting", async () => {
99
+ const complexMarkdown = `# Complex Documentation
100
+
101
+ ## Section 1
102
+
103
+ - List item 1
104
+ - List item 2
105
+
106
+ ### Subsection
107
+
108
+ \`\`\`typescript
109
+ const code = "example";
110
+ \`\`\`
111
+
112
+ **Bold text** and *italic text*.
113
+
114
+ | Header 1 | Header 2 |
115
+ |----------|----------|
116
+ | Cell 1 | Cell 2 |`;
117
+
118
+ writeFileSync(join(testDir, "definition.md"), complexMarkdown);
119
+
120
+ const docs = await loadDocs(testDir, "test-cap");
121
+ expect(docs).toHaveLength(1);
122
+ expect(docs[0]?.content).toBe(complexMarkdown);
123
+ });
124
+
125
+ test("handles empty doc files", async () => {
126
+ writeFileSync(join(testDir, "definition.md"), "");
127
+
128
+ const docsDir = join(testDir, "docs");
129
+ mkdirSync(docsDir);
130
+ writeFileSync(join(docsDir, "guide.md"), "");
131
+
132
+ const docs = await loadDocs(testDir, "test-cap");
133
+ expect(docs).toHaveLength(2);
134
+ expect(docs[0]?.content).toBe("");
135
+ expect(docs[1]?.content).toBe("");
136
+ });
137
+
138
+ test("handles docs with only whitespace", async () => {
139
+ writeFileSync(join(testDir, "definition.md"), " \n\n ");
140
+
141
+ const docsDir = join(testDir, "docs");
142
+ mkdirSync(docsDir);
143
+ writeFileSync(join(docsDir, "guide.md"), "\n\n\n");
144
+
145
+ const docs = await loadDocs(testDir, "test-cap");
146
+ expect(docs).toHaveLength(2);
147
+ expect(docs[0]?.content).toBe("");
148
+ expect(docs[1]?.content).toBe("");
149
+ });
150
+
151
+ test("handles doc names with hyphens and underscores", async () => {
152
+ const docsDir = join(testDir, "docs");
153
+ mkdirSync(docsDir);
154
+ writeFileSync(join(docsDir, "api-reference.md"), "Content");
155
+ writeFileSync(join(docsDir, "getting_started.md"), "Content");
156
+
157
+ const docs = await loadDocs(testDir, "test-cap");
158
+ expect(docs).toHaveLength(2);
159
+
160
+ const names = docs.map((d) => d.name).sort();
161
+ expect(names).toEqual(["api-reference", "getting_started"]);
162
+ });
163
+
164
+ test("loads multiple docs in consistent order", async () => {
165
+ writeFileSync(join(testDir, "definition.md"), "Definition");
166
+
167
+ const docsDir = join(testDir, "docs");
168
+ mkdirSync(docsDir);
169
+ writeFileSync(join(docsDir, "aaa.md"), "AAA");
170
+ writeFileSync(join(docsDir, "zzz.md"), "ZZZ");
171
+ writeFileSync(join(docsDir, "mmm.md"), "MMM");
172
+
173
+ const docs = await loadDocs(testDir, "test-cap");
174
+ expect(docs).toHaveLength(4);
175
+
176
+ // definition.md should be first, then docs in filesystem order
177
+ expect(docs[0]?.name).toBe("definition");
178
+ const docNames = docs.slice(1).map((d) => d.name);
179
+ expect(docNames).toHaveLength(3);
180
+ expect(docNames).toContain("aaa");
181
+ expect(docNames).toContain("zzz");
182
+ expect(docNames).toContain("mmm");
183
+ });
184
+
185
+ test("returns empty array when docs directory is empty", async () => {
186
+ mkdirSync(join(testDir, "docs"));
187
+ const docs = await loadDocs(testDir, "test-cap");
188
+ expect(docs).toEqual([]);
189
+ });
190
+
191
+ test("handles very long doc content", async () => {
192
+ const longContent = `# Long Document\n\n${"Content line.\n".repeat(1000)}`;
193
+ writeFileSync(join(testDir, "definition.md"), longContent);
194
+
195
+ const docs = await loadDocs(testDir, "test-cap");
196
+ expect(docs).toHaveLength(1);
197
+ expect(docs[0]?.content).toBe(longContent.trim());
198
+ });
199
+ });
@@ -0,0 +1,46 @@
1
+ import { existsSync, readdirSync } from "node:fs";
2
+ import { basename, join } from "node:path";
3
+ import type { Doc } from "../types";
4
+
5
+ /**
6
+ * Load documentation from a capability directory
7
+ * Loads both definition.md and all files from docs/ directory
8
+ * @param capabilityPath Path to the capability directory
9
+ * @param capabilityId ID of the capability
10
+ * @returns Array of Doc objects
11
+ */
12
+ export async function loadDocs(capabilityPath: string, capabilityId: string): Promise<Doc[]> {
13
+ const docs: Doc[] = [];
14
+
15
+ // Load definition.md if exists
16
+ const definitionPath = join(capabilityPath, "definition.md");
17
+ if (existsSync(definitionPath)) {
18
+ const content = await Bun.file(definitionPath).text();
19
+ docs.push({
20
+ name: "definition",
21
+ content: content.trim(),
22
+ capabilityId,
23
+ });
24
+ }
25
+
26
+ // Load docs/*.md
27
+ const docsDir = join(capabilityPath, "docs");
28
+ if (existsSync(docsDir)) {
29
+ const entries = readdirSync(docsDir, { withFileTypes: true });
30
+
31
+ for (const entry of entries) {
32
+ if (entry.isFile() && entry.name.endsWith(".md")) {
33
+ const docPath = join(docsDir, entry.name);
34
+ const content = await Bun.file(docPath).text();
35
+
36
+ docs.push({
37
+ name: basename(entry.name, ".md"),
38
+ content: content.trim(),
39
+ capabilityId,
40
+ });
41
+ }
42
+ }
43
+ }
44
+
45
+ return docs;
46
+ }
@@ -0,0 +1,20 @@
1
+ export { loadCommands } from "./commands";
2
+ export { loadDocs } from "./docs";
3
+ export { discoverCapabilities, loadCapability, loadCapabilityConfig } from "./loader";
4
+ export type { CapabilityRegistry } from "./registry";
5
+ export { buildCapabilityRegistry } from "./registry";
6
+ export { loadRules, writeRules } from "./rules";
7
+ export { loadSkills } from "./skills";
8
+ export {
9
+ fetchAllCapabilitySources,
10
+ fetchCapabilitySource,
11
+ checkForUpdates,
12
+ loadLockFile,
13
+ saveLockFile,
14
+ parseSourceConfig,
15
+ sourceToGitUrl,
16
+ getSourceCapabilityPath,
17
+ getLockFilePath,
18
+ } from "./sources";
19
+ export type { FetchResult, SourceUpdateInfo, DiscoveredContent } from "./sources";
20
+ export { loadSubagents } from "./subagents";