@oxis-dev/tessra 2.19.0 → 2.19.4
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/README.md +16 -1
- package/package.json +2 -2
- package/scripts/postinstall.js +219 -0
package/README.md
CHANGED
|
@@ -4,12 +4,27 @@ Semantic code intelligence engine for AI agents. Tessra indexes your codebase an
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
-
```
|
|
7
|
+
```sh
|
|
8
8
|
npm install -g @oxis-dev/tessra
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Requires a valid Tessra license. Get one at [oxis.dev/tessra](https://oxis.dev/tessra).
|
|
12
12
|
|
|
13
|
+
On Windows and Linux, Tessra now checks whether the global npm bin directory is available in
|
|
14
|
+
`PATH` during `postinstall`.
|
|
15
|
+
|
|
16
|
+
- Windows: Tessra attempts to repair the user `PATH` automatically if `%APPDATA%\\npm` is missing.
|
|
17
|
+
- Linux: Tessra attempts a safe shell-profile fix when the npm global bin directory lives under
|
|
18
|
+
the user's home directory.
|
|
19
|
+
- If automatic repair is not safe or fails, the installer prints the exact command or path the
|
|
20
|
+
user needs to add manually.
|
|
21
|
+
|
|
22
|
+
After a PATH repair, open a new terminal and run:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
tessra --version
|
|
26
|
+
```
|
|
27
|
+
|
|
13
28
|
---
|
|
14
29
|
|
|
15
30
|
## Quick start
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxis-dev/tessra",
|
|
3
|
-
"version": "2.19.
|
|
3
|
+
"version": "2.19.4",
|
|
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
|
+
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -19,6 +19,9 @@ const VERSION = require('../package.json').version;
|
|
|
19
19
|
const BIN_DIR = path.join(__dirname, '..', 'bin');
|
|
20
20
|
const BIN_PATH_UNIX = path.join(BIN_DIR, 'tessra');
|
|
21
21
|
const BIN_PATH_WIN = path.join(BIN_DIR, 'tessra.exe');
|
|
22
|
+
const PATH_DELIMITER = process.platform === 'win32' ? ';' : ':';
|
|
23
|
+
const LINUX_PATH_MARKER_START = '# >>> tessra npm bin >>>';
|
|
24
|
+
const LINUX_PATH_MARKER_END = '# <<< tessra npm bin <<<';
|
|
22
25
|
|
|
23
26
|
// ── Detección de plataforma ──────────────────────────────────────────────────
|
|
24
27
|
|
|
@@ -126,6 +129,220 @@ function extractFromTar(buffer, targetName) {
|
|
|
126
129
|
return null;
|
|
127
130
|
}
|
|
128
131
|
|
|
132
|
+
function escapeRegExp(value) {
|
|
133
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function normalizePathForCompare(targetPath) {
|
|
137
|
+
const resolved = path.resolve(targetPath);
|
|
138
|
+
return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function pathContains(targetPath, pathValue) {
|
|
142
|
+
const normalizedTarget = normalizePathForCompare(targetPath);
|
|
143
|
+
return (pathValue || '')
|
|
144
|
+
.split(PATH_DELIMITER)
|
|
145
|
+
.map((entry) => entry.trim())
|
|
146
|
+
.filter(Boolean)
|
|
147
|
+
.some((entry) => normalizePathForCompare(entry) === normalizedTarget);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function profileReferencesBinDir(profileContent, binDir) {
|
|
151
|
+
return profileContent.includes(binDir);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function inferGlobalPrefix() {
|
|
155
|
+
if (process.env.npm_config_prefix) {
|
|
156
|
+
return process.env.npm_config_prefix;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
160
|
+
const parts = packageRoot.split(path.sep);
|
|
161
|
+
const nodeModulesIndex = parts.lastIndexOf('node_modules');
|
|
162
|
+
|
|
163
|
+
if (nodeModulesIndex === -1) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let prefixParts = parts.slice(0, nodeModulesIndex);
|
|
168
|
+
if (prefixParts[prefixParts.length - 1] === 'lib') {
|
|
169
|
+
prefixParts = prefixParts.slice(0, -1);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (prefixParts.length === 0) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return prefixParts.join(path.sep);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function getGlobalBinDir() {
|
|
180
|
+
const prefix = inferGlobalPrefix();
|
|
181
|
+
if (!prefix) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
return process.platform === 'win32' ? prefix : path.join(prefix, 'bin');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function printManualFix(binDir) {
|
|
188
|
+
if (process.platform === 'win32') {
|
|
189
|
+
console.warn(`tessra: agrega esta ruta a tu PATH de usuario: ${binDir}`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
console.warn(`tessra: agrega esta linea a tu shell profile: export PATH="$PATH:${binDir}"`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function repairWindowsUserPath(binDir) {
|
|
197
|
+
const currentUserPath = execFileSync(
|
|
198
|
+
'powershell.exe',
|
|
199
|
+
['-NoProfile', '-Command', "[Environment]::GetEnvironmentVariable('Path','User')"],
|
|
200
|
+
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }
|
|
201
|
+
).trim();
|
|
202
|
+
|
|
203
|
+
if (pathContains(binDir, currentUserPath)) {
|
|
204
|
+
return { changed: false };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const nextPath = [currentUserPath, binDir].filter(Boolean).join(';');
|
|
208
|
+
const escapedPath = nextPath.replace(/'/g, "''");
|
|
209
|
+
|
|
210
|
+
execFileSync(
|
|
211
|
+
'powershell.exe',
|
|
212
|
+
[
|
|
213
|
+
'-NoProfile',
|
|
214
|
+
'-Command',
|
|
215
|
+
`[Environment]::SetEnvironmentVariable('Path', '${escapedPath}', 'User')`,
|
|
216
|
+
],
|
|
217
|
+
{ stdio: ['ignore', 'pipe', 'pipe'] }
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
process.env.PATH = process.env.PATH ? `${process.env.PATH};${binDir}` : binDir;
|
|
221
|
+
return { changed: true };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function detectLinuxShellProfile() {
|
|
225
|
+
const home = process.env.HOME;
|
|
226
|
+
if (!home) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const shellName = path.basename(process.env.SHELL || '');
|
|
231
|
+
if (shellName === 'bash') {
|
|
232
|
+
return path.join(home, '.bashrc');
|
|
233
|
+
}
|
|
234
|
+
if (shellName === 'zsh') {
|
|
235
|
+
return path.join(home, '.zshrc');
|
|
236
|
+
}
|
|
237
|
+
return path.join(home, '.profile');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function repairLinuxShellPath(binDir) {
|
|
241
|
+
const home = process.env.HOME;
|
|
242
|
+
if (!home) {
|
|
243
|
+
return { changed: false, reason: 'missing-home' };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const normalizedHome = normalizePathForCompare(home) + path.sep;
|
|
247
|
+
const normalizedBinDir = normalizePathForCompare(binDir);
|
|
248
|
+
|
|
249
|
+
if (!normalizedBinDir.startsWith(normalizedHome)) {
|
|
250
|
+
return { changed: false, reason: 'bin-outside-home' };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const profilePath = detectLinuxShellProfile();
|
|
254
|
+
if (!profilePath) {
|
|
255
|
+
return { changed: false, reason: 'missing-profile' };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let profileContent = '';
|
|
259
|
+
if (fs.existsSync(profilePath)) {
|
|
260
|
+
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
|
+
}
|
|
277
|
+
|
|
278
|
+
if (profileReferencesBinDir(profileContent, binDir)) {
|
|
279
|
+
return { changed: false, configured: true, profilePath };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const prefix = profileContent && !profileContent.endsWith('\n') ? '\n' : '';
|
|
284
|
+
const block =
|
|
285
|
+
`${prefix}${LINUX_PATH_MARKER_START}\n` +
|
|
286
|
+
`export PATH="$PATH:${binDir}"\n` +
|
|
287
|
+
`${LINUX_PATH_MARKER_END}\n`;
|
|
288
|
+
fs.appendFileSync(profilePath, block);
|
|
289
|
+
process.env.PATH = process.env.PATH ? `${process.env.PATH}:${binDir}` : binDir;
|
|
290
|
+
return { changed: true, profilePath };
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function ensureGlobalBinOnPath() {
|
|
294
|
+
const binDir = getGlobalBinDir();
|
|
295
|
+
if (!binDir) {
|
|
296
|
+
console.warn('tessra: no se pudo inferir el directorio global de npm para verificar PATH.');
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (pathContains(binDir, process.env.PATH || '')) {
|
|
301
|
+
console.log(`tessra: comando global disponible en PATH (${binDir}).`);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (process.platform === 'win32') {
|
|
306
|
+
try {
|
|
307
|
+
const result = repairWindowsUserPath(binDir);
|
|
308
|
+
if (result.changed) {
|
|
309
|
+
console.log('tessra: PATH reparado en Windows.');
|
|
310
|
+
} else {
|
|
311
|
+
console.log('tessra: PATH de usuario ya estaba configurado en Windows.');
|
|
312
|
+
}
|
|
313
|
+
console.log('tessra: abre una PowerShell nueva y ejecuta: tessra --version');
|
|
314
|
+
return;
|
|
315
|
+
} catch (err) {
|
|
316
|
+
console.warn('tessra: no se pudo reparar PATH automaticamente en Windows.');
|
|
317
|
+
console.warn(`tessra: ${err.message}`);
|
|
318
|
+
printManualFix(binDir);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (process.platform === 'linux') {
|
|
324
|
+
try {
|
|
325
|
+
const result = repairLinuxShellPath(binDir);
|
|
326
|
+
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');
|
|
329
|
+
} 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');
|
|
332
|
+
} else {
|
|
333
|
+
console.warn('tessra: no se aplico auto-fix de PATH en Linux.');
|
|
334
|
+
printManualFix(binDir);
|
|
335
|
+
}
|
|
336
|
+
return;
|
|
337
|
+
} catch (err) {
|
|
338
|
+
console.warn('tessra: no se pudo reparar PATH automaticamente en Linux.');
|
|
339
|
+
console.warn(`tessra: ${err.message}`);
|
|
340
|
+
printManualFix(binDir);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
129
346
|
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
130
347
|
|
|
131
348
|
async function main() {
|
|
@@ -167,6 +384,8 @@ async function main() {
|
|
|
167
384
|
// --version puede fallar si el binario requiere inicialización — no es fatal
|
|
168
385
|
}
|
|
169
386
|
|
|
387
|
+
ensureGlobalBinOnPath();
|
|
388
|
+
|
|
170
389
|
console.log(`tessra: binario instalado correctamente en ${dest}`);
|
|
171
390
|
}
|
|
172
391
|
|