aidex-mcp 1.4.1

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 (76) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/LICENSE +21 -0
  3. package/MCP-API-REFERENCE.md +690 -0
  4. package/README.md +314 -0
  5. package/build/commands/files.d.ts +28 -0
  6. package/build/commands/files.js +124 -0
  7. package/build/commands/index.d.ts +14 -0
  8. package/build/commands/index.js +14 -0
  9. package/build/commands/init.d.ts +24 -0
  10. package/build/commands/init.js +396 -0
  11. package/build/commands/link.d.ts +45 -0
  12. package/build/commands/link.js +167 -0
  13. package/build/commands/note.d.ts +29 -0
  14. package/build/commands/note.js +105 -0
  15. package/build/commands/query.d.ts +36 -0
  16. package/build/commands/query.js +176 -0
  17. package/build/commands/scan.d.ts +25 -0
  18. package/build/commands/scan.js +104 -0
  19. package/build/commands/session.d.ts +52 -0
  20. package/build/commands/session.js +216 -0
  21. package/build/commands/signature.d.ts +52 -0
  22. package/build/commands/signature.js +171 -0
  23. package/build/commands/summary.d.ts +56 -0
  24. package/build/commands/summary.js +324 -0
  25. package/build/commands/update.d.ts +36 -0
  26. package/build/commands/update.js +273 -0
  27. package/build/constants.d.ts +10 -0
  28. package/build/constants.js +10 -0
  29. package/build/db/database.d.ts +69 -0
  30. package/build/db/database.js +126 -0
  31. package/build/db/index.d.ts +7 -0
  32. package/build/db/index.js +6 -0
  33. package/build/db/queries.d.ts +163 -0
  34. package/build/db/queries.js +273 -0
  35. package/build/db/schema.sql +136 -0
  36. package/build/index.d.ts +13 -0
  37. package/build/index.js +74 -0
  38. package/build/parser/extractor.d.ts +41 -0
  39. package/build/parser/extractor.js +249 -0
  40. package/build/parser/index.d.ts +7 -0
  41. package/build/parser/index.js +7 -0
  42. package/build/parser/languages/c.d.ts +28 -0
  43. package/build/parser/languages/c.js +70 -0
  44. package/build/parser/languages/cpp.d.ts +28 -0
  45. package/build/parser/languages/cpp.js +91 -0
  46. package/build/parser/languages/csharp.d.ts +32 -0
  47. package/build/parser/languages/csharp.js +97 -0
  48. package/build/parser/languages/go.d.ts +28 -0
  49. package/build/parser/languages/go.js +83 -0
  50. package/build/parser/languages/index.d.ts +21 -0
  51. package/build/parser/languages/index.js +107 -0
  52. package/build/parser/languages/java.d.ts +28 -0
  53. package/build/parser/languages/java.js +58 -0
  54. package/build/parser/languages/php.d.ts +28 -0
  55. package/build/parser/languages/php.js +75 -0
  56. package/build/parser/languages/python.d.ts +28 -0
  57. package/build/parser/languages/python.js +67 -0
  58. package/build/parser/languages/ruby.d.ts +28 -0
  59. package/build/parser/languages/ruby.js +68 -0
  60. package/build/parser/languages/rust.d.ts +28 -0
  61. package/build/parser/languages/rust.js +73 -0
  62. package/build/parser/languages/typescript.d.ts +28 -0
  63. package/build/parser/languages/typescript.js +82 -0
  64. package/build/parser/tree-sitter.d.ts +30 -0
  65. package/build/parser/tree-sitter.js +132 -0
  66. package/build/server/mcp-server.d.ts +7 -0
  67. package/build/server/mcp-server.js +36 -0
  68. package/build/server/tools.d.ts +18 -0
  69. package/build/server/tools.js +1245 -0
  70. package/build/viewer/git-status.d.ts +25 -0
  71. package/build/viewer/git-status.js +163 -0
  72. package/build/viewer/index.d.ts +5 -0
  73. package/build/viewer/index.js +5 -0
  74. package/build/viewer/server.d.ts +12 -0
  75. package/build/viewer/server.js +1122 -0
  76. package/package.json +66 -0
@@ -0,0 +1,216 @@
1
+ /**
2
+ * session command - Session tracking and external change detection
3
+ *
4
+ * Tracks session start/end times and detects files changed outside of sessions.
5
+ * This enables:
6
+ * - "What did we do last session?" queries using time filtering
7
+ * - Automatic detection of externally modified files that need re-indexing
8
+ */
9
+ import { existsSync, readFileSync } from 'fs';
10
+ import { join, resolve } from 'path';
11
+ import { createHash } from 'crypto';
12
+ import { PRODUCT_NAME, INDEX_DIR, TOOL_PREFIX } from '../constants.js';
13
+ import { openDatabase, createQueries } from '../db/index.js';
14
+ import { update } from './update.js';
15
+ // ============================================================
16
+ // Constants
17
+ // ============================================================
18
+ const KEY_LAST_SESSION_START = 'last_session_start';
19
+ const KEY_LAST_SESSION_END = 'last_session_end';
20
+ const KEY_CURRENT_SESSION_START = 'current_session_start';
21
+ const KEY_SESSION_NOTE = 'session_note';
22
+ // Session is considered "new" if more than 5 minutes have passed since last activity
23
+ const SESSION_TIMEOUT_MS = 5 * 60 * 1000;
24
+ // ============================================================
25
+ // Implementation
26
+ // ============================================================
27
+ /**
28
+ * Start or continue a session.
29
+ * - If new session: detect external changes, store previous session times
30
+ * - Always: update session_end timestamp
31
+ */
32
+ export function session(params) {
33
+ const { path: projectPath } = params;
34
+ // Validate project path
35
+ const dbPath = join(projectPath, INDEX_DIR, 'index.db');
36
+ if (!existsSync(dbPath)) {
37
+ return {
38
+ success: false,
39
+ isNewSession: false,
40
+ sessionInfo: { lastSessionStart: null, lastSessionEnd: null, currentSessionStart: null },
41
+ externalChanges: [],
42
+ reindexed: [],
43
+ note: null,
44
+ error: `No ${PRODUCT_NAME} index found at ${projectPath}. Run ${TOOL_PREFIX}init first.`,
45
+ };
46
+ }
47
+ const db = openDatabase(dbPath, false);
48
+ const queries = createQueries(db);
49
+ const now = Date.now();
50
+ try {
51
+ // Get current session info
52
+ const currentStart = db.getMetadata(KEY_CURRENT_SESSION_START);
53
+ const lastEnd = db.getMetadata(KEY_LAST_SESSION_END);
54
+ // Determine if this is a new session
55
+ const lastActivity = lastEnd ? parseInt(lastEnd, 10) : (currentStart ? parseInt(currentStart, 10) : 0);
56
+ const isNewSession = !currentStart || (now - lastActivity > SESSION_TIMEOUT_MS);
57
+ let sessionInfo;
58
+ let externalChanges = [];
59
+ let reindexed = [];
60
+ if (isNewSession) {
61
+ // Archive previous session times
62
+ if (currentStart) {
63
+ db.setMetadata(KEY_LAST_SESSION_START, currentStart);
64
+ }
65
+ if (lastEnd) {
66
+ // Use the last recorded end time as last_session_end
67
+ }
68
+ else if (currentStart) {
69
+ db.setMetadata(KEY_LAST_SESSION_END, currentStart);
70
+ }
71
+ // Start new session
72
+ db.setMetadata(KEY_CURRENT_SESSION_START, now.toString());
73
+ // Detect external changes
74
+ externalChanges = detectExternalChanges(projectPath, queries);
75
+ // Auto-reindex modified files
76
+ for (const change of externalChanges) {
77
+ if (change.reason === 'modified') {
78
+ const result = update({ path: projectPath, file: change.path });
79
+ if (result.success) {
80
+ reindexed.push(change.path);
81
+ }
82
+ }
83
+ }
84
+ sessionInfo = {
85
+ lastSessionStart: currentStart ? parseInt(currentStart, 10) : null,
86
+ lastSessionEnd: lastEnd ? parseInt(lastEnd, 10) : null,
87
+ currentSessionStart: now,
88
+ };
89
+ }
90
+ else {
91
+ // Continue existing session
92
+ sessionInfo = {
93
+ lastSessionStart: db.getMetadata(KEY_LAST_SESSION_START) ? parseInt(db.getMetadata(KEY_LAST_SESSION_START), 10) : null,
94
+ lastSessionEnd: db.getMetadata(KEY_LAST_SESSION_END) ? parseInt(db.getMetadata(KEY_LAST_SESSION_END), 10) : null,
95
+ currentSessionStart: parseInt(currentStart, 10),
96
+ };
97
+ }
98
+ // Always update session end time (heartbeat)
99
+ db.setMetadata(KEY_LAST_SESSION_END, now.toString());
100
+ // Get session note
101
+ const note = db.getMetadata(KEY_SESSION_NOTE);
102
+ db.close();
103
+ return {
104
+ success: true,
105
+ isNewSession,
106
+ sessionInfo,
107
+ externalChanges,
108
+ reindexed,
109
+ note,
110
+ };
111
+ }
112
+ catch (error) {
113
+ db.close();
114
+ return {
115
+ success: false,
116
+ isNewSession: false,
117
+ sessionInfo: { lastSessionStart: null, lastSessionEnd: null, currentSessionStart: null },
118
+ externalChanges: [],
119
+ reindexed: [],
120
+ note: null,
121
+ error: error instanceof Error ? error.message : String(error),
122
+ };
123
+ }
124
+ }
125
+ /**
126
+ * Update session heartbeat (call periodically during session)
127
+ */
128
+ export function updateSessionHeartbeat(projectPath) {
129
+ const dbPath = join(projectPath, INDEX_DIR, 'index.db');
130
+ if (!existsSync(dbPath)) {
131
+ return;
132
+ }
133
+ try {
134
+ const db = openDatabase(dbPath, false);
135
+ db.setMetadata(KEY_LAST_SESSION_END, Date.now().toString());
136
+ db.close();
137
+ }
138
+ catch {
139
+ // Silently ignore errors
140
+ }
141
+ }
142
+ /**
143
+ * Get session info without starting/updating
144
+ */
145
+ export function getSessionInfo(projectPath) {
146
+ const dbPath = join(projectPath, INDEX_DIR, 'index.db');
147
+ if (!existsSync(dbPath)) {
148
+ return null;
149
+ }
150
+ try {
151
+ const db = openDatabase(dbPath, true);
152
+ const info = {
153
+ lastSessionStart: db.getMetadata(KEY_LAST_SESSION_START) ? parseInt(db.getMetadata(KEY_LAST_SESSION_START), 10) : null,
154
+ lastSessionEnd: db.getMetadata(KEY_LAST_SESSION_END) ? parseInt(db.getMetadata(KEY_LAST_SESSION_END), 10) : null,
155
+ currentSessionStart: db.getMetadata(KEY_CURRENT_SESSION_START) ? parseInt(db.getMetadata(KEY_CURRENT_SESSION_START), 10) : null,
156
+ };
157
+ db.close();
158
+ return info;
159
+ }
160
+ catch {
161
+ return null;
162
+ }
163
+ }
164
+ // ============================================================
165
+ // Helper functions
166
+ // ============================================================
167
+ /**
168
+ * Detect files that were changed outside of the session
169
+ */
170
+ function detectExternalChanges(projectPath, queries) {
171
+ const changes = [];
172
+ const projectRoot = resolve(projectPath);
173
+ // Get all indexed files
174
+ const indexedFiles = queries.getAllFiles();
175
+ for (const file of indexedFiles) {
176
+ const fullPath = join(projectRoot, file.path);
177
+ if (!existsSync(fullPath)) {
178
+ // File was deleted
179
+ changes.push({ path: file.path, reason: 'deleted' });
180
+ continue;
181
+ }
182
+ // Check if file hash changed
183
+ try {
184
+ const content = readFileSync(fullPath);
185
+ const currentHash = createHash('sha256').update(content).digest('hex');
186
+ if (currentHash !== file.hash) {
187
+ changes.push({ path: file.path, reason: 'modified' });
188
+ }
189
+ }
190
+ catch {
191
+ // Can't read file - skip
192
+ }
193
+ }
194
+ return changes;
195
+ }
196
+ /**
197
+ * Format session time for display
198
+ */
199
+ export function formatSessionTime(timestamp) {
200
+ if (!timestamp)
201
+ return 'N/A';
202
+ return new Date(timestamp).toISOString();
203
+ }
204
+ /**
205
+ * Format duration between two timestamps
206
+ */
207
+ export function formatDuration(startMs, endMs) {
208
+ const durationMs = endMs - startMs;
209
+ const minutes = Math.floor(durationMs / 60000);
210
+ const hours = Math.floor(minutes / 60);
211
+ if (hours > 0) {
212
+ return `${hours}h ${minutes % 60}m`;
213
+ }
214
+ return `${minutes}m`;
215
+ }
216
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Signature commands for AiDex
3
+ * Retrieves file signatures (header comments, types, methods)
4
+ */
5
+ export interface SignatureParams {
6
+ /** Project path (containing index dir) */
7
+ path: string;
8
+ /** Relative file path within the project */
9
+ file: string;
10
+ }
11
+ export interface SignatureResult {
12
+ success: boolean;
13
+ file: string;
14
+ headerComments: string | null;
15
+ types: Array<{
16
+ name: string;
17
+ kind: string;
18
+ lineNumber: number;
19
+ }>;
20
+ methods: Array<{
21
+ name: string;
22
+ prototype: string;
23
+ lineNumber: number;
24
+ visibility: string | null;
25
+ isStatic: boolean;
26
+ isAsync: boolean;
27
+ }>;
28
+ error?: string;
29
+ }
30
+ export interface SignaturesParams {
31
+ /** Project path (containing index dir) */
32
+ path: string;
33
+ /** Glob pattern to match files (e.g., "src/Core/**.cs") */
34
+ pattern?: string;
35
+ /** Explicit list of relative file paths */
36
+ files?: string[];
37
+ }
38
+ export interface SignaturesResult {
39
+ success: boolean;
40
+ signatures: SignatureResult[];
41
+ totalFiles: number;
42
+ error?: string;
43
+ }
44
+ /**
45
+ * Get signature for a single file
46
+ */
47
+ export declare function signature(params: SignatureParams): SignatureResult;
48
+ /**
49
+ * Get signatures for multiple files
50
+ */
51
+ export declare function signatures(params: SignaturesParams): SignaturesResult;
52
+ //# sourceMappingURL=signature.d.ts.map
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Signature commands for AiDex
3
+ * Retrieves file signatures (header comments, types, methods)
4
+ */
5
+ import { join } from 'path';
6
+ import { existsSync } from 'fs';
7
+ import { PRODUCT_NAME, INDEX_DIR, TOOL_PREFIX } from '../constants.js';
8
+ import { openDatabase } from '../db/index.js';
9
+ import { createQueries } from '../db/queries.js';
10
+ // ============================================================
11
+ // Implementation
12
+ // ============================================================
13
+ /**
14
+ * Get signature for a single file
15
+ */
16
+ export function signature(params) {
17
+ const { path: projectPath } = params;
18
+ // Normalize path to forward slashes
19
+ const file = params.file.replace(/\\/g, '/');
20
+ // Validate project path
21
+ const indexDir = join(projectPath, INDEX_DIR);
22
+ const dbPath = join(indexDir, 'index.db');
23
+ if (!existsSync(dbPath)) {
24
+ return {
25
+ success: false,
26
+ file,
27
+ headerComments: null,
28
+ types: [],
29
+ methods: [],
30
+ error: `No ${PRODUCT_NAME} index found at ${projectPath}. Run ${TOOL_PREFIX}init first.`,
31
+ };
32
+ }
33
+ // Open database
34
+ const db = openDatabase(dbPath, true); // readonly
35
+ const queries = createQueries(db);
36
+ try {
37
+ // Find file in database
38
+ const fileRow = queries.getFileByPath(file);
39
+ if (!fileRow) {
40
+ db.close();
41
+ return {
42
+ success: false,
43
+ file,
44
+ headerComments: null,
45
+ types: [],
46
+ methods: [],
47
+ error: `File "${file}" not found in index. It may not be indexed or the path is incorrect.`,
48
+ };
49
+ }
50
+ // Get signature data
51
+ const signatureRow = queries.getSignatureByFile(fileRow.id);
52
+ const methodRows = queries.getMethodsByFile(fileRow.id);
53
+ const typeRows = queries.getTypesByFile(fileRow.id);
54
+ db.close();
55
+ return {
56
+ success: true,
57
+ file: fileRow.path,
58
+ headerComments: signatureRow?.header_comments ?? null,
59
+ types: typeRows.map(t => ({
60
+ name: t.name,
61
+ kind: t.kind,
62
+ lineNumber: t.line_number,
63
+ })),
64
+ methods: methodRows.map(m => ({
65
+ name: m.name,
66
+ prototype: m.prototype,
67
+ lineNumber: m.line_number,
68
+ visibility: m.visibility,
69
+ isStatic: m.is_static === 1,
70
+ isAsync: m.is_async === 1,
71
+ })),
72
+ };
73
+ }
74
+ catch (error) {
75
+ db.close();
76
+ return {
77
+ success: false,
78
+ file,
79
+ headerComments: null,
80
+ types: [],
81
+ methods: [],
82
+ error: `Error retrieving signature: ${error instanceof Error ? error.message : String(error)}`,
83
+ };
84
+ }
85
+ }
86
+ /**
87
+ * Get signatures for multiple files
88
+ */
89
+ export function signatures(params) {
90
+ const { path: projectPath, pattern, files } = params;
91
+ // Validate project path
92
+ const indexDir = join(projectPath, INDEX_DIR);
93
+ const dbPath = join(indexDir, 'index.db');
94
+ if (!existsSync(dbPath)) {
95
+ return {
96
+ success: false,
97
+ signatures: [],
98
+ totalFiles: 0,
99
+ error: `No ${PRODUCT_NAME} index found at ${projectPath}. Run ${TOOL_PREFIX}init first.`,
100
+ };
101
+ }
102
+ // Determine which files to query
103
+ let filesToQuery = [];
104
+ if (files && files.length > 0) {
105
+ // Use explicit file list (paths as-is, signature() handles normalization)
106
+ filesToQuery = files;
107
+ }
108
+ else if (pattern) {
109
+ // Use glob pattern against indexed files
110
+ const db = openDatabase(dbPath, true);
111
+ const queries = createQueries(db);
112
+ const allFiles = queries.getAllFiles();
113
+ db.close();
114
+ // Convert glob pattern to regex (normalize pattern to forward slashes)
115
+ const normalizedPattern = pattern.replace(/\\/g, '/');
116
+ const regex = globToRegex(normalizedPattern);
117
+ // Test against both original path and forward-slash normalized version
118
+ filesToQuery = allFiles
119
+ .map(f => f.path)
120
+ .filter(p => {
121
+ const normalizedPath = p.replace(/\\/g, '/');
122
+ return regex.test(normalizedPath);
123
+ });
124
+ }
125
+ else {
126
+ return {
127
+ success: false,
128
+ signatures: [],
129
+ totalFiles: 0,
130
+ error: 'Either pattern or files parameter is required.',
131
+ };
132
+ }
133
+ // Get signatures for all matched files
134
+ const results = [];
135
+ for (const file of filesToQuery) {
136
+ const result = signature({ path: projectPath, file });
137
+ results.push(result);
138
+ }
139
+ return {
140
+ success: true,
141
+ signatures: results,
142
+ totalFiles: results.length,
143
+ };
144
+ }
145
+ /**
146
+ * Convert a glob pattern to a regular expression
147
+ * Supports: *, **, ?
148
+ */
149
+ function globToRegex(pattern) {
150
+ // Normalize to forward slashes
151
+ pattern = pattern.replace(/\\/g, '/');
152
+ // Escape regex special chars except * and ?
153
+ let regex = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&');
154
+ // Convert glob patterns to regex:
155
+ // ** matches any path (including /)
156
+ // * matches any characters except /
157
+ // ? matches any single character except /
158
+ // Handle ** first (must match across directories)
159
+ // **/ at start or middle means "any path prefix"
160
+ regex = regex.replace(/\*\*\//g, '(.*/)?');
161
+ // /** at end means "any path suffix"
162
+ regex = regex.replace(/\/\*\*/g, '(/.*)?');
163
+ // Standalone ** (rare)
164
+ regex = regex.replace(/\*\*/g, '.*');
165
+ // Handle single * (matches within directory)
166
+ regex = regex.replace(/\*/g, '[^/]*');
167
+ // Handle ? (single character)
168
+ regex = regex.replace(/\?/g, '[^/]');
169
+ return new RegExp(`^${regex}$`, 'i');
170
+ }
171
+ //# sourceMappingURL=signature.js.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * summary and tree commands
3
+ *
4
+ * - summary: Get project summary with auto-detected info
5
+ * - tree: Get indexed file tree with optional stats
6
+ */
7
+ export interface SummaryParams {
8
+ path: string;
9
+ }
10
+ export interface SummaryResult {
11
+ success: boolean;
12
+ name: string;
13
+ content: string;
14
+ autoGenerated: {
15
+ entryPoints: string[];
16
+ mainTypes: string[];
17
+ fileCount: number;
18
+ languages: string[];
19
+ };
20
+ error?: string;
21
+ }
22
+ export interface TreeParams {
23
+ path: string;
24
+ subpath?: string;
25
+ depth?: number;
26
+ includeStats?: boolean;
27
+ }
28
+ export interface TreeEntry {
29
+ path: string;
30
+ type: 'file' | 'directory';
31
+ itemCount?: number;
32
+ methodCount?: number;
33
+ typeCount?: number;
34
+ }
35
+ export interface TreeResult {
36
+ success: boolean;
37
+ root: string;
38
+ entries: TreeEntry[];
39
+ totalFiles: number;
40
+ error?: string;
41
+ }
42
+ export declare function summary(params: SummaryParams): SummaryResult;
43
+ export declare function tree(params: TreeParams): TreeResult;
44
+ export interface DescribeParams {
45
+ path: string;
46
+ section: 'purpose' | 'architecture' | 'concepts' | 'patterns' | 'notes';
47
+ content: string;
48
+ replace?: boolean;
49
+ }
50
+ export interface DescribeResult {
51
+ success: boolean;
52
+ section: string;
53
+ error?: string;
54
+ }
55
+ export declare function describe(params: DescribeParams): DescribeResult;
56
+ //# sourceMappingURL=summary.d.ts.map