xibecode 0.5.7 → 0.6.1

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 (84) hide show
  1. package/README.md +56 -8
  2. package/dist/commands/chat.d.ts.map +1 -1
  3. package/dist/commands/chat.js +132 -33
  4. package/dist/commands/chat.js.map +1 -1
  5. package/dist/commands/config.d.ts.map +1 -1
  6. package/dist/commands/config.js +10 -22
  7. package/dist/commands/config.js.map +1 -1
  8. package/dist/commands/run-pr.d.ts +16 -0
  9. package/dist/commands/run-pr.d.ts.map +1 -0
  10. package/dist/commands/run-pr.js +475 -0
  11. package/dist/commands/run-pr.js.map +1 -0
  12. package/dist/commands/run.d.ts +1 -0
  13. package/dist/commands/run.d.ts.map +1 -1
  14. package/dist/commands/run.js +7 -1
  15. package/dist/commands/run.js.map +1 -1
  16. package/dist/core/agent.d.ts +4 -0
  17. package/dist/core/agent.d.ts.map +1 -1
  18. package/dist/core/agent.js +52 -11
  19. package/dist/core/agent.js.map +1 -1
  20. package/dist/core/background-agent.d.ts +23 -0
  21. package/dist/core/background-agent.d.ts.map +1 -0
  22. package/dist/core/background-agent.js +138 -0
  23. package/dist/core/background-agent.js.map +1 -0
  24. package/dist/core/code-graph.d.ts +18 -0
  25. package/dist/core/code-graph.d.ts.map +1 -0
  26. package/dist/core/code-graph.js +105 -0
  27. package/dist/core/code-graph.js.map +1 -0
  28. package/dist/core/conflict-solver.d.ts +26 -0
  29. package/dist/core/conflict-solver.d.ts.map +1 -0
  30. package/dist/core/conflict-solver.js +108 -0
  31. package/dist/core/conflict-solver.js.map +1 -0
  32. package/dist/core/modes.d.ts +1 -1
  33. package/dist/core/modes.d.ts.map +1 -1
  34. package/dist/core/modes.js +105 -5
  35. package/dist/core/modes.js.map +1 -1
  36. package/dist/core/pattern-miner.d.ts +43 -0
  37. package/dist/core/pattern-miner.d.ts.map +1 -0
  38. package/dist/core/pattern-miner.js +123 -0
  39. package/dist/core/pattern-miner.js.map +1 -0
  40. package/dist/core/planMode.d.ts.map +1 -1
  41. package/dist/core/planMode.js +6 -2
  42. package/dist/core/planMode.js.map +1 -1
  43. package/dist/core/session-bridge.d.ts +5 -1
  44. package/dist/core/session-bridge.d.ts.map +1 -1
  45. package/dist/core/session-bridge.js +13 -0
  46. package/dist/core/session-bridge.js.map +1 -1
  47. package/dist/core/swarm.d.ts +19 -0
  48. package/dist/core/swarm.d.ts.map +1 -0
  49. package/dist/core/swarm.js +77 -0
  50. package/dist/core/swarm.js.map +1 -0
  51. package/dist/core/tools.d.ts +8 -0
  52. package/dist/core/tools.d.ts.map +1 -1
  53. package/dist/core/tools.js +289 -8
  54. package/dist/core/tools.js.map +1 -1
  55. package/dist/core/visual-feedback.d.ts +20 -0
  56. package/dist/core/visual-feedback.d.ts.map +1 -0
  57. package/dist/core/visual-feedback.js +117 -0
  58. package/dist/core/visual-feedback.js.map +1 -0
  59. package/dist/index.js +19 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/utils/config.d.ts +0 -2
  62. package/dist/utils/config.d.ts.map +1 -1
  63. package/dist/utils/config.js +18 -23
  64. package/dist/utils/config.js.map +1 -1
  65. package/dist/utils/git.d.ts +14 -0
  66. package/dist/utils/git.d.ts.map +1 -1
  67. package/dist/utils/git.js +101 -0
  68. package/dist/utils/git.js.map +1 -1
  69. package/dist/webui/server.d.ts.map +1 -1
  70. package/dist/webui/server.js +6 -9
  71. package/dist/webui/server.js.map +1 -1
  72. package/package.json +23 -22
  73. package/webui-dist/assets/index-CSla6Lzy.css +32 -0
  74. package/webui-dist/assets/{index-C2cXuEP4.js → index-jeWUzIG0.js} +110 -103
  75. package/webui-dist/assets/index-jeWUzIG0.js.map +1 -0
  76. package/webui-dist/assets/{xterm-EDtERSgU.js → xterm-B4aZdZLt.js} +2 -2
  77. package/webui-dist/assets/{xterm-EDtERSgU.js.map → xterm-B4aZdZLt.js.map} +1 -1
  78. package/webui-dist/assets/{xterm-addon-fit-CDWriKNb.js → xterm-addon-fit-CY2T_uda.js} +2 -2
  79. package/webui-dist/assets/{xterm-addon-fit-CDWriKNb.js.map → xterm-addon-fit-CY2T_uda.js.map} +1 -1
  80. package/webui-dist/assets/{xterm-addon-web-links--YKXSM2J.js → xterm-addon-web-links-D93WX0PV.js} +2 -2
  81. package/webui-dist/assets/{xterm-addon-web-links--YKXSM2J.js.map → xterm-addon-web-links-D93WX0PV.js.map} +1 -1
  82. package/webui-dist/index.html +2 -2
  83. package/webui-dist/assets/index-C2cXuEP4.js.map +0 -1
  84. package/webui-dist/assets/index-Jga8KONu.css +0 -32
package/README.md CHANGED
@@ -214,6 +214,56 @@ Options:
214
214
  - `--dry-run`
215
215
  - `--changed-only`
216
216
 
217
+ ### `xibecode run-pr`
218
+
219
+ Autonomous coding **with automatic branch + GitHub PR creation**. The command:
220
+
221
+ 1. Runs the full agent task (same as `run`)
222
+ 2. Executes your project's test suite for verification
223
+ 3. Creates a new branch (`xibecode/<slug>-<timestamp>` by default)
224
+ 4. Commits + pushes the branch to `origin`
225
+ 5. Opens a PR against the remote default branch via the GitHub CLI (`gh`)
226
+ 6. Prints the PR URL and exits
227
+
228
+ **Prerequisites:**
229
+
230
+ - [`gh` (GitHub CLI)](https://cli.github.com/) must be installed and authenticated:
231
+
232
+ ```bash
233
+ gh auth login
234
+ ```
235
+
236
+ **Usage:**
237
+
238
+ ```bash
239
+ xibecode run-pr "Fix the TypeScript errors in src/core/agent.ts"
240
+ xibecode run-pr "Add input validation" --verbose
241
+ xibecode run-pr "Refactor utils" --branch feat/refactor-utils --draft
242
+ xibecode run-pr --file task.txt --skip-tests
243
+ ```
244
+
245
+ Options:
246
+
247
+ - `-f, --file <path>` prompt from file
248
+ - `-m, --model <model>` model override
249
+ - `-b, --base-url <url>` custom API URL
250
+ - `-k, --api-key <key>` API key override
251
+ - `--provider <provider>` `anthropic` or `openai`
252
+ - `-d, --max-iterations <number>` default `150` (`0` = unlimited)
253
+ - `-v, --verbose`
254
+ - `--branch <name>` override generated branch name
255
+ - `--title <title>` override PR title
256
+ - `--draft` open PR as draft
257
+ - `--skip-tests` skip test verification before creating PR
258
+
259
+ **Example output:**
260
+
261
+ ```
262
+ ✅ Pull Request created successfully!
263
+
264
+ PR URL: https://github.com/your-org/your-repo/pull/42
265
+ ```
266
+
217
267
  ### `xibecode chat`
218
268
 
219
269
  Interactive terminal chat + tool use.
@@ -299,16 +349,10 @@ Type `/` in the input to open the command palette:
299
349
  | `/mode agent` | 🤖 | Autonomous coding (default) |
300
350
  | `/mode plan` | 📝 | Interactive planning with web research |
301
351
  | `/mode tester` | 🧪 | Testing and QA |
302
- | `/mode debugger` | 🐛 | Bug investigation |
303
352
  | `/mode security` | 🔒 | Security analysis |
353
+ | `/mode pentest` | 🔓 | Penetration testing - run app and probe for vulnerabilities |
304
354
  | `/mode review` | 👀 | Code review |
305
355
  | `/mode team_leader` | 👑 | Coordinate team |
306
- | `/mode architect` | 🏛️ | System design |
307
- | `/mode engineer` | 🛠️ | Implementation |
308
- | `/mode seo` | 🌐 | SEO optimization |
309
- | `/mode product` | 🔥 | Product strategy |
310
- | `/mode data` | 📊 | Data analysis |
311
- | `/mode researcher` | 📚 | Deep research |
312
356
 
313
357
  ### File References (`@`)
314
358
 
@@ -472,7 +516,7 @@ POST /api/tests/run # Run project tests
472
516
 
473
517
  ## Project Structure
474
518
 
475
- ```
519
+ ```text
476
520
  xibecode/
477
521
  ├── src/
478
522
  │ ├── core/ # Agent, tools, context
@@ -485,6 +529,10 @@ xibecode/
485
529
  └── tests/ # Test suites
486
530
  ```
487
531
 
532
+ ## Dependency Map
533
+
534
+ ![Dependency Map](dependencies.svg)
535
+
488
536
  ## Project Docs
489
537
 
490
538
  - `CHANGELOG.md` — release history
@@ -1 +1 @@
1
- {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAqBA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,iBA64CrD"}
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAqBA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,iBA8+CrD"}
@@ -90,6 +90,8 @@ export async function chatCommand(options) {
90
90
  model,
91
91
  maxIterations: 150,
92
92
  verbose: false,
93
+ provider: currentProvider,
94
+ customProviderFormat: config.get('customProviderFormat'),
93
95
  }, currentProvider);
94
96
  const allModes = getAllModes();
95
97
  let currentMode = agent.getMode();
@@ -126,9 +128,23 @@ export async function chatCommand(options) {
126
128
  // File might not exist yet
127
129
  }
128
130
  }
131
+ // Check for [[PENTEST_READY]] tag
132
+ if (text.includes('[[PENTEST_READY]]')) {
133
+ const fs = require('fs');
134
+ const path = require('path');
135
+ const reportPath = path.join(process.cwd(), 'pentest-report.md');
136
+ try {
137
+ const reportContent = fs.readFileSync(reportPath, 'utf-8');
138
+ SessionBridge.onPentestReady(reportContent, 'pentest-report.md');
139
+ }
140
+ catch {
141
+ // File might not exist yet
142
+ }
143
+ }
129
144
  }
130
145
  async function handlePlannerQuestions(questions) {
131
146
  try {
147
+ closeRL();
132
148
  const inquirer = (await import('inquirer')).default;
133
149
  const answers = {};
134
150
  for (const q of questions) {
@@ -189,6 +205,9 @@ export async function chatCommand(options) {
189
205
  catch {
190
206
  // Inquirer not available or error
191
207
  }
208
+ finally {
209
+ createRL();
210
+ }
192
211
  }
193
212
  function setupAgentHandlers() {
194
213
  agent.removeAllListeners('event');
@@ -308,18 +327,87 @@ export async function chatCommand(options) {
308
327
  messages: agent.getMessages(),
309
328
  isProcessing: false,
310
329
  });
311
- // Queue for WebUI messages and resolver for interrupting TUI input
312
- let pendingWebUIMessage = null;
313
- let webUIMessageResolver = null;
330
+ const inputQueue = [];
331
+ let inputResolver = null;
332
+ let isAgentRunning = false;
333
+ let rl = null;
334
+ let sigintCount = 0;
335
+ let lastSigintTime = 0;
336
+ function handleSigint() {
337
+ const now = Date.now();
338
+ // debounce in case both rl and keypress emit SIGINT
339
+ if (now - lastSigintTime < 100)
340
+ return;
341
+ lastSigintTime = now;
342
+ if (sigintCount === 0) {
343
+ console.log('');
344
+ ui.warning('Press Ctrl+C again to exit.');
345
+ if (!isAgentRunning)
346
+ promptUser();
347
+ sigintCount++;
348
+ setTimeout(() => { sigintCount = 0; }, 3000);
349
+ }
350
+ else {
351
+ process.exit(0);
352
+ }
353
+ }
354
+ function promptUser() {
355
+ process.stdout.write(chalk.hex('#00E676').bold('❯ You ') + '');
356
+ }
357
+ function createRL() {
358
+ if (rl)
359
+ return;
360
+ rl = readline.createInterface({
361
+ input: process.stdin,
362
+ output: process.stdout,
363
+ terminal: true
364
+ });
365
+ rl.on('line', (line) => {
366
+ const msg = line.trim();
367
+ if (!msg) {
368
+ if (!isAgentRunning)
369
+ promptUser();
370
+ return;
371
+ }
372
+ if (isAgentRunning) {
373
+ agent.injectMessage(msg);
374
+ console.log('');
375
+ ui.success(`Message queued for agent's next step.`);
376
+ }
377
+ else {
378
+ const item = { message: msg, source: 'tui' };
379
+ if (inputResolver) {
380
+ inputResolver(item);
381
+ }
382
+ else {
383
+ inputQueue.push(item);
384
+ }
385
+ }
386
+ });
387
+ rl.on('SIGINT', handleSigint);
388
+ }
389
+ function closeRL() {
390
+ if (rl) {
391
+ rl.close();
392
+ rl = null;
393
+ }
394
+ }
314
395
  // Listen for messages from WebUI
315
396
  SessionBridge.on('user_message', async (content, source) => {
316
397
  if (source === 'webui') {
317
- pendingWebUIMessage = content;
318
- // If TUI is waiting for input, resolve immediately with WebUI message
319
- if (webUIMessageResolver) {
320
- webUIMessageResolver(content);
321
- webUIMessageResolver = null;
322
- pendingWebUIMessage = null;
398
+ if (isAgentRunning) {
399
+ agent.injectMessage(content);
400
+ console.log('');
401
+ ui.info(`WebUI message injected into agent's thought process.`);
402
+ }
403
+ else {
404
+ const item = { message: content, source: 'webui' };
405
+ if (inputResolver) {
406
+ inputResolver(item);
407
+ }
408
+ else {
409
+ inputQueue.push(item);
410
+ }
323
411
  }
324
412
  }
325
413
  });
@@ -327,33 +415,16 @@ export async function chatCommand(options) {
327
415
  * Get input from either TUI or WebUI (whichever comes first)
328
416
  */
329
417
  async function getInput() {
330
- // Check if there's already a pending WebUI message
331
- if (pendingWebUIMessage) {
332
- const msg = pendingWebUIMessage;
333
- pendingWebUIMessage = null;
334
- return { message: msg, source: 'webui' };
418
+ createRL();
419
+ if (inputQueue.length > 0) {
420
+ return inputQueue.shift();
335
421
  }
336
422
  return new Promise((resolve) => {
337
- // Set up resolver for WebUI messages
338
- webUIMessageResolver = (msg) => {
339
- rl.close();
340
- resolve({ message: msg, source: 'webui' });
423
+ promptUser();
424
+ inputResolver = (input) => {
425
+ inputResolver = null;
426
+ resolve(input);
341
427
  };
342
- // Create readline interface for TUI input
343
- const rl = readline.createInterface({
344
- input: process.stdin,
345
- output: process.stdout,
346
- });
347
- // Show prompt
348
- process.stdout.write(chalk.hex('#00E676').bold('❯ You ') + '');
349
- rl.on('line', (line) => {
350
- webUIMessageResolver = null;
351
- rl.close();
352
- resolve({ message: line, source: 'tui' });
353
- });
354
- rl.on('close', () => {
355
- webUIMessageResolver = null;
356
- });
357
428
  });
358
429
  }
359
430
  async function showPathSuggestions(raw) {
@@ -477,6 +548,7 @@ export async function chatCommand(options) {
477
548
  ui.info('No saved sessions yet.');
478
549
  return;
479
550
  }
551
+ closeRL();
480
552
  const { picked } = await inquirer.prompt([
481
553
  {
482
554
  type: 'list',
@@ -488,6 +560,7 @@ export async function chatCommand(options) {
488
560
  })),
489
561
  },
490
562
  ]);
563
+ createRL();
491
564
  const loaded = await sessionManager.loadSession(picked);
492
565
  if (!loaded) {
493
566
  ui.error('Failed to load selected session');
@@ -505,6 +578,8 @@ export async function chatCommand(options) {
505
578
  model,
506
579
  maxIterations: 150,
507
580
  verbose: false,
581
+ provider: currentProvider || config.get('provider'),
582
+ customProviderFormat: config.get('customProviderFormat'),
508
583
  }, currentProvider || config.get('provider'));
509
584
  setupAgentHandlers();
510
585
  currentMode = agent.getMode();
@@ -519,6 +594,7 @@ export async function chatCommand(options) {
519
594
  ];
520
595
  const customModels = (config.get('customModels') || []);
521
596
  const unique = Array.from(new Set([current, ...fixedModels, ...customModels.map(m => m.id)]));
597
+ closeRL();
522
598
  const { picked } = await inquirer.prompt([
523
599
  {
524
600
  type: 'list',
@@ -532,11 +608,13 @@ export async function chatCommand(options) {
532
608
  }),
533
609
  },
534
610
  ]);
611
+ createRL();
535
612
  config.set('model', picked);
536
613
  ui.success(`Model set to: ${picked}`);
537
614
  }
538
615
  async function handleThemesCommand() {
539
616
  const current = ui.getThemeName();
617
+ closeRL();
540
618
  const { picked } = await inquirer.prompt([
541
619
  {
542
620
  type: 'list',
@@ -548,6 +626,7 @@ export async function chatCommand(options) {
548
626
  })),
549
627
  },
550
628
  ]);
629
+ createRL();
551
630
  ui.setTheme(picked);
552
631
  config.set('theme', picked);
553
632
  ui.success(`Theme set to: ${picked}`);
@@ -663,6 +742,10 @@ export async function chatCommand(options) {
663
742
  process.stdin.on('keypress', (_str, key) => {
664
743
  if (!key)
665
744
  return;
745
+ if (key.ctrl && key.name === 'c') {
746
+ handleSigint();
747
+ return;
748
+ }
666
749
  if (key.name === 'tab') {
667
750
  const idx = allModes.indexOf(currentMode);
668
751
  const next = allModes[(idx + 1) % allModes.length];
@@ -713,6 +796,7 @@ export async function chatCommand(options) {
713
796
  value: entry.name + (isDir ? '/' : ''),
714
797
  };
715
798
  });
799
+ closeRL();
716
800
  const { picked } = await inquirer.prompt([
717
801
  {
718
802
  type: 'list',
@@ -730,9 +814,11 @@ export async function chatCommand(options) {
730
814
  default: '@' + picked,
731
815
  },
732
816
  ]);
817
+ createRL();
733
818
  message = followUp.message;
734
819
  }
735
820
  catch (error) {
821
+ createRL();
736
822
  ui.error('Failed to list files for selection', error);
737
823
  continue;
738
824
  }
@@ -804,6 +890,7 @@ export async function chatCommand(options) {
804
890
  continue;
805
891
  }
806
892
  if (lowerMessage === '/provider') {
893
+ closeRL();
807
894
  const { picked } = await inquirer.prompt([
808
895
  {
809
896
  type: 'list',
@@ -815,6 +902,7 @@ export async function chatCommand(options) {
815
902
  ],
816
903
  },
817
904
  ]);
905
+ createRL();
818
906
  currentProvider = picked;
819
907
  config.set('provider', picked);
820
908
  // Recreate agent with new provider but keep conversation history
@@ -1040,6 +1128,7 @@ export async function chatCommand(options) {
1040
1128
  });
1041
1129
  console.log('');
1042
1130
  // Prompt to install
1131
+ closeRL();
1043
1132
  const { installChoice } = await inquirer.prompt([
1044
1133
  {
1045
1134
  type: 'list',
@@ -1054,6 +1143,7 @@ export async function chatCommand(options) {
1054
1143
  ],
1055
1144
  },
1056
1145
  ]);
1146
+ createRL();
1057
1147
  if (installChoice === '__cancel__') {
1058
1148
  continue;
1059
1149
  }
@@ -1244,10 +1334,19 @@ export async function chatCommand(options) {
1244
1334
  if (messageSource === 'tui') {
1245
1335
  SessionBridge.onTUIUserMessage(message);
1246
1336
  }
1337
+ isAgentRunning = true;
1247
1338
  // agent.run() resets its iteration/tool counters but KEEPS
1248
1339
  // the conversation history (this.messages), so the AI has
1249
1340
  // full context of everything discussed in this session.
1250
1341
  await agent.run(message, tools, toolExecutor);
1342
+ isAgentRunning = false;
1343
+ const unhandled = agent.getInjectedMessages();
1344
+ if (unhandled.length > 0) {
1345
+ agent.clearInjectedMessages();
1346
+ for (const msg of unhandled) {
1347
+ inputQueue.push({ message: msg, source: 'tui' });
1348
+ }
1349
+ }
1251
1350
  const stats = agent.getStats();
1252
1351
  // Update SessionBridge with latest messages
1253
1352
  SessionBridge.updateState({ messages: agent.getMessages() });