@deimoscloud/coreai 0.1.8 → 0.1.10

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 (196) hide show
  1. package/dist/cli/index.js +5 -0
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/index.js +3 -1
  4. package/dist/index.js.map +1 -1
  5. package/package.json +6 -1
  6. package/.prettierrc +0 -9
  7. package/AGENT_SPEC.md +0 -347
  8. package/ARCHITECTURE.md +0 -547
  9. package/DRAFT_PRD.md +0 -1440
  10. package/IMPLEMENTATION_PLAN.md +0 -256
  11. package/PRODUCT.md +0 -473
  12. package/WORKFLOWS.md +0 -295
  13. package/commands/core/check-inbox.md +0 -34
  14. package/commands/core/delegate.md +0 -30
  15. package/commands/core/git-commit.md +0 -144
  16. package/commands/core/pr-create.md +0 -193
  17. package/commands/core/review.md +0 -56
  18. package/commands/core/sprint-status.md +0 -65
  19. package/commands/optional/docs-update.md +0 -200
  20. package/commands/optional/jira-create.md +0 -200
  21. package/commands/optional/jira-transition.md +0 -184
  22. package/commands/optional/worktree-cleanup.md +0 -167
  23. package/commands/optional/worktree-setup.md +0 -110
  24. package/eslint.config.js +0 -29
  25. package/jest.config.js +0 -22
  26. package/knowledge-library/README.md +0 -118
  27. package/knowledge-library/android-engineer/context/current.txt +0 -42
  28. package/knowledge-library/android-engineer/control/decisions.txt +0 -9
  29. package/knowledge-library/android-engineer/control/dependencies.txt +0 -19
  30. package/knowledge-library/android-engineer/control/objectives.txt +0 -26
  31. package/knowledge-library/android-engineer/history/.gitkeep +0 -0
  32. package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
  33. package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
  34. package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
  35. package/knowledge-library/architecture.txt +0 -61
  36. package/knowledge-library/backend-engineer/context/current.txt +0 -42
  37. package/knowledge-library/backend-engineer/control/decisions.txt +0 -9
  38. package/knowledge-library/backend-engineer/control/dependencies.txt +0 -19
  39. package/knowledge-library/backend-engineer/control/objectives.txt +0 -26
  40. package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
  41. package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
  42. package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
  43. package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
  44. package/knowledge-library/context.txt +0 -52
  45. package/knowledge-library/devops-engineer/context/current.txt +0 -42
  46. package/knowledge-library/devops-engineer/control/decisions.txt +0 -9
  47. package/knowledge-library/devops-engineer/control/dependencies.txt +0 -19
  48. package/knowledge-library/devops-engineer/control/objectives.txt +0 -26
  49. package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
  50. package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
  51. package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
  52. package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
  53. package/knowledge-library/engineering-manager/context/current.txt +0 -40
  54. package/knowledge-library/engineering-manager/control/decisions.txt +0 -9
  55. package/knowledge-library/engineering-manager/control/objectives.txt +0 -27
  56. package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
  57. package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
  58. package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
  59. package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
  60. package/knowledge-library/prd.txt +0 -81
  61. package/knowledge-library/product-manager/context/current.txt +0 -42
  62. package/knowledge-library/product-manager/control/decisions.txt +0 -9
  63. package/knowledge-library/product-manager/control/dependencies.txt +0 -19
  64. package/knowledge-library/product-manager/control/objectives.txt +0 -26
  65. package/knowledge-library/product-manager/history/.gitkeep +0 -0
  66. package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
  67. package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
  68. package/knowledge-library/product-manager/tech/.gitkeep +0 -0
  69. package/knowledge-library/qa-engineer/context/current.txt +0 -42
  70. package/knowledge-library/qa-engineer/control/decisions.txt +0 -9
  71. package/knowledge-library/qa-engineer/control/dependencies.txt +0 -19
  72. package/knowledge-library/qa-engineer/control/objectives.txt +0 -26
  73. package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
  74. package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
  75. package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
  76. package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
  77. package/knowledge-library/security-engineer/context/current.txt +0 -42
  78. package/knowledge-library/security-engineer/control/decisions.txt +0 -9
  79. package/knowledge-library/security-engineer/control/dependencies.txt +0 -19
  80. package/knowledge-library/security-engineer/control/objectives.txt +0 -26
  81. package/knowledge-library/security-engineer/history/.gitkeep +0 -0
  82. package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
  83. package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
  84. package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
  85. package/knowledge-library/solutions-architect/context/current.txt +0 -42
  86. package/knowledge-library/solutions-architect/control/decisions.txt +0 -9
  87. package/knowledge-library/solutions-architect/control/dependencies.txt +0 -19
  88. package/knowledge-library/solutions-architect/control/objectives.txt +0 -26
  89. package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
  90. package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
  91. package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
  92. package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
  93. package/knowledge-library/wearos-engineer/context/current.txt +0 -42
  94. package/knowledge-library/wearos-engineer/control/decisions.txt +0 -9
  95. package/knowledge-library/wearos-engineer/control/dependencies.txt +0 -19
  96. package/knowledge-library/wearos-engineer/control/objectives.txt +0 -26
  97. package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
  98. package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
  99. package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
  100. package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
  101. package/scripts/add-agent.sh +0 -323
  102. package/scripts/install.sh +0 -354
  103. package/src/adapters/factory.test.ts +0 -386
  104. package/src/adapters/factory.ts +0 -305
  105. package/src/adapters/index.ts +0 -113
  106. package/src/adapters/interfaces.ts +0 -268
  107. package/src/adapters/mcp/client.test.ts +0 -130
  108. package/src/adapters/mcp/client.ts +0 -451
  109. package/src/adapters/mcp/discovery.test.ts +0 -315
  110. package/src/adapters/mcp/discovery.ts +0 -340
  111. package/src/adapters/mcp/index.ts +0 -66
  112. package/src/adapters/mcp/mapper.test.ts +0 -218
  113. package/src/adapters/mcp/mapper.ts +0 -536
  114. package/src/adapters/mcp/registry.test.ts +0 -433
  115. package/src/adapters/mcp/registry.ts +0 -550
  116. package/src/adapters/mcp/types.ts +0 -258
  117. package/src/adapters/native/filesystem.test.ts +0 -350
  118. package/src/adapters/native/filesystem.ts +0 -393
  119. package/src/adapters/native/github.test.ts +0 -173
  120. package/src/adapters/native/github.ts +0 -627
  121. package/src/adapters/native/index.ts +0 -22
  122. package/src/adapters/native/selector.test.ts +0 -224
  123. package/src/adapters/native/selector.ts +0 -150
  124. package/src/adapters/types.ts +0 -270
  125. package/src/agents/compiler.test.ts +0 -399
  126. package/src/agents/compiler.ts +0 -422
  127. package/src/agents/index.ts +0 -37
  128. package/src/agents/loader.test.ts +0 -319
  129. package/src/agents/loader.ts +0 -143
  130. package/src/agents/resolver.test.ts +0 -282
  131. package/src/agents/resolver.ts +0 -262
  132. package/src/agents/types.ts +0 -97
  133. package/src/cache/index.ts +0 -38
  134. package/src/cache/interfaces.ts +0 -283
  135. package/src/cache/manager.test.ts +0 -266
  136. package/src/cache/manager.ts +0 -388
  137. package/src/cache/provider.test.ts +0 -485
  138. package/src/cache/provider.ts +0 -745
  139. package/src/cache/types.test.ts +0 -192
  140. package/src/cache/types.ts +0 -313
  141. package/src/cli/commands/build.test.ts +0 -248
  142. package/src/cli/commands/build.ts +0 -284
  143. package/src/cli/commands/cache.test.ts +0 -221
  144. package/src/cli/commands/cache.ts +0 -229
  145. package/src/cli/commands/index.ts +0 -63
  146. package/src/cli/commands/init.test.ts +0 -173
  147. package/src/cli/commands/init.ts +0 -296
  148. package/src/cli/commands/skills.test.ts +0 -272
  149. package/src/cli/commands/skills.ts +0 -348
  150. package/src/cli/commands/status.test.ts +0 -392
  151. package/src/cli/commands/status.ts +0 -332
  152. package/src/cli/commands/sync.test.ts +0 -213
  153. package/src/cli/commands/sync.ts +0 -251
  154. package/src/cli/commands/validate.test.ts +0 -216
  155. package/src/cli/commands/validate.ts +0 -340
  156. package/src/cli/index.test.ts +0 -190
  157. package/src/cli/index.ts +0 -493
  158. package/src/commands/context.test.ts +0 -163
  159. package/src/commands/context.ts +0 -111
  160. package/src/commands/index.ts +0 -56
  161. package/src/commands/loader.test.ts +0 -273
  162. package/src/commands/loader.ts +0 -355
  163. package/src/commands/registry.test.ts +0 -384
  164. package/src/commands/registry.ts +0 -248
  165. package/src/commands/runner.test.ts +0 -297
  166. package/src/commands/runner.ts +0 -222
  167. package/src/commands/types.ts +0 -361
  168. package/src/config/index.ts +0 -19
  169. package/src/config/loader.test.ts +0 -262
  170. package/src/config/loader.ts +0 -188
  171. package/src/config/types.ts +0 -154
  172. package/src/context/index.ts +0 -14
  173. package/src/context/loader.test.ts +0 -334
  174. package/src/context/loader.ts +0 -357
  175. package/src/index.test.ts +0 -13
  176. package/src/index.ts +0 -268
  177. package/src/knowledge-library/index.ts +0 -44
  178. package/src/knowledge-library/manager.test.ts +0 -536
  179. package/src/knowledge-library/manager.ts +0 -804
  180. package/src/knowledge-library/types.ts +0 -432
  181. package/src/skills/generator.test.ts +0 -602
  182. package/src/skills/generator.ts +0 -491
  183. package/src/skills/index.ts +0 -27
  184. package/src/skills/templates.ts +0 -520
  185. package/src/skills/types.ts +0 -251
  186. package/templates/completion-report.md +0 -72
  187. package/templates/feedback.md +0 -56
  188. package/templates/project-files/CLAUDE.md.template +0 -109
  189. package/templates/project-files/coreai.json.example +0 -47
  190. package/templates/project-files/mcp.json.template +0 -20
  191. package/templates/review-complete.md +0 -64
  192. package/templates/review-request.md +0 -67
  193. package/templates/task-assignment.md +0 -51
  194. package/tsconfig.build.json +0 -4
  195. package/tsconfig.json +0 -26
  196. package/tsup.config.ts +0 -23
@@ -1,188 +0,0 @@
1
- /**
2
- * Configuration Loader
3
- *
4
- * Handles config file discovery, parsing, validation, and defaults.
5
- */
6
-
7
- import { existsSync, readFileSync } from 'fs';
8
- import { dirname, join, resolve } from 'path';
9
- import { parse as parseYaml } from 'yaml';
10
- import Ajv, { type ErrorObject } from 'ajv';
11
- import addFormats from 'ajv-formats';
12
- import type { CoreAIConfig, ResolvedCoreAIConfig } from './types.js';
13
-
14
- // Import schema as JSON
15
- import { createRequire } from 'module';
16
- const require = createRequire(import.meta.url);
17
- const configSchema = require('../../schemas/coreai.config.schema.json') as object;
18
-
19
- const CONFIG_FILE_NAME = 'coreai.config.yaml';
20
- const CONFIG_FILE_NAMES = [CONFIG_FILE_NAME, 'coreai.config.yml'];
21
-
22
- export class ConfigError extends Error {
23
- constructor(
24
- message: string,
25
- public readonly code: ConfigErrorCode,
26
- public readonly details?: unknown
27
- ) {
28
- super(message);
29
- this.name = 'ConfigError';
30
- }
31
- }
32
-
33
- export type ConfigErrorCode = 'NOT_FOUND' | 'PARSE_ERROR' | 'VALIDATION_ERROR' | 'READ_ERROR';
34
-
35
- /**
36
- * Default team agents
37
- */
38
- const DEFAULT_AGENTS = [
39
- 'backend-engineer',
40
- 'frontend-engineer',
41
- 'devops-engineer',
42
- 'engineering-manager',
43
- ];
44
-
45
- /**
46
- * Find the config file by walking up the directory tree
47
- */
48
- export function findConfigFile(startDir: string = process.cwd()): string | null {
49
- let currentDir = resolve(startDir);
50
- const root = dirname(currentDir);
51
-
52
- while (currentDir !== root) {
53
- for (const fileName of CONFIG_FILE_NAMES) {
54
- const configPath = join(currentDir, fileName);
55
- if (existsSync(configPath)) {
56
- return configPath;
57
- }
58
- }
59
-
60
- const parentDir = dirname(currentDir);
61
- if (parentDir === currentDir) {
62
- break;
63
- }
64
- currentDir = parentDir;
65
- }
66
-
67
- // Check root directory
68
- for (const fileName of CONFIG_FILE_NAMES) {
69
- const configPath = join(currentDir, fileName);
70
- if (existsSync(configPath)) {
71
- return configPath;
72
- }
73
- }
74
-
75
- return null;
76
- }
77
-
78
- /**
79
- * Parse YAML content into an object
80
- */
81
- export function parseConfig(content: string, filePath?: string): unknown {
82
- try {
83
- return parseYaml(content);
84
- } catch (error) {
85
- const message = error instanceof Error ? error.message : 'Unknown parse error';
86
- throw new ConfigError(
87
- `Failed to parse YAML${filePath ? ` in ${filePath}` : ''}: ${message}`,
88
- 'PARSE_ERROR',
89
- error
90
- );
91
- }
92
- }
93
-
94
- /**
95
- * Validate config against JSON schema
96
- */
97
- export function validateConfig(config: unknown): CoreAIConfig {
98
- const ajv = new Ajv.default({ allErrors: true, strict: false });
99
- addFormats.default(ajv);
100
-
101
- const validate = ajv.compile<CoreAIConfig>(configSchema);
102
- const valid = validate(config);
103
-
104
- if (!valid) {
105
- const errors: ErrorObject[] = validate.errors ?? [];
106
- const errorMessages = errors.map((err: ErrorObject) => {
107
- const path = err.instancePath || 'root';
108
- return `${path}: ${err.message ?? 'unknown error'}`;
109
- });
110
-
111
- throw new ConfigError(
112
- `Configuration validation failed:\n - ${errorMessages.join('\n - ')}`,
113
- 'VALIDATION_ERROR',
114
- errors
115
- );
116
- }
117
-
118
- return config as CoreAIConfig;
119
- }
120
-
121
- /**
122
- * Apply default values to config
123
- */
124
- export function applyDefaults(config: CoreAIConfig): ResolvedCoreAIConfig {
125
- return {
126
- ...config,
127
- project: {
128
- name: config.project?.name ?? 'unnamed',
129
- type: config.project?.type ?? 'software',
130
- root: config.project?.root ?? process.cwd(),
131
- },
132
- team: {
133
- agents: config.team?.agents ?? DEFAULT_AGENTS,
134
- },
135
- };
136
- }
137
-
138
- /**
139
- * Load and validate configuration from a file path
140
- */
141
- export function loadConfigFromFile(filePath: string): ResolvedCoreAIConfig {
142
- let content: string;
143
-
144
- try {
145
- content = readFileSync(filePath, 'utf-8');
146
- } catch (error) {
147
- const message = error instanceof Error ? error.message : 'Unknown read error';
148
- throw new ConfigError(
149
- `Failed to read config file ${filePath}: ${message}`,
150
- 'READ_ERROR',
151
- error
152
- );
153
- }
154
-
155
- const parsed = parseConfig(content, filePath);
156
- const validated = validateConfig(parsed);
157
- return applyDefaults(validated);
158
- }
159
-
160
- /**
161
- * Load configuration by searching for config file
162
- */
163
- export function loadConfig(startDir?: string): ResolvedCoreAIConfig {
164
- const configPath = findConfigFile(startDir);
165
-
166
- if (!configPath) {
167
- throw new ConfigError(
168
- `Configuration file not found. Create a ${CONFIG_FILE_NAME} file or run 'coreai init'.`,
169
- 'NOT_FOUND'
170
- );
171
- }
172
-
173
- return loadConfigFromFile(configPath);
174
- }
175
-
176
- /**
177
- * Check if a config file exists in the given directory or parent directories
178
- */
179
- export function configExists(startDir?: string): boolean {
180
- return findConfigFile(startDir) !== null;
181
- }
182
-
183
- /**
184
- * Get the path to the config file, or null if not found
185
- */
186
- export function getConfigPath(startDir?: string): string | null {
187
- return findConfigFile(startDir);
188
- }
@@ -1,154 +0,0 @@
1
- /**
2
- * CoreAI Configuration Types
3
- *
4
- * TypeScript types corresponding to the JSON schema at schemas/coreai.config.schema.json
5
- */
6
-
7
- export type ProjectType = 'software' | 'infrastructure' | 'data' | 'mobile';
8
-
9
- export type IssueTrackerProvider = 'jira' | 'linear' | 'github-issues' | 'azure-devops';
10
- export type GitProvider = 'github' | 'gitlab' | 'bitbucket' | 'azure-devops';
11
- export type DocumentationProvider = 'confluence' | 'notion' | 'github-wiki' | 'local';
12
- export type StateProvider = 'confluence' | 'notion' | 's3' | 'github-repo' | 'local';
13
-
14
- export type AdapterStrategy = 'mcp' | 'native' | 'auto';
15
-
16
- export interface ProjectConfig {
17
- name: string;
18
- type?: ProjectType;
19
- root?: string;
20
- }
21
-
22
- export interface TeamConfig {
23
- agents?: string[];
24
- }
25
-
26
- export interface IssueTrackerConfig {
27
- project_key?: string;
28
- base_url?: string;
29
- [key: string]: unknown;
30
- }
31
-
32
- export interface IssueTrackerIntegration {
33
- provider: IssueTrackerProvider;
34
- config?: IssueTrackerConfig;
35
- strategy?: AdapterStrategy;
36
- }
37
-
38
- export interface GitConfig {
39
- owner?: string;
40
- repo?: string;
41
- default_branch?: string;
42
- [key: string]: unknown;
43
- }
44
-
45
- export interface GitIntegration {
46
- provider: GitProvider;
47
- config?: GitConfig;
48
- strategy?: AdapterStrategy;
49
- }
50
-
51
- export interface DocumentationConfig {
52
- space_key?: string;
53
- base_url?: string;
54
- base_path?: string;
55
- [key: string]: unknown;
56
- }
57
-
58
- export interface DocumentationIntegration {
59
- provider: DocumentationProvider;
60
- config?: DocumentationConfig;
61
- strategy?: AdapterStrategy;
62
- }
63
-
64
- export interface StateConfig {
65
- space_key?: string;
66
- base_path?: string;
67
- bucket?: string;
68
- [key: string]: unknown;
69
- }
70
-
71
- export interface StateIntegration {
72
- provider: StateProvider;
73
- config?: StateConfig;
74
- strategy?: AdapterStrategy;
75
- }
76
-
77
- export interface IntegrationsConfig {
78
- issue_tracker?: IssueTrackerIntegration;
79
- git?: GitIntegration;
80
- documentation?: DocumentationIntegration;
81
- state?: StateIntegration;
82
- }
83
-
84
- export interface QualityGate {
85
- command: string;
86
- required?: boolean;
87
- }
88
-
89
- export type QualityGatesConfig = Record<string, QualityGate>;
90
-
91
- export interface TechStackConfig {
92
- primary_language?: string;
93
- frameworks?: string[];
94
- cloud?: string;
95
- [key: string]: unknown;
96
- }
97
-
98
- export interface McpConfig {
99
- expose_adapters?: boolean;
100
- }
101
-
102
- /**
103
- * Cache configuration
104
- */
105
- export interface CacheConfig {
106
- /**
107
- * Enable caching (default: true)
108
- */
109
- enabled?: boolean;
110
-
111
- /**
112
- * Cache directory path (default: .coreai/cache)
113
- */
114
- path?: string;
115
-
116
- /**
117
- * Default TTL in seconds (default: 3600)
118
- */
119
- ttl?: number;
120
-
121
- /**
122
- * Maximum cache size in bytes (default: 100MB)
123
- */
124
- max_size?: number;
125
-
126
- /**
127
- * Maximum number of entries (default: 1000)
128
- */
129
- max_entries?: number;
130
-
131
- /**
132
- * Enable automatic cleanup of expired entries (default: true)
133
- */
134
- auto_cleanup?: boolean;
135
- }
136
-
137
- export interface CoreAIConfig {
138
- version?: '1.0';
139
- project?: ProjectConfig;
140
- team?: TeamConfig;
141
- integrations?: IntegrationsConfig;
142
- quality_gates?: QualityGatesConfig;
143
- tech_stack?: TechStackConfig;
144
- mcp?: McpConfig;
145
- cache?: CacheConfig;
146
- }
147
-
148
- /**
149
- * Configuration with all defaults applied
150
- */
151
- export interface ResolvedCoreAIConfig extends CoreAIConfig {
152
- project: Required<ProjectConfig>;
153
- team: Required<TeamConfig>;
154
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * Context Module
3
- *
4
- * Provides context loading and resolution from remote documentation providers.
5
- */
6
-
7
- export type {
8
- ContextSource,
9
- ContextLoadResult,
10
- ContextLoaderOptions,
11
- LoadOptions,
12
- } from './loader.js';
13
-
14
- export { ContextLoader, createContextLoader } from './loader.js';
@@ -1,334 +0,0 @@
1
- /**
2
- * Context Loader Tests
3
- */
4
-
5
- import { promises as fs } from 'fs';
6
- import { join } from 'path';
7
- import { tmpdir } from 'os';
8
- import { ContextLoader, createContextLoader } from './loader.js';
9
- import { FileCacheProvider } from '../cache/provider.js';
10
- import { CacheManager } from '../cache/manager.js';
11
- import type { RemoteFetcher, ContextSource } from '../index.js';
12
-
13
- describe('ContextLoader', () => {
14
- let testDir: string;
15
- let cacheProvider: FileCacheProvider;
16
- let cacheManager: CacheManager;
17
- let loader: ContextLoader;
18
-
19
- // Mock fetcher for testing
20
- const createMockFetcher = (responses: Record<string, string>): RemoteFetcher => ({
21
- async fetch(url: string) {
22
- const content = responses[url];
23
- if (!content) {
24
- throw new Error(`Not found: ${url}`);
25
- }
26
- return {
27
- content,
28
- metadata: {
29
- contentType: 'text/markdown',
30
- title: `Doc: ${url}`,
31
- },
32
- };
33
- },
34
- async hasChanged() {
35
- return true;
36
- },
37
- });
38
-
39
- beforeEach(async () => {
40
- testDir = join(tmpdir(), `context-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
41
- await fs.mkdir(testDir, { recursive: true });
42
-
43
- cacheProvider = new FileCacheProvider({ basePath: testDir });
44
- await cacheProvider.initialize();
45
-
46
- cacheManager = new CacheManager({ cache: cacheProvider });
47
-
48
- loader = new ContextLoader({
49
- cacheManager,
50
- defaultTtl: 3600,
51
- });
52
- });
53
-
54
- afterEach(async () => {
55
- try {
56
- await fs.rm(testDir, { recursive: true, force: true });
57
- } catch {
58
- // Ignore cleanup errors
59
- }
60
- });
61
-
62
- describe('constructor', () => {
63
- it('should create loader with options', () => {
64
- expect(loader).toBeInstanceOf(ContextLoader);
65
- });
66
- });
67
-
68
- describe('load', () => {
69
- it('should load a single context', async () => {
70
- const fetcher = createMockFetcher({
71
- 'https://github.com/docs/readme': '# README\nProject documentation',
72
- });
73
- cacheManager.registerFetcher('github', fetcher);
74
-
75
- const source: ContextSource = {
76
- key: 'readme',
77
- url: 'https://github.com/docs/readme',
78
- title: 'README',
79
- };
80
-
81
- const entry = await loader.load(source);
82
-
83
- expect(entry.content).toBe('# README\nProject documentation');
84
- expect(entry.metadata.key).toBe('readme');
85
- });
86
-
87
- it('should throw for required context if fetch fails', async () => {
88
- const fetcher = createMockFetcher({}); // No responses
89
- cacheManager.registerFetcher('github', fetcher);
90
-
91
- const source: ContextSource = {
92
- key: 'missing',
93
- url: 'https://github.com/missing',
94
- required: true,
95
- };
96
-
97
- await expect(loader.load(source)).rejects.toThrow(/Failed to load required context/);
98
- });
99
-
100
- it('should apply tags from source', async () => {
101
- const fetcher = createMockFetcher({
102
- 'https://github.com/docs': 'Content',
103
- });
104
- cacheManager.registerFetcher('github', fetcher);
105
-
106
- const source: ContextSource = {
107
- key: 'tagged',
108
- url: 'https://github.com/docs',
109
- tags: ['docs', 'architecture'],
110
- };
111
-
112
- await loader.load(source);
113
-
114
- const metadata = await cacheProvider.getMetadata('tagged');
115
- expect(metadata?.tags).toEqual(['docs', 'architecture']);
116
- });
117
- });
118
-
119
- describe('loadMany', () => {
120
- it('should load multiple contexts', async () => {
121
- const fetcher = createMockFetcher({
122
- 'https://github.com/docs/1': 'Content 1',
123
- 'https://github.com/docs/2': 'Content 2',
124
- 'https://github.com/docs/3': 'Content 3',
125
- });
126
- cacheManager.registerFetcher('github', fetcher);
127
-
128
- const sources: ContextSource[] = [
129
- { key: 'doc1', url: 'https://github.com/docs/1' },
130
- { key: 'doc2', url: 'https://github.com/docs/2' },
131
- { key: 'doc3', url: 'https://github.com/docs/3' },
132
- ];
133
-
134
- const result = await loader.loadMany(sources);
135
-
136
- expect(result.loaded).toHaveLength(3);
137
- expect(result.failed).toHaveLength(0);
138
- expect(result.duration).toBeGreaterThan(0);
139
- });
140
-
141
- it('should report failures with continueOnError', async () => {
142
- const fetcher = createMockFetcher({
143
- 'https://github.com/docs/1': 'Content 1',
144
- // doc2 missing
145
- });
146
- cacheManager.registerFetcher('github', fetcher);
147
-
148
- const sources: ContextSource[] = [
149
- { key: 'doc1', url: 'https://github.com/docs/1' },
150
- { key: 'doc2', url: 'https://github.com/docs/2' },
151
- ];
152
-
153
- const result = await loader.loadMany(sources, { continueOnError: true });
154
-
155
- expect(result.loaded).toHaveLength(1);
156
- expect(result.failed).toHaveLength(1);
157
- expect(result.failed[0].key).toBe('doc2');
158
- });
159
-
160
- it('should fail on required context by default', async () => {
161
- const fetcher = createMockFetcher({
162
- 'https://github.com/docs/1': 'Content',
163
- });
164
- cacheManager.registerFetcher('github', fetcher);
165
-
166
- const sources: ContextSource[] = [
167
- { key: 'doc1', url: 'https://github.com/docs/1' },
168
- { key: 'required-missing', url: 'https://github.com/missing', required: true },
169
- ];
170
-
171
- await expect(loader.loadMany(sources)).rejects.toThrow(/Failed to load required context/);
172
- });
173
-
174
- it('should track fromCache status', async () => {
175
- const fetcher = createMockFetcher({
176
- 'https://github.com/docs/cached': 'Cached content',
177
- 'https://github.com/docs/fresh': 'Fresh content',
178
- });
179
- cacheManager.registerFetcher('github', fetcher);
180
-
181
- // Pre-cache one entry
182
- await cacheProvider.set('cached-doc', 'Pre-cached content', {
183
- source: 'github',
184
- sourceUrl: 'https://github.com/docs/cached',
185
- });
186
-
187
- const sources: ContextSource[] = [
188
- { key: 'cached-doc', url: 'https://github.com/docs/cached' },
189
- { key: 'fresh-doc', url: 'https://github.com/docs/fresh' },
190
- ];
191
-
192
- const result = await loader.loadMany(sources);
193
-
194
- const cachedEntry = result.loaded.find((l) => l.key === 'cached-doc');
195
- const freshEntry = result.loaded.find((l) => l.key === 'fresh-doc');
196
-
197
- expect(cachedEntry?.fromCache).toBe(true);
198
- expect(freshEntry?.fromCache).toBe(false);
199
- });
200
-
201
- it('should call progress callback', async () => {
202
- const fetcher = createMockFetcher({
203
- 'https://github.com/docs/1': 'Content 1',
204
- 'https://github.com/docs/2': 'Content 2',
205
- });
206
- cacheManager.registerFetcher('github', fetcher);
207
-
208
- const progressCalls: { loaded: number; total: number; current: string }[] = [];
209
-
210
- const sources: ContextSource[] = [
211
- { key: 'doc1', url: 'https://github.com/docs/1' },
212
- { key: 'doc2', url: 'https://github.com/docs/2' },
213
- ];
214
-
215
- await loader.loadMany(sources, {
216
- onProgress: (loaded, total, current) => {
217
- progressCalls.push({ loaded, total, current });
218
- },
219
- });
220
-
221
- expect(progressCalls.length).toBeGreaterThan(0);
222
- expect(progressCalls[progressCalls.length - 1].loaded).toBe(2);
223
- });
224
- });
225
-
226
- describe('refresh', () => {
227
- it('should force refresh all contexts', async () => {
228
- const responses = {
229
- 'https://github.com/docs/1': 'Original',
230
- };
231
- const fetcher = createMockFetcher(responses);
232
- cacheManager.registerFetcher('github', fetcher);
233
-
234
- const sources: ContextSource[] = [{ key: 'doc1', url: 'https://github.com/docs/1' }];
235
-
236
- // Initial load
237
- await loader.loadMany(sources);
238
-
239
- // Change content
240
- responses['https://github.com/docs/1'] = 'Updated';
241
-
242
- // Refresh
243
- const result = await loader.refresh(sources);
244
-
245
- expect(result.loaded).toHaveLength(1);
246
- expect(result.loaded[0].content).toBe('Updated');
247
- });
248
- });
249
-
250
- describe('loadByTag', () => {
251
- it('should load all contexts with a specific tag', async () => {
252
- // Pre-cache entries with tags
253
- await cacheProvider.set(
254
- 'doc1',
255
- 'Content 1',
256
- { source: 'github', sourceUrl: '' },
257
- { tags: ['architecture'] }
258
- );
259
- await cacheProvider.set(
260
- 'doc2',
261
- 'Content 2',
262
- { source: 'github', sourceUrl: '' },
263
- { tags: ['architecture', 'design'] }
264
- );
265
- await cacheProvider.set(
266
- 'doc3',
267
- 'Content 3',
268
- { source: 'github', sourceUrl: '' },
269
- { tags: ['other'] }
270
- );
271
-
272
- const result = await loader.loadByTag('architecture');
273
-
274
- expect(result.loaded).toHaveLength(2);
275
- expect(result.loaded.map((l) => l.key).sort()).toEqual(['doc1', 'doc2']);
276
- });
277
- });
278
-
279
- describe('checkAvailability', () => {
280
- it('should report available cached contexts', async () => {
281
- cacheManager.registerFetcher('github', createMockFetcher({}));
282
-
283
- // Pre-cache an entry
284
- await cacheProvider.set('cached', 'Content', {
285
- source: 'github',
286
- sourceUrl: 'https://github.com/test',
287
- });
288
-
289
- const sources: ContextSource[] = [
290
- { key: 'cached', url: 'https://github.com/test' },
291
- { key: 'missing', url: 'https://github.com/missing' },
292
- ];
293
-
294
- const { available, missing } = await loader.checkAvailability(sources);
295
-
296
- expect(available).toContain('cached');
297
- expect(missing).toContain('missing');
298
- });
299
-
300
- it('should report errors for sources without fetchers', async () => {
301
- const sources: ContextSource[] = [
302
- { key: 'no-fetcher', url: 'https://unknown-source.com/doc' },
303
- ];
304
-
305
- const { errors } = await loader.checkAvailability(sources);
306
-
307
- expect(errors.length).toBe(1);
308
- expect(errors[0]).toContain('No fetcher');
309
- });
310
- });
311
-
312
- describe('getCacheManager', () => {
313
- it('should return the cache manager', () => {
314
- expect(loader.getCacheManager()).toBe(cacheManager);
315
- });
316
- });
317
- });
318
-
319
- describe('createContextLoader', () => {
320
- it('should create a loader instance', async () => {
321
- const testDir = join(tmpdir(), `create-loader-${Date.now()}`);
322
- await fs.mkdir(testDir, { recursive: true });
323
-
324
- const cache = new FileCacheProvider({ basePath: testDir });
325
- await cache.initialize();
326
-
327
- const manager = new CacheManager({ cache });
328
- const loader = createContextLoader({ cacheManager: manager });
329
-
330
- expect(loader).toBeInstanceOf(ContextLoader);
331
-
332
- await fs.rm(testDir, { recursive: true, force: true });
333
- });
334
- });