@stackmemoryai/stackmemory 0.3.6 → 0.3.7

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 (213) hide show
  1. package/dist/agents/verifiers/base-verifier.js.map +2 -2
  2. package/dist/agents/verifiers/formatter-verifier.js.map +2 -2
  3. package/dist/agents/verifiers/llm-judge.js.map +2 -2
  4. package/dist/cli/claude-sm.js +24 -13
  5. package/dist/cli/claude-sm.js.map +2 -2
  6. package/dist/cli/codex-sm.js +24 -13
  7. package/dist/cli/codex-sm.js.map +2 -2
  8. package/dist/cli/commands/agent.js.map +2 -2
  9. package/dist/cli/commands/chromadb.js +217 -32
  10. package/dist/cli/commands/chromadb.js.map +2 -2
  11. package/dist/cli/commands/clear.js +12 -1
  12. package/dist/cli/commands/clear.js.map +2 -2
  13. package/dist/cli/commands/context.js +13 -2
  14. package/dist/cli/commands/context.js.map +2 -2
  15. package/dist/cli/commands/dashboard.js.map +2 -2
  16. package/dist/cli/commands/gc.js +202 -0
  17. package/dist/cli/commands/gc.js.map +7 -0
  18. package/dist/cli/commands/handoff.js +12 -1
  19. package/dist/cli/commands/handoff.js.map +2 -2
  20. package/dist/cli/commands/infinite-storage.js +32 -21
  21. package/dist/cli/commands/infinite-storage.js.map +2 -2
  22. package/dist/cli/commands/linear-create.js +13 -2
  23. package/dist/cli/commands/linear-create.js.map +2 -2
  24. package/dist/cli/commands/linear-list.js +12 -1
  25. package/dist/cli/commands/linear-list.js.map +2 -2
  26. package/dist/cli/commands/linear-migrate.js +12 -1
  27. package/dist/cli/commands/linear-migrate.js.map +2 -2
  28. package/dist/cli/commands/linear-test.js +12 -1
  29. package/dist/cli/commands/linear-test.js.map +2 -2
  30. package/dist/cli/commands/linear-unified.js +262 -0
  31. package/dist/cli/commands/linear-unified.js.map +7 -0
  32. package/dist/cli/commands/linear.js +17 -6
  33. package/dist/cli/commands/linear.js.map +2 -2
  34. package/dist/cli/commands/monitor.js.map +2 -2
  35. package/dist/cli/commands/onboard.js.map +2 -2
  36. package/dist/cli/commands/quality.js.map +2 -2
  37. package/dist/cli/commands/search.js.map +2 -2
  38. package/dist/cli/commands/session.js.map +2 -2
  39. package/dist/cli/commands/skills.js +12 -1
  40. package/dist/cli/commands/skills.js.map +2 -2
  41. package/dist/cli/commands/storage.js +18 -7
  42. package/dist/cli/commands/storage.js.map +2 -2
  43. package/dist/cli/commands/tasks.js.map +2 -2
  44. package/dist/cli/commands/tui.js +13 -2
  45. package/dist/cli/commands/tui.js.map +2 -2
  46. package/dist/cli/commands/webhook.js +14 -3
  47. package/dist/cli/commands/webhook.js.map +2 -2
  48. package/dist/cli/commands/workflow.js +14 -3
  49. package/dist/cli/commands/workflow.js.map +2 -2
  50. package/dist/cli/commands/worktree.js.map +2 -2
  51. package/dist/cli/index.js +18 -5
  52. package/dist/cli/index.js.map +2 -2
  53. package/dist/core/config/config-manager.js.map +2 -2
  54. package/dist/core/context/auto-context.js.map +2 -2
  55. package/dist/core/context/compaction-handler.js.map +2 -2
  56. package/dist/core/context/context-bridge.js.map +2 -2
  57. package/dist/core/context/dual-stack-manager.js.map +2 -2
  58. package/dist/core/context/frame-database.js.map +2 -2
  59. package/dist/core/context/frame-digest.js.map +2 -2
  60. package/dist/core/context/frame-handoff-manager.js.map +2 -2
  61. package/dist/core/context/frame-manager.js +12 -1
  62. package/dist/core/context/frame-manager.js.map +2 -2
  63. package/dist/core/context/frame-stack.js.map +2 -2
  64. package/dist/core/context/incremental-gc.js +279 -0
  65. package/dist/core/context/incremental-gc.js.map +7 -0
  66. package/dist/core/context/permission-manager.js +12 -1
  67. package/dist/core/context/permission-manager.js.map +2 -2
  68. package/dist/core/context/refactored-frame-manager.js.map +2 -2
  69. package/dist/core/context/shared-context-layer.js +12 -1
  70. package/dist/core/context/shared-context-layer.js.map +2 -2
  71. package/dist/core/context/stack-merge-resolver.js.map +2 -2
  72. package/dist/core/context/validation.js.map +2 -2
  73. package/dist/core/database/batch-operations.js.map +2 -2
  74. package/dist/core/database/connection-pool.js.map +2 -2
  75. package/dist/core/database/migration-manager.js.map +2 -2
  76. package/dist/core/database/paradedb-adapter.js.map +2 -2
  77. package/dist/core/database/query-cache.js.map +2 -2
  78. package/dist/core/database/query-router.js.map +2 -2
  79. package/dist/core/database/sqlite-adapter.js.map +2 -2
  80. package/dist/core/digest/enhanced-hybrid-digest.js.map +2 -2
  81. package/dist/core/errors/recovery.js.map +2 -2
  82. package/dist/core/merge/resolution-engine.js.map +2 -2
  83. package/dist/core/monitoring/error-handler.js.map +2 -2
  84. package/dist/core/monitoring/logger.js +14 -3
  85. package/dist/core/monitoring/logger.js.map +2 -2
  86. package/dist/core/monitoring/metrics.js +13 -2
  87. package/dist/core/monitoring/metrics.js.map +2 -2
  88. package/dist/core/monitoring/progress-tracker.js +12 -1
  89. package/dist/core/monitoring/progress-tracker.js.map +2 -2
  90. package/dist/core/monitoring/session-monitor.js.map +2 -2
  91. package/dist/core/performance/context-cache.js.map +2 -2
  92. package/dist/core/performance/lazy-context-loader.js.map +2 -2
  93. package/dist/core/performance/monitor.js.map +2 -2
  94. package/dist/core/performance/optimized-frame-context.js.map +2 -2
  95. package/dist/core/performance/performance-benchmark.js.map +2 -2
  96. package/dist/core/performance/performance-profiler.js +12 -1
  97. package/dist/core/performance/performance-profiler.js.map +2 -2
  98. package/dist/core/performance/streaming-jsonl-parser.js.map +2 -2
  99. package/dist/core/persistence/postgres-adapter.js.map +2 -2
  100. package/dist/core/projects/project-manager.js.map +2 -2
  101. package/dist/core/retrieval/context-retriever.js.map +2 -2
  102. package/dist/core/retrieval/graph-retrieval.js.map +2 -2
  103. package/dist/core/retrieval/llm-context-retrieval.js.map +2 -2
  104. package/dist/core/retrieval/retrieval-benchmarks.js.map +2 -2
  105. package/dist/core/retrieval/summary-generator.js.map +2 -2
  106. package/dist/core/session/clear-survival.js.map +2 -2
  107. package/dist/core/session/handoff-generator.js.map +2 -2
  108. package/dist/core/session/session-manager.js +16 -5
  109. package/dist/core/session/session-manager.js.map +2 -2
  110. package/dist/core/skills/skill-storage.js +13 -2
  111. package/dist/core/skills/skill-storage.js.map +2 -2
  112. package/dist/core/storage/chromadb-adapter.js.map +2 -2
  113. package/dist/core/storage/chromadb-simple.js.map +2 -2
  114. package/dist/core/storage/infinite-storage.js.map +2 -2
  115. package/dist/core/storage/railway-optimized-storage.js +19 -8
  116. package/dist/core/storage/railway-optimized-storage.js.map +2 -2
  117. package/dist/core/storage/remote-storage.js +12 -1
  118. package/dist/core/storage/remote-storage.js.map +2 -2
  119. package/dist/core/trace/cli-trace-wrapper.js +16 -5
  120. package/dist/core/trace/cli-trace-wrapper.js.map +2 -2
  121. package/dist/core/trace/db-trace-wrapper.js.map +2 -2
  122. package/dist/core/trace/debug-trace.js +21 -10
  123. package/dist/core/trace/debug-trace.js.map +2 -2
  124. package/dist/core/trace/index.js +46 -35
  125. package/dist/core/trace/index.js.map +2 -2
  126. package/dist/core/trace/trace-demo.js +12 -1
  127. package/dist/core/trace/trace-demo.js.map +2 -2
  128. package/dist/core/trace/trace-detector.js.map +2 -2
  129. package/dist/core/trace/trace-store.js.map +2 -2
  130. package/dist/core/utils/update-checker.js.map +2 -2
  131. package/dist/core/worktree/worktree-manager.js.map +2 -2
  132. package/dist/features/analytics/api/analytics-api.js.map +2 -2
  133. package/dist/features/analytics/core/analytics-service.js +12 -1
  134. package/dist/features/analytics/core/analytics-service.js.map +2 -2
  135. package/dist/features/analytics/queries/metrics-queries.js.map +2 -2
  136. package/dist/features/tasks/pebbles-task-store.js.map +2 -2
  137. package/dist/features/tui/components/analytics-panel.js.map +2 -2
  138. package/dist/features/tui/components/pr-tracker.js.map +2 -2
  139. package/dist/features/tui/components/session-monitor.js.map +2 -2
  140. package/dist/features/tui/components/subagent-fleet.js.map +2 -2
  141. package/dist/features/tui/components/task-board.js +650 -2
  142. package/dist/features/tui/components/task-board.js.map +2 -2
  143. package/dist/features/tui/index.js +16 -5
  144. package/dist/features/tui/index.js.map +2 -2
  145. package/dist/features/tui/services/data-service.js +25 -14
  146. package/dist/features/tui/services/data-service.js.map +2 -2
  147. package/dist/features/tui/services/linear-task-reader.js.map +2 -2
  148. package/dist/features/tui/services/websocket-client.js +13 -2
  149. package/dist/features/tui/services/websocket-client.js.map +2 -2
  150. package/dist/features/tui/terminal-compat.js +27 -16
  151. package/dist/features/tui/terminal-compat.js.map +2 -2
  152. package/dist/features/web/client/stores/task-store.js.map +2 -2
  153. package/dist/features/web/server/index.js +13 -2
  154. package/dist/features/web/server/index.js.map +2 -2
  155. package/dist/integrations/claude-code/enhanced-pre-clear-hooks.js.map +2 -2
  156. package/dist/integrations/claude-code/lifecycle-hooks.js.map +2 -2
  157. package/dist/integrations/claude-code/post-task-hooks.js.map +2 -2
  158. package/dist/integrations/linear/auth.js +17 -6
  159. package/dist/integrations/linear/auth.js.map +2 -2
  160. package/dist/integrations/linear/auto-sync.js.map +2 -2
  161. package/dist/integrations/linear/client.js.map +2 -2
  162. package/dist/integrations/linear/config.js.map +2 -2
  163. package/dist/integrations/linear/migration.js.map +2 -2
  164. package/dist/integrations/linear/oauth-server.js +13 -2
  165. package/dist/integrations/linear/oauth-server.js.map +2 -2
  166. package/dist/integrations/linear/rest-client.js.map +2 -2
  167. package/dist/integrations/linear/sync-enhanced.js +202 -0
  168. package/dist/integrations/linear/sync-enhanced.js.map +7 -0
  169. package/dist/integrations/linear/sync-manager.js.map +2 -2
  170. package/dist/integrations/linear/sync-service.js +12 -1
  171. package/dist/integrations/linear/sync-service.js.map +2 -2
  172. package/dist/integrations/linear/sync.js +34 -3
  173. package/dist/integrations/linear/sync.js.map +2 -2
  174. package/dist/integrations/linear/unified-sync.js +560 -0
  175. package/dist/integrations/linear/unified-sync.js.map +7 -0
  176. package/dist/integrations/linear/webhook-handler.js +12 -1
  177. package/dist/integrations/linear/webhook-handler.js.map +2 -2
  178. package/dist/integrations/linear/webhook-server.js +14 -3
  179. package/dist/integrations/linear/webhook-server.js.map +2 -2
  180. package/dist/integrations/linear/webhook.js +12 -1
  181. package/dist/integrations/linear/webhook.js.map +2 -2
  182. package/dist/integrations/mcp/handlers/context-handlers.js.map +2 -2
  183. package/dist/integrations/mcp/handlers/linear-handlers.js.map +2 -2
  184. package/dist/integrations/mcp/handlers/skill-handlers.js +13 -2
  185. package/dist/integrations/mcp/handlers/skill-handlers.js.map +2 -2
  186. package/dist/integrations/mcp/handlers/task-handlers.js.map +2 -2
  187. package/dist/integrations/mcp/handlers/trace-handlers.js.map +2 -2
  188. package/dist/integrations/mcp/middleware/tool-scoring.js.map +2 -2
  189. package/dist/integrations/mcp/refactored-server.js +15 -4
  190. package/dist/integrations/mcp/refactored-server.js.map +2 -2
  191. package/dist/integrations/mcp/server.js +12 -1
  192. package/dist/integrations/mcp/server.js.map +2 -2
  193. package/dist/integrations/mcp/tool-definitions.js.map +2 -2
  194. package/dist/integrations/pg-aiguide/embedding-provider.js +13 -2
  195. package/dist/integrations/pg-aiguide/embedding-provider.js.map +2 -2
  196. package/dist/integrations/pg-aiguide/semantic-search.js.map +2 -2
  197. package/dist/mcp/stackmemory-mcp-server.js +12 -1
  198. package/dist/mcp/stackmemory-mcp-server.js.map +2 -2
  199. package/dist/middleware/exponential-rate-limiter.js.map +2 -2
  200. package/dist/servers/production/auth-middleware.js +13 -2
  201. package/dist/servers/production/auth-middleware.js.map +2 -2
  202. package/dist/servers/railway/index.js +22 -11
  203. package/dist/servers/railway/index.js.map +2 -2
  204. package/dist/services/config-service.js.map +2 -2
  205. package/dist/services/context-service.js.map +2 -2
  206. package/dist/skills/claude-skills.js +105 -2
  207. package/dist/skills/claude-skills.js.map +2 -2
  208. package/dist/skills/dashboard-launcher.js.map +2 -2
  209. package/dist/skills/repo-ingestion-skill.js +561 -0
  210. package/dist/skills/repo-ingestion-skill.js.map +7 -0
  211. package/dist/utils/logger.js +12 -1
  212. package/dist/utils/logger.js.map +2 -2
  213. package/package.json +5 -1
@@ -0,0 +1,561 @@
1
+ import { ChromaDBAdapter } from "../core/storage/chromadb-adapter.js";
2
+ import { Logger } from "../core/monitoring/logger.js";
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ import * as crypto from "crypto";
6
+ import { execSync } from "child_process";
7
+ import ignore from "ignore";
8
+ class RepoIngestionSkill {
9
+ constructor(config, userId, teamId) {
10
+ this.config = config;
11
+ this.userId = userId;
12
+ this.teamId = teamId;
13
+ this.logger = new Logger("RepoIngestionSkill");
14
+ this.adapter = new ChromaDBAdapter(
15
+ {
16
+ ...config,
17
+ collectionName: config.collectionName || "stackmemory_repos"
18
+ },
19
+ userId,
20
+ teamId
21
+ );
22
+ }
23
+ logger;
24
+ adapter;
25
+ metadataCache = /* @__PURE__ */ new Map();
26
+ fileHashCache = /* @__PURE__ */ new Map();
27
+ async initialize() {
28
+ await this.adapter.initialize();
29
+ await this.loadMetadataCache();
30
+ }
31
+ /**
32
+ * Ingest a repository into ChromaDB
33
+ */
34
+ async ingestRepository(repoPath, repoName, options = {}) {
35
+ const startTime = Date.now();
36
+ try {
37
+ this.logger.info(`Starting repository ingestion for ${repoName}`);
38
+ if (!fs.existsSync(repoPath)) {
39
+ throw new Error(`Repository path not found: ${repoPath}`);
40
+ }
41
+ const metadata = await this.getRepoMetadata(repoPath, repoName);
42
+ const existingMetadata = this.metadataCache.get(metadata.repoId);
43
+ if (options.incremental && existingMetadata && !options.forceUpdate) {
44
+ const changedFiles = await this.getChangedFiles(
45
+ repoPath,
46
+ existingMetadata.lastCommit,
47
+ metadata.lastCommit
48
+ );
49
+ if (changedFiles.length === 0) {
50
+ return {
51
+ success: true,
52
+ message: "No changes detected since last ingestion"
53
+ };
54
+ }
55
+ this.logger.info(
56
+ `Incremental update: ${changedFiles.length} files changed`
57
+ );
58
+ }
59
+ const files = await this.getRepoFiles(repoPath, options);
60
+ this.logger.info(`Found ${files.length} files to process`);
61
+ let filesProcessed = 0;
62
+ let chunksCreated = 0;
63
+ let totalSize = 0;
64
+ for (const file of files) {
65
+ try {
66
+ const chunks = await this.processFile(
67
+ file,
68
+ repoPath,
69
+ repoName,
70
+ metadata,
71
+ options
72
+ );
73
+ for (const chunk of chunks) {
74
+ await this.storeChunk(chunk, metadata);
75
+ chunksCreated++;
76
+ }
77
+ filesProcessed++;
78
+ totalSize += fs.statSync(file).size;
79
+ if (filesProcessed % 100 === 0) {
80
+ this.logger.info(
81
+ `Processed ${filesProcessed}/${files.length} files`
82
+ );
83
+ }
84
+ } catch (error) {
85
+ this.logger.warn(`Failed to process file ${file}:`, error);
86
+ }
87
+ }
88
+ metadata.filesCount = filesProcessed;
89
+ metadata.totalSize = totalSize;
90
+ metadata.lastIngested = Date.now();
91
+ await this.saveMetadata(metadata);
92
+ const timeElapsed = Date.now() - startTime;
93
+ this.logger.info(
94
+ `Repository ingestion complete: ${filesProcessed} files, ${chunksCreated} chunks in ${timeElapsed}ms`
95
+ );
96
+ return {
97
+ success: true,
98
+ message: `Successfully ingested ${repoName}`,
99
+ stats: {
100
+ filesProcessed,
101
+ chunksCreated,
102
+ timeElapsed,
103
+ totalSize
104
+ }
105
+ };
106
+ } catch (error) {
107
+ this.logger.error("Repository ingestion failed:", error);
108
+ return {
109
+ success: false,
110
+ message: `Failed to ingest repository: ${error instanceof Error ? error.message : "Unknown error"}`
111
+ };
112
+ }
113
+ }
114
+ /**
115
+ * Update an existing repository in ChromaDB
116
+ */
117
+ async updateRepository(repoPath, repoName, options = {}) {
118
+ const startTime = Date.now();
119
+ try {
120
+ const metadata = await this.getRepoMetadata(repoPath, repoName);
121
+ const existingMetadata = this.metadataCache.get(metadata.repoId);
122
+ if (!existingMetadata) {
123
+ return this.ingestRepository(repoPath, repoName, options);
124
+ }
125
+ const changedFiles = await this.getChangedFiles(
126
+ repoPath,
127
+ existingMetadata.lastCommit,
128
+ metadata.lastCommit
129
+ );
130
+ if (changedFiles.length === 0) {
131
+ return {
132
+ success: true,
133
+ message: "No changes detected",
134
+ stats: {
135
+ filesUpdated: 0,
136
+ filesAdded: 0,
137
+ filesRemoved: 0,
138
+ timeElapsed: Date.now() - startTime
139
+ }
140
+ };
141
+ }
142
+ let filesUpdated = 0;
143
+ let filesAdded = 0;
144
+ let filesRemoved = 0;
145
+ for (const change of changedFiles) {
146
+ const filePath = path.join(repoPath, change.path);
147
+ if (change.status === "deleted") {
148
+ await this.removeFileChunks(change.path, metadata.repoId);
149
+ filesRemoved++;
150
+ } else if (change.status === "added") {
151
+ const chunks = await this.processFile(
152
+ filePath,
153
+ repoPath,
154
+ repoName,
155
+ metadata,
156
+ options
157
+ );
158
+ for (const chunk of chunks) {
159
+ await this.storeChunk(chunk, metadata);
160
+ }
161
+ filesAdded++;
162
+ } else if (change.status === "modified") {
163
+ await this.removeFileChunks(change.path, metadata.repoId);
164
+ const chunks = await this.processFile(
165
+ filePath,
166
+ repoPath,
167
+ repoName,
168
+ metadata,
169
+ options
170
+ );
171
+ for (const chunk of chunks) {
172
+ await this.storeChunk(chunk, metadata);
173
+ }
174
+ filesUpdated++;
175
+ }
176
+ }
177
+ metadata.lastIngested = Date.now();
178
+ await this.saveMetadata(metadata);
179
+ const timeElapsed = Date.now() - startTime;
180
+ return {
181
+ success: true,
182
+ message: `Successfully updated ${repoName}`,
183
+ stats: {
184
+ filesUpdated,
185
+ filesAdded,
186
+ filesRemoved,
187
+ timeElapsed
188
+ }
189
+ };
190
+ } catch (error) {
191
+ this.logger.error("Repository update failed:", error);
192
+ return {
193
+ success: false,
194
+ message: `Failed to update repository: ${error instanceof Error ? error.message : "Unknown error"}`
195
+ };
196
+ }
197
+ }
198
+ /**
199
+ * Search code in ingested repositories
200
+ */
201
+ async searchCode(query, options) {
202
+ try {
203
+ const filters = {
204
+ type: ["code_chunk"]
205
+ };
206
+ if (options?.repoName) {
207
+ filters.repo_name = options.repoName;
208
+ }
209
+ if (options?.language) {
210
+ filters.language = options.language;
211
+ }
212
+ const results = await this.adapter.queryContexts(
213
+ query,
214
+ options?.limit || 20,
215
+ filters
216
+ );
217
+ return results.map((result) => ({
218
+ filePath: result.metadata.file_path,
219
+ content: result.content,
220
+ score: 1 - result.distance,
221
+ // Convert distance to similarity score
222
+ startLine: result.metadata.start_line,
223
+ endLine: result.metadata.end_line,
224
+ repoName: result.metadata.repo_name
225
+ }));
226
+ } catch (error) {
227
+ this.logger.error("Code search failed:", error);
228
+ return [];
229
+ }
230
+ }
231
+ /**
232
+ * Get repository metadata
233
+ */
234
+ async getRepoMetadata(repoPath, repoName) {
235
+ const branch = this.getCurrentBranch(repoPath);
236
+ const lastCommit = this.getLastCommit(repoPath);
237
+ const repoId = `${repoName}_${branch}`.replace(/[^a-zA-Z0-9_-]/g, "_");
238
+ const { language, framework } = await this.detectLanguageAndFramework(repoPath);
239
+ return {
240
+ repoId,
241
+ repoName,
242
+ branch,
243
+ lastCommit,
244
+ lastIngested: Date.now(),
245
+ filesCount: 0,
246
+ totalSize: 0,
247
+ language,
248
+ framework
249
+ };
250
+ }
251
+ /**
252
+ * Get current git branch
253
+ */
254
+ getCurrentBranch(repoPath) {
255
+ try {
256
+ return execSync("git rev-parse --abbrev-ref HEAD", {
257
+ cwd: repoPath,
258
+ encoding: "utf8"
259
+ }).trim();
260
+ } catch {
261
+ return "main";
262
+ }
263
+ }
264
+ /**
265
+ * Get last commit hash
266
+ */
267
+ getLastCommit(repoPath) {
268
+ try {
269
+ return execSync("git rev-parse HEAD", {
270
+ cwd: repoPath,
271
+ encoding: "utf8"
272
+ }).trim();
273
+ } catch {
274
+ return "unknown";
275
+ }
276
+ }
277
+ /**
278
+ * Get changed files between commits
279
+ */
280
+ async getChangedFiles(repoPath, fromCommit, toCommit) {
281
+ try {
282
+ const diff = execSync(
283
+ `git diff --name-status ${fromCommit}..${toCommit}`,
284
+ {
285
+ cwd: repoPath,
286
+ encoding: "utf8"
287
+ }
288
+ );
289
+ return diff.split("\n").filter((line) => line.trim()).map((line) => {
290
+ const [status, ...pathParts] = line.split(" ");
291
+ return {
292
+ path: pathParts.join(" "),
293
+ status: status === "A" ? "added" : status === "D" ? "deleted" : "modified"
294
+ };
295
+ });
296
+ } catch {
297
+ return [];
298
+ }
299
+ }
300
+ /**
301
+ * Get repository files to process
302
+ */
303
+ async getRepoFiles(repoPath, options) {
304
+ const files = [];
305
+ const ig = ignore();
306
+ const gitignorePath = path.join(repoPath, ".gitignore");
307
+ if (fs.existsSync(gitignorePath)) {
308
+ ig.add(fs.readFileSync(gitignorePath, "utf8"));
309
+ }
310
+ const defaultExcludes = [
311
+ "node_modules",
312
+ ".git",
313
+ "dist",
314
+ "build",
315
+ "coverage",
316
+ ".env",
317
+ "*.log",
318
+ ...options.excludePatterns || []
319
+ ];
320
+ ig.add(defaultExcludes);
321
+ const extensions = options.extensions || [
322
+ ".ts",
323
+ ".tsx",
324
+ ".js",
325
+ ".jsx",
326
+ ".py",
327
+ ".java",
328
+ ".go",
329
+ ".rs",
330
+ ".c",
331
+ ".cpp",
332
+ ".h",
333
+ ".hpp",
334
+ ".cs",
335
+ ".rb",
336
+ ".php",
337
+ ".swift",
338
+ ".kt",
339
+ ".scala",
340
+ ".r",
341
+ ".m",
342
+ ".sql",
343
+ ".yaml",
344
+ ".yml",
345
+ ".json"
346
+ ];
347
+ if (options.includeDocs) {
348
+ extensions.push(".md", ".rst", ".txt");
349
+ }
350
+ const maxFileSize = options.maxFileSize || 1024 * 1024;
351
+ const walkDir = (dir, baseDir = repoPath) => {
352
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
353
+ for (const entry of entries) {
354
+ const fullPath = path.join(dir, entry.name);
355
+ const relativePath = path.relative(baseDir, fullPath);
356
+ if (ig.ignores(relativePath)) {
357
+ continue;
358
+ }
359
+ if (entry.isDirectory()) {
360
+ walkDir(fullPath, baseDir);
361
+ } else if (entry.isFile()) {
362
+ const ext = path.extname(entry.name);
363
+ if (!extensions.includes(ext)) {
364
+ continue;
365
+ }
366
+ if (!options.includeTests && (entry.name.includes(".test.") || entry.name.includes(".spec.") || relativePath.includes("__tests__") || relativePath.includes("test/") || relativePath.includes("tests/"))) {
367
+ continue;
368
+ }
369
+ const stats = fs.statSync(fullPath);
370
+ if (stats.size > maxFileSize) {
371
+ this.logger.debug(`Skipping large file: ${relativePath}`);
372
+ continue;
373
+ }
374
+ files.push(fullPath);
375
+ }
376
+ }
377
+ };
378
+ walkDir(repoPath);
379
+ return files;
380
+ }
381
+ /**
382
+ * Process a file into chunks
383
+ */
384
+ async processFile(filePath, repoPath, repoName, metadata, options) {
385
+ const relativePath = path.relative(repoPath, filePath);
386
+ const content = fs.readFileSync(filePath, "utf8");
387
+ const lines = content.split("\n");
388
+ const language = this.detectFileLanguage(filePath);
389
+ const chunkSize = options.chunkSize || 100;
390
+ const chunks = [];
391
+ const fileHash = crypto.createHash("md5").update(content).digest("hex");
392
+ const cachedHash = this.fileHashCache.get(relativePath);
393
+ if (cachedHash === fileHash && !options.forceUpdate) {
394
+ return [];
395
+ }
396
+ this.fileHashCache.set(relativePath, fileHash);
397
+ for (let i = 0; i < lines.length; i += chunkSize) {
398
+ const chunkLines = lines.slice(i, Math.min(i + chunkSize, lines.length));
399
+ const chunkContent = chunkLines.join("\n");
400
+ if (chunkContent.trim().length === 0) {
401
+ continue;
402
+ }
403
+ const chunkId = `${metadata.repoId}_${relativePath}_${i}`;
404
+ const chunkHash = crypto.createHash("md5").update(chunkContent).digest("hex");
405
+ chunks.push({
406
+ id: chunkId,
407
+ filePath: relativePath,
408
+ content: chunkContent,
409
+ startLine: i + 1,
410
+ endLine: Math.min(i + chunkSize, lines.length),
411
+ hash: chunkHash,
412
+ language
413
+ });
414
+ }
415
+ return chunks;
416
+ }
417
+ /**
418
+ * Store a chunk in ChromaDB
419
+ */
420
+ async storeChunk(chunk, metadata) {
421
+ const documentContent = `File: ${chunk.filePath} (Lines ${chunk.startLine}-${chunk.endLine})
422
+ Language: ${chunk.language}
423
+ Repository: ${metadata.repoName}/${metadata.branch}
424
+
425
+ ${chunk.content}`;
426
+ await this.adapter.storeContext("observation", documentContent, {
427
+ type: "code_chunk",
428
+ repo_id: metadata.repoId,
429
+ repo_name: metadata.repoName,
430
+ branch: metadata.branch,
431
+ file_path: chunk.filePath,
432
+ start_line: chunk.startLine,
433
+ end_line: chunk.endLine,
434
+ language: chunk.language,
435
+ framework: metadata.framework,
436
+ chunk_hash: chunk.hash,
437
+ last_commit: metadata.lastCommit
438
+ });
439
+ }
440
+ /**
441
+ * Remove file chunks from ChromaDB
442
+ */
443
+ async removeFileChunks(filePath, repoId) {
444
+ this.logger.debug(
445
+ `Would remove chunks for file: ${filePath} from repo: ${repoId}`
446
+ );
447
+ }
448
+ /**
449
+ * Detect file language
450
+ */
451
+ detectFileLanguage(filePath) {
452
+ const ext = path.extname(filePath).toLowerCase();
453
+ const languageMap = {
454
+ ".ts": "typescript",
455
+ ".tsx": "typescript",
456
+ ".js": "javascript",
457
+ ".jsx": "javascript",
458
+ ".py": "python",
459
+ ".java": "java",
460
+ ".go": "go",
461
+ ".rs": "rust",
462
+ ".c": "c",
463
+ ".cpp": "cpp",
464
+ ".cs": "csharp",
465
+ ".rb": "ruby",
466
+ ".php": "php",
467
+ ".swift": "swift",
468
+ ".kt": "kotlin",
469
+ ".scala": "scala",
470
+ ".r": "r",
471
+ ".sql": "sql",
472
+ ".yaml": "yaml",
473
+ ".yml": "yaml",
474
+ ".json": "json",
475
+ ".md": "markdown"
476
+ };
477
+ return languageMap[ext] || "unknown";
478
+ }
479
+ /**
480
+ * Detect language and framework
481
+ */
482
+ async detectLanguageAndFramework(repoPath) {
483
+ const packageJsonPath = path.join(repoPath, "package.json");
484
+ if (fs.existsSync(packageJsonPath)) {
485
+ try {
486
+ const packageJson = JSON.parse(
487
+ fs.readFileSync(packageJsonPath, "utf8")
488
+ );
489
+ const deps = {
490
+ ...packageJson.dependencies,
491
+ ...packageJson.devDependencies
492
+ };
493
+ let framework;
494
+ if (deps.react) framework = "react";
495
+ else if (deps.vue) framework = "vue";
496
+ else if (deps.angular) framework = "angular";
497
+ else if (deps.express) framework = "express";
498
+ else if (deps.next) framework = "nextjs";
499
+ else if (deps.svelte) framework = "svelte";
500
+ return {
501
+ language: deps.typescript ? "typescript" : "javascript",
502
+ framework
503
+ };
504
+ } catch {
505
+ }
506
+ }
507
+ if (fs.existsSync(path.join(repoPath, "requirements.txt")) || fs.existsSync(path.join(repoPath, "setup.py"))) {
508
+ return { language: "python" };
509
+ }
510
+ if (fs.existsSync(path.join(repoPath, "go.mod"))) {
511
+ return { language: "go" };
512
+ }
513
+ if (fs.existsSync(path.join(repoPath, "Cargo.toml"))) {
514
+ return { language: "rust" };
515
+ }
516
+ if (fs.existsSync(path.join(repoPath, "pom.xml")) || fs.existsSync(path.join(repoPath, "build.gradle"))) {
517
+ return { language: "java" };
518
+ }
519
+ return { language: "unknown" };
520
+ }
521
+ /**
522
+ * Load metadata cache
523
+ */
524
+ async loadMetadataCache() {
525
+ this.metadataCache.clear();
526
+ }
527
+ /**
528
+ * Save metadata
529
+ */
530
+ async saveMetadata(metadata) {
531
+ this.metadataCache.set(metadata.repoId, metadata);
532
+ }
533
+ /**
534
+ * Get repository statistics
535
+ */
536
+ async getRepoStats(repoName) {
537
+ const stats = {
538
+ totalRepos: this.metadataCache.size,
539
+ totalFiles: 0,
540
+ totalChunks: 0,
541
+ languages: {},
542
+ frameworks: {}
543
+ };
544
+ for (const metadata of this.metadataCache.values()) {
545
+ if (!repoName || metadata.repoName === repoName) {
546
+ stats.totalFiles += metadata.filesCount;
547
+ if (metadata.language) {
548
+ stats.languages[metadata.language] = (stats.languages[metadata.language] || 0) + 1;
549
+ }
550
+ if (metadata.framework) {
551
+ stats.frameworks[metadata.framework] = (stats.frameworks[metadata.framework] || 0) + 1;
552
+ }
553
+ }
554
+ }
555
+ return stats;
556
+ }
557
+ }
558
+ export {
559
+ RepoIngestionSkill
560
+ };
561
+ //# sourceMappingURL=repo-ingestion-skill.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/skills/repo-ingestion-skill.ts"],
4
+ "sourcesContent": ["/**\n * Repository Ingestion Skill for ChromaDB\n *\n * Ingests and maintains code repositories in ChromaDB for enhanced code search and context\n */\n\nimport { ChromaDBAdapter } from '../core/storage/chromadb-adapter.js';\nimport { Logger } from '../core/monitoring/logger.js';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport { execSync } from 'child_process';\nimport ignore from 'ignore';\n\nexport interface RepoIngestionOptions {\n incremental?: boolean;\n forceUpdate?: boolean;\n includeTests?: boolean;\n includeDocs?: boolean;\n maxFileSize?: number;\n chunkSize?: number;\n extensions?: string[];\n excludePatterns?: string[];\n}\n\nexport interface RepoMetadata {\n repoId: string;\n repoName: string;\n branch: string;\n lastCommit: string;\n lastIngested: number;\n filesCount: number;\n totalSize: number;\n language: string;\n framework?: string;\n}\n\nexport interface FileChunk {\n id: string;\n filePath: string;\n content: string;\n startLine: number;\n endLine: number;\n hash: string;\n language: string;\n}\n\nexport class RepoIngestionSkill {\n private logger: Logger;\n private adapter: ChromaDBAdapter;\n private metadataCache: Map<string, RepoMetadata> = new Map();\n private fileHashCache: Map<string, string> = new Map();\n\n constructor(\n private config: {\n apiKey: string;\n tenant: string;\n database: string;\n collectionName?: string;\n },\n private userId: string,\n private teamId?: string\n ) {\n this.logger = new Logger('RepoIngestionSkill');\n this.adapter = new ChromaDBAdapter(\n {\n ...config,\n collectionName: config.collectionName || 'stackmemory_repos',\n },\n userId,\n teamId\n );\n }\n\n async initialize(): Promise<void> {\n await this.adapter.initialize();\n await this.loadMetadataCache();\n }\n\n /**\n * Ingest a repository into ChromaDB\n */\n async ingestRepository(\n repoPath: string,\n repoName: string,\n options: RepoIngestionOptions = {}\n ): Promise<{\n success: boolean;\n message: string;\n stats?: {\n filesProcessed: number;\n chunksCreated: number;\n timeElapsed: number;\n totalSize: number;\n };\n }> {\n const startTime = Date.now();\n\n try {\n this.logger.info(`Starting repository ingestion for ${repoName}`);\n\n // Validate repository path\n if (!fs.existsSync(repoPath)) {\n throw new Error(`Repository path not found: ${repoPath}`);\n }\n\n // Get repository metadata\n const metadata = await this.getRepoMetadata(repoPath, repoName);\n\n // Check if incremental update is possible\n const existingMetadata = this.metadataCache.get(metadata.repoId);\n if (options.incremental && existingMetadata && !options.forceUpdate) {\n const changedFiles = await this.getChangedFiles(\n repoPath,\n existingMetadata.lastCommit,\n metadata.lastCommit\n );\n\n if (changedFiles.length === 0) {\n return {\n success: true,\n message: 'No changes detected since last ingestion',\n };\n }\n\n this.logger.info(\n `Incremental update: ${changedFiles.length} files changed`\n );\n }\n\n // Get files to process\n const files = await this.getRepoFiles(repoPath, options);\n this.logger.info(`Found ${files.length} files to process`);\n\n // Process files and create chunks\n let filesProcessed = 0;\n let chunksCreated = 0;\n let totalSize = 0;\n\n for (const file of files) {\n try {\n const chunks = await this.processFile(\n file,\n repoPath,\n repoName,\n metadata,\n options\n );\n\n for (const chunk of chunks) {\n await this.storeChunk(chunk, metadata);\n chunksCreated++;\n }\n\n filesProcessed++;\n totalSize += fs.statSync(file).size;\n\n // Log progress every 100 files\n if (filesProcessed % 100 === 0) {\n this.logger.info(\n `Processed ${filesProcessed}/${files.length} files`\n );\n }\n } catch (error: unknown) {\n this.logger.warn(`Failed to process file ${file}:`, error);\n }\n }\n\n // Update metadata\n metadata.filesCount = filesProcessed;\n metadata.totalSize = totalSize;\n metadata.lastIngested = Date.now();\n await this.saveMetadata(metadata);\n\n const timeElapsed = Date.now() - startTime;\n\n this.logger.info(\n `Repository ingestion complete: ${filesProcessed} files, ${chunksCreated} chunks in ${timeElapsed}ms`\n );\n\n return {\n success: true,\n message: `Successfully ingested ${repoName}`,\n stats: {\n filesProcessed,\n chunksCreated,\n timeElapsed,\n totalSize,\n },\n };\n } catch (error: unknown) {\n this.logger.error('Repository ingestion failed:', error);\n return {\n success: false,\n message: `Failed to ingest repository: ${error instanceof Error ? error.message : 'Unknown error'}`,\n };\n }\n }\n\n /**\n * Update an existing repository in ChromaDB\n */\n async updateRepository(\n repoPath: string,\n repoName: string,\n options: RepoIngestionOptions = {}\n ): Promise<{\n success: boolean;\n message: string;\n stats?: {\n filesUpdated: number;\n filesAdded: number;\n filesRemoved: number;\n timeElapsed: number;\n };\n }> {\n const startTime = Date.now();\n\n try {\n const metadata = await this.getRepoMetadata(repoPath, repoName);\n const existingMetadata = this.metadataCache.get(metadata.repoId);\n\n if (!existingMetadata) {\n // No existing data, perform full ingestion\n return this.ingestRepository(repoPath, repoName, options);\n }\n\n // Get changed files since last ingestion\n const changedFiles = await this.getChangedFiles(\n repoPath,\n existingMetadata.lastCommit,\n metadata.lastCommit\n );\n\n if (changedFiles.length === 0) {\n return {\n success: true,\n message: 'No changes detected',\n stats: {\n filesUpdated: 0,\n filesAdded: 0,\n filesRemoved: 0,\n timeElapsed: Date.now() - startTime,\n },\n };\n }\n\n let filesUpdated = 0;\n let filesAdded = 0;\n let filesRemoved = 0;\n\n for (const change of changedFiles) {\n const filePath = path.join(repoPath, change.path);\n\n if (change.status === 'deleted') {\n await this.removeFileChunks(change.path, metadata.repoId);\n filesRemoved++;\n } else if (change.status === 'added') {\n const chunks = await this.processFile(\n filePath,\n repoPath,\n repoName,\n metadata,\n options\n );\n for (const chunk of chunks) {\n await this.storeChunk(chunk, metadata);\n }\n filesAdded++;\n } else if (change.status === 'modified') {\n // Remove old chunks and add new ones\n await this.removeFileChunks(change.path, metadata.repoId);\n const chunks = await this.processFile(\n filePath,\n repoPath,\n repoName,\n metadata,\n options\n );\n for (const chunk of chunks) {\n await this.storeChunk(chunk, metadata);\n }\n filesUpdated++;\n }\n }\n\n // Update metadata\n metadata.lastIngested = Date.now();\n await this.saveMetadata(metadata);\n\n const timeElapsed = Date.now() - startTime;\n\n return {\n success: true,\n message: `Successfully updated ${repoName}`,\n stats: {\n filesUpdated,\n filesAdded,\n filesRemoved,\n timeElapsed,\n },\n };\n } catch (error: unknown) {\n this.logger.error('Repository update failed:', error);\n return {\n success: false,\n message: `Failed to update repository: ${error instanceof Error ? error.message : 'Unknown error'}`,\n };\n }\n }\n\n /**\n * Search code in ingested repositories\n */\n async searchCode(\n query: string,\n options?: {\n repoName?: string;\n language?: string;\n limit?: number;\n includeContext?: boolean;\n }\n ): Promise<\n Array<{\n filePath: string;\n content: string;\n score: number;\n startLine: number;\n endLine: number;\n repoName: string;\n }>\n > {\n try {\n const filters: any = {\n type: ['code_chunk'],\n };\n\n if (options?.repoName) {\n filters.repo_name = options.repoName;\n }\n\n if (options?.language) {\n filters.language = options.language;\n }\n\n const results = await this.adapter.queryContexts(\n query,\n options?.limit || 20,\n filters\n );\n\n return results.map((result) => ({\n filePath: result.metadata.file_path,\n content: result.content,\n score: 1 - result.distance, // Convert distance to similarity score\n startLine: result.metadata.start_line,\n endLine: result.metadata.end_line,\n repoName: result.metadata.repo_name,\n }));\n } catch (error: unknown) {\n this.logger.error('Code search failed:', error);\n return [];\n }\n }\n\n /**\n * Get repository metadata\n */\n private async getRepoMetadata(\n repoPath: string,\n repoName: string\n ): Promise<RepoMetadata> {\n const branch = this.getCurrentBranch(repoPath);\n const lastCommit = this.getLastCommit(repoPath);\n const repoId = `${repoName}_${branch}`.replace(/[^a-zA-Z0-9_-]/g, '_');\n\n // Detect primary language and framework\n const { language, framework } =\n await this.detectLanguageAndFramework(repoPath);\n\n return {\n repoId,\n repoName,\n branch,\n lastCommit,\n lastIngested: Date.now(),\n filesCount: 0,\n totalSize: 0,\n language,\n framework,\n };\n }\n\n /**\n * Get current git branch\n */\n private getCurrentBranch(repoPath: string): string {\n try {\n return execSync('git rev-parse --abbrev-ref HEAD', {\n cwd: repoPath,\n encoding: 'utf8',\n }).trim();\n } catch {\n return 'main';\n }\n }\n\n /**\n * Get last commit hash\n */\n private getLastCommit(repoPath: string): string {\n try {\n return execSync('git rev-parse HEAD', {\n cwd: repoPath,\n encoding: 'utf8',\n }).trim();\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Get changed files between commits\n */\n private async getChangedFiles(\n repoPath: string,\n fromCommit: string,\n toCommit: string\n ): Promise<Array<{ path: string; status: string }>> {\n try {\n const diff = execSync(\n `git diff --name-status ${fromCommit}..${toCommit}`,\n {\n cwd: repoPath,\n encoding: 'utf8',\n }\n );\n\n return diff\n .split('\\n')\n .filter((line) => line.trim())\n .map((line) => {\n const [status, ...pathParts] = line.split('\\t');\n return {\n path: pathParts.join('\\t'),\n status:\n status === 'A'\n ? 'added'\n : status === 'D'\n ? 'deleted'\n : 'modified',\n };\n });\n } catch {\n return [];\n }\n }\n\n /**\n * Get repository files to process\n */\n private async getRepoFiles(\n repoPath: string,\n options: RepoIngestionOptions\n ): Promise<string[]> {\n const files: string[] = [];\n const ig = ignore();\n\n // Load .gitignore if it exists\n const gitignorePath = path.join(repoPath, '.gitignore');\n if (fs.existsSync(gitignorePath)) {\n ig.add(fs.readFileSync(gitignorePath, 'utf8'));\n }\n\n // Add default exclude patterns\n const defaultExcludes = [\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n 'coverage',\n '.env',\n '*.log',\n ...(options.excludePatterns || []),\n ];\n ig.add(defaultExcludes);\n\n // Default extensions to include\n const extensions = options.extensions || [\n '.ts',\n '.tsx',\n '.js',\n '.jsx',\n '.py',\n '.java',\n '.go',\n '.rs',\n '.c',\n '.cpp',\n '.h',\n '.hpp',\n '.cs',\n '.rb',\n '.php',\n '.swift',\n '.kt',\n '.scala',\n '.r',\n '.m',\n '.sql',\n '.yaml',\n '.yml',\n '.json',\n ];\n\n // Add documentation if requested\n if (options.includeDocs) {\n extensions.push('.md', '.rst', '.txt');\n }\n\n const maxFileSize = options.maxFileSize || 1024 * 1024; // 1MB default\n\n const walkDir = (dir: string, baseDir: string = repoPath) => {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativePath = path.relative(baseDir, fullPath);\n\n if (ig.ignores(relativePath)) {\n continue;\n }\n\n if (entry.isDirectory()) {\n walkDir(fullPath, baseDir);\n } else if (entry.isFile()) {\n const ext = path.extname(entry.name);\n\n // Check if file should be included\n if (!extensions.includes(ext)) {\n continue;\n }\n\n // Check if it's a test file\n if (\n !options.includeTests &&\n (entry.name.includes('.test.') ||\n entry.name.includes('.spec.') ||\n relativePath.includes('__tests__') ||\n relativePath.includes('test/') ||\n relativePath.includes('tests/'))\n ) {\n continue;\n }\n\n // Check file size\n const stats = fs.statSync(fullPath);\n if (stats.size > maxFileSize) {\n this.logger.debug(`Skipping large file: ${relativePath}`);\n continue;\n }\n\n files.push(fullPath);\n }\n }\n };\n\n walkDir(repoPath);\n return files;\n }\n\n /**\n * Process a file into chunks\n */\n private async processFile(\n filePath: string,\n repoPath: string,\n repoName: string,\n metadata: RepoMetadata,\n options: RepoIngestionOptions\n ): Promise<FileChunk[]> {\n const relativePath = path.relative(repoPath, filePath);\n const content = fs.readFileSync(filePath, 'utf8');\n const lines = content.split('\\n');\n const language = this.detectFileLanguage(filePath);\n\n const chunkSize = options.chunkSize || 100; // 100 lines per chunk\n const chunks: FileChunk[] = [];\n\n // Calculate file hash for caching\n const fileHash = crypto.createHash('md5').update(content).digest('hex');\n\n // Check if file has changed\n const cachedHash = this.fileHashCache.get(relativePath);\n if (cachedHash === fileHash && !options.forceUpdate) {\n return []; // File hasn't changed\n }\n\n this.fileHashCache.set(relativePath, fileHash);\n\n // Split into chunks\n for (let i = 0; i < lines.length; i += chunkSize) {\n const chunkLines = lines.slice(i, Math.min(i + chunkSize, lines.length));\n const chunkContent = chunkLines.join('\\n');\n\n if (chunkContent.trim().length === 0) {\n continue; // Skip empty chunks\n }\n\n const chunkId = `${metadata.repoId}_${relativePath}_${i}`;\n const chunkHash = crypto\n .createHash('md5')\n .update(chunkContent)\n .digest('hex');\n\n chunks.push({\n id: chunkId,\n filePath: relativePath,\n content: chunkContent,\n startLine: i + 1,\n endLine: Math.min(i + chunkSize, lines.length),\n hash: chunkHash,\n language,\n });\n }\n\n return chunks;\n }\n\n /**\n * Store a chunk in ChromaDB\n */\n private async storeChunk(\n chunk: FileChunk,\n metadata: RepoMetadata\n ): Promise<void> {\n const documentContent = `File: ${chunk.filePath} (Lines ${chunk.startLine}-${chunk.endLine})\nLanguage: ${chunk.language}\nRepository: ${metadata.repoName}/${metadata.branch}\n\n${chunk.content}`;\n\n await this.adapter.storeContext('observation', documentContent, {\n type: 'code_chunk',\n repo_id: metadata.repoId,\n repo_name: metadata.repoName,\n branch: metadata.branch,\n file_path: chunk.filePath,\n start_line: chunk.startLine,\n end_line: chunk.endLine,\n language: chunk.language,\n framework: metadata.framework,\n chunk_hash: chunk.hash,\n last_commit: metadata.lastCommit,\n });\n }\n\n /**\n * Remove file chunks from ChromaDB\n */\n private async removeFileChunks(\n filePath: string,\n repoId: string\n ): Promise<void> {\n // This would need to be implemented in ChromaDBAdapter\n // For now, we'll log it\n this.logger.debug(\n `Would remove chunks for file: ${filePath} from repo: ${repoId}`\n );\n }\n\n /**\n * Detect file language\n */\n private detectFileLanguage(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase();\n const languageMap: Record<string, string> = {\n '.ts': 'typescript',\n '.tsx': 'typescript',\n '.js': 'javascript',\n '.jsx': 'javascript',\n '.py': 'python',\n '.java': 'java',\n '.go': 'go',\n '.rs': 'rust',\n '.c': 'c',\n '.cpp': 'cpp',\n '.cs': 'csharp',\n '.rb': 'ruby',\n '.php': 'php',\n '.swift': 'swift',\n '.kt': 'kotlin',\n '.scala': 'scala',\n '.r': 'r',\n '.sql': 'sql',\n '.yaml': 'yaml',\n '.yml': 'yaml',\n '.json': 'json',\n '.md': 'markdown',\n };\n\n return languageMap[ext] || 'unknown';\n }\n\n /**\n * Detect language and framework\n */\n private async detectLanguageAndFramework(repoPath: string): Promise<{\n language: string;\n framework?: string;\n }> {\n // Check for package.json (JavaScript/TypeScript)\n const packageJsonPath = path.join(repoPath, 'package.json');\n if (fs.existsSync(packageJsonPath)) {\n try {\n const packageJson = JSON.parse(\n fs.readFileSync(packageJsonPath, 'utf8')\n );\n const deps = {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n };\n\n let framework: string | undefined;\n if (deps.react) framework = 'react';\n else if (deps.vue) framework = 'vue';\n else if (deps.angular) framework = 'angular';\n else if (deps.express) framework = 'express';\n else if (deps.next) framework = 'nextjs';\n else if (deps.svelte) framework = 'svelte';\n\n return {\n language: deps.typescript ? 'typescript' : 'javascript',\n framework,\n };\n } catch {}\n }\n\n // Check for requirements.txt or setup.py (Python)\n if (\n fs.existsSync(path.join(repoPath, 'requirements.txt')) ||\n fs.existsSync(path.join(repoPath, 'setup.py'))\n ) {\n return { language: 'python' };\n }\n\n // Check for go.mod (Go)\n if (fs.existsSync(path.join(repoPath, 'go.mod'))) {\n return { language: 'go' };\n }\n\n // Check for Cargo.toml (Rust)\n if (fs.existsSync(path.join(repoPath, 'Cargo.toml'))) {\n return { language: 'rust' };\n }\n\n // Check for pom.xml or build.gradle (Java)\n if (\n fs.existsSync(path.join(repoPath, 'pom.xml')) ||\n fs.existsSync(path.join(repoPath, 'build.gradle'))\n ) {\n return { language: 'java' };\n }\n\n // Default to unknown\n return { language: 'unknown' };\n }\n\n /**\n * Load metadata cache\n */\n private async loadMetadataCache(): Promise<void> {\n // In a real implementation, this would load from a persistent store\n // For now, we'll just initialize an empty cache\n this.metadataCache.clear();\n }\n\n /**\n * Save metadata\n */\n private async saveMetadata(metadata: RepoMetadata): Promise<void> {\n this.metadataCache.set(metadata.repoId, metadata);\n // In a real implementation, this would persist to a store\n }\n\n /**\n * Get repository statistics\n */\n async getRepoStats(repoName?: string): Promise<{\n totalRepos: number;\n totalFiles: number;\n totalChunks: number;\n languages: Record<string, number>;\n frameworks: Record<string, number>;\n }> {\n // This would query ChromaDB for statistics\n const stats = {\n totalRepos: this.metadataCache.size,\n totalFiles: 0,\n totalChunks: 0,\n languages: {} as Record<string, number>,\n frameworks: {} as Record<string, number>,\n };\n\n for (const metadata of this.metadataCache.values()) {\n if (!repoName || metadata.repoName === repoName) {\n stats.totalFiles += metadata.filesCount;\n\n if (metadata.language) {\n stats.languages[metadata.language] =\n (stats.languages[metadata.language] || 0) + 1;\n }\n\n if (metadata.framework) {\n stats.frameworks[metadata.framework] =\n (stats.frameworks[metadata.framework] || 0) + 1;\n }\n }\n }\n\n return stats;\n }\n}\n"],
5
+ "mappings": "AAMA,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,YAAY,YAAY;AACxB,SAAS,gBAAgB;AACzB,OAAO,YAAY;AAmCZ,MAAM,mBAAmB;AAAA,EAM9B,YACU,QAMA,QACA,QACR;AARQ;AAMA;AACA;AAER,SAAK,SAAS,IAAI,OAAO,oBAAoB;AAC7C,SAAK,UAAU,IAAI;AAAA,MACjB;AAAA,QACE,GAAG;AAAA,QACH,gBAAgB,OAAO,kBAAkB;AAAA,MAC3C;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAxBQ;AAAA,EACA;AAAA,EACA,gBAA2C,oBAAI,IAAI;AAAA,EACnD,gBAAqC,oBAAI,IAAI;AAAA,EAuBrD,MAAM,aAA4B;AAChC,UAAM,KAAK,QAAQ,WAAW;AAC9B,UAAM,KAAK,kBAAkB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBACJ,UACA,UACA,UAAgC,CAAC,GAUhC;AACD,UAAM,YAAY,KAAK,IAAI;AAE3B,QAAI;AACF,WAAK,OAAO,KAAK,qCAAqC,QAAQ,EAAE;AAGhE,UAAI,CAAC,GAAG,WAAW,QAAQ,GAAG;AAC5B,cAAM,IAAI,MAAM,8BAA8B,QAAQ,EAAE;AAAA,MAC1D;AAGA,YAAM,WAAW,MAAM,KAAK,gBAAgB,UAAU,QAAQ;AAG9D,YAAM,mBAAmB,KAAK,cAAc,IAAI,SAAS,MAAM;AAC/D,UAAI,QAAQ,eAAe,oBAAoB,CAAC,QAAQ,aAAa;AACnE,cAAM,eAAe,MAAM,KAAK;AAAA,UAC9B;AAAA,UACA,iBAAiB;AAAA,UACjB,SAAS;AAAA,QACX;AAEA,YAAI,aAAa,WAAW,GAAG;AAC7B,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,QACF;AAEA,aAAK,OAAO;AAAA,UACV,uBAAuB,aAAa,MAAM;AAAA,QAC5C;AAAA,MACF;AAGA,YAAM,QAAQ,MAAM,KAAK,aAAa,UAAU,OAAO;AACvD,WAAK,OAAO,KAAK,SAAS,MAAM,MAAM,mBAAmB;AAGzD,UAAI,iBAAiB;AACrB,UAAI,gBAAgB;AACpB,UAAI,YAAY;AAEhB,iBAAW,QAAQ,OAAO;AACxB,YAAI;AACF,gBAAM,SAAS,MAAM,KAAK;AAAA,YACxB;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAEA,qBAAW,SAAS,QAAQ;AAC1B,kBAAM,KAAK,WAAW,OAAO,QAAQ;AACrC;AAAA,UACF;AAEA;AACA,uBAAa,GAAG,SAAS,IAAI,EAAE;AAG/B,cAAI,iBAAiB,QAAQ,GAAG;AAC9B,iBAAK,OAAO;AAAA,cACV,aAAa,cAAc,IAAI,MAAM,MAAM;AAAA,YAC7C;AAAA,UACF;AAAA,QACF,SAAS,OAAgB;AACvB,eAAK,OAAO,KAAK,0BAA0B,IAAI,KAAK,KAAK;AAAA,QAC3D;AAAA,MACF;AAGA,eAAS,aAAa;AACtB,eAAS,YAAY;AACrB,eAAS,eAAe,KAAK,IAAI;AACjC,YAAM,KAAK,aAAa,QAAQ;AAEhC,YAAM,cAAc,KAAK,IAAI,IAAI;AAEjC,WAAK,OAAO;AAAA,QACV,kCAAkC,cAAc,WAAW,aAAa,cAAc,WAAW;AAAA,MACnG;AAEA,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,yBAAyB,QAAQ;AAAA,QAC1C,OAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAgB;AACvB,WAAK,OAAO,MAAM,gCAAgC,KAAK;AACvD,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,gCAAgC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MACnG;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBACJ,UACA,UACA,UAAgC,CAAC,GAUhC;AACD,UAAM,YAAY,KAAK,IAAI;AAE3B,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,gBAAgB,UAAU,QAAQ;AAC9D,YAAM,mBAAmB,KAAK,cAAc,IAAI,SAAS,MAAM;AAE/D,UAAI,CAAC,kBAAkB;AAErB,eAAO,KAAK,iBAAiB,UAAU,UAAU,OAAO;AAAA,MAC1D;AAGA,YAAM,eAAe,MAAM,KAAK;AAAA,QAC9B;AAAA,QACA,iBAAiB;AAAA,QACjB,SAAS;AAAA,MACX;AAEA,UAAI,aAAa,WAAW,GAAG;AAC7B,eAAO;AAAA,UACL,SAAS;AAAA,UACT,SAAS;AAAA,UACT,OAAO;AAAA,YACL,cAAc;AAAA,YACd,YAAY;AAAA,YACZ,cAAc;AAAA,YACd,aAAa,KAAK,IAAI,IAAI;AAAA,UAC5B;AAAA,QACF;AAAA,MACF;AAEA,UAAI,eAAe;AACnB,UAAI,aAAa;AACjB,UAAI,eAAe;AAEnB,iBAAW,UAAU,cAAc;AACjC,cAAM,WAAW,KAAK,KAAK,UAAU,OAAO,IAAI;AAEhD,YAAI,OAAO,WAAW,WAAW;AAC/B,gBAAM,KAAK,iBAAiB,OAAO,MAAM,SAAS,MAAM;AACxD;AAAA,QACF,WAAW,OAAO,WAAW,SAAS;AACpC,gBAAM,SAAS,MAAM,KAAK;AAAA,YACxB;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AACA,qBAAW,SAAS,QAAQ;AAC1B,kBAAM,KAAK,WAAW,OAAO,QAAQ;AAAA,UACvC;AACA;AAAA,QACF,WAAW,OAAO,WAAW,YAAY;AAEvC,gBAAM,KAAK,iBAAiB,OAAO,MAAM,SAAS,MAAM;AACxD,gBAAM,SAAS,MAAM,KAAK;AAAA,YACxB;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AACA,qBAAW,SAAS,QAAQ;AAC1B,kBAAM,KAAK,WAAW,OAAO,QAAQ;AAAA,UACvC;AACA;AAAA,QACF;AAAA,MACF;AAGA,eAAS,eAAe,KAAK,IAAI;AACjC,YAAM,KAAK,aAAa,QAAQ;AAEhC,YAAM,cAAc,KAAK,IAAI,IAAI;AAEjC,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,wBAAwB,QAAQ;AAAA,QACzC,OAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAgB;AACvB,WAAK,OAAO,MAAM,6BAA6B,KAAK;AACpD,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,gCAAgC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MACnG;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WACJ,OACA,SAeA;AACA,QAAI;AACF,YAAM,UAAe;AAAA,QACnB,MAAM,CAAC,YAAY;AAAA,MACrB;AAEA,UAAI,SAAS,UAAU;AACrB,gBAAQ,YAAY,QAAQ;AAAA,MAC9B;AAEA,UAAI,SAAS,UAAU;AACrB,gBAAQ,WAAW,QAAQ;AAAA,MAC7B;AAEA,YAAM,UAAU,MAAM,KAAK,QAAQ;AAAA,QACjC;AAAA,QACA,SAAS,SAAS;AAAA,QAClB;AAAA,MACF;AAEA,aAAO,QAAQ,IAAI,CAAC,YAAY;AAAA,QAC9B,UAAU,OAAO,SAAS;AAAA,QAC1B,SAAS,OAAO;AAAA,QAChB,OAAO,IAAI,OAAO;AAAA;AAAA,QAClB,WAAW,OAAO,SAAS;AAAA,QAC3B,SAAS,OAAO,SAAS;AAAA,QACzB,UAAU,OAAO,SAAS;AAAA,MAC5B,EAAE;AAAA,IACJ,SAAS,OAAgB;AACvB,WAAK,OAAO,MAAM,uBAAuB,KAAK;AAC9C,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBACZ,UACA,UACuB;AACvB,UAAM,SAAS,KAAK,iBAAiB,QAAQ;AAC7C,UAAM,aAAa,KAAK,cAAc,QAAQ;AAC9C,UAAM,SAAS,GAAG,QAAQ,IAAI,MAAM,GAAG,QAAQ,mBAAmB,GAAG;AAGrE,UAAM,EAAE,UAAU,UAAU,IAC1B,MAAM,KAAK,2BAA2B,QAAQ;AAEhD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,KAAK,IAAI;AAAA,MACvB,YAAY;AAAA,MACZ,WAAW;AAAA,MACX;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,UAA0B;AACjD,QAAI;AACF,aAAO,SAAS,mCAAmC;AAAA,QACjD,KAAK;AAAA,QACL,UAAU;AAAA,MACZ,CAAC,EAAE,KAAK;AAAA,IACV,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,UAA0B;AAC9C,QAAI;AACF,aAAO,SAAS,sBAAsB;AAAA,QACpC,KAAK;AAAA,QACL,UAAU;AAAA,MACZ,CAAC,EAAE,KAAK;AAAA,IACV,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,gBACZ,UACA,YACA,UACkD;AAClD,QAAI;AACF,YAAM,OAAO;AAAA,QACX,0BAA0B,UAAU,KAAK,QAAQ;AAAA,QACjD;AAAA,UACE,KAAK;AAAA,UACL,UAAU;AAAA,QACZ;AAAA,MACF;AAEA,aAAO,KACJ,MAAM,IAAI,EACV,OAAO,CAAC,SAAS,KAAK,KAAK,CAAC,EAC5B,IAAI,CAAC,SAAS;AACb,cAAM,CAAC,QAAQ,GAAG,SAAS,IAAI,KAAK,MAAM,GAAI;AAC9C,eAAO;AAAA,UACL,MAAM,UAAU,KAAK,GAAI;AAAA,UACzB,QACE,WAAW,MACP,UACA,WAAW,MACT,YACA;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACL,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,aACZ,UACA,SACmB;AACnB,UAAM,QAAkB,CAAC;AACzB,UAAM,KAAK,OAAO;AAGlB,UAAM,gBAAgB,KAAK,KAAK,UAAU,YAAY;AACtD,QAAI,GAAG,WAAW,aAAa,GAAG;AAChC,SAAG,IAAI,GAAG,aAAa,eAAe,MAAM,CAAC;AAAA,IAC/C;AAGA,UAAM,kBAAkB;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,QAAQ,mBAAmB,CAAC;AAAA,IAClC;AACA,OAAG,IAAI,eAAe;AAGtB,UAAM,aAAa,QAAQ,cAAc;AAAA,MACvC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAGA,QAAI,QAAQ,aAAa;AACvB,iBAAW,KAAK,OAAO,QAAQ,MAAM;AAAA,IACvC;AAEA,UAAM,cAAc,QAAQ,eAAe,OAAO;AAElD,UAAM,UAAU,CAAC,KAAa,UAAkB,aAAa;AAC3D,YAAM,UAAU,GAAG,YAAY,KAAK,EAAE,eAAe,KAAK,CAAC;AAE3D,iBAAW,SAAS,SAAS;AAC3B,cAAM,WAAW,KAAK,KAAK,KAAK,MAAM,IAAI;AAC1C,cAAM,eAAe,KAAK,SAAS,SAAS,QAAQ;AAEpD,YAAI,GAAG,QAAQ,YAAY,GAAG;AAC5B;AAAA,QACF;AAEA,YAAI,MAAM,YAAY,GAAG;AACvB,kBAAQ,UAAU,OAAO;AAAA,QAC3B,WAAW,MAAM,OAAO,GAAG;AACzB,gBAAM,MAAM,KAAK,QAAQ,MAAM,IAAI;AAGnC,cAAI,CAAC,WAAW,SAAS,GAAG,GAAG;AAC7B;AAAA,UACF;AAGA,cACE,CAAC,QAAQ,iBACR,MAAM,KAAK,SAAS,QAAQ,KAC3B,MAAM,KAAK,SAAS,QAAQ,KAC5B,aAAa,SAAS,WAAW,KACjC,aAAa,SAAS,OAAO,KAC7B,aAAa,SAAS,QAAQ,IAChC;AACA;AAAA,UACF;AAGA,gBAAM,QAAQ,GAAG,SAAS,QAAQ;AAClC,cAAI,MAAM,OAAO,aAAa;AAC5B,iBAAK,OAAO,MAAM,wBAAwB,YAAY,EAAE;AACxD;AAAA,UACF;AAEA,gBAAM,KAAK,QAAQ;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,QAAQ;AAChB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,YACZ,UACA,UACA,UACA,UACA,SACsB;AACtB,UAAM,eAAe,KAAK,SAAS,UAAU,QAAQ;AACrD,UAAM,UAAU,GAAG,aAAa,UAAU,MAAM;AAChD,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,UAAM,WAAW,KAAK,mBAAmB,QAAQ;AAEjD,UAAM,YAAY,QAAQ,aAAa;AACvC,UAAM,SAAsB,CAAC;AAG7B,UAAM,WAAW,OAAO,WAAW,KAAK,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAGtE,UAAM,aAAa,KAAK,cAAc,IAAI,YAAY;AACtD,QAAI,eAAe,YAAY,CAAC,QAAQ,aAAa;AACnD,aAAO,CAAC;AAAA,IACV;AAEA,SAAK,cAAc,IAAI,cAAc,QAAQ;AAG7C,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,WAAW;AAChD,YAAM,aAAa,MAAM,MAAM,GAAG,KAAK,IAAI,IAAI,WAAW,MAAM,MAAM,CAAC;AACvE,YAAM,eAAe,WAAW,KAAK,IAAI;AAEzC,UAAI,aAAa,KAAK,EAAE,WAAW,GAAG;AACpC;AAAA,MACF;AAEA,YAAM,UAAU,GAAG,SAAS,MAAM,IAAI,YAAY,IAAI,CAAC;AACvD,YAAM,YAAY,OACf,WAAW,KAAK,EAChB,OAAO,YAAY,EACnB,OAAO,KAAK;AAEf,aAAO,KAAK;AAAA,QACV,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,WAAW,IAAI;AAAA,QACf,SAAS,KAAK,IAAI,IAAI,WAAW,MAAM,MAAM;AAAA,QAC7C,MAAM;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,WACZ,OACA,UACe;AACf,UAAM,kBAAkB,SAAS,MAAM,QAAQ,WAAW,MAAM,SAAS,IAAI,MAAM,OAAO;AAAA,YAClF,MAAM,QAAQ;AAAA,cACZ,SAAS,QAAQ,IAAI,SAAS,MAAM;AAAA;AAAA,EAEhD,MAAM,OAAO;AAEX,UAAM,KAAK,QAAQ,aAAa,eAAe,iBAAiB;AAAA,MAC9D,MAAM;AAAA,MACN,SAAS,SAAS;AAAA,MAClB,WAAW,SAAS;AAAA,MACpB,QAAQ,SAAS;AAAA,MACjB,WAAW,MAAM;AAAA,MACjB,YAAY,MAAM;AAAA,MAClB,UAAU,MAAM;AAAA,MAChB,UAAU,MAAM;AAAA,MAChB,WAAW,SAAS;AAAA,MACpB,YAAY,MAAM;AAAA,MAClB,aAAa,SAAS;AAAA,IACxB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBACZ,UACA,QACe;AAGf,SAAK,OAAO;AAAA,MACV,iCAAiC,QAAQ,eAAe,MAAM;AAAA,IAChE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,UAA0B;AACnD,UAAM,MAAM,KAAK,QAAQ,QAAQ,EAAE,YAAY;AAC/C,UAAM,cAAsC;AAAA,MAC1C,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,SAAS;AAAA,MACT,OAAO;AAAA,MACP,OAAO;AAAA,MACP,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,OAAO;AAAA,MACP,UAAU;AAAA,MACV,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAEA,WAAO,YAAY,GAAG,KAAK;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,2BAA2B,UAGtC;AAED,UAAM,kBAAkB,KAAK,KAAK,UAAU,cAAc;AAC1D,QAAI,GAAG,WAAW,eAAe,GAAG;AAClC,UAAI;AACF,cAAM,cAAc,KAAK;AAAA,UACvB,GAAG,aAAa,iBAAiB,MAAM;AAAA,QACzC;AACA,cAAM,OAAO;AAAA,UACX,GAAG,YAAY;AAAA,UACf,GAAG,YAAY;AAAA,QACjB;AAEA,YAAI;AACJ,YAAI,KAAK,MAAO,aAAY;AAAA,iBACnB,KAAK,IAAK,aAAY;AAAA,iBACtB,KAAK,QAAS,aAAY;AAAA,iBAC1B,KAAK,QAAS,aAAY;AAAA,iBAC1B,KAAK,KAAM,aAAY;AAAA,iBACvB,KAAK,OAAQ,aAAY;AAElC,eAAO;AAAA,UACL,UAAU,KAAK,aAAa,eAAe;AAAA,UAC3C;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAAC;AAAA,IACX;AAGA,QACE,GAAG,WAAW,KAAK,KAAK,UAAU,kBAAkB,CAAC,KACrD,GAAG,WAAW,KAAK,KAAK,UAAU,UAAU,CAAC,GAC7C;AACA,aAAO,EAAE,UAAU,SAAS;AAAA,IAC9B;AAGA,QAAI,GAAG,WAAW,KAAK,KAAK,UAAU,QAAQ,CAAC,GAAG;AAChD,aAAO,EAAE,UAAU,KAAK;AAAA,IAC1B;AAGA,QAAI,GAAG,WAAW,KAAK,KAAK,UAAU,YAAY,CAAC,GAAG;AACpD,aAAO,EAAE,UAAU,OAAO;AAAA,IAC5B;AAGA,QACE,GAAG,WAAW,KAAK,KAAK,UAAU,SAAS,CAAC,KAC5C,GAAG,WAAW,KAAK,KAAK,UAAU,cAAc,CAAC,GACjD;AACA,aAAO,EAAE,UAAU,OAAO;AAAA,IAC5B;AAGA,WAAO,EAAE,UAAU,UAAU;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,oBAAmC;AAG/C,SAAK,cAAc,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,aAAa,UAAuC;AAChE,SAAK,cAAc,IAAI,SAAS,QAAQ,QAAQ;AAAA,EAElD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,UAMhB;AAED,UAAM,QAAQ;AAAA,MACZ,YAAY,KAAK,cAAc;AAAA,MAC/B,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,WAAW,CAAC;AAAA,MACZ,YAAY,CAAC;AAAA,IACf;AAEA,eAAW,YAAY,KAAK,cAAc,OAAO,GAAG;AAClD,UAAI,CAAC,YAAY,SAAS,aAAa,UAAU;AAC/C,cAAM,cAAc,SAAS;AAE7B,YAAI,SAAS,UAAU;AACrB,gBAAM,UAAU,SAAS,QAAQ,KAC9B,MAAM,UAAU,SAAS,QAAQ,KAAK,KAAK;AAAA,QAChD;AAEA,YAAI,SAAS,WAAW;AACtB,gBAAM,WAAW,SAAS,SAAS,KAChC,MAAM,WAAW,SAAS,SAAS,KAAK,KAAK;AAAA,QAClD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;",
6
+ "names": []
7
+ }
@@ -1,10 +1,21 @@
1
1
  import chalk from "chalk";
2
+ function getEnv(key, defaultValue) {
3
+ const value = process.env[key];
4
+ if (value === void 0) {
5
+ if (defaultValue !== void 0) return defaultValue;
6
+ throw new Error(`Environment variable ${key} is required`);
7
+ }
8
+ return value;
9
+ }
10
+ function getOptionalEnv(key) {
11
+ return process.env[key];
12
+ }
2
13
  class Logger {
3
14
  name;
4
15
  logLevel;
5
16
  constructor(name, logLevel = "info") {
6
17
  this.name = name;
7
- this.logLevel = process.env.LOG_LEVEL || logLevel;
18
+ this.logLevel = process.env["LOG_LEVEL"] || logLevel;
8
19
  }
9
20
  shouldLog(level) {
10
21
  const levels = ["debug", "info", "warn", "error"];