@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,219 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { existsSync, mkdirSync, rmSync } from "node:fs";
3
+ import {
4
+ addCapabilityPatterns,
5
+ buildGitignoreContent,
6
+ parseCapabilitySections,
7
+ readGitignore,
8
+ rebuildGitignore,
9
+ removeCapabilityPatterns,
10
+ writeGitignore,
11
+ } from "./manager.js";
12
+
13
+ const TEST_DIR = ".omni-test-gitignore";
14
+
15
+ describe("Gitignore Manager", () => {
16
+ beforeEach(() => {
17
+ // Create test directory
18
+ if (existsSync(TEST_DIR)) {
19
+ rmSync(TEST_DIR, { recursive: true });
20
+ }
21
+ mkdirSync(TEST_DIR, { recursive: true });
22
+
23
+ // Change working directory
24
+ process.chdir(TEST_DIR);
25
+ });
26
+
27
+ afterEach(() => {
28
+ // Restore working directory
29
+ process.chdir("..");
30
+
31
+ // Clean up test directory
32
+ if (existsSync(TEST_DIR)) {
33
+ rmSync(TEST_DIR, { recursive: true });
34
+ }
35
+ });
36
+
37
+ describe("readGitignore", () => {
38
+ test("returns base content when file doesn't exist", async () => {
39
+ const content = await readGitignore();
40
+ expect(content).toContain("# OmniDev working files");
41
+ expect(content).toContain(".env");
42
+ expect(content).toContain("generated/");
43
+ });
44
+
45
+ test("returns existing content when file exists", async () => {
46
+ await writeGitignore("# Custom content\n*.tmp\n");
47
+ const content = await readGitignore();
48
+ expect(content).toBe("# Custom content\n*.tmp\n");
49
+ });
50
+ });
51
+
52
+ describe("parseCapabilitySections", () => {
53
+ test("parses empty content", () => {
54
+ const sections = parseCapabilitySections("");
55
+ expect(sections.size).toBe(0);
56
+ });
57
+
58
+ test("parses single capability section", () => {
59
+ const content = `# Base content
60
+
61
+ # tasks capability
62
+ work/
63
+ *.tmp
64
+
65
+ # End`;
66
+
67
+ const sections = parseCapabilitySections(content);
68
+ expect(sections.size).toBe(1);
69
+ expect(sections.get("tasks")).toEqual(["work/", "*.tmp"]);
70
+ });
71
+
72
+ test("parses multiple capability sections", () => {
73
+ const content = `# Base content
74
+
75
+ # tasks capability
76
+ work/
77
+ *.tmp
78
+
79
+ # ralph capability
80
+ progress.txt
81
+ *.log
82
+
83
+ # End`;
84
+
85
+ const sections = parseCapabilitySections(content);
86
+ expect(sections.size).toBe(2);
87
+ expect(sections.get("tasks")).toEqual(["work/", "*.tmp"]);
88
+ expect(sections.get("ralph")).toEqual(["progress.txt", "*.log"]);
89
+ });
90
+
91
+ test("handles capability with no patterns", () => {
92
+ const content = `# tasks capability
93
+
94
+ # ralph capability
95
+ progress.txt`;
96
+
97
+ const sections = parseCapabilitySections(content);
98
+ expect(sections.size).toBe(1);
99
+ expect(sections.get("ralph")).toEqual(["progress.txt"]);
100
+ });
101
+ });
102
+
103
+ describe("buildGitignoreContent", () => {
104
+ test("builds base content with no capabilities", () => {
105
+ const content = buildGitignoreContent(new Map());
106
+ expect(content).toContain("# OmniDev working files");
107
+ expect(content).toContain(".env");
108
+ expect(content).not.toContain("# tasks capability");
109
+ });
110
+
111
+ test("builds content with single capability", () => {
112
+ const sections = new Map([["tasks", ["work/", "*.tmp"]]]);
113
+ const content = buildGitignoreContent(sections);
114
+ expect(content).toContain("# tasks capability");
115
+ expect(content).toContain("work/");
116
+ expect(content).toContain("*.tmp");
117
+ });
118
+
119
+ test("builds content with multiple capabilities", () => {
120
+ const sections = new Map([
121
+ ["tasks", ["work/", "*.tmp"]],
122
+ ["ralph", ["progress.txt"]],
123
+ ]);
124
+ const content = buildGitignoreContent(sections);
125
+ expect(content).toContain("# tasks capability");
126
+ expect(content).toContain("work/");
127
+ expect(content).toContain("# ralph capability");
128
+ expect(content).toContain("progress.txt");
129
+ });
130
+
131
+ test("skips capabilities with empty patterns", () => {
132
+ const sections = new Map([
133
+ ["tasks", []],
134
+ ["ralph", ["progress.txt"]],
135
+ ]);
136
+ const content = buildGitignoreContent(sections);
137
+ expect(content).not.toContain("# tasks capability");
138
+ expect(content).toContain("# ralph capability");
139
+ });
140
+ });
141
+
142
+ describe("addCapabilityPatterns", () => {
143
+ test("adds patterns to empty file", async () => {
144
+ await addCapabilityPatterns("tasks", ["work/", "*.tmp"]);
145
+ const content = await readGitignore();
146
+ expect(content).toContain("# tasks capability");
147
+ expect(content).toContain("work/");
148
+ expect(content).toContain("*.tmp");
149
+ });
150
+
151
+ test("adds patterns to existing file", async () => {
152
+ await addCapabilityPatterns("tasks", ["work/"]);
153
+ await addCapabilityPatterns("ralph", ["progress.txt"]);
154
+
155
+ const content = await readGitignore();
156
+ expect(content).toContain("# tasks capability");
157
+ expect(content).toContain("work/");
158
+ expect(content).toContain("# ralph capability");
159
+ expect(content).toContain("progress.txt");
160
+ });
161
+
162
+ test("updates existing capability patterns", async () => {
163
+ await addCapabilityPatterns("tasks", ["work/"]);
164
+ await addCapabilityPatterns("tasks", ["work/", "*.tmp"]);
165
+
166
+ const content = await readGitignore();
167
+ const sections = parseCapabilitySections(content);
168
+ expect(sections.get("tasks")).toEqual(["work/", "*.tmp"]);
169
+ });
170
+ });
171
+
172
+ describe("removeCapabilityPatterns", () => {
173
+ test("removes capability patterns", async () => {
174
+ await addCapabilityPatterns("tasks", ["work/"]);
175
+ await addCapabilityPatterns("ralph", ["progress.txt"]);
176
+ await removeCapabilityPatterns("tasks");
177
+
178
+ const content = await readGitignore();
179
+ expect(content).not.toContain("# tasks capability");
180
+ expect(content).toContain("# ralph capability");
181
+ });
182
+
183
+ test("handles removing non-existent capability", async () => {
184
+ await addCapabilityPatterns("tasks", ["work/"]);
185
+ await removeCapabilityPatterns("nonexistent");
186
+
187
+ const content = await readGitignore();
188
+ expect(content).toContain("# tasks capability");
189
+ });
190
+ });
191
+
192
+ describe("rebuildGitignore", () => {
193
+ test("rebuilds gitignore from scratch", async () => {
194
+ // Add some capabilities manually
195
+ await addCapabilityPatterns("tasks", ["work/"]);
196
+ await addCapabilityPatterns("ralph", ["progress.txt"]);
197
+
198
+ // Rebuild with only one capability
199
+ const newSections = new Map([["tasks", ["new-work/", "*.new"]]]);
200
+ await rebuildGitignore(newSections);
201
+
202
+ const content = await readGitignore();
203
+ expect(content).toContain("# tasks capability");
204
+ expect(content).toContain("new-work/");
205
+ expect(content).toContain("*.new");
206
+ expect(content).not.toContain("# ralph capability");
207
+ expect(content).not.toContain("progress.txt");
208
+ });
209
+
210
+ test("rebuilds empty gitignore", async () => {
211
+ await addCapabilityPatterns("tasks", ["work/"]);
212
+ await rebuildGitignore(new Map());
213
+
214
+ const content = await readGitignore();
215
+ expect(content).not.toContain("# tasks capability");
216
+ expect(content).toContain("# OmniDev working files");
217
+ });
218
+ });
219
+ });
@@ -0,0 +1,167 @@
1
+ import { existsSync } from "node:fs";
2
+
3
+ const GITIGNORE_PATH = ".omni/.gitignore";
4
+
5
+ /**
6
+ * Base gitignore patterns that are always present
7
+ */
8
+ function getBaseGitignore(): string {
9
+ return `# OmniDev working files - always ignored
10
+ # These files change frequently and are machine-specific
11
+
12
+ # Secrets
13
+ .env
14
+
15
+ # Generated content (rebuilt on sync)
16
+ generated/
17
+
18
+ # Runtime state
19
+ state/
20
+
21
+ # Sandbox execution
22
+ sandbox/
23
+
24
+ # Logs
25
+ *.log
26
+
27
+ # ============================================
28
+ # Capability-specific patterns (auto-managed)
29
+ # ============================================
30
+ `;
31
+ }
32
+
33
+ /**
34
+ * Read the current .omni/.gitignore file
35
+ * @returns Current gitignore content or base content if file doesn't exist
36
+ */
37
+ export async function readGitignore(): Promise<string> {
38
+ if (!existsSync(GITIGNORE_PATH)) {
39
+ return getBaseGitignore();
40
+ }
41
+ return Bun.file(GITIGNORE_PATH).text();
42
+ }
43
+
44
+ /**
45
+ * Write content to .omni/.gitignore
46
+ * @param content - Content to write
47
+ */
48
+ export async function writeGitignore(content: string): Promise<void> {
49
+ await Bun.write(GITIGNORE_PATH, content);
50
+ }
51
+
52
+ /**
53
+ * Parse gitignore content and extract capability sections
54
+ * @param content - The gitignore file content
55
+ * @returns Map of capability names to their patterns
56
+ */
57
+ export function parseCapabilitySections(content: string): Map<string, string[]> {
58
+ const sections = new Map<string, string[]>();
59
+ const lines = content.split("\n");
60
+
61
+ let currentCapability: string | null = null;
62
+ const patterns: string[] = [];
63
+
64
+ for (const line of lines) {
65
+ // Check for capability header (e.g., "# tasks capability")
66
+ const headerMatch = line.match(/^# (\S+) capability$/);
67
+ if (headerMatch?.[1]) {
68
+ // Save previous capability if exists
69
+ if (currentCapability !== null) {
70
+ sections.set(currentCapability, [...patterns]);
71
+ patterns.length = 0;
72
+ }
73
+ currentCapability = headerMatch[1];
74
+ continue;
75
+ }
76
+
77
+ // If we're in a capability section, collect patterns
78
+ if (currentCapability !== null) {
79
+ // Stop at the next comment or empty line after patterns
80
+ if (line.trim() === "" || (line.startsWith("#") && !line.match(/^# \S+ capability$/))) {
81
+ // Only save if we have patterns
82
+ if (patterns.length > 0) {
83
+ sections.set(currentCapability, [...patterns]);
84
+ }
85
+ patterns.length = 0;
86
+ currentCapability = null;
87
+ continue;
88
+ }
89
+
90
+ // Add pattern if it's not a comment
91
+ if (line.trim() !== "" && !line.startsWith("#")) {
92
+ patterns.push(line);
93
+ }
94
+ }
95
+ }
96
+
97
+ // Save last capability if exists
98
+ if (currentCapability !== null && patterns.length > 0) {
99
+ sections.set(currentCapability, patterns);
100
+ }
101
+
102
+ return sections;
103
+ }
104
+
105
+ /**
106
+ * Build gitignore content from base + capability sections
107
+ * @param capabilitySections - Map of capability names to their patterns
108
+ * @returns Full gitignore content
109
+ */
110
+ export function buildGitignoreContent(capabilitySections: Map<string, string[]>): string {
111
+ let content = getBaseGitignore();
112
+
113
+ // Add each capability section
114
+ for (const [capability, patterns] of capabilitySections) {
115
+ if (patterns.length > 0) {
116
+ content += `\n# ${capability} capability\n`;
117
+ for (const pattern of patterns) {
118
+ content += `${pattern}\n`;
119
+ }
120
+ }
121
+ }
122
+
123
+ return content;
124
+ }
125
+
126
+ /**
127
+ * Add gitignore patterns for a capability
128
+ * @param capabilityId - The capability ID
129
+ * @param patterns - Array of gitignore patterns
130
+ */
131
+ export async function addCapabilityPatterns(
132
+ capabilityId: string,
133
+ patterns: string[],
134
+ ): Promise<void> {
135
+ const content = await readGitignore();
136
+ const sections = parseCapabilitySections(content);
137
+
138
+ // Add or update the capability section
139
+ sections.set(capabilityId, patterns);
140
+
141
+ const newContent = buildGitignoreContent(sections);
142
+ await writeGitignore(newContent);
143
+ }
144
+
145
+ /**
146
+ * Remove gitignore patterns for a capability
147
+ * @param capabilityId - The capability ID
148
+ */
149
+ export async function removeCapabilityPatterns(capabilityId: string): Promise<void> {
150
+ const content = await readGitignore();
151
+ const sections = parseCapabilitySections(content);
152
+
153
+ // Remove the capability section
154
+ sections.delete(capabilityId);
155
+
156
+ const newContent = buildGitignoreContent(sections);
157
+ await writeGitignore(newContent);
158
+ }
159
+
160
+ /**
161
+ * Rebuild the entire .omni/.gitignore from enabled capabilities
162
+ * @param enabledCapabilities - Map of enabled capability IDs to their gitignore patterns
163
+ */
164
+ export async function rebuildGitignore(enabledCapabilities: Map<string, string[]>): Promise<void> {
165
+ const newContent = buildGitignoreContent(enabledCapabilities);
166
+ await writeGitignore(newContent);
167
+ }
@@ -0,0 +1,26 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { getVersion, version } from "./index";
3
+
4
+ describe("@omnidev-ai/core", () => {
5
+ describe("version", () => {
6
+ test("should be defined as a string", () => {
7
+ expect(typeof version).toBe("string");
8
+ });
9
+
10
+ test("should follow semantic versioning format", () => {
11
+ expect(version).toMatch(/^\d+\.\d+\.\d+$/);
12
+ });
13
+ });
14
+
15
+ describe("getVersion", () => {
16
+ test("should return the version string", () => {
17
+ const result = getVersion();
18
+ expect(result).toBe(version);
19
+ });
20
+
21
+ test("should return a non-empty string", () => {
22
+ const result = getVersion();
23
+ expect(result.length).toBeGreaterThan(0);
24
+ });
25
+ });
26
+ });
package/src/index.ts ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @omnidev-ai/core - Core functionality for OmniDev
3
+ *
4
+ * This package contains shared types, utilities, and core logic
5
+ * used across the CLI and MCP server packages.
6
+ */
7
+
8
+ // Re-export @stricli/core for capabilities to use
9
+ // This ensures all capabilities use the same @stricli/core instance as the CLI
10
+ export { buildCommand, buildRouteMap } from "@stricli/core";
11
+
12
+ export const version = "0.1.0";
13
+
14
+ export function getVersion(): string {
15
+ return version;
16
+ }
17
+
18
+ // Export capability system
19
+ export * from "./capability";
20
+
21
+ // Export config functionality
22
+ export * from "./config";
23
+ // Export gitignore management
24
+ export * from "./gitignore/manager";
25
+ // Export MCP JSON management
26
+ export * from "./mcp-json";
27
+ // Export state management
28
+ export * from "./state";
29
+ // Export sync functionality
30
+ export * from "./sync";
31
+
32
+ // Export templates
33
+ export * from "./templates/agents";
34
+ export * from "./templates/claude";
35
+ // Export core types
36
+ export * from "./types";
37
+
38
+ // Export debug utilities
39
+ export * from "./debug";
@@ -0,0 +1 @@
1
+ export * from "./manager";