agen-vektor 0.3.0 → 0.3.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.
package/README.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  **AI Coding Agent for Linux & Termux** — a terminal-native coding agent that inspects your project, plans, edits code, runs commands and tests, and iterates until the task is done.
4
4
 
5
+ ```
6
+ ██╗ ██╗███████╗ ██████╗████████╗ ██████╗ ██████╗ ██╗ ██╗███████╗ █████╗ ██████╗
7
+ ██║ ██║██╔════╝██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗██║ ██║██╔════╝██╔══██╗██╔══██╗
8
+ ██║ ██║█████╗ ██║ ██║ ██║ ██║██████╔╝███████║█████╗ ███████║██║ ██║
9
+ ╚██╗ ██╔╝██╔══╝ ██║ ██║ ██║ ██║██╔══██╗██╔══██║██╔══╝ ██╔══██║██║ ██║
10
+ ╚████╔╝ ███████╗╚██████╗ ██║ ╚██████╔╝██║ ██║██║ ██║███████╗██║ ██║██████╔╝
11
+ ╚═══╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═════╝
12
+ ```
13
+
5
14
  ```
6
15
  ╭──────────────────────────────────────────────────────────────╮
7
16
  │ VectorHead ● Ready openrouter · deepseek/deepseek-chat │
package/dist/cli/index.js CHANGED
@@ -250,7 +250,13 @@ async function runTui(opts) {
250
250
  const onResize = () => doRender();
251
251
  process.stdout.on('resize', onResize);
252
252
  process.on('SIGINT', () => {
253
- // Ctrl+C is handled via raw mode keys; SIGINT may still fire
253
+ // In raw mode Ctrl+C arrives as a key event (0x03), but some terminals
254
+ // (web/embedded, some SSH clients) deliver it as SIGINT instead.
255
+ // Never swallow it: request a clean exit so the user is not forced to kill.
256
+ if (app)
257
+ app.requestExit();
258
+ else
259
+ process.exit(0);
254
260
  });
255
261
  process.on('SIGWINCH', onResize);
256
262
  const keyHandler = (ev) => {
package/dist/tui/app.js CHANGED
@@ -117,6 +117,7 @@ class App {
117
117
  this.status = 'Stopping…';
118
118
  this.statusColor = terminal_1.ANSI.yellow;
119
119
  this.running = false;
120
+ this.addSystem('⏹ Stopped. Press Ctrl+C again to quit.');
120
121
  }
121
122
  return;
122
123
  }
@@ -211,6 +212,17 @@ class App {
211
212
  await this.shutdown();
212
213
  }
213
214
  }
215
+ /**
216
+ * Abort any running task and request a clean exit.
217
+ * Used by Ctrl+C and SIGINT so the TUI never needs to be killed.
218
+ */
219
+ requestExit() {
220
+ if (this.running) {
221
+ this.abortController?.abort();
222
+ this.running = false;
223
+ }
224
+ this.exitRequested = true;
225
+ }
214
226
  // ─── Submission ───────────────────────────────────────────────
215
227
  async submit(text) {
216
228
  if (text.startsWith('/')) {
@@ -395,6 +407,7 @@ class App {
395
407
  this.modal = { type: 'model', selected: 0 };
396
408
  break;
397
409
  case '/provider':
410
+ case '/setup':
398
411
  this.modal = { type: 'provider', selected: 0 };
399
412
  break;
400
413
  case '/session':
@@ -485,6 +498,11 @@ class App {
485
498
  if (ev.name === 'escape' || ev.name === 'enter' || ev.name === 'ctrl_c' || (ev.name === 'char' && ev.char === '?')) {
486
499
  this.modal = { type: 'none' };
487
500
  }
501
+ else if (ev.name === 'char' && ev.char) {
502
+ // Typing while an info modal is open: close it and insert the char
503
+ this.modal = { type: 'none' };
504
+ this.input.insert(ev.char);
505
+ }
488
506
  else if (ev.name === 'pageup') {
489
507
  this.scroll = (0, chat_1.clampScroll)(this.scroll - 10, this.messages, this.cols(), this.chatHeight());
490
508
  }
@@ -499,6 +517,11 @@ class App {
499
517
  this.agent.rebuildProvider();
500
518
  (0, config_1.saveConfig)(this.agent.config);
501
519
  this.addSystem(`Provider set to ${opt.label}.`);
520
+ if (pid === 'custom' && !this.agent.config.apiUrl) {
521
+ // Wizard: ask base URL first, then API token
522
+ this.modal = { type: 'custom-url', value: '' };
523
+ return;
524
+ }
502
525
  if (!(0, credentials_1.hasApiKey)(pid) && pid !== 'ollama') {
503
526
  this.modal = { type: 'apikey', provider: pid, value: '' };
504
527
  return;
@@ -604,7 +627,12 @@ class App {
604
627
  this.agent.config.apiUrl = value;
605
628
  (0, config_1.saveConfig)(this.agent.config);
606
629
  this.addSystem('Custom API URL set.');
607
- this.modal = { type: 'none' };
630
+ if (!(0, credentials_1.hasApiKey)('custom')) {
631
+ this.modal = { type: 'custom-key', value: '' };
632
+ }
633
+ else {
634
+ this.modal = { type: 'none' };
635
+ }
608
636
  });
609
637
  break;
610
638
  case 'custom-key':
@@ -763,9 +791,9 @@ class App {
763
791
  ' Ctrl+C stop agent / quit',
764
792
  ' Ctrl+L clear conversation',
765
793
  '',
766
- 'Commands: /model /provider /session /settings',
767
- '/status /clear /continue /diff /git /compact',
768
- '/yolo /exit',
794
+ 'Commands: /setup /model /provider /session',
795
+ '/settings /status /clear /continue /diff',
796
+ '/git /compact /yolo /exit',
769
797
  ],
770
798
  }, rows, cols);
771
799
  case 'commands':
@@ -773,6 +801,7 @@ class App {
773
801
  title: 'Commands',
774
802
  body: [
775
803
  '/help show help',
804
+ '/setup setup provider (base URL + token)',
776
805
  '/model select model',
777
806
  '/provider select provider',
778
807
  '/session manage sessions',
@@ -898,8 +927,15 @@ class App {
898
927
  }, rows, cols);
899
928
  case 'custom-url':
900
929
  return (0, components_1.renderModal)({
901
- title: 'Custom API URL',
902
- body: ['Endpoint, e.g. https://example.com/v1', '', m.value || '_', '', 'Enter to save · Esc to cancel'],
930
+ title: 'Custom API Base URL',
931
+ body: [
932
+ 'Base URL (OpenAI-compatible), e.g.',
933
+ 'https://example.com/v1',
934
+ '',
935
+ m.value || '_',
936
+ '',
937
+ 'Enter to save · Esc to cancel',
938
+ ],
903
939
  }, rows, cols);
904
940
  case 'custom-key':
905
941
  return (0, components_1.renderModal)({
package/dist/tui/chat.js CHANGED
@@ -4,12 +4,18 @@ exports.renderMessages = renderMessages;
4
4
  exports.defaultScroll = defaultScroll;
5
5
  exports.clampScroll = clampScroll;
6
6
  /**
7
- * Chat view — renders conversation messages with scrolling.
7
+ * Chat view — renders conversation messages as boxed blocks with scrolling.
8
8
  * Message types: user, assistant (streamed), tool, system/status, plan.
9
+ * Each message renders as a bordered box (Freebuff-style):
10
+ * ╭───────────────────────────╮
11
+ * │ ● VectorHead · model │
12
+ * │ content │
13
+ * ╰───────────────────────────╯
9
14
  */
10
15
  const theme_1 = require("./theme");
16
+ const terminal_1 = require("../utils/terminal");
11
17
  const KIND_STYLE = {
12
- user: { prefix: 'YOU', color: theme_1.THEME.info },
18
+ user: { prefix: 'YOU', color: theme_1.THEME.gold },
13
19
  assistant: { prefix: 'VectorHead', color: theme_1.THEME.accent },
14
20
  tool: { prefix: 'TOOL', color: theme_1.THEME.warn },
15
21
  system: { prefix: '···', color: theme_1.THEME.muted },
@@ -17,37 +23,36 @@ const KIND_STYLE = {
17
23
  success: { prefix: '✓', color: theme_1.THEME.success },
18
24
  warning: { prefix: '⚠', color: theme_1.THEME.warn },
19
25
  };
20
- function plainLines(text, width) {
21
- const lines = text.split('\n');
26
+ /** Render one message as a boxed block. Box width = `width` (whole chat area). */
27
+ function boxMessage(m, width) {
28
+ const style = KIND_STYLE[m.kind];
29
+ const prefix = m.prefix || style.prefix;
30
+ const dot = m.streaming ? `${theme_1.THEME.warn}◐${theme_1.THEME.reset} ` : `${style.color}●${theme_1.THEME.reset} `;
31
+ const title = `${dot}${theme_1.THEME.bold}${style.color}${prefix}${theme_1.THEME.reset}`;
32
+ const meta = m.meta ? ` ${theme_1.THEME.dim}${m.meta}${theme_1.THEME.reset}` : '';
33
+ const toolName = m.tool ? ` ${theme_1.THEME.warn}${m.tool}${theme_1.THEME.reset}` : '';
34
+ const inner = Math.max(8, width - 4);
35
+ const border = style.color;
22
36
  const out = [];
23
- for (const l of lines) {
24
- if (l.length <= width) {
25
- out.push(l);
26
- }
27
- else {
28
- for (let i = 0; i < l.length; i += width)
29
- out.push(l.slice(i, i + width));
30
- }
37
+ // Top border
38
+ out.push(border + terminal_1.BOX.tl + terminal_1.BOX.h.repeat(inner + 2) + terminal_1.BOX.tr + theme_1.THEME.reset);
39
+ // Title row: ● prefix · model / tool badge
40
+ out.push(`${border}${terminal_1.BOX.v}${theme_1.THEME.reset} ` + (0, terminal_1.pad)(title + toolName + meta, inner) + ` ${border}${terminal_1.BOX.v}${theme_1.THEME.reset}`);
41
+ // Content rows
42
+ const body = m.content || '(no content)';
43
+ for (const line of (0, terminal_1.wrapText)(body, inner)) {
44
+ out.push(`${border}${terminal_1.BOX.v}${theme_1.THEME.reset} ` + (0, terminal_1.pad)(line, inner) + ` ${border}${terminal_1.BOX.v}${theme_1.THEME.reset}`);
31
45
  }
46
+ // Bottom border + blank separator
47
+ out.push(border + terminal_1.BOX.bl + terminal_1.BOX.h.repeat(inner + 2) + terminal_1.BOX.br + theme_1.THEME.reset);
48
+ out.push('');
32
49
  return out;
33
50
  }
34
51
  /** Precompute the full list of rendered lines for all messages. */
35
52
  function renderMessages(messages, width) {
36
53
  const out = [];
37
54
  for (const m of messages) {
38
- const style = KIND_STYLE[m.kind];
39
- const prefix = m.prefix || style.prefix;
40
- const dot = m.streaming ? `${theme_1.THEME.warn}◐${theme_1.THEME.reset} ` : `${style.color}●${theme_1.THEME.reset} `;
41
- const header = `${dot}${theme_1.THEME.bold}${style.color}${prefix}${theme_1.THEME.reset}`;
42
- const meta = m.meta ? ` ${theme_1.THEME.dim}${m.meta}${theme_1.THEME.reset}` : '';
43
- const toolName = m.tool ? ` ${theme_1.THEME.warn}${m.tool}${theme_1.THEME.reset}` : '';
44
- out.push(header + toolName + meta);
45
- const body = m.content || '(no content)';
46
- const bar = `${style.color}│${theme_1.THEME.reset} `;
47
- for (const line of plainLines(body, width)) {
48
- out.push(`${bar}${line}`);
49
- }
50
- out.push(''); // blank line between messages
55
+ out.push(...boxMessage(m, width));
51
56
  }
52
57
  return out;
53
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agen-vektor",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "VectorHead (agen-vektor) — AI Coding Agent CLI/TUI for Linux & Termux. Multi-provider, tool calling, session, permission system.",
5
5
  "type": "commonjs",
6
6
  "bin": {