@softerist/heuristic-mcp 3.0.17 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,25 +1,16 @@
1
- /**
2
- * Runtime Workspace Switching Tool
3
- *
4
- * Changes the workspace path at runtime, reinitializing the cache
5
- * and optionally triggering reindexing.
6
- */
7
1
 
8
- import path from 'path';
9
- import fs from 'fs/promises';
10
- import crypto from 'crypto';
11
- import { acquireWorkspaceLock, releaseWorkspaceLock } from '../lib/server-lifecycle.js';
12
2
 
13
- /**
14
- * Generate a workspace-specific cache directory path
15
- */
16
- function getWorkspaceCacheDir(workspacePath, globalCacheDir) {
17
- const normalized = path.resolve(workspacePath);
18
- const hash = crypto.createHash('md5').update(normalized).digest('hex').slice(0, 12);
19
- return path.join(globalCacheDir, 'heuristic-mcp', hash);
20
- }
3
+ import path from 'path';
4
+ import fs from 'fs/promises';
5
+ import { acquireWorkspaceLock, releaseWorkspaceLock } from '../lib/server-lifecycle.js';
6
+ import { getWorkspaceCachePath } from '../lib/workspace-cache-key.js';
7
+
8
+
9
+ function getWorkspaceCacheDir(workspacePath, globalCacheDir) {
10
+ return getWorkspaceCachePath(workspacePath, globalCacheDir);
11
+ }
12
+
21
13
 
22
- // MCP Tool definition
23
14
  export function getToolDefinition() {
24
15
  return {
25
16
  name: 'f_set_workspace',
@@ -50,10 +41,7 @@ export function getToolDefinition() {
50
41
  };
51
42
  }
52
43
 
53
- /**
54
- * Create the SetWorkspace feature class
55
- * This needs access to shared state (config, cache, indexer) to actually perform the switch
56
- */
44
+
57
45
  export class SetWorkspaceFeature {
58
46
  constructor(config, cache, indexer, getGlobalCacheDir) {
59
47
  this.config = config;
@@ -63,7 +51,7 @@ export class SetWorkspaceFeature {
63
51
  }
64
52
 
65
53
  async execute({ workspacePath, reindex = true }) {
66
- // Validate workspace path
54
+
67
55
  if (!workspacePath || typeof workspacePath !== 'string') {
68
56
  return {
69
57
  success: false,
@@ -73,7 +61,7 @@ export class SetWorkspaceFeature {
73
61
 
74
62
  const normalizedPath = path.resolve(workspacePath);
75
63
 
76
- // Check if directory exists
64
+
77
65
  try {
78
66
  const stat = await fs.stat(normalizedPath);
79
67
  if (!stat.isDirectory()) {
@@ -92,14 +80,14 @@ export class SetWorkspaceFeature {
92
80
  const previousWorkspace = this.config.searchDirectory;
93
81
  const previousCache = this.config.cacheDirectory;
94
82
 
95
- // Update config
83
+
96
84
  this.config.searchDirectory = normalizedPath;
97
85
 
98
- // Calculate new cache directory (match config.js behavior)
86
+
99
87
  const globalCacheDir = this.getGlobalCacheDir();
100
88
  let newCacheDir = getWorkspaceCacheDir(normalizedPath, globalCacheDir);
101
89
 
102
- // Prefer legacy local cache if present
90
+
103
91
  const legacyPath = path.join(normalizedPath, '.smart-coding-cache');
104
92
  try {
105
93
  const legacyStats = await fs.stat(legacyPath);
@@ -107,15 +95,15 @@ export class SetWorkspaceFeature {
107
95
  newCacheDir = legacyPath;
108
96
  }
109
97
  } catch {
110
- // ignore missing legacy cache
98
+
111
99
  }
112
100
  this.config.cacheDirectory = newCacheDir;
113
101
 
114
- // Create cache directory if needed
102
+
115
103
  try {
116
104
  await fs.mkdir(newCacheDir, { recursive: true });
117
105
  } catch (err) {
118
- // Revert config on failure
106
+
119
107
  this.config.searchDirectory = previousWorkspace;
120
108
  this.config.cacheDirectory = previousCache;
121
109
  return {
@@ -124,13 +112,13 @@ export class SetWorkspaceFeature {
124
112
  };
125
113
  }
126
114
 
127
- // Acquire new workspace lock before proceeding
115
+
128
116
  const lock = await acquireWorkspaceLock({
129
117
  cacheDirectory: newCacheDir,
130
118
  workspaceDir: normalizedPath,
131
119
  });
132
120
  if (!lock.acquired) {
133
- // Revert config on failure
121
+
134
122
  this.config.searchDirectory = previousWorkspace;
135
123
  this.config.cacheDirectory = previousCache;
136
124
  return {
@@ -140,7 +128,7 @@ export class SetWorkspaceFeature {
140
128
  }
141
129
  let indexerUpdateError = null;
142
130
 
143
- // Update indexer's workspace root and related state
131
+
144
132
  if (this.indexer) {
145
133
  if (typeof this.indexer.terminateWorkers === 'function') {
146
134
  try {
@@ -154,7 +142,7 @@ export class SetWorkspaceFeature {
154
142
  await this.indexer.updateWorkspaceState({ restartWatcher: true });
155
143
  } else {
156
144
  this.indexer.workspaceRoot = normalizedPath;
157
- this.indexer.workspaceRootReal = null; // Reset cached realpath
145
+ this.indexer.workspaceRootReal = null;
158
146
  if (this.config.watchFiles && typeof this.indexer.setupFileWatcher === 'function') {
159
147
  await this.indexer.setupFileWatcher();
160
148
  }
@@ -165,7 +153,7 @@ export class SetWorkspaceFeature {
165
153
  }
166
154
 
167
155
  if (indexerUpdateError) {
168
- // Roll back config + lock on failure to avoid partial switch
156
+
169
157
  this.config.searchDirectory = previousWorkspace;
170
158
  this.config.cacheDirectory = previousCache;
171
159
  await releaseWorkspaceLock({ cacheDirectory: newCacheDir });
@@ -192,12 +180,12 @@ export class SetWorkspaceFeature {
192
180
  };
193
181
  }
194
182
 
195
- // Release old workspace lock after successful indexer update
183
+
196
184
  if (previousCache) {
197
185
  await releaseWorkspaceLock({ cacheDirectory: previousCache });
198
186
  }
199
187
 
200
- // Re-initialize cache for new workspace if cache has a load method
188
+
201
189
  if (this.cache && typeof this.cache.load === 'function') {
202
190
  try {
203
191
  await this.cache.load();
@@ -206,11 +194,11 @@ export class SetWorkspaceFeature {
206
194
  }
207
195
  }
208
196
 
209
- // Optionally trigger reindex
197
+
210
198
  let reindexStatus = null;
211
199
  if (reindex && this.indexer && typeof this.indexer.indexAll === 'function') {
212
200
  try {
213
- // Start indexing asynchronously
201
+
214
202
  this.indexer.indexAll().catch((err) => {
215
203
  console.warn(`[SetWorkspace] Reindex failed: ${err.message}`);
216
204
  });
@@ -232,7 +220,7 @@ export class SetWorkspaceFeature {
232
220
  }
233
221
  }
234
222
 
235
- // Tool handler (needs instance context, so this is a factory)
223
+
236
224
  export function createHandleToolCall(featureInstance) {
237
225
  return async (request) => {
238
226
  const args = request.params?.arguments || {};
@@ -240,7 +228,7 @@ export function createHandleToolCall(featureInstance) {
240
228
 
241
229
  const result = await featureInstance.execute({
242
230
  workspacePath,
243
- reindex: reindex !== false, // Default to true
231
+ reindex: reindex !== false,
244
232
  });
245
233
 
246
234
  if (result.success) {
@@ -261,5 +249,5 @@ export function createHandleToolCall(featureInstance) {
261
249
  };
262
250
  }
263
251
 
264
- // Export for use in registration
252
+
265
253
  export { getWorkspaceCacheDir };