bortexcode 1.2.1 → 1.2.3
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/bin/bortex.js +377 -7
- package/package.json +2 -1
package/bin/bortex.js
CHANGED
|
@@ -170,7 +170,7 @@ function usage() {
|
|
|
170
170
|
console.log(' -h, --help Show help');
|
|
171
171
|
console.log('');
|
|
172
172
|
console.log('REPL commands: /agent on|off, /status, /exit, /help, /llm-config');
|
|
173
|
-
console.log('Local tools: /pwd, /cd, /ls, /tree, /read, /write, /append, /mkdir, /git, /sh');
|
|
173
|
+
console.log('Local tools: /pwd, /cd, /ls, /tree, /read, /write, /append, /mkdir, /git, /ssh-status, /sh');
|
|
174
174
|
console.log('');
|
|
175
175
|
console.log('Environment:');
|
|
176
176
|
console.log(' BORTEX_URL Server URL');
|
|
@@ -427,16 +427,15 @@ async function maybePromptForCliUpdate(opts = {}) {
|
|
|
427
427
|
const installTarget = latest.installTarget || 'bortexcode';
|
|
428
428
|
const installCommand = latest.installCommand || `npm install -g ${installTarget}`;
|
|
429
429
|
|
|
430
|
-
if (shouldSkipUpdatePrompt(latest.version) && !opts.forceUpdateCheck) {
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
430
|
console.log(`Update available: Bortex Code ${CLI_VERSION} -> ${latest.version}`);
|
|
435
431
|
console.log(`Install command: ${installCommand}`);
|
|
436
432
|
|
|
433
|
+
if (!process.stdin?.isTTY || !process.stdout?.isTTY) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
437
|
const shouldInstall = await promptYesNo('Install update now?', true);
|
|
438
438
|
if (!shouldInstall) {
|
|
439
|
-
rememberDeclinedUpdate(latest.version);
|
|
440
439
|
return;
|
|
441
440
|
}
|
|
442
441
|
|
|
@@ -1147,10 +1146,257 @@ function detectWorkspaceProfile(opts) {
|
|
|
1147
1146
|
return profile;
|
|
1148
1147
|
}
|
|
1149
1148
|
|
|
1149
|
+
function classifyLocalPromptIntent(text) {
|
|
1150
|
+
const t = String(text || '').toLowerCase();
|
|
1151
|
+
if (!t) return null;
|
|
1152
|
+
const wantsStatus = /(verifica|controlla|check|show|mostra|stato|status|attivo|active|running|enabled|on)/.test(t);
|
|
1153
|
+
if (wantsStatus && (/(\bssh\b|sshd|remote login)/.test(t))) {
|
|
1154
|
+
return { command: '/ssh-status' };
|
|
1155
|
+
}
|
|
1156
|
+
if (wantsStatus && (/(whoami|username|user name|hostname|host name|machine|computer|system|sistema|platform|os|runtime|shell|environment|env|path)/.test(t) || /(\bnode\b|\bnpm\b)/.test(t))) {
|
|
1157
|
+
return { command: '/sys-status' };
|
|
1158
|
+
}
|
|
1159
|
+
if (wantsStatus && /(\bprocess\b|\bprocessi\b|\bpid\b|\bcpu\b|\bram\b|\bmemory\b|\bmem\b)/.test(t)) {
|
|
1160
|
+
const processMatch = t.match(/(?:process(?:o|i)?|pid)\s+(?:di\s+|of\s+)?([a-z0-9._-]{2,})/i) || t.match(/([a-z0-9._-]{2,})\s+(?:process(?:o|i)?|pid)/i);
|
|
1161
|
+
return { command: processMatch ? `/process-status ${processMatch[1]}` : '/process-status' };
|
|
1162
|
+
}
|
|
1163
|
+
if (wantsStatus && /(\bport\b|\bporte\b|\bsocket\b|\blisten\b|\blistening\b)/.test(t)) {
|
|
1164
|
+
const portMatch = t.match(/\b(\d{2,5})\b/);
|
|
1165
|
+
return { command: portMatch ? `/port-status ${portMatch[1]}` : '/port-status' };
|
|
1166
|
+
}
|
|
1167
|
+
return null;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
async function runSshStatusCommand(opts, { quiet = false } = {}) {
|
|
1171
|
+
const cwd = opts?.cwd || process.cwd();
|
|
1172
|
+
const platform = os.platform();
|
|
1173
|
+
let command = '';
|
|
1174
|
+
let stdout = '';
|
|
1175
|
+
let stderr = '';
|
|
1176
|
+
let enabled = null;
|
|
1177
|
+
let active = null;
|
|
1178
|
+
|
|
1179
|
+
if (platform === 'darwin') {
|
|
1180
|
+
command = 'launchctl print-disabled system | grep com.openssh.sshd';
|
|
1181
|
+
const res = await runChild('bash', ['-lc', 'launchctl print-disabled system 2>/dev/null | grep -i "com.openssh.sshd" || true'], { cwd, shell: false });
|
|
1182
|
+
stdout = String(res.stdout || '').trim();
|
|
1183
|
+
stderr = String(res.stderr || '').trim();
|
|
1184
|
+
const match = stdout.match(/com\.openssh\.sshd"\s*=>\s*(enabled|disabled)/i);
|
|
1185
|
+
if (match) {
|
|
1186
|
+
enabled = match[1].toLowerCase() === 'enabled';
|
|
1187
|
+
active = enabled;
|
|
1188
|
+
}
|
|
1189
|
+
} else if (platform === 'win32') {
|
|
1190
|
+
command = 'Get-Service sshd';
|
|
1191
|
+
const res = await runChild('powershell.exe', [
|
|
1192
|
+
'-NoProfile',
|
|
1193
|
+
'-Command',
|
|
1194
|
+
'if (Get-Service sshd -ErrorAction SilentlyContinue) { (Get-Service sshd).Status } else { "Missing" }'
|
|
1195
|
+
], { cwd, shell: false });
|
|
1196
|
+
stdout = String(res.stdout || '').trim();
|
|
1197
|
+
stderr = String(res.stderr || '').trim();
|
|
1198
|
+
if (/running/i.test(stdout)) active = true;
|
|
1199
|
+
else if (/stopped/i.test(stdout)) active = false;
|
|
1200
|
+
} else {
|
|
1201
|
+
command = 'systemctl is-active sshd || systemctl is-active ssh';
|
|
1202
|
+
const res = await runChild('bash', [
|
|
1203
|
+
'-lc',
|
|
1204
|
+
'if command -v systemctl >/dev/null 2>&1; then if systemctl is-active sshd >/dev/null 2>&1 || systemctl is-active ssh >/dev/null 2>&1; then echo active; elif systemctl is-enabled sshd >/dev/null 2>&1 || systemctl is-enabled ssh >/dev/null 2>&1; then echo enabled; else echo inactive; fi; elif pgrep -x sshd >/dev/null 2>&1; then echo active; else echo unknown; fi'
|
|
1205
|
+
], { cwd, shell: false });
|
|
1206
|
+
stdout = String(res.stdout || '').trim();
|
|
1207
|
+
stderr = String(res.stderr || '').trim();
|
|
1208
|
+
if (/active/i.test(stdout)) active = true;
|
|
1209
|
+
else if (/enabled/i.test(stdout)) active = true;
|
|
1210
|
+
else if (/inactive|stopped/i.test(stdout)) active = false;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
const statusText = platform === 'darwin'
|
|
1214
|
+
? `macOS Remote Login: ${enabled === true ? 'On' : enabled === false ? 'Off' : 'Unknown'}`
|
|
1215
|
+
: platform === 'win32'
|
|
1216
|
+
? `Windows SSH service: ${stdout || 'Unknown'}`
|
|
1217
|
+
: `SSH service: ${active === true ? 'active' : active === false ? 'inactive' : stdout || 'unknown'}`;
|
|
1218
|
+
|
|
1219
|
+
if (!quiet) console.log(statusText);
|
|
1220
|
+
return {
|
|
1221
|
+
ok: true,
|
|
1222
|
+
stdout: statusText,
|
|
1223
|
+
stderr: stderr || null,
|
|
1224
|
+
data: {
|
|
1225
|
+
platform,
|
|
1226
|
+
command,
|
|
1227
|
+
enabled,
|
|
1228
|
+
active,
|
|
1229
|
+
raw: stdout || null,
|
|
1230
|
+
stderr: stderr || null
|
|
1231
|
+
}
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
async function runSystemStatusCommand(opts) {
|
|
1236
|
+
const cwd = opts?.cwd || process.cwd();
|
|
1237
|
+
const platform = os.platform();
|
|
1238
|
+
const arch = os.arch();
|
|
1239
|
+
const release = os.release();
|
|
1240
|
+
const shell = String(process.env.SHELL || process.env.ComSpec || '').trim() || null;
|
|
1241
|
+
|
|
1242
|
+
const runText = async (command, args, fallback = '') => {
|
|
1243
|
+
const res = await runChild(command, args, { cwd, shell: false });
|
|
1244
|
+
const text = String(res.stdout || '').trim();
|
|
1245
|
+
if (text) return text.split(/\r?\n/)[0].trim();
|
|
1246
|
+
return fallback;
|
|
1247
|
+
};
|
|
1248
|
+
|
|
1249
|
+
let user = null;
|
|
1250
|
+
let host = null;
|
|
1251
|
+
if (platform === 'win32') {
|
|
1252
|
+
user = await runText('powershell.exe', ['-NoProfile', '-Command', '$env:USERNAME'], 'unknown');
|
|
1253
|
+
host = await runText('powershell.exe', ['-NoProfile', '-Command', '$env:COMPUTERNAME'], 'unknown');
|
|
1254
|
+
} else {
|
|
1255
|
+
user = await runText('bash', ['-lc', 'whoami'], 'unknown');
|
|
1256
|
+
host = await runText('bash', ['-lc', 'hostname'], 'unknown');
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
const nodeRes = await runChild('node', ['--version'], { cwd, shell: false });
|
|
1260
|
+
const npmRes = await runChild('npm', ['--version'], { cwd, shell: false });
|
|
1261
|
+
const gitRes = await runChild('git', ['--version'], { cwd, shell: false });
|
|
1262
|
+
const sshRes = await runSshStatusCommand(opts, { quiet: true });
|
|
1263
|
+
const lines = [
|
|
1264
|
+
`Platform: ${platform} ${release} ${arch}`,
|
|
1265
|
+
`User: ${user}`,
|
|
1266
|
+
`Host: ${host}`,
|
|
1267
|
+
`Shell: ${shell || 'unknown'}`,
|
|
1268
|
+
`Node: ${nodeRes.ok ? String(nodeRes.stdout || '').trim() : 'not found'}`,
|
|
1269
|
+
`npm: ${npmRes.ok ? String(npmRes.stdout || '').trim() : 'not found'}`,
|
|
1270
|
+
`Git: ${gitRes.ok ? String(gitRes.stdout || '').trim() : 'not found'}`,
|
|
1271
|
+
sshRes?.stdout ? `SSH: ${sshRes.stdout}` : 'SSH: unknown'
|
|
1272
|
+
];
|
|
1273
|
+
const summary = lines.join('\n');
|
|
1274
|
+
console.log(summary);
|
|
1275
|
+
return {
|
|
1276
|
+
ok: true,
|
|
1277
|
+
stdout: summary,
|
|
1278
|
+
stderr: null,
|
|
1279
|
+
data: {
|
|
1280
|
+
platform,
|
|
1281
|
+
release,
|
|
1282
|
+
arch,
|
|
1283
|
+
user,
|
|
1284
|
+
host,
|
|
1285
|
+
shell,
|
|
1286
|
+
nodeVersion: nodeRes.ok ? String(nodeRes.stdout || '').trim() : null,
|
|
1287
|
+
npmVersion: npmRes.ok ? String(npmRes.stdout || '').trim() : null,
|
|
1288
|
+
gitVersion: gitRes.ok ? String(gitRes.stdout || '').trim() : null,
|
|
1289
|
+
ssh: sshRes?.data || null
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
async function runProcessStatusCommand(opts, call = {}) {
|
|
1295
|
+
const cwd = opts?.cwd || process.cwd();
|
|
1296
|
+
const platform = os.platform();
|
|
1297
|
+
const filter = String(call.name || call.filter || '').trim();
|
|
1298
|
+
const filterLabel = filter || null;
|
|
1299
|
+
let command = '';
|
|
1300
|
+
let stdout = '';
|
|
1301
|
+
let stderr = '';
|
|
1302
|
+
|
|
1303
|
+
if (platform === 'win32') {
|
|
1304
|
+
const escaped = filter.replace(/'/g, "''");
|
|
1305
|
+
const ps = filter
|
|
1306
|
+
? `Get-Process | Where-Object { $_.ProcessName -like '*${escaped}*' } | Select-Object -First 20 Id,ProcessName,CPU,WS | Format-Table -AutoSize`
|
|
1307
|
+
: 'Get-Process | Sort-Object CPU -Descending | Select-Object -First 12 Id,ProcessName,CPU,WS | Format-Table -AutoSize';
|
|
1308
|
+
command = ps;
|
|
1309
|
+
const res = await runChild('powershell.exe', ['-NoProfile', '-Command', ps], { cwd, shell: false });
|
|
1310
|
+
stdout = String(res.stdout || '').trim();
|
|
1311
|
+
stderr = String(res.stderr || '').trim();
|
|
1312
|
+
} else {
|
|
1313
|
+
const quoted = JSON.stringify(filter);
|
|
1314
|
+
const bashCmd = filter
|
|
1315
|
+
? `pgrep -af ${quoted} || true`
|
|
1316
|
+
: 'ps -ax -o pid=,ppid=,comm=,%cpu=,%mem= | head -n 12';
|
|
1317
|
+
command = bashCmd;
|
|
1318
|
+
const res = await runChild('bash', ['-lc', bashCmd], { cwd, shell: false });
|
|
1319
|
+
stdout = String(res.stdout || '').trim();
|
|
1320
|
+
stderr = String(res.stderr || '').trim();
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
const text = stdout || '(no matching processes)';
|
|
1324
|
+
console.log(text);
|
|
1325
|
+
return {
|
|
1326
|
+
ok: true,
|
|
1327
|
+
stdout: text,
|
|
1328
|
+
stderr: stderr || null,
|
|
1329
|
+
data: {
|
|
1330
|
+
platform,
|
|
1331
|
+
filter: filterLabel,
|
|
1332
|
+
command,
|
|
1333
|
+
raw: stdout || null,
|
|
1334
|
+
stderr: stderr || null
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
async function runPortStatusCommand(opts, call = {}) {
|
|
1340
|
+
const cwd = opts?.cwd || process.cwd();
|
|
1341
|
+
const platform = os.platform();
|
|
1342
|
+
const target = String(call.port || call.target || '').trim();
|
|
1343
|
+
const isPort = /^\d+$/.test(target);
|
|
1344
|
+
let command = '';
|
|
1345
|
+
let stdout = '';
|
|
1346
|
+
let stderr = '';
|
|
1347
|
+
|
|
1348
|
+
if (platform === 'win32') {
|
|
1349
|
+
const ps = isPort
|
|
1350
|
+
? `Get-NetTCPConnection -State Listen -LocalPort ${Number(target)} | Select-Object LocalAddress,LocalPort,OwningProcess | Format-Table -AutoSize`
|
|
1351
|
+
: 'Get-NetTCPConnection -State Listen | Select-Object -First 20 LocalAddress,LocalPort,OwningProcess | Format-Table -AutoSize';
|
|
1352
|
+
command = ps;
|
|
1353
|
+
const res = await runChild('powershell.exe', ['-NoProfile', '-Command', ps], { cwd, shell: false });
|
|
1354
|
+
stdout = String(res.stdout || '').trim();
|
|
1355
|
+
stderr = String(res.stderr || '').trim();
|
|
1356
|
+
} else {
|
|
1357
|
+
const bashCmd = isPort
|
|
1358
|
+
? `if command -v lsof >/dev/null 2>&1; then lsof -nP -iTCP:${Number(target)} -sTCP:LISTEN || true; elif command -v ss >/dev/null 2>&1; then ss -ltnp | grep ":${Number(target)} " || true; else netstat -an 2>/dev/null | grep "${Number(target)}" || true; fi`
|
|
1359
|
+
: 'if command -v lsof >/dev/null 2>&1; then lsof -nP -iTCP -sTCP:LISTEN | head -n 20; elif command -v ss >/dev/null 2>&1; then ss -ltnp | head -n 20; else netstat -an 2>/dev/null | grep LISTEN | head -n 20; fi';
|
|
1360
|
+
command = bashCmd;
|
|
1361
|
+
const res = await runChild('bash', ['-lc', bashCmd], { cwd, shell: false });
|
|
1362
|
+
stdout = String(res.stdout || '').trim();
|
|
1363
|
+
stderr = String(res.stderr || '').trim();
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
const text = stdout || (isPort ? `(port ${target} not listening)` : '(no listening ports found)');
|
|
1367
|
+
console.log(text);
|
|
1368
|
+
return {
|
|
1369
|
+
ok: true,
|
|
1370
|
+
stdout: text,
|
|
1371
|
+
stderr: stderr || null,
|
|
1372
|
+
data: {
|
|
1373
|
+
platform,
|
|
1374
|
+
target: target || null,
|
|
1375
|
+
command,
|
|
1376
|
+
raw: stdout || null,
|
|
1377
|
+
stderr: stderr || null
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1150
1382
|
function suggestCommandsForStep(stepText, opts) {
|
|
1151
1383
|
const t = String(stepText || '').toLowerCase();
|
|
1152
1384
|
const profile = opts ? detectWorkspaceProfile(opts) : { suggested: { test: [] } };
|
|
1153
1385
|
const testCmds = Array.isArray(profile.suggested?.test) ? profile.suggested.test.slice(0, 2) : [];
|
|
1386
|
+
if (/(\bssh\b|sshd|remote login)/.test(t) && /(verifica|controlla|check|show|mostra|stato|status|attivo|active|running|enabled|on)/.test(t)) {
|
|
1387
|
+
return ['/ssh-status', '/status'];
|
|
1388
|
+
}
|
|
1389
|
+
if ((/(whoami|username|user name|hostname|host name|machine|computer|system|sistema|platform|os|runtime|shell|environment|env|path)/.test(t) || /(\bnode\b|\bnpm\b)/.test(t)) && /(verifica|controlla|check|show|mostra|stato|status|attivo|active|running|enabled|on)/.test(t)) {
|
|
1390
|
+
return ['/sys-status', '/status'];
|
|
1391
|
+
}
|
|
1392
|
+
if (/(\bprocess\b|\bprocessi\b|\bpid\b|\bcpu\b|\bram\b|\bmemory\b|\bmem\b)/.test(t) && /(verifica|controlla|check|show|mostra|stato|status|attivo|active|running|enabled|on)/.test(t)) {
|
|
1393
|
+
const processMatch = t.match(/(?:process(?:o|i)?|pid)\s+(?:di\s+|of\s+)?([a-z0-9._-]{2,})/i) || t.match(/([a-z0-9._-]{2,})\s+(?:process(?:o|i)?|pid)/i);
|
|
1394
|
+
return [processMatch ? `/process-status ${processMatch[1]}` : '/process-status', '/status'];
|
|
1395
|
+
}
|
|
1396
|
+
if (/(\bport\b|\bporte\b|\bsocket\b|\blisten\b|\blistening\b)/.test(t) && /(verifica|controlla|check|show|mostra|stato|status|attivo|active|running|enabled|on)/.test(t)) {
|
|
1397
|
+
const portMatch = t.match(/\b(\d{2,5})\b/);
|
|
1398
|
+
return [portMatch ? `/port-status ${portMatch[1]}` : '/port-status', '/status'];
|
|
1399
|
+
}
|
|
1154
1400
|
if (/analizza|contesto|file/.test(t)) return ['/ls', '/tree . 2', '/git status -sb', '/diff all'];
|
|
1155
1401
|
if (/riproduci|problema|causa/.test(t)) return ['/git status -sb', '/read <file>', ...(testCmds[0] ? [`/sh ${testCmds[0]}`] : ['/sh <cmd test/run>'])];
|
|
1156
1402
|
if (/fix|modifiche|implementa/.test(t)) return ['/read <file>', '/write <file> <text> | /patch-apply-inline', '/diff unstaged'];
|
|
@@ -1194,6 +1440,10 @@ function isReadMostlyCommandForAuto(cmd) {
|
|
|
1194
1440
|
c.startsWith('/hunks') ||
|
|
1195
1441
|
c.startsWith('/show-hunk') ||
|
|
1196
1442
|
c.startsWith('/status') ||
|
|
1443
|
+
c.startsWith('/ssh-status') ||
|
|
1444
|
+
c.startsWith('/sys-status') ||
|
|
1445
|
+
c.startsWith('/process-status') ||
|
|
1446
|
+
c.startsWith('/port-status') ||
|
|
1197
1447
|
c.startsWith('/history') ||
|
|
1198
1448
|
c.startsWith('/artifacts') ||
|
|
1199
1449
|
c.startsWith('/project') ||
|
|
@@ -2456,6 +2706,18 @@ async function executeStructuredBuiltinTool(opts, call) {
|
|
|
2456
2706
|
};
|
|
2457
2707
|
return { ok: true, stdout: stdout || null, stderr: null, data };
|
|
2458
2708
|
}
|
|
2709
|
+
if (tool === 'sshStatus') {
|
|
2710
|
+
return runSshStatusCommand(opts);
|
|
2711
|
+
}
|
|
2712
|
+
if (tool === 'systemStatus') {
|
|
2713
|
+
return runSystemStatusCommand(opts);
|
|
2714
|
+
}
|
|
2715
|
+
if (tool === 'processStatus') {
|
|
2716
|
+
return runProcessStatusCommand(opts, call);
|
|
2717
|
+
}
|
|
2718
|
+
if (tool === 'portStatus') {
|
|
2719
|
+
return runPortStatusCommand(opts, call);
|
|
2720
|
+
}
|
|
2459
2721
|
if (tool === 'search') {
|
|
2460
2722
|
const pattern = String(call.pattern || '').trim();
|
|
2461
2723
|
const searchPath = String(call.path || '.');
|
|
@@ -2596,6 +2858,8 @@ function validateStructuredToolCall(raw) {
|
|
|
2596
2858
|
case 'pwd':
|
|
2597
2859
|
case 'project':
|
|
2598
2860
|
case 'gitStatus':
|
|
2861
|
+
case 'systemStatus':
|
|
2862
|
+
case 'sshStatus':
|
|
2599
2863
|
break;
|
|
2600
2864
|
case 'runTest':
|
|
2601
2865
|
case 'runBuild':
|
|
@@ -2666,6 +2930,14 @@ function validateStructuredToolCall(raw) {
|
|
|
2666
2930
|
break;
|
|
2667
2931
|
case 'commitSuggest':
|
|
2668
2932
|
break;
|
|
2933
|
+
case 'processStatus':
|
|
2934
|
+
if (call.name != null && typeof call.name !== 'string') return { ok: false, error: '`name` deve essere stringa.' };
|
|
2935
|
+
if (call.filter != null && typeof call.filter !== 'string') return { ok: false, error: '`filter` deve essere stringa.' };
|
|
2936
|
+
break;
|
|
2937
|
+
case 'portStatus':
|
|
2938
|
+
if (call.port != null && typeof call.port !== 'string' && !Number.isFinite(Number(call.port))) return { ok: false, error: '`port` deve essere stringa o numero.' };
|
|
2939
|
+
if (call.target != null && typeof call.target !== 'string' && !Number.isFinite(Number(call.target))) return { ok: false, error: '`target` deve essere stringa o numero.' };
|
|
2940
|
+
break;
|
|
2669
2941
|
default:
|
|
2670
2942
|
return { ok: false, error: `Tool non supportato: ${tool}` };
|
|
2671
2943
|
}
|
|
@@ -2693,6 +2965,10 @@ function structuredToolCallToLocalCommand(call) {
|
|
|
2693
2965
|
case 'gitDiff': return `gitDiff(${call.mode || 'all'}${call.statOnly === false ? ',patch' : ',stat'})`;
|
|
2694
2966
|
case 'search': return `search(${call.pattern}${call.path ? ` in ${call.path}` : ''}${call.glob ? ` glob=${call.glob}` : ''})`;
|
|
2695
2967
|
case 'git': return `/git ${call.args.join(' ')}`;
|
|
2968
|
+
case 'sshStatus': return '/ssh-status';
|
|
2969
|
+
case 'systemStatus': return '/sys-status';
|
|
2970
|
+
case 'processStatus': return `/process-status${call.name || call.filter ? ` ${call.name || call.filter}` : ''}`;
|
|
2971
|
+
case 'portStatus': return `/port-status${call.port || call.target ? ` ${call.port || call.target}` : ''}`;
|
|
2696
2972
|
case 'sh': return `/sh ${call.cmd}`;
|
|
2697
2973
|
case 'review': {
|
|
2698
2974
|
const mode = call.mode ? ` --${call.mode}` : '';
|
|
@@ -2946,6 +3222,23 @@ function buildStructuredToolPlanFromGoal(opts, goal) {
|
|
|
2946
3222
|
{ tool: 'review', mode: 'all', fixSuggestions: true }
|
|
2947
3223
|
];
|
|
2948
3224
|
|
|
3225
|
+
const wantsStatus = /(verifica|controlla|check|mostra|stato|status|attivo|active|running|enabled|on)/.test(lower);
|
|
3226
|
+
if (wantsStatus && /(\bssh\b|sshd|remote login)/.test(lower)) {
|
|
3227
|
+
return [{ tool: 'project' }, { tool: 'sshStatus' }];
|
|
3228
|
+
}
|
|
3229
|
+
if (wantsStatus && (/(whoami|username|user name|hostname|host name|machine|computer|system|sistema|platform|os|runtime|shell|environment|env|path)/.test(lower) || /(\bnode\b|\bnpm\b)/.test(lower))) {
|
|
3230
|
+
return [{ tool: 'project' }, { tool: 'systemStatus' }];
|
|
3231
|
+
}
|
|
3232
|
+
if (wantsStatus && /(\bprocess\b|\bprocessi\b|\bpid\b|\bcpu\b|\bram\b|\bmemory\b|\bmem\b)/.test(lower)) {
|
|
3233
|
+
const processMatch = g.match(/(?:process(?:o|i)?|pid)\s+(?:di\s+|of\s+)?([a-z0-9._-]{2,})/i) || g.match(/([a-z0-9._-]{2,})\s+(?:process(?:o|i)?|pid)/i);
|
|
3234
|
+
const filter = processMatch ? processMatch[1] : '';
|
|
3235
|
+
return [{ tool: 'project' }, { tool: 'processStatus', ...(filter ? { name: filter } : {}) }];
|
|
3236
|
+
}
|
|
3237
|
+
if (wantsStatus && /(\bport\b|\bporte\b|\bsocket\b|\blisten\b|\blistening\b)/.test(lower)) {
|
|
3238
|
+
const portMatch = g.match(/\b(\d{2,5})\b/);
|
|
3239
|
+
const port = portMatch ? portMatch[1] : '';
|
|
3240
|
+
return [{ tool: 'project' }, { tool: 'portStatus', ...(port ? { port } : {}) }];
|
|
3241
|
+
}
|
|
2949
3242
|
if (/bug|errore|fix/.test(lower)) {
|
|
2950
3243
|
const extractedKeyword = g.split(/\s+/).filter((w) => w.length >= 4).slice(-1)[0];
|
|
2951
3244
|
if (extractedKeyword) plan.push({ tool: 'search', pattern: extractedKeyword, path: '.', maxResults: 20 });
|
|
@@ -3064,7 +3357,7 @@ async function buildStructuredToolPlanWithLlm(opts, goal, llmOptions = {}) {
|
|
|
3064
3357
|
const profile = detectWorkspaceProfile(opts);
|
|
3065
3358
|
const supportedTools = [
|
|
3066
3359
|
'project', 'pwd', 'ls', 'tree', 'glob', 'read', 'readMany', 'mkdir', 'write', 'append',
|
|
3067
|
-
'diff', 'git', 'gitStatus', 'gitDiff', 'search', 'runTest', 'runBuild', 'runLint', 'sh', 'review', 'commitSuggest'
|
|
3360
|
+
'diff', 'git', 'gitStatus', 'gitDiff', 'search', 'runTest', 'runBuild', 'runLint', 'sshStatus', 'systemStatus', 'processStatus', 'portStatus', 'sh', 'review', 'commitSuggest'
|
|
3068
3361
|
];
|
|
3069
3362
|
const testCmd = profile.suggested.test?.[0] || '';
|
|
3070
3363
|
const buildCmd = profile.suggested.build?.[0] || '';
|
|
@@ -3155,6 +3448,7 @@ async function buildStructuredToolPlanWithLlm(opts, goal, llmOptions = {}) {
|
|
|
3155
3448
|
'Usa solo campi validi per tool.',
|
|
3156
3449
|
'Regole:',
|
|
3157
3450
|
'- Preferisci tool read-only prima dei tool rischiosi',
|
|
3451
|
+
'- Per controlli di stato locale usa sshStatus/systemStatus/processStatus/portStatus invece di sh quando possibile',
|
|
3158
3452
|
'- Includi review e commitSuggest verso la fine',
|
|
3159
3453
|
'- Se proponi `runTest`/`runBuild`/`runLint` preferiscili a `sh` quando possibile',
|
|
3160
3454
|
'- Se proponi `sh`, usa comandi test/build/lint concreti se disponibili',
|
|
@@ -3368,6 +3662,10 @@ function printLocalHelp() {
|
|
|
3368
3662
|
console.log(' /append <file> <text>');
|
|
3369
3663
|
console.log(' /mkdir <path>');
|
|
3370
3664
|
console.log(' /git <args...> es: /git status -sb');
|
|
3665
|
+
console.log(' /ssh-status check SSH / Remote Login status');
|
|
3666
|
+
console.log(' /sys-status check system / runtime status');
|
|
3667
|
+
console.log(' /process-status check running processes');
|
|
3668
|
+
console.log(' /port-status check listening ports');
|
|
3371
3669
|
console.log(' /diff [unstaged|staged|all]');
|
|
3372
3670
|
console.log(' /stage <file>|--all');
|
|
3373
3671
|
console.log(' /unstage <file>|--all');
|
|
@@ -3453,6 +3751,34 @@ function printTodoList(opts) {
|
|
|
3453
3751
|
function makeSimplePlan(goal) {
|
|
3454
3752
|
const g = String(goal || '').trim();
|
|
3455
3753
|
if (!g) return [];
|
|
3754
|
+
if (/(\bssh\b|sshd|remote login)/i.test(g) && /(verifica|controlla|check|mostra|stato|status|attivo|active|running|enabled|on)/i.test(g)) {
|
|
3755
|
+
return [
|
|
3756
|
+
'Verifica stato SSH / Remote Login',
|
|
3757
|
+
"Riporta l'esito del controllo"
|
|
3758
|
+
];
|
|
3759
|
+
}
|
|
3760
|
+
if (/(whoami|username|user name|hostname|host name|machine|computer|system|sistema|platform|os|runtime|shell|environment|env|path|\bnode\b|\bnpm\b)/i.test(g) && /(verifica|controlla|check|mostra|stato|status|attivo|active|running|enabled|on)/i.test(g)) {
|
|
3761
|
+
return [
|
|
3762
|
+
'Verifica stato del sistema locale',
|
|
3763
|
+
"Riporta l'esito del controllo"
|
|
3764
|
+
];
|
|
3765
|
+
}
|
|
3766
|
+
if (/(\bprocess\b|\bprocessi\b|\bpid\b|\bcpu\b|\bram\b|\bmemory\b|\bmem\b)/i.test(g) && /(verifica|controlla|check|mostra|stato|status|attivo|active|running|enabled|on)/i.test(g)) {
|
|
3767
|
+
const processMatch = g.match(/(?:process(?:o|i)?|pid)\s+(?:di\s+|of\s+)?([a-z0-9._-]{2,})/i) || g.match(/([a-z0-9._-]{2,})\s+(?:process(?:o|i)?|pid)/i);
|
|
3768
|
+
const target = processMatch ? processMatch[1] : '';
|
|
3769
|
+
return [
|
|
3770
|
+
target ? `Verifica il processo ${target}` : 'Verifica i processi attivi o il processo indicato',
|
|
3771
|
+
"Riporta l'esito del controllo"
|
|
3772
|
+
];
|
|
3773
|
+
}
|
|
3774
|
+
if (/(\bport\b|\bporte\b|\bsocket\b|\blisten\b|\blistening\b)/i.test(g) && /(verifica|controlla|check|mostra|stato|status|attivo|active|running|enabled|on)/i.test(g)) {
|
|
3775
|
+
const portMatch = g.match(/\b(\d{2,5})\b/);
|
|
3776
|
+
const target = portMatch ? portMatch[1] : '';
|
|
3777
|
+
return [
|
|
3778
|
+
target ? `Verifica la porta ${target} in ascolto` : 'Verifica le porte in ascolto',
|
|
3779
|
+
"Riporta l'esito del controllo"
|
|
3780
|
+
];
|
|
3781
|
+
}
|
|
3456
3782
|
const steps = ['Analizza contesto e file coinvolti'];
|
|
3457
3783
|
if (/bug|errore|fix/i.test(g)) {
|
|
3458
3784
|
steps.push('Riproduci il problema e isola la causa');
|
|
@@ -5567,6 +5893,31 @@ async function handleLocalCommand(opts, line) {
|
|
|
5567
5893
|
}
|
|
5568
5894
|
};
|
|
5569
5895
|
}
|
|
5896
|
+
if (lc === '/sys-status') {
|
|
5897
|
+
recordArtifact(opts, 'sys-status', 'check');
|
|
5898
|
+
const data = await runSystemStatusCommand(opts);
|
|
5899
|
+
try { saveCliWorkspaceState(opts); } catch (_err) { }
|
|
5900
|
+
return { handled: true, data };
|
|
5901
|
+
}
|
|
5902
|
+
if (lc === '/process-status') {
|
|
5903
|
+
recordArtifact(opts, 'process-status', rest.join(' ') || 'check');
|
|
5904
|
+
const data = await runProcessStatusCommand(opts, { name: rest.join(' ') });
|
|
5905
|
+
try { saveCliWorkspaceState(opts); } catch (_err) { }
|
|
5906
|
+
return { handled: true, data };
|
|
5907
|
+
}
|
|
5908
|
+
if (lc === '/port-status') {
|
|
5909
|
+
const target = String(rest[0] || '').trim();
|
|
5910
|
+
recordArtifact(opts, 'port-status', target || 'check');
|
|
5911
|
+
const data = await runPortStatusCommand(opts, { port: target });
|
|
5912
|
+
try { saveCliWorkspaceState(opts); } catch (_err) { }
|
|
5913
|
+
return { handled: true, data };
|
|
5914
|
+
}
|
|
5915
|
+
if (lc === '/ssh-status') {
|
|
5916
|
+
recordArtifact(opts, 'ssh-status', 'check');
|
|
5917
|
+
const data = await runSshStatusCommand(opts);
|
|
5918
|
+
try { saveCliWorkspaceState(opts); } catch (_err) { }
|
|
5919
|
+
return { handled: true, data };
|
|
5920
|
+
}
|
|
5570
5921
|
if (lc === '/agent-run') {
|
|
5571
5922
|
const sub = String(rest[0] || '').toLowerCase();
|
|
5572
5923
|
if (sub === 'policy') {
|
|
@@ -5932,6 +6283,10 @@ const SLASH_COMMANDS = [
|
|
|
5932
6283
|
['/write <file> <text>', 'Write a file'],
|
|
5933
6284
|
['/append <file> <text>', 'Append to a file'],
|
|
5934
6285
|
['/git <args>', 'Run a git command'],
|
|
6286
|
+
['/ssh-status', 'Check SSH/Remote Login status'],
|
|
6287
|
+
['/sys-status', 'Check local system/runtime status'],
|
|
6288
|
+
['/process-status', 'Check running processes'],
|
|
6289
|
+
['/port-status', 'Check listening ports'],
|
|
5935
6290
|
['/diff [unstaged|staged|all]', 'Show git diff'],
|
|
5936
6291
|
['/review [--staged|--all]', 'Review local changes'],
|
|
5937
6292
|
['/commit-suggest [--staged]', 'Suggest a commit message'],
|
|
@@ -6038,6 +6393,11 @@ function printWelcomeCard(opts) {
|
|
|
6038
6393
|
}
|
|
6039
6394
|
|
|
6040
6395
|
async function runSinglePrompt(opts) {
|
|
6396
|
+
const localIntent = classifyLocalPromptIntent(opts.prompt);
|
|
6397
|
+
if (localIntent?.command) {
|
|
6398
|
+
const localResult = await handleLocalCommand(opts, localIntent.command);
|
|
6399
|
+
if (localResult.handled) return;
|
|
6400
|
+
}
|
|
6041
6401
|
if (opts.offline) {
|
|
6042
6402
|
console.error('Modalita --offline: usa REPL interattiva e comandi locali (/help).');
|
|
6043
6403
|
process.exit(1);
|
|
@@ -6121,6 +6481,16 @@ async function runRepl(opts) {
|
|
|
6121
6481
|
console.error(`Local tool error: ${err.message}`);
|
|
6122
6482
|
continue;
|
|
6123
6483
|
}
|
|
6484
|
+
const localIntent = classifyLocalPromptIntent(line);
|
|
6485
|
+
if (localIntent?.command) {
|
|
6486
|
+
try {
|
|
6487
|
+
const localResult = await handleLocalCommand(opts, localIntent.command);
|
|
6488
|
+
if (localResult.handled) continue;
|
|
6489
|
+
} catch (err) {
|
|
6490
|
+
console.error(`Local tool error: ${err.message}`);
|
|
6491
|
+
continue;
|
|
6492
|
+
}
|
|
6493
|
+
}
|
|
6124
6494
|
if (opts.offline) {
|
|
6125
6495
|
console.log('Offline mode: use /help for local tools or restart without --offline to use the server.');
|
|
6126
6496
|
continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bortexcode",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "Bortex Code CLI - AI coding assistant powered by bortex.site",
|
|
5
5
|
"homepage": "https://bortex.site",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"node": ">=18.0.0"
|
|
18
18
|
},
|
|
19
19
|
"bin": {
|
|
20
|
+
"bortex": "bin/bortexcode.js",
|
|
20
21
|
"bortexcode": "bin/bortexcode.js",
|
|
21
22
|
"bortex-code": "bin/bortexcode.js",
|
|
22
23
|
"bortex-agent": "bin/bortexcode-agent.js"
|