agen-vektor 0.2.1 → 0.3.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.
- package/LICENSE +1 -1
- package/README.md +17 -8
- package/dist/agent/prompts.js +2 -2
- package/dist/cli/index.js +9 -9
- package/dist/config/config.js +1 -1
- package/dist/providers/openrouter.js +1 -1
- package/dist/tools/git.js +1 -1
- package/dist/tools/shell.js +1 -1
- package/dist/tools/web.js +1 -1
- package/dist/tui/app.js +36 -10
- package/dist/tui/chat.js +31 -26
- package/dist/tui/input.js +5 -4
- package/dist/tui/statusbar.js +3 -3
- package/dist/tui/theme.js +6 -6
- package/dist/utils/paths.js +1 -1
- package/package.json +2 -2
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
|
-
#
|
|
1
|
+
# VectorHead
|
|
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 │
|
|
8
17
|
│ Project: ~/my-project Mode: ask │
|
|
9
18
|
├──────────────────────────────────────────────────────────────┤
|
|
10
19
|
│ │
|
|
11
20
|
│ YOU │
|
|
12
21
|
│ > Perbaiki error authentication pada project ini │
|
|
13
22
|
│ │
|
|
14
|
-
│
|
|
23
|
+
│ VectorHead │
|
|
15
24
|
│ ● Inspecting project... │
|
|
16
25
|
│ ● Searching authentication files │
|
|
17
26
|
│ │
|
|
@@ -73,7 +82,7 @@ npm install -g agen-vektor
|
|
|
73
82
|
vector
|
|
74
83
|
```
|
|
75
84
|
|
|
76
|
-
>
|
|
85
|
+
> VectorHead has **zero runtime dependencies** and does not assume `systemd`, `sudo`, `apt`, or a desktop GUI — everything runs in a plain terminal.
|
|
77
86
|
|
|
78
87
|
## Quick start
|
|
79
88
|
|
|
@@ -104,7 +113,7 @@ vector
|
|
|
104
113
|
|
|
105
114
|
## TUI commands
|
|
106
115
|
|
|
107
|
-
Type `/` inside
|
|
116
|
+
Type `/` inside VectorHead:
|
|
108
117
|
|
|
109
118
|
| Command | Description |
|
|
110
119
|
|---|---|
|
|
@@ -132,7 +141,7 @@ export GEMINI_API_KEY=...
|
|
|
132
141
|
export OPENROUTER_API_KEY=...
|
|
133
142
|
```
|
|
134
143
|
|
|
135
|
-
Or inside
|
|
144
|
+
Or inside VectorHead: `/provider` → pick provider → paste key (stored masked in `~/.vector/credentials.json`, permissions `0600`).
|
|
136
145
|
|
|
137
146
|
Generic variables: `VECTOR_API_KEY`, `VECTOR_API_URL`, `VECTOR_MODEL`, `VECTOR_PROVIDER`, `VECTOR_HOME`.
|
|
138
147
|
|
|
@@ -197,14 +206,14 @@ npm run build
|
|
|
197
206
|
- **"401" / auth failed** — check the key, or for OpenRouter use `OPENROUTER_API_KEY`.
|
|
198
207
|
- **Nothing happens on `vector` in Termux** — make sure your terminal is a TTY and TERM is set (e.g. `export TERM=xterm-256color`).
|
|
199
208
|
- **Runs out of iterations** — raise `maxIterations` in `~/.vector/config.json` or refine the request.
|
|
200
|
-
- **Rate limited (429)** —
|
|
209
|
+
- **Rate limited (429)** — VectorHead retries automatically with backoff; wait and retry.
|
|
201
210
|
|
|
202
211
|
## Roadmap
|
|
203
212
|
|
|
204
213
|
- Web search tool (provider-backed) behind the `web` abstraction
|
|
205
214
|
- Interactive diff approval before edits in safe mode
|
|
206
215
|
- Persistent permission rules per project
|
|
207
|
-
-
|
|
216
|
+
- Publish under a dedicated npm package name for the VectorHead brand
|
|
208
217
|
- Windows support (WSL-focused)
|
|
209
218
|
|
|
210
219
|
## License
|
package/dist/agent/prompts.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Prompts — system prompt + task framing for
|
|
3
|
+
* Prompts — system prompt + task framing for VectorHead.
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.SYSTEM_PROMPT = void 0;
|
|
7
7
|
exports.taskPrompt = taskPrompt;
|
|
8
8
|
exports.continuationPrompt = continuationPrompt;
|
|
9
|
-
exports.SYSTEM_PROMPT = `You are
|
|
9
|
+
exports.SYSTEM_PROMPT = `You are VectorHead, an AI coding agent operating inside a terminal.
|
|
10
10
|
|
|
11
11
|
You work autonomously inside the user's project directory. You can:
|
|
12
12
|
- Inspect the project structure and files
|
package/dist/cli/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* VectorHead — AI Coding Agent CLI/TUI
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
8
|
* vector start interactive TUI
|
|
@@ -84,7 +84,7 @@ function parseArgs(argv) {
|
|
|
84
84
|
return opts;
|
|
85
85
|
}
|
|
86
86
|
function printHelp() {
|
|
87
|
-
console.log(`
|
|
87
|
+
console.log(`VectorHead — AI Coding Agent for Linux & Termux v${VERSION}
|
|
88
88
|
|
|
89
89
|
Usage:
|
|
90
90
|
vector Start the interactive terminal UI
|
|
@@ -128,16 +128,16 @@ async function runNonInteractive(opts) {
|
|
|
128
128
|
(0, credentials_1.hasApiKey)(agent.config.provider) ||
|
|
129
129
|
(agent.config.provider === 'custom' && Boolean(agent.config.apiUrl));
|
|
130
130
|
if (!hasKey) {
|
|
131
|
-
console.error(`
|
|
131
|
+
console.error(`VectorHead: no API key configured for provider "${agent.config.provider}".\n` +
|
|
132
132
|
`Set ${providerEnv(agent.config.provider)} or run \`vector\` and use /provider to enter a key.`);
|
|
133
133
|
process.exit(1);
|
|
134
134
|
}
|
|
135
135
|
const prompt = opts.prompt;
|
|
136
136
|
if (!prompt) {
|
|
137
|
-
console.error('
|
|
137
|
+
console.error('VectorHead: no task provided (non-interactive mode). Pass a prompt as an argument.');
|
|
138
138
|
process.exit(1);
|
|
139
139
|
}
|
|
140
|
-
console.log(`${terminal_1.ANSI.bold}${terminal_1.ANSI.
|
|
140
|
+
console.log(`${terminal_1.ANSI.bold}${terminal_1.ANSI.fg(208)}VectorHead${terminal_1.ANSI.reset} — ${agent.config.provider}/${agent.config.model}`);
|
|
141
141
|
console.log(`${terminal_1.ANSI.dim}Project: ${agent.cwd}${terminal_1.ANSI.reset}\n`);
|
|
142
142
|
// Continue session if requested
|
|
143
143
|
let sessionMessages = null;
|
|
@@ -205,7 +205,7 @@ async function runTui(opts) {
|
|
|
205
205
|
(config.provider === 'custom' && Boolean(config.apiUrl));
|
|
206
206
|
if (!hasKey) {
|
|
207
207
|
console.log(`${terminal_1.ANSI.yellow}⚠${terminal_1.ANSI.reset} No API key configured for provider "${config.provider}".\n` +
|
|
208
|
-
`Set ${providerEnv(config.provider)} in your environment, or run /provider inside
|
|
208
|
+
`Set ${providerEnv(config.provider)} in your environment, or run /provider inside VectorHead to add one.\n`);
|
|
209
209
|
}
|
|
210
210
|
// Resolve session name
|
|
211
211
|
let sessionName = opts.sessionName;
|
|
@@ -295,7 +295,7 @@ async function main() {
|
|
|
295
295
|
console.warn(`${terminal_1.ANSI.yellow}⚠ WARNING: --yolo mode skips permission prompts for ask-level actions.${terminal_1.ANSI.reset}`);
|
|
296
296
|
}
|
|
297
297
|
(0, paths_1.ensureDirs)();
|
|
298
|
-
logger_1.logger.info(`
|
|
298
|
+
logger_1.logger.info(`VectorHead v${VERSION} started in ${process.cwd()}`);
|
|
299
299
|
if (opts.nonInteractive) {
|
|
300
300
|
await runNonInteractive(opts);
|
|
301
301
|
return;
|
|
@@ -305,11 +305,11 @@ async function main() {
|
|
|
305
305
|
}
|
|
306
306
|
catch (err) {
|
|
307
307
|
logger_1.logger.error(`fatal: ${err.message}`);
|
|
308
|
-
console.error(`
|
|
308
|
+
console.error(`VectorHead error: ${err.message}`);
|
|
309
309
|
process.exitCode = 1;
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
main().catch((err) => {
|
|
313
|
-
console.error(`
|
|
313
|
+
console.error(`VectorHead fatal: ${err.message}`);
|
|
314
314
|
process.exit(1);
|
|
315
315
|
});
|
package/dist/config/config.js
CHANGED
|
@@ -38,7 +38,7 @@ exports.loadConfig = loadConfig;
|
|
|
38
38
|
exports.saveConfig = saveConfig;
|
|
39
39
|
exports.effectiveConfig = effectiveConfig;
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
41
|
+
* VectorHead configuration — stored in ~/.vector/config.json
|
|
42
42
|
*
|
|
43
43
|
* Config is deliberately minimal and JSON-based. All secrets live in
|
|
44
44
|
* credentials.json (0600) or environment variables, never in config.json.
|
|
@@ -16,7 +16,7 @@ class OpenRouterProvider extends openai_compat_1.OpenAICompatProvider {
|
|
|
16
16
|
headers() {
|
|
17
17
|
const h = super.headers();
|
|
18
18
|
h['HTTP-Referer'] = 'https://github.com/clickmamaheti-prog/vector-agent';
|
|
19
|
-
h['X-Title'] = '
|
|
19
|
+
h['X-Title'] = 'VectorHead AI Coding Agent';
|
|
20
20
|
return h;
|
|
21
21
|
}
|
|
22
22
|
}
|
package/dist/tools/git.js
CHANGED
|
@@ -48,7 +48,7 @@ function createGitTool() {
|
|
|
48
48
|
ctx.onActivity?.('git', `$ ${cmd}`);
|
|
49
49
|
const level = classifyGit(gitArgs);
|
|
50
50
|
if (level === 'blocked') {
|
|
51
|
-
return { output: `BLOCKED: "git push" is not allowed —
|
|
51
|
+
return { output: `BLOCKED: "git push" is not allowed — VectorHead never pushes without explicit user approval outside this policy.` };
|
|
52
52
|
}
|
|
53
53
|
const allowed = await ctx.permissions.decide({ tool: 'git', summary: cmd, level });
|
|
54
54
|
if (!allowed) {
|
package/dist/tools/shell.js
CHANGED
|
@@ -70,7 +70,7 @@ function createShellTool() {
|
|
|
70
70
|
const level = ctx.permissions.classifyCommand(command);
|
|
71
71
|
if (level === 'blocked') {
|
|
72
72
|
return {
|
|
73
|
-
output: `BLOCKED: command is not permitted by
|
|
73
|
+
output: `BLOCKED: command is not permitted by VectorHead security policy:\n${command}`,
|
|
74
74
|
summary: 'blocked by policy',
|
|
75
75
|
};
|
|
76
76
|
}
|
package/dist/tools/web.js
CHANGED
|
@@ -7,7 +7,7 @@ async function fetchText(url, maxChars) {
|
|
|
7
7
|
try {
|
|
8
8
|
const res = await fetch(url, {
|
|
9
9
|
signal: controller.signal,
|
|
10
|
-
headers: { 'User-Agent': '
|
|
10
|
+
headers: { 'User-Agent': 'VectorHead-AI-Agent/0.1' },
|
|
11
11
|
redirect: 'follow',
|
|
12
12
|
});
|
|
13
13
|
if (!res.ok)
|
package/dist/tui/app.js
CHANGED
|
@@ -38,7 +38,7 @@ exports.App = void 0;
|
|
|
38
38
|
* TUI App — the main interactive loop.
|
|
39
39
|
*
|
|
40
40
|
* Layout (top to bottom):
|
|
41
|
-
* header
|
|
41
|
+
* header VectorHead ● status provider · model
|
|
42
42
|
* project bar Project: ~/x Mode: ask
|
|
43
43
|
* ───────────────────────────────────────────────
|
|
44
44
|
* chat scrollable message area
|
|
@@ -90,7 +90,7 @@ class App {
|
|
|
90
90
|
start() {
|
|
91
91
|
this.connected = true;
|
|
92
92
|
this.status = 'Ready';
|
|
93
|
-
this.addSystem('
|
|
93
|
+
this.addSystem('VectorHead ready. Type a request or press ? for help.');
|
|
94
94
|
if (this.opts.continueSession && this.sessionName) {
|
|
95
95
|
const sess = (0, session_1.loadSession)(this.sessionName);
|
|
96
96
|
if (sess) {
|
|
@@ -395,6 +395,7 @@ class App {
|
|
|
395
395
|
this.modal = { type: 'model', selected: 0 };
|
|
396
396
|
break;
|
|
397
397
|
case '/provider':
|
|
398
|
+
case '/setup':
|
|
398
399
|
this.modal = { type: 'provider', selected: 0 };
|
|
399
400
|
break;
|
|
400
401
|
case '/session':
|
|
@@ -499,6 +500,11 @@ class App {
|
|
|
499
500
|
this.agent.rebuildProvider();
|
|
500
501
|
(0, config_1.saveConfig)(this.agent.config);
|
|
501
502
|
this.addSystem(`Provider set to ${opt.label}.`);
|
|
503
|
+
if (pid === 'custom' && !this.agent.config.apiUrl) {
|
|
504
|
+
// Wizard: ask base URL first, then API token
|
|
505
|
+
this.modal = { type: 'custom-url', value: '' };
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
502
508
|
if (!(0, credentials_1.hasApiKey)(pid) && pid !== 'ollama') {
|
|
503
509
|
this.modal = { type: 'apikey', provider: pid, value: '' };
|
|
504
510
|
return;
|
|
@@ -604,7 +610,12 @@ class App {
|
|
|
604
610
|
this.agent.config.apiUrl = value;
|
|
605
611
|
(0, config_1.saveConfig)(this.agent.config);
|
|
606
612
|
this.addSystem('Custom API URL set.');
|
|
607
|
-
|
|
613
|
+
if (!(0, credentials_1.hasApiKey)('custom')) {
|
|
614
|
+
this.modal = { type: 'custom-key', value: '' };
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
this.modal = { type: 'none' };
|
|
618
|
+
}
|
|
608
619
|
});
|
|
609
620
|
break;
|
|
610
621
|
case 'custom-key':
|
|
@@ -742,7 +753,14 @@ class App {
|
|
|
742
753
|
return (0, components_1.renderModal)({
|
|
743
754
|
title: 'Help',
|
|
744
755
|
body: [
|
|
745
|
-
|
|
756
|
+
`${theme_1.THEME.accent}██╗ ██╗███████╗ ██████╗████████╗ ██████╗ ██████╗ ██╗ ██╗███████╗ █████╗ ██████╗${theme_1.THEME.reset}`,
|
|
757
|
+
`${theme_1.THEME.accent}██║ ██║██╔════╝██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗██║ ██║██╔════╝██╔══██╗██╔══██╗${theme_1.THEME.reset}`,
|
|
758
|
+
`${theme_1.THEME.accent}██║ ██║█████╗ ██║ ██║ ██║ ██║██████╔╝███████║█████╗ ███████║██║ ██║${theme_1.THEME.reset}`,
|
|
759
|
+
`${theme_1.THEME.accent}╚██╗ ██╔╝██╔══╝ ██║ ██║ ██║ ██║██╔══██╗██╔══██║██╔══╝ ██╔══██║██║ ██║${theme_1.THEME.reset}`,
|
|
760
|
+
`${theme_1.THEME.accent} ╚████╔╝ ███████╗╚██████╗ ██║ ╚██████╔╝██║ ██║██║ ██║███████╗██║ ██║██████╔╝${theme_1.THEME.reset}`,
|
|
761
|
+
`${theme_1.THEME.accent} ╚═══╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═════╝ ${theme_1.THEME.reset}`,
|
|
762
|
+
'',
|
|
763
|
+
`${theme_1.THEME.gold}${theme_1.THEME.bold}VectorHead${theme_1.THEME.reset} — AI Coding Agent for Linux & Termux`,
|
|
746
764
|
'',
|
|
747
765
|
'Type a task and press Enter. The agent inspects,',
|
|
748
766
|
'plans, edits, runs tests, and iterates until done.',
|
|
@@ -756,9 +774,9 @@ class App {
|
|
|
756
774
|
' Ctrl+C stop agent / quit',
|
|
757
775
|
' Ctrl+L clear conversation',
|
|
758
776
|
'',
|
|
759
|
-
'Commands: /model /provider /session
|
|
760
|
-
'/status /clear /continue /diff
|
|
761
|
-
'/yolo /exit',
|
|
777
|
+
'Commands: /setup /model /provider /session',
|
|
778
|
+
'/settings /status /clear /continue /diff',
|
|
779
|
+
'/git /compact /yolo /exit',
|
|
762
780
|
],
|
|
763
781
|
}, rows, cols);
|
|
764
782
|
case 'commands':
|
|
@@ -766,6 +784,7 @@ class App {
|
|
|
766
784
|
title: 'Commands',
|
|
767
785
|
body: [
|
|
768
786
|
'/help show help',
|
|
787
|
+
'/setup setup provider (base URL + token)',
|
|
769
788
|
'/model select model',
|
|
770
789
|
'/provider select provider',
|
|
771
790
|
'/session manage sessions',
|
|
@@ -850,7 +869,7 @@ class App {
|
|
|
850
869
|
return (0, components_1.renderModal)({
|
|
851
870
|
title: `Permission (${m.req.level})`,
|
|
852
871
|
body: [
|
|
853
|
-
`${danger ? terminal_1.ANSI.brightRed + '⚠' : terminal_1.ANSI.yellow + '⚠'}${terminal_1.ANSI.reset}
|
|
872
|
+
`${danger ? terminal_1.ANSI.brightRed + '⚠' : terminal_1.ANSI.yellow + '⚠'}${terminal_1.ANSI.reset} VectorHead wants to:`,
|
|
854
873
|
'',
|
|
855
874
|
`${terminal_1.ANSI.bold}${m.req.summary}${terminal_1.ANSI.reset}`,
|
|
856
875
|
'',
|
|
@@ -891,8 +910,15 @@ class App {
|
|
|
891
910
|
}, rows, cols);
|
|
892
911
|
case 'custom-url':
|
|
893
912
|
return (0, components_1.renderModal)({
|
|
894
|
-
title: 'Custom API URL',
|
|
895
|
-
body: [
|
|
913
|
+
title: 'Custom API Base URL',
|
|
914
|
+
body: [
|
|
915
|
+
'Base URL (OpenAI-compatible), e.g.',
|
|
916
|
+
'https://example.com/v1',
|
|
917
|
+
'',
|
|
918
|
+
m.value || '_',
|
|
919
|
+
'',
|
|
920
|
+
'Enter to save · Esc to cancel',
|
|
921
|
+
],
|
|
896
922
|
}, rows, cols);
|
|
897
923
|
case 'custom-key':
|
|
898
924
|
return (0, components_1.renderModal)({
|
package/dist/tui/chat.js
CHANGED
|
@@ -4,50 +4,55 @@ 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.
|
|
13
|
-
assistant: { prefix: '
|
|
18
|
+
user: { prefix: 'YOU', color: theme_1.THEME.gold },
|
|
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 },
|
|
16
22
|
error: { prefix: '✗', color: theme_1.THEME.error },
|
|
17
23
|
success: { prefix: '✓', color: theme_1.THEME.success },
|
|
18
24
|
warning: { prefix: '⚠', color: theme_1.THEME.warn },
|
|
19
25
|
};
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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/dist/tui/input.js
CHANGED
|
@@ -97,10 +97,11 @@ class InputBox {
|
|
|
97
97
|
}
|
|
98
98
|
/** Render the input line, returns the cursor column (0-based within line). */
|
|
99
99
|
render(width) {
|
|
100
|
-
const prefix =
|
|
101
|
-
const
|
|
100
|
+
const prefix = `${terminal_1.ANSI.fg(220)}❯${terminal_1.ANSI.reset} `;
|
|
101
|
+
const prefixWidth = 2; // ❯ + space (ANSI codes are invisible)
|
|
102
|
+
const available = Math.max(1, width - prefixWidth - 1);
|
|
102
103
|
let display = this.text;
|
|
103
|
-
let cursorCol =
|
|
104
|
+
let cursorCol = prefixWidth + this.cursor;
|
|
104
105
|
if (this.text.length > available) {
|
|
105
106
|
// Scroll window around the cursor
|
|
106
107
|
let start = this.cursor - Math.floor(available / 2);
|
|
@@ -113,7 +114,7 @@ class InputBox {
|
|
|
113
114
|
suffix = terminal_1.ANSI.dim + this.hint + terminal_1.ANSI.reset;
|
|
114
115
|
}
|
|
115
116
|
return {
|
|
116
|
-
line: `${
|
|
117
|
+
line: `${prefix}${display}${suffix}`,
|
|
117
118
|
cursorCol,
|
|
118
119
|
};
|
|
119
120
|
}
|
package/dist/tui/statusbar.js
CHANGED
|
@@ -5,13 +5,13 @@ exports.renderProjectBar = renderProjectBar;
|
|
|
5
5
|
exports.renderStatusBar = renderStatusBar;
|
|
6
6
|
/**
|
|
7
7
|
* Status bar — top header + project bar + bottom hint bar.
|
|
8
|
-
* Uses the
|
|
8
|
+
* Uses the VectorHead theme with a live spinner while busy.
|
|
9
9
|
*/
|
|
10
10
|
const terminal_1 = require("../utils/terminal");
|
|
11
11
|
const theme_1 = require("./theme");
|
|
12
|
-
/** Render the top header line:
|
|
12
|
+
/** Render the top header line: VectorHead ● status .... provider · model */
|
|
13
13
|
function renderHeader(width, info) {
|
|
14
|
-
const brand = `${theme_1.THEME.bold}${theme_1.THEME.accent}
|
|
14
|
+
const brand = `${theme_1.THEME.bold}${theme_1.THEME.accent}VectorHead${theme_1.THEME.reset}`;
|
|
15
15
|
const dot = info.connected ? `${theme_1.THEME.success}●${theme_1.THEME.reset}` : `${theme_1.THEME.error}○${theme_1.THEME.reset}`;
|
|
16
16
|
const status = `${dot} ${(0, theme_1.statusColor)(info.status)}${info.status}${theme_1.THEME.reset}`;
|
|
17
17
|
const left = `${brand} ${status}`;
|
package/dist/tui/theme.js
CHANGED
|
@@ -3,19 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Spinner = exports.SPINNERS = exports.THEME = void 0;
|
|
4
4
|
exports.statusColor = statusColor;
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* VectorHead theme — consistent premium palette for the TUI.
|
|
7
|
+
* Warm dark palette with orange accent + gold highlights.
|
|
8
8
|
*/
|
|
9
9
|
exports.THEME = {
|
|
10
|
-
accent: '\x1b[38;5;
|
|
11
|
-
accentDim: '\x1b[38;5;
|
|
10
|
+
accent: '\x1b[38;5;208m', // bright orange
|
|
11
|
+
accentDim: '\x1b[38;5;172m',
|
|
12
12
|
gold: '\x1b[38;5;220m',
|
|
13
13
|
text: '\x1b[38;5;252m',
|
|
14
14
|
textBright: '\x1b[38;5;255m',
|
|
15
15
|
muted: '\x1b[38;5;244m',
|
|
16
16
|
faint: '\x1b[38;5;240m',
|
|
17
|
-
border: '\x1b[38;5;
|
|
18
|
-
borderBright: '\x1b[38;5;
|
|
17
|
+
border: '\x1b[38;5;94m',
|
|
18
|
+
borderBright: '\x1b[38;5;208m',
|
|
19
19
|
success: '\x1b[38;5;114m',
|
|
20
20
|
warn: '\x1b[38;5;215m',
|
|
21
21
|
error: '\x1b[38;5;203m',
|
package/dist/utils/paths.js
CHANGED
|
@@ -44,7 +44,7 @@ exports.getLogsDir = getLogsDir;
|
|
|
44
44
|
exports.ensureDirs = ensureDirs;
|
|
45
45
|
exports.sanitizeName = sanitizeName;
|
|
46
46
|
/**
|
|
47
|
-
* Path helpers for the
|
|
47
|
+
* Path helpers for the VectorHead config directory (~/.vector).
|
|
48
48
|
*/
|
|
49
49
|
const fs = __importStar(require("node:fs"));
|
|
50
50
|
const os = __importStar(require("node:os"));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agen-vektor",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.1",
|
|
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": {
|
|
7
7
|
"vector": "dist/cli/index.js"
|