@sashabogi/argus-mcp 1.2.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,3 +1,6 @@
1
+ import * as http from 'http';
2
+ import * as express_serve_static_core from 'express-serve-static-core';
3
+
1
4
  /**
2
5
  * Argus Onboarding Module
3
6
  *
@@ -238,4 +241,70 @@ declare function getProviderDisplayName(type: ProviderType): string;
238
241
  */
239
242
  declare function listProviderTypes(): ProviderType[];
240
243
 
241
- export { type AIProvider, type ProviderConfig as AIProviderConfig, type AnalysisOptions, type AnalysisResult, type ArgusConfig, type CompletionOptions, type CompletionResult, type ProviderConfig as CoreProviderConfig, type Message, PROVIDER_DEFAULTS, type ProviderType, type SnapshotOptions, type SnapshotResult, analyze, createAnthropicProvider, createDeepSeekProvider, createOllamaProvider, createOpenAIProvider, createProvider, createProviderByType, createSnapshot, createZAIProvider, ensureConfigDir, getConfigPath, getProviderConfig, getProviderDisplayName, getSnapshotStats, listProviderTypes, loadConfig, saveConfig, searchDocument, validateConfig };
244
+ interface CachedSnapshot {
245
+ path: string;
246
+ content: string;
247
+ lines: string[];
248
+ fileIndex: Map<string, {
249
+ start: number;
250
+ end: number;
251
+ }>;
252
+ loadedAt: Date;
253
+ fileCount: number;
254
+ mtime: number;
255
+ }
256
+ declare class SnapshotCache {
257
+ private cache;
258
+ private accessOrder;
259
+ private maxSize;
260
+ constructor(options: {
261
+ maxSize: number;
262
+ });
263
+ get size(): number;
264
+ load(path: string): Promise<CachedSnapshot>;
265
+ invalidate(path: string): void;
266
+ search(path: string, pattern: string, options?: {
267
+ caseInsensitive?: boolean;
268
+ maxResults?: number;
269
+ offset?: number;
270
+ }): {
271
+ matches: Array<{
272
+ lineNum: number;
273
+ line: string;
274
+ match: string;
275
+ }>;
276
+ count: number;
277
+ };
278
+ getContext(path: string, file: string, line: number, before?: number, after?: number): {
279
+ content: string;
280
+ range: {
281
+ start: number;
282
+ end: number;
283
+ };
284
+ };
285
+ private buildFileIndex;
286
+ private touchAccess;
287
+ }
288
+
289
+ declare class ProjectWatcher {
290
+ private projectPath;
291
+ private snapshotPath;
292
+ private onUpdate;
293
+ private debounceMs;
294
+ private watcher;
295
+ private debounceTimer;
296
+ private changedFiles;
297
+ constructor(projectPath: string, snapshotPath: string, onUpdate: (changedFiles: string[]) => void, debounceMs?: number);
298
+ private start;
299
+ private scheduleUpdate;
300
+ close(): void;
301
+ }
302
+
303
+ declare function startWorker(): {
304
+ app: express_serve_static_core.Express;
305
+ server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
306
+ cache: SnapshotCache;
307
+ watchers: Map<string, ProjectWatcher>;
308
+ };
309
+
310
+ export { type AIProvider, type ProviderConfig as AIProviderConfig, type AnalysisOptions, type AnalysisResult, type ArgusConfig, type CompletionOptions, type CompletionResult, type ProviderConfig as CoreProviderConfig, type Message, PROVIDER_DEFAULTS, ProjectWatcher, type ProviderType, SnapshotCache, type SnapshotOptions, type SnapshotResult, analyze, createAnthropicProvider, createDeepSeekProvider, createOllamaProvider, createOpenAIProvider, createProvider, createProviderByType, createSnapshot, createZAIProvider, ensureConfigDir, getConfigPath, getProviderConfig, getProviderDisplayName, getSnapshotStats, listProviderTypes, loadConfig, saveConfig, searchDocument, startWorker, validateConfig };
package/dist/index.mjs CHANGED
@@ -1023,8 +1023,259 @@ function getProviderDisplayName(type) {
1023
1023
  function listProviderTypes() {
1024
1024
  return ["zai", "anthropic", "openai", "deepseek", "ollama"];
1025
1025
  }
1026
+
1027
+ // src/worker/server.ts
1028
+ import express from "express";
1029
+
1030
+ // src/worker/cache.ts
1031
+ import { readFileSync as readFileSync4, statSync as statSync2 } from "fs";
1032
+ var SnapshotCache = class {
1033
+ cache = /* @__PURE__ */ new Map();
1034
+ accessOrder = [];
1035
+ maxSize;
1036
+ constructor(options) {
1037
+ this.maxSize = options.maxSize;
1038
+ }
1039
+ get size() {
1040
+ return this.cache.size;
1041
+ }
1042
+ async load(path) {
1043
+ const stats = statSync2(path);
1044
+ const cached = this.cache.get(path);
1045
+ if (cached && cached.mtime === stats.mtimeMs) {
1046
+ this.touchAccess(path);
1047
+ return cached;
1048
+ }
1049
+ const content = readFileSync4(path, "utf-8");
1050
+ const lines = content.split("\n");
1051
+ const fileIndex = this.buildFileIndex(lines);
1052
+ const fileCount = (content.match(/^FILE: /gm) || []).length;
1053
+ const snapshot = {
1054
+ path,
1055
+ content,
1056
+ lines,
1057
+ fileIndex,
1058
+ loadedAt: /* @__PURE__ */ new Date(),
1059
+ fileCount,
1060
+ mtime: stats.mtimeMs
1061
+ };
1062
+ if (this.cache.size >= this.maxSize) {
1063
+ const oldest = this.accessOrder.shift();
1064
+ this.cache.delete(oldest);
1065
+ }
1066
+ this.cache.set(path, snapshot);
1067
+ this.accessOrder.push(path);
1068
+ return snapshot;
1069
+ }
1070
+ invalidate(path) {
1071
+ this.cache.delete(path);
1072
+ this.accessOrder = this.accessOrder.filter((p) => p !== path);
1073
+ }
1074
+ search(path, pattern, options = {}) {
1075
+ const snapshot = this.cache.get(path);
1076
+ if (!snapshot) {
1077
+ throw new Error("Snapshot not loaded. Call /snapshot/load first.");
1078
+ }
1079
+ const flags = options.caseInsensitive ? "gi" : "g";
1080
+ const regex = new RegExp(pattern, flags);
1081
+ const matches = [];
1082
+ const maxResults = options.maxResults || 50;
1083
+ const offset = options.offset || 0;
1084
+ let found = 0;
1085
+ for (let i = 0; i < snapshot.lines.length; i++) {
1086
+ const line = snapshot.lines[i];
1087
+ const match = regex.exec(line);
1088
+ regex.lastIndex = 0;
1089
+ if (match) {
1090
+ if (found >= offset && matches.length < maxResults) {
1091
+ matches.push({
1092
+ lineNum: i + 1,
1093
+ line: line.trim(),
1094
+ match: match[0]
1095
+ });
1096
+ }
1097
+ found++;
1098
+ if (matches.length >= maxResults) break;
1099
+ }
1100
+ }
1101
+ return { matches, count: matches.length };
1102
+ }
1103
+ getContext(path, file, line, before = 10, after = 10) {
1104
+ const snapshot = this.cache.get(path);
1105
+ if (!snapshot) throw new Error("Snapshot not loaded");
1106
+ const normalizedFile = file.replace(/^\.\//, "");
1107
+ const fileRange = snapshot.fileIndex.get(normalizedFile);
1108
+ if (!fileRange) throw new Error(`File not found: ${file}`);
1109
+ const fileLines = snapshot.lines.slice(fileRange.start, fileRange.end);
1110
+ const startLine = Math.max(0, line - before - 1);
1111
+ const endLine = Math.min(fileLines.length, line + after);
1112
+ const contextLines = fileLines.slice(startLine, endLine).map((l, idx) => {
1113
+ const lineNum = startLine + idx + 1;
1114
+ const marker = lineNum === line ? ">>>" : " ";
1115
+ return `${marker} ${lineNum.toString().padStart(4)}: ${l}`;
1116
+ });
1117
+ return {
1118
+ content: contextLines.join("\n"),
1119
+ range: { start: startLine + 1, end: endLine }
1120
+ };
1121
+ }
1122
+ buildFileIndex(lines) {
1123
+ const index = /* @__PURE__ */ new Map();
1124
+ let currentFile = null;
1125
+ let currentStart = 0;
1126
+ for (let i = 0; i < lines.length; i++) {
1127
+ const line = lines[i];
1128
+ if (line.startsWith("FILE: ./")) {
1129
+ if (currentFile) {
1130
+ index.set(currentFile, { start: currentStart, end: i - 1 });
1131
+ }
1132
+ currentFile = line.slice(8);
1133
+ currentStart = i + 2;
1134
+ }
1135
+ if (line.startsWith("METADATA:") && currentFile) {
1136
+ index.set(currentFile, { start: currentStart, end: i - 1 });
1137
+ break;
1138
+ }
1139
+ }
1140
+ if (currentFile && !index.has(currentFile)) {
1141
+ index.set(currentFile, { start: currentStart, end: lines.length - 1 });
1142
+ }
1143
+ return index;
1144
+ }
1145
+ touchAccess(path) {
1146
+ this.accessOrder = this.accessOrder.filter((p) => p !== path);
1147
+ this.accessOrder.push(path);
1148
+ }
1149
+ };
1150
+
1151
+ // src/worker/watcher.ts
1152
+ import { watch } from "fs";
1153
+ var ProjectWatcher = class {
1154
+ constructor(projectPath, snapshotPath, onUpdate, debounceMs = 1e3) {
1155
+ this.projectPath = projectPath;
1156
+ this.snapshotPath = snapshotPath;
1157
+ this.onUpdate = onUpdate;
1158
+ this.debounceMs = debounceMs;
1159
+ this.start();
1160
+ }
1161
+ watcher = null;
1162
+ debounceTimer = null;
1163
+ changedFiles = /* @__PURE__ */ new Set();
1164
+ start() {
1165
+ try {
1166
+ this.watcher = watch(this.projectPath, { recursive: true }, (eventType, filename) => {
1167
+ if (!filename) return;
1168
+ if (filename.includes("node_modules") || filename.includes(".git") || filename.includes(".argus")) {
1169
+ return;
1170
+ }
1171
+ this.changedFiles.add(filename);
1172
+ this.scheduleUpdate();
1173
+ });
1174
+ } catch (error) {
1175
+ console.error(`Failed to watch ${this.projectPath}:`, error);
1176
+ }
1177
+ }
1178
+ scheduleUpdate() {
1179
+ if (this.debounceTimer) {
1180
+ clearTimeout(this.debounceTimer);
1181
+ }
1182
+ this.debounceTimer = setTimeout(() => {
1183
+ const files = Array.from(this.changedFiles);
1184
+ this.changedFiles.clear();
1185
+ this.onUpdate(files);
1186
+ }, this.debounceMs);
1187
+ }
1188
+ close() {
1189
+ if (this.watcher) {
1190
+ this.watcher.close();
1191
+ this.watcher = null;
1192
+ }
1193
+ if (this.debounceTimer) {
1194
+ clearTimeout(this.debounceTimer);
1195
+ }
1196
+ }
1197
+ };
1198
+
1199
+ // src/worker/server.ts
1200
+ var PORT = process.env.ARGUS_WORKER_PORT || 37778;
1201
+ function startWorker() {
1202
+ const app = express();
1203
+ const cache = new SnapshotCache({ maxSize: 5 });
1204
+ const watchers = /* @__PURE__ */ new Map();
1205
+ app.use(express.json());
1206
+ app.get("/health", (_req, res) => {
1207
+ res.json({
1208
+ status: "ok",
1209
+ version: "2.0.0",
1210
+ cached: cache.size,
1211
+ watching: watchers.size
1212
+ });
1213
+ });
1214
+ app.post("/snapshot/load", async (req, res) => {
1215
+ const { path } = req.body;
1216
+ try {
1217
+ const snapshot = await cache.load(path);
1218
+ res.json({
1219
+ success: true,
1220
+ fileCount: snapshot.fileCount,
1221
+ cached: true
1222
+ });
1223
+ } catch (error) {
1224
+ res.status(400).json({ error: error.message });
1225
+ }
1226
+ });
1227
+ app.post("/search", (req, res) => {
1228
+ const { path, pattern, options } = req.body;
1229
+ try {
1230
+ const results = cache.search(path, pattern, options || {});
1231
+ res.json(results);
1232
+ } catch (error) {
1233
+ res.status(400).json({ error: error.message });
1234
+ }
1235
+ });
1236
+ app.post("/context", (req, res) => {
1237
+ const { path, file, line, before, after } = req.body;
1238
+ try {
1239
+ const context = cache.getContext(path, file, line, before || 10, after || 10);
1240
+ res.json(context);
1241
+ } catch (error) {
1242
+ res.status(400).json({ error: error.message });
1243
+ }
1244
+ });
1245
+ app.post("/notify-change", (req, res) => {
1246
+ const { projectPath } = req.body;
1247
+ const snapshotPath = `${projectPath}/.argus/snapshot.txt`;
1248
+ cache.invalidate(snapshotPath);
1249
+ res.json({ invalidated: true });
1250
+ });
1251
+ app.post("/watch", (req, res) => {
1252
+ const { projectPath, snapshotPath } = req.body;
1253
+ if (!watchers.has(projectPath)) {
1254
+ const watcher = new ProjectWatcher(projectPath, snapshotPath, () => {
1255
+ cache.invalidate(snapshotPath);
1256
+ });
1257
+ watchers.set(projectPath, watcher);
1258
+ }
1259
+ res.json({ watching: true, path: projectPath });
1260
+ });
1261
+ app.delete("/watch", (req, res) => {
1262
+ const { projectPath } = req.body;
1263
+ const watcher = watchers.get(projectPath);
1264
+ if (watcher) {
1265
+ watcher.close();
1266
+ watchers.delete(projectPath);
1267
+ }
1268
+ res.json({ watching: false });
1269
+ });
1270
+ const server = app.listen(PORT, () => {
1271
+ console.log(`Argus worker listening on port ${PORT}`);
1272
+ });
1273
+ return { app, server, cache, watchers };
1274
+ }
1026
1275
  export {
1027
1276
  PROVIDER_DEFAULTS,
1277
+ ProjectWatcher,
1278
+ SnapshotCache,
1028
1279
  analyze,
1029
1280
  createAnthropicProvider,
1030
1281
  createDeepSeekProvider,
@@ -1043,6 +1294,7 @@ export {
1043
1294
  loadConfig,
1044
1295
  saveConfig,
1045
1296
  searchDocument,
1297
+ startWorker,
1046
1298
  validateConfig
1047
1299
  };
1048
1300
  //# sourceMappingURL=index.mjs.map