@oxis-dev/tessra 2.19.4 → 2.19.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxis-dev/tessra",
3
- "version": "2.19.4",
3
+ "version": "2.19.6",
4
4
  "description": "MCP server for AI coding tools and agents. Provides semantic codebase context for Cursor, Claude Code, Codex, Copilot, Antigravity, and CLI workflows without requiring full file uploads.",
5
5
  "license": "UNLICENSED",
6
6
  "author": {
@@ -59,4 +59,4 @@
59
59
  "LICENSE",
60
60
  "README.md"
61
61
  ]
62
- }
62
+ }
@@ -237,47 +237,52 @@ function detectLinuxShellProfile() {
237
237
  return path.join(home, '.profile');
238
238
  }
239
239
 
240
- function repairLinuxShellPath(binDir) {
240
+ function detectLinuxShellProfiles() {
241
241
  const home = process.env.HOME;
242
242
  if (!home) {
243
- return { changed: false, reason: 'missing-home' };
243
+ return [];
244
244
  }
245
245
 
246
- const normalizedHome = normalizePathForCompare(home) + path.sep;
247
- const normalizedBinDir = normalizePathForCompare(binDir);
246
+ const shellName = path.basename(process.env.SHELL || '');
247
+ const profiles = [];
248
248
 
249
- if (!normalizedBinDir.startsWith(normalizedHome)) {
250
- return { changed: false, reason: 'bin-outside-home' };
249
+ if (shellName === 'bash') {
250
+ profiles.push(path.join(home, '.bashrc'));
251
+ } else if (shellName === 'zsh') {
252
+ profiles.push(path.join(home, '.zshrc'));
251
253
  }
252
254
 
253
- const profilePath = detectLinuxShellProfile();
254
- if (!profilePath) {
255
- return { changed: false, reason: 'missing-profile' };
256
- }
255
+ profiles.push(path.join(home, '.profile'));
257
256
 
257
+ return [...new Set(profiles)];
258
+ }
259
+
260
+ function upsertLinuxProfilePath(profilePath, binDir) {
258
261
  let profileContent = '';
259
262
  if (fs.existsSync(profilePath)) {
260
263
  profileContent = fs.readFileSync(profilePath, 'utf8');
261
- const blockRegex = new RegExp(
262
- `${escapeRegExp(LINUX_PATH_MARKER_START)}[\\s\\S]*?${escapeRegExp(LINUX_PATH_MARKER_END)}`,
263
- 'm'
264
- );
265
- if (blockRegex.test(profileContent)) {
266
- if (profileReferencesBinDir(profileContent, binDir)) {
267
- return { changed: false, configured: true, profilePath };
268
- }
269
- profileContent = profileContent.replace(
270
- blockRegex,
271
- `${LINUX_PATH_MARKER_START}\nexport PATH="$PATH:${binDir}"\n${LINUX_PATH_MARKER_END}`
272
- );
273
- fs.writeFileSync(profilePath, profileContent);
274
- process.env.PATH = process.env.PATH ? `${process.env.PATH}:${binDir}` : binDir;
275
- return { changed: true, profilePath };
276
- }
264
+ }
277
265
 
266
+ const blockRegex = new RegExp(
267
+ `${escapeRegExp(LINUX_PATH_MARKER_START)}[\\s\\S]*?${escapeRegExp(LINUX_PATH_MARKER_END)}`,
268
+ 'm'
269
+ );
270
+
271
+ if (blockRegex.test(profileContent)) {
278
272
  if (profileReferencesBinDir(profileContent, binDir)) {
279
- return { changed: false, configured: true, profilePath };
273
+ return { changed: false, configured: true };
280
274
  }
275
+
276
+ const nextContent = profileContent.replace(
277
+ blockRegex,
278
+ `${LINUX_PATH_MARKER_START}\nexport PATH="$PATH:${binDir}"\n${LINUX_PATH_MARKER_END}`
279
+ );
280
+ fs.writeFileSync(profilePath, nextContent);
281
+ return { changed: true, configured: true };
282
+ }
283
+
284
+ if (profileReferencesBinDir(profileContent, binDir)) {
285
+ return { changed: false, configured: true };
281
286
  }
282
287
 
283
288
  const prefix = profileContent && !profileContent.endsWith('\n') ? '\n' : '';
@@ -286,8 +291,68 @@ function repairLinuxShellPath(binDir) {
286
291
  `export PATH="$PATH:${binDir}"\n` +
287
292
  `${LINUX_PATH_MARKER_END}\n`;
288
293
  fs.appendFileSync(profilePath, block);
294
+ return { changed: true, configured: true };
295
+ }
296
+
297
+ function repairLinuxShellPath(binDir) {
298
+ const home = process.env.HOME;
299
+ if (!home) {
300
+ return { changed: false, reason: 'missing-home' };
301
+ }
302
+
303
+ const profilePaths = detectLinuxShellProfiles();
304
+ if (profilePaths.length === 0) {
305
+ return { changed: false, reason: 'missing-profile' };
306
+ }
307
+
308
+ const changedProfiles = [];
309
+ const configuredProfiles = [];
310
+
311
+ for (const profilePath of profilePaths) {
312
+ const result = upsertLinuxProfilePath(profilePath, binDir);
313
+ if (result.configured) {
314
+ configuredProfiles.push(profilePath);
315
+ }
316
+ if (result.changed) {
317
+ changedProfiles.push(profilePath);
318
+ }
319
+ }
320
+
289
321
  process.env.PATH = process.env.PATH ? `${process.env.PATH}:${binDir}` : binDir;
290
- return { changed: true, profilePath };
322
+ return {
323
+ changed: changedProfiles.length > 0,
324
+ configured: configuredProfiles.length > 0,
325
+ profilePath: detectLinuxShellProfile() || profilePaths[0],
326
+ profilePaths: configuredProfiles,
327
+ };
328
+ }
329
+
330
+ // En Linux con nvm/fnm, el bin de node solo está en PATH en shells interactivos.
331
+ // Crear un symlink del binario nativo en ~/.local/bin garantiza que tessra sea
332
+ // accesible en cualquier contexto: scripts, IDEs lanzando MCP, shells de login.
333
+ function installLinuxLocalBinSymlink(binaryPath) {
334
+ const home = process.env.HOME;
335
+ if (!home) return;
336
+
337
+ const localBin = path.join(home, '.local', 'bin');
338
+ const symlinkPath = path.join(localBin, 'tessra');
339
+
340
+ try {
341
+ if (!fs.existsSync(localBin)) {
342
+ fs.mkdirSync(localBin, { recursive: true });
343
+ }
344
+
345
+ try {
346
+ fs.lstatSync(symlinkPath); // lanza si no existe
347
+ fs.unlinkSync(symlinkPath);
348
+ } catch (_) { /* no existía — bien */ }
349
+
350
+ fs.symlinkSync(binaryPath, symlinkPath);
351
+ console.log(`tessra: symlink creado en ${symlinkPath} (disponible sin nvm activo).`);
352
+ } catch (err) {
353
+ // No fatal — tessra sigue disponible vía el bin de npm si nvm está activo
354
+ console.warn(`tessra: no se pudo crear symlink en ${symlinkPath}: ${err.message}`);
355
+ }
291
356
  }
292
357
 
293
358
  function ensureGlobalBinOnPath() {
@@ -324,11 +389,12 @@ function ensureGlobalBinOnPath() {
324
389
  try {
325
390
  const result = repairLinuxShellPath(binDir);
326
391
  if (result.changed) {
327
- console.log(`tessra: PATH reparado en Linux via ${result.profilePath}.`);
328
- console.log('tessra: abre una terminal nueva y ejecuta: tessra --version');
392
+ console.log(`tessra: PATH reparado en Linux via ${result.profilePaths.join(', ')}.`);
393
+ console.log(`tessra: para usarlo en esta terminal ejecuta: source ${result.profilePath}`);
394
+ console.log('tessra: en una terminal nueva ya deberia reconocer: tessra --version');
329
395
  } else if (result.configured) {
330
- console.log(`tessra: PATH ya configurado en Linux via ${result.profilePath}.`);
331
- console.log('tessra: abre una terminal nueva y ejecuta: tessra --version');
396
+ console.log(`tessra: PATH ya configurado en Linux via ${result.profilePaths.join(', ')}.`);
397
+ console.log(`tessra: si esta terminal no lo ve aun, ejecuta: source ${result.profilePath}`);
332
398
  } else {
333
399
  console.warn('tessra: no se aplico auto-fix de PATH en Linux.');
334
400
  printManualFix(binDir);
@@ -384,6 +450,11 @@ async function main() {
384
450
  // --version puede fallar si el binario requiere inicialización — no es fatal
385
451
  }
386
452
 
453
+ // En Linux: symlink en ~/.local/bin para que tessra funcione sin nvm activo
454
+ if (process.platform === 'linux') {
455
+ installLinuxLocalBinSymlink(dest);
456
+ }
457
+
387
458
  ensureGlobalBinOnPath();
388
459
 
389
460
  console.log(`tessra: binario instalado correctamente en ${dest}`);