bortexcode 1.2.4 → 1.2.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.
Files changed (2) hide show
  1. package/bin/bortex.js +145 -8
  2. package/package.json +1 -1
package/bin/bortex.js CHANGED
@@ -1156,17 +1156,96 @@ function classifyLocalPromptIntent(text) {
1156
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
1157
  return { command: '/sys-status' };
1158
1158
  }
1159
- if (wantsStatus && /(\bprocess\b|\bprocessi\b|\bpid\b|\bcpu\b|\bram\b|\bmemory\b|\bmem\b)/.test(t)) {
1159
+ if (wantsStatus && /(\bprocess\b|\bprocesso\b|\bprocessi\b|\bpid\b|\bcpu\b|\bram\b|\bmemory\b|\bmem\b)/.test(t)) {
1160
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
1161
  return { command: processMatch ? `/process-status ${processMatch[1]}` : '/process-status' };
1162
1162
  }
1163
- if (wantsStatus && /(\bport\b|\bporte\b|\bsocket\b|\blisten\b|\blistening\b)/.test(t)) {
1163
+ if (wantsStatus && /(\bport\b|\bporta\b|\bporte\b|\bsocket\b|\blisten\b|\blistening\b)/.test(t)) {
1164
1164
  const portMatch = t.match(/\b(\d{2,5})\b/);
1165
1165
  return { command: portMatch ? `/port-status ${portMatch[1]}` : '/port-status' };
1166
1166
  }
1167
1167
  return null;
1168
1168
  }
1169
1169
 
1170
+ function stripMatchingQuotes(value) {
1171
+ const s = String(value || '').trim();
1172
+ if (s.length >= 2 && ((s[0] === '"' && s[s.length - 1] === '"') || (s[0] === '\'' && s[s.length - 1] === '\'') || (s[0] === '`' && s[s.length - 1] === '`'))) {
1173
+ return s.slice(1, -1);
1174
+ }
1175
+ return s;
1176
+ }
1177
+
1178
+ function extractNaturalFilePath(text) {
1179
+ const raw = String(text || '');
1180
+ const quotedAfterFile = raw.match(/(?:file|path|percorso)\s+["'`]([^"'`]+)["'`]/i);
1181
+ if (quotedAfterFile?.[1]) return quotedAfterFile[1].trim();
1182
+ const tokenAfterFile = raw.match(/(?:file|path|percorso)\s+([^\s,;]+)/i);
1183
+ if (tokenAfterFile?.[1]) return stripMatchingQuotes(tokenAfterFile[1]);
1184
+ const absoluteOrRelative = raw.match(/["'`]?((?:[A-Za-z]:[\\/]|\/|~\/|\.{1,2}[\\/])[^"'`\s,;:]+)["'`]?/);
1185
+ if (absoluteOrRelative?.[1]) return absoluteOrRelative[1].trim();
1186
+ return '';
1187
+ }
1188
+
1189
+ function extractNaturalFileContent(text, filePath) {
1190
+ const raw = String(text || '').trim();
1191
+ const marker = raw.match(/(?:con\s+(?:contenuto|testo)|contenente|with\s+(?:content|text)|containing)\s+([\s\S]+)$/i);
1192
+ let content = marker?.[1] || '';
1193
+ if (!content && filePath) {
1194
+ const beforeFile = raw.slice(0, raw.toLowerCase().indexOf(filePath.toLowerCase())).trim();
1195
+ const writePrefix = beforeFile.match(/(?:scrivi|write|salva|metti)\s+([\s\S]+?)(?:\s+(?:nel|nella|in|su|to)\s+(?:file|path|percorso)?\s*)?$/i);
1196
+ if (writePrefix?.[1]) content = writePrefix[1];
1197
+ }
1198
+ content = String(content || '').trim();
1199
+ if (!content) return '';
1200
+ if ((content[0] === '"' || content[0] === '\'' || content[0] === '`')) {
1201
+ const quote = content[0];
1202
+ const end = content.indexOf(quote, 1);
1203
+ if (end > 0) return content.slice(1, end);
1204
+ }
1205
+ content = content
1206
+ .replace(/\s+(?:e|and|poi|then)\s+(?:rispondi|reply|dimmi|dillo|fammi sapere|tell me)\b[\s\S]*$/i, '')
1207
+ .replace(/\s+(?:rispondi|reply)\b[\s\S]*$/i, '')
1208
+ .trim();
1209
+ return stripMatchingQuotes(content);
1210
+ }
1211
+
1212
+ function buildNaturalLocalFileActionCalls(text) {
1213
+ const raw = String(text || '').trim();
1214
+ const lower = raw.toLowerCase();
1215
+ if (!raw) return null;
1216
+ const filePath = extractNaturalFilePath(raw);
1217
+ if (!filePath) return null;
1218
+
1219
+ if (/(?:^|\b)(leggi|mostra|apri|read|show|cat)(?:\b|$)/i.test(raw) && /(?:file|path|percorso|\/|\\|\.\.?[\\/])/.test(raw)) {
1220
+ return [{ tool: 'read', path: filePath }];
1221
+ }
1222
+
1223
+ const wantsAppend = /(?:^|\b)(appendi|accoda|aggiungi|append|add)(?:\b|$)/i.test(raw) && /(?:file|path|percorso|\/|\\|\.\.?[\\/])/.test(raw);
1224
+ const wantsWrite = /(?:^|\b)(crea|creare|scrivi|write|create|salva|metti)(?:\b|$)/i.test(raw) && /(?:file|path|percorso|\/|\\|\.\.?[\\/])/.test(raw);
1225
+ if (!wantsAppend && !wantsWrite) return null;
1226
+
1227
+ const textValue = extractNaturalFileContent(raw, filePath);
1228
+ if (!textValue && !/(?:^|\b)(crea|create)(?:\b|$)/i.test(raw)) return null;
1229
+ return [{
1230
+ tool: wantsAppend ? 'append' : 'write',
1231
+ path: filePath,
1232
+ text: textValue,
1233
+ confirm: true
1234
+ }];
1235
+ }
1236
+
1237
+ async function runNaturalLocalFileAction(opts, prompt) {
1238
+ const calls = buildNaturalLocalFileActionCalls(prompt);
1239
+ if (!calls) return { handled: false };
1240
+ opts.lastToolRunReport = await executeStructuredToolBatch(opts, calls, {
1241
+ continueOnError: true,
1242
+ goal: prompt,
1243
+ source: 'natural-local-file-action',
1244
+ reportLabel: 'natural-local-action'
1245
+ });
1246
+ return { handled: true, data: opts.lastToolRunReport };
1247
+ }
1248
+
1170
1249
  async function runSshStatusCommand(opts, { quiet = false } = {}) {
1171
1250
  const cwd = opts?.cwd || process.cwd();
1172
1251
  const platform = os.platform();
@@ -1389,11 +1468,11 @@ function suggestCommandsForStep(stepText, opts) {
1389
1468
  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
1469
  return ['/sys-status', '/status'];
1391
1470
  }
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)) {
1471
+ if (/(\bprocess\b|\bprocesso\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
1472
  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
1473
  return [processMatch ? `/process-status ${processMatch[1]}` : '/process-status', '/status'];
1395
1474
  }
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)) {
1475
+ if (/(\bport\b|\bporta\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
1476
  const portMatch = t.match(/\b(\d{2,5})\b/);
1398
1477
  return [portMatch ? `/port-status ${portMatch[1]}` : '/port-status', '/status'];
1399
1478
  }
@@ -2644,6 +2723,50 @@ async function executeStructuredBuiltinTool(opts, call) {
2644
2723
  error: res.ok ? null : `${label} exit ${res.code == null ? 'null' : res.code}`
2645
2724
  };
2646
2725
  };
2726
+ if (tool === 'read') {
2727
+ const filePath = resolveCliPath(opts, call.path);
2728
+ try {
2729
+ const text = fs.readFileSync(filePath, 'utf8');
2730
+ process.stdout.write(text.endsWith('\n') ? text : `${text}\n`);
2731
+ return {
2732
+ ok: true,
2733
+ stdout: text,
2734
+ stderr: null,
2735
+ data: { path: filePath, bytes: Buffer.byteLength(text, 'utf8') }
2736
+ };
2737
+ } catch (err) {
2738
+ return { ok: false, stdout: null, stderr: err.message, data: { path: filePath }, error: err.message };
2739
+ }
2740
+ }
2741
+ if (tool === 'mkdir') {
2742
+ const dirPath = resolveCliPath(opts, call.path || '.');
2743
+ try {
2744
+ fs.mkdirSync(dirPath, { recursive: true });
2745
+ console.log(`Mkdir ok -> ${dirPath}`);
2746
+ return { ok: true, stdout: `Mkdir ok -> ${dirPath}\n`, stderr: null, data: { path: dirPath } };
2747
+ } catch (err) {
2748
+ return { ok: false, stdout: null, stderr: err.message, data: { path: dirPath }, error: err.message };
2749
+ }
2750
+ }
2751
+ if (tool === 'write' || tool === 'append') {
2752
+ const filePath = resolveCliPath(opts, call.path);
2753
+ const text = String(call.text ?? '');
2754
+ try {
2755
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
2756
+ if (tool === 'append') fs.appendFileSync(filePath, text, 'utf8');
2757
+ else fs.writeFileSync(filePath, text, 'utf8');
2758
+ const label = tool === 'append' ? 'Append' : 'Write';
2759
+ console.log(`${label} ok -> ${filePath}`);
2760
+ return {
2761
+ ok: true,
2762
+ stdout: `${label} ok -> ${filePath}\n`,
2763
+ stderr: null,
2764
+ data: { path: filePath, bytes: Buffer.byteLength(text, 'utf8'), mode: tool }
2765
+ };
2766
+ } catch (err) {
2767
+ return { ok: false, stdout: null, stderr: err.message, data: { path: filePath, mode: tool }, error: err.message };
2768
+ }
2769
+ }
2647
2770
  if (tool === 'gitStatus') {
2648
2771
  const res = await runChild('git', ['status', '--short', ...(call.branch ? ['-b'] : [])], { cwd: opts.cwd, shell: false });
2649
2772
  const entries = parseGitStatusShortEntries(res.stdout || '');
@@ -3222,6 +3345,11 @@ function buildStructuredToolPlanFromGoal(opts, goal) {
3222
3345
  { tool: 'review', mode: 'all', fixSuggestions: true }
3223
3346
  ];
3224
3347
 
3348
+ const naturalFileCalls = buildNaturalLocalFileActionCalls(g);
3349
+ if (naturalFileCalls) {
3350
+ return naturalFileCalls;
3351
+ }
3352
+
3225
3353
  const wantsStatus = /(verifica|controlla|check|mostra|stato|status|attivo|active|running|enabled|on)/.test(lower);
3226
3354
  if (wantsStatus && /(\bssh\b|sshd|remote login)/.test(lower)) {
3227
3355
  return [{ tool: 'project' }, { tool: 'sshStatus' }];
@@ -3229,12 +3357,12 @@ function buildStructuredToolPlanFromGoal(opts, goal) {
3229
3357
  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
3358
  return [{ tool: 'project' }, { tool: 'systemStatus' }];
3231
3359
  }
3232
- if (wantsStatus && /(\bprocess\b|\bprocessi\b|\bpid\b|\bcpu\b|\bram\b|\bmemory\b|\bmem\b)/.test(lower)) {
3360
+ if (wantsStatus && /(\bprocess\b|\bprocesso\b|\bprocessi\b|\bpid\b|\bcpu\b|\bram\b|\bmemory\b|\bmem\b)/.test(lower)) {
3233
3361
  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
3362
  const filter = processMatch ? processMatch[1] : '';
3235
3363
  return [{ tool: 'project' }, { tool: 'processStatus', ...(filter ? { name: filter } : {}) }];
3236
3364
  }
3237
- if (wantsStatus && /(\bport\b|\bporte\b|\bsocket\b|\blisten\b|\blistening\b)/.test(lower)) {
3365
+ if (wantsStatus && /(\bport\b|\bporta\b|\bporte\b|\bsocket\b|\blisten\b|\blistening\b)/.test(lower)) {
3238
3366
  const portMatch = g.match(/\b(\d{2,5})\b/);
3239
3367
  const port = portMatch ? portMatch[1] : '';
3240
3368
  return [{ tool: 'project' }, { tool: 'portStatus', ...(port ? { port } : {}) }];
@@ -3763,7 +3891,7 @@ function makeSimplePlan(goal) {
3763
3891
  "Riporta l'esito del controllo"
3764
3892
  ];
3765
3893
  }
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)) {
3894
+ if (/(\bprocess\b|\bprocesso\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
3895
  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
3896
  const target = processMatch ? processMatch[1] : '';
3769
3897
  return [
@@ -3771,7 +3899,7 @@ function makeSimplePlan(goal) {
3771
3899
  "Riporta l'esito del controllo"
3772
3900
  ];
3773
3901
  }
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)) {
3902
+ if (/(\bport\b|\bporta\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
3903
  const portMatch = g.match(/\b(\d{2,5})\b/);
3776
3904
  const target = portMatch ? portMatch[1] : '';
3777
3905
  return [
@@ -6397,6 +6525,8 @@ async function runSinglePrompt(opts) {
6397
6525
  const localResult = await handleLocalCommand(opts, opts.prompt);
6398
6526
  if (localResult.handled) return;
6399
6527
  }
6528
+ const naturalFileAction = await runNaturalLocalFileAction(opts, opts.prompt);
6529
+ if (naturalFileAction.handled) return;
6400
6530
  const localIntent = classifyLocalPromptIntent(opts.prompt);
6401
6531
  if (localIntent?.command) {
6402
6532
  const localResult = await handleLocalCommand(opts, localIntent.command);
@@ -6495,6 +6625,13 @@ async function runRepl(opts) {
6495
6625
  continue;
6496
6626
  }
6497
6627
  }
6628
+ try {
6629
+ const naturalFileAction = await runNaturalLocalFileAction(opts, line);
6630
+ if (naturalFileAction.handled) continue;
6631
+ } catch (err) {
6632
+ console.error(`Local tool error: ${err.message}`);
6633
+ continue;
6634
+ }
6498
6635
  if (opts.offline) {
6499
6636
  console.log('Offline mode: use /help for local tools or restart without --offline to use the server.');
6500
6637
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bortexcode",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "description": "Bortex Code CLI - AI coding assistant powered by bortex.site",
5
5
  "homepage": "https://bortex.site",
6
6
  "license": "UNLICENSED",