bortexcode 1.2.1 → 1.2.2

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