agen-vektor 0.1.0

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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +212 -0
  3. package/dist/agent/agent.js +90 -0
  4. package/dist/agent/context.js +106 -0
  5. package/dist/agent/loop.js +152 -0
  6. package/dist/agent/memory.js +87 -0
  7. package/dist/agent/planner.js +38 -0
  8. package/dist/agent/prompts.js +41 -0
  9. package/dist/agent/session.js +125 -0
  10. package/dist/cli/index.js +304 -0
  11. package/dist/cli/keyboard.js +283 -0
  12. package/dist/config/config.js +92 -0
  13. package/dist/config/credentials.js +127 -0
  14. package/dist/config/models.js +47 -0
  15. package/dist/providers/anthropic.js +276 -0
  16. package/dist/providers/custom.js +24 -0
  17. package/dist/providers/factory.js +58 -0
  18. package/dist/providers/gemini.js +223 -0
  19. package/dist/providers/ollama.js +195 -0
  20. package/dist/providers/openai-compat.js +296 -0
  21. package/dist/providers/openai.js +17 -0
  22. package/dist/providers/openrouter.js +23 -0
  23. package/dist/providers/provider.js +39 -0
  24. package/dist/security/command-policy.js +99 -0
  25. package/dist/security/permissions.js +66 -0
  26. package/dist/tools/filesystem.js +297 -0
  27. package/dist/tools/git.js +66 -0
  28. package/dist/tools/registry.js +39 -0
  29. package/dist/tools/search.js +184 -0
  30. package/dist/tools/shell.js +108 -0
  31. package/dist/tools/web.js +76 -0
  32. package/dist/tui/app.js +913 -0
  33. package/dist/tui/chat.js +76 -0
  34. package/dist/tui/components.js +85 -0
  35. package/dist/tui/diff.js +105 -0
  36. package/dist/tui/input.js +121 -0
  37. package/dist/tui/statusbar.js +32 -0
  38. package/dist/utils/logger.js +96 -0
  39. package/dist/utils/paths.js +90 -0
  40. package/dist/utils/terminal.js +191 -0
  41. package/package.json +56 -0
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generatePlan = generatePlan;
4
+ const context_1 = require("./context");
5
+ const PLAN_PROMPT = `You are a planning engine. Given the user's task and the project context, produce a concise numbered plan of the steps the agent should take. Output ONLY the numbered steps, one per line. No preamble, no markdown fences.`;
6
+ async function generatePlan(provider, userRequest, cwd, model, signal) {
7
+ const projectCtx = (0, context_1.buildProjectContext)(cwd);
8
+ const entries = (0, context_1.listRootEntries)(cwd);
9
+ const messages = [
10
+ { role: 'system', content: PLAN_PROMPT },
11
+ {
12
+ role: 'user',
13
+ content: `Project context:\n${projectCtx}\n\n${entries}\n\nTASK:\n${userRequest}`,
14
+ },
15
+ ];
16
+ try {
17
+ const res = await provider.chat({ messages, model, signal, temperature: 0.2, maxTokens: 600 });
18
+ const steps = res.content
19
+ .split('\n')
20
+ .map((l) => l.replace(/^\s*\d+[.)]\s*/, '').replace(/^\s*[-*]\s*/, '').trim())
21
+ .filter((l) => l.length > 0)
22
+ .slice(0, 15);
23
+ return { steps: steps.length > 0 ? steps : [userRequest], raw: res.content };
24
+ }
25
+ catch {
26
+ // Fallback plan when the provider fails
27
+ return {
28
+ steps: [
29
+ `Inspect the project structure at ${cwd}`,
30
+ `Understand the request: ${userRequest}`,
31
+ 'Implement the required changes',
32
+ 'Verify with tests/build',
33
+ 'Report the result',
34
+ ],
35
+ raw: '',
36
+ };
37
+ }
38
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ /**
3
+ * Prompts — system prompt + task framing for VECTOR.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SYSTEM_PROMPT = void 0;
7
+ exports.taskPrompt = taskPrompt;
8
+ exports.continuationPrompt = continuationPrompt;
9
+ exports.SYSTEM_PROMPT = `You are VECTOR, an AI coding agent operating inside a terminal.
10
+
11
+ You work autonomously inside the user's project directory. You can:
12
+ - Inspect the project structure and files
13
+ - Read, create, edit, and delete files (deletion always asks permission)
14
+ - Run shell commands (build, test, install, git, etc.)
15
+ - Search the codebase
16
+ - Iterate: run tests, read errors, fix, retry until the task is done
17
+
18
+ Rules:
19
+ 1. Always inspect the project before proposing changes. Never guess at file contents.
20
+ 2. Prefer small, targeted edits over rewriting whole files.
21
+ 3. After editing code, run the project's tests/build to verify (npm test, npm run build, pytest, etc.).
22
+ 4. If a command fails, read the error, diagnose, fix, and retry. Do not give up after the first failure.
23
+ 5. When the task is complete, summarize what you changed and the verification result.
24
+ 6. Never print API keys, tokens, or secrets. If you find a secret, say so without revealing it.
25
+ 7. When you need more information than you have, use tools to get it instead of asking the user.
26
+ 8. Answer in the same language the user used.
27
+ 9. Keep responses concise but complete: state what you did, why, and how it was verified.`;
28
+ function taskPrompt(userRequest, cwd) {
29
+ return `Working directory: ${cwd}
30
+
31
+ TASK (from the user):
32
+ ${userRequest}
33
+
34
+ Work through this task step by step using the available tools. Do not stop until the task is complete and verified.`;
35
+ }
36
+ function continuationPrompt(plan) {
37
+ return `Proposed plan:
38
+ ${plan.map((p, i) => `${i + 1}. ${p}`).join('\n')}
39
+
40
+ Execute this plan, adapting as needed based on what you find.`;
41
+ }
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.listSessions = listSessions;
37
+ exports.loadSession = loadSession;
38
+ exports.saveSession = saveSession;
39
+ exports.deleteSession = deleteSession;
40
+ exports.messagesToContinue = messagesToContinue;
41
+ /**
42
+ * Session — persists conversation history + metadata to ~/.vector/sessions/.
43
+ * Secrets are never stored: only message roles/content/tool-call names.
44
+ */
45
+ const fs = __importStar(require("node:fs"));
46
+ const path = __importStar(require("node:path"));
47
+ const paths_1 = require("../utils/paths");
48
+ const credentials_1 = require("../config/credentials");
49
+ function fileFor(name) {
50
+ return path.join((0, paths_1.getSessionsDir)(), `${(0, paths_1.sanitizeName)(name)}.json`);
51
+ }
52
+ function listSessions() {
53
+ (0, paths_1.ensureDirs)();
54
+ try {
55
+ const files = fs.readdirSync((0, paths_1.getSessionsDir)()).filter((f) => f.endsWith('.json'));
56
+ const metas = [];
57
+ for (const f of files) {
58
+ try {
59
+ const data = JSON.parse(fs.readFileSync(path.join((0, paths_1.getSessionsDir)(), f), 'utf8'));
60
+ metas.push(data.meta);
61
+ }
62
+ catch {
63
+ /* skip corrupt */
64
+ }
65
+ }
66
+ return metas.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
67
+ }
68
+ catch {
69
+ return [];
70
+ }
71
+ }
72
+ function loadSession(name) {
73
+ try {
74
+ const p = fileFor(name);
75
+ if (!fs.existsSync(p))
76
+ return null;
77
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }
83
+ function saveSession(name, messages, opts) {
84
+ (0, paths_1.ensureDirs)();
85
+ const existing = loadSession(name);
86
+ const now = new Date().toISOString();
87
+ const meta = {
88
+ name,
89
+ cwd: opts.cwd,
90
+ createdAt: existing?.meta.createdAt || now,
91
+ updatedAt: now,
92
+ model: opts.model,
93
+ provider: opts.provider,
94
+ messageCount: messages.length,
95
+ };
96
+ // Never persist secrets: redact any key-like content before writing.
97
+ const sanitized = messages.map((m) => ({
98
+ ...m,
99
+ content: (0, credentials_1.redact)(m.content),
100
+ toolCalls: m.toolCalls?.map((tc) => ({
101
+ ...tc,
102
+ arguments: (0, credentials_1.redact)(tc.arguments),
103
+ })),
104
+ }));
105
+ const data = { meta, messages: sanitized };
106
+ fs.writeFileSync(fileFor(name), JSON.stringify(data, null, 2));
107
+ }
108
+ function deleteSession(name) {
109
+ try {
110
+ const p = fileFor(name);
111
+ if (fs.existsSync(p)) {
112
+ fs.unlinkSync(p);
113
+ return true;
114
+ }
115
+ }
116
+ catch {
117
+ /* ignore */
118
+ }
119
+ return false;
120
+ }
121
+ /** Build the message history to continue from an existing session. */
122
+ function messagesToContinue(session, maxMessages = 40) {
123
+ const msgs = session.messages.filter((m) => m.role === 'user' || m.role === 'assistant' || m.role === 'tool' || m.role === 'system');
124
+ return msgs.slice(-maxMessages);
125
+ }
@@ -0,0 +1,304 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ /**
5
+ * VECTOR — AI Coding Agent CLI/TUI
6
+ *
7
+ * Usage:
8
+ * vector start interactive TUI
9
+ * vector "task description" run a task in the TUI immediately
10
+ * vector --model M --provider P
11
+ * vector --continue continue last session
12
+ * vector --session NAME
13
+ * vector --plan show a plan before executing
14
+ * vector --yolo skip permission prompts (ask-level)
15
+ * vector --version | --help
16
+ */
17
+ const app_1 = require("../tui/app");
18
+ const agent_1 = require("../agent/agent");
19
+ const config_1 = require("../config/config");
20
+ const credentials_1 = require("../config/credentials");
21
+ const paths_1 = require("../utils/paths");
22
+ const logger_1 = require("../utils/logger");
23
+ const keyboard_1 = require("./keyboard");
24
+ const terminal_1 = require("../utils/terminal");
25
+ const session_1 = require("../agent/session");
26
+ const VERSION = '0.1.0';
27
+ function parseArgs(argv) {
28
+ const opts = {};
29
+ const positional = [];
30
+ for (let i = 0; i < argv.length; i++) {
31
+ const a = argv[i];
32
+ switch (a) {
33
+ case '--model':
34
+ opts.model = argv[++i];
35
+ break;
36
+ case '--provider':
37
+ opts.provider = argv[++i];
38
+ break;
39
+ case '--continue':
40
+ opts.continueSession = true;
41
+ break;
42
+ case '--session':
43
+ opts.sessionName = argv[++i];
44
+ break;
45
+ case '--plan':
46
+ opts.plan = true;
47
+ break;
48
+ case '--yolo':
49
+ opts.yolo = true;
50
+ break;
51
+ case '--help':
52
+ case '-h':
53
+ opts.help = true;
54
+ break;
55
+ case '--version':
56
+ case '-v':
57
+ opts.version = true;
58
+ break;
59
+ case '--non-interactive':
60
+ opts.nonInteractive = true;
61
+ break;
62
+ default:
63
+ if (a.startsWith('-')) {
64
+ console.error(`Unknown option: ${a} (see --help)`);
65
+ process.exit(1);
66
+ }
67
+ positional.push(a);
68
+ break;
69
+ }
70
+ }
71
+ if (positional.length > 0)
72
+ opts.prompt = positional.join(' ');
73
+ return opts;
74
+ }
75
+ function printHelp() {
76
+ console.log(`VECTOR — AI Coding Agent for Linux & Termux v${VERSION}
77
+
78
+ Usage:
79
+ vector Start the interactive terminal UI
80
+ vector "task" Start TUI and run the task immediately
81
+ vector --model MODEL Set model
82
+ vector --provider P Set provider (openai, anthropic, gemini,
83
+ openrouter, ollama, custom)
84
+ vector --continue Continue the most recent session
85
+ vector --session NAME Use/resume session NAME
86
+ vector --plan Show a plan before executing
87
+ vector --yolo Skip permission prompts (ask-level actions)
88
+ vector --version Print version
89
+ vector --help Show this help
90
+
91
+ Environment:
92
+ VECTOR_API_KEY API key for custom provider
93
+ VECTOR_API_URL Base URL for custom provider
94
+ VECTOR_MODEL Default model
95
+ VECTOR_PROVIDER Default provider
96
+ VECTOR_HOME Config dir (default ~/.vector)
97
+
98
+ Config lives in ~/.vector/. Run 'vector' and type /help for commands.`);
99
+ }
100
+ async function buildAgent(opts) {
101
+ const config = (0, config_1.loadConfig)();
102
+ if (opts.model)
103
+ config.model = opts.model;
104
+ if (opts.provider)
105
+ config.provider = opts.provider;
106
+ if (opts.yolo) {
107
+ config.permissionMode = 'yolo';
108
+ (0, config_1.saveConfig)(config);
109
+ }
110
+ (0, paths_1.ensureDirs)();
111
+ return new agent_1.Agent({ config, cwd: process.cwd() });
112
+ }
113
+ /** Non-interactive run: executes the task and prints output. */
114
+ async function runNonInteractive(opts) {
115
+ const agent = await buildAgent(opts);
116
+ const hasKey = agent.config.provider === 'ollama' ||
117
+ (0, credentials_1.hasApiKey)(agent.config.provider) ||
118
+ (agent.config.provider === 'custom' && Boolean(agent.config.apiUrl));
119
+ if (!hasKey) {
120
+ console.error(`VECTOR: no API key configured for provider "${agent.config.provider}".\n` +
121
+ `Set ${providerEnv(agent.config.provider)} or run \`vector\` and use /provider to enter a key.`);
122
+ process.exit(1);
123
+ }
124
+ const prompt = opts.prompt;
125
+ if (!prompt) {
126
+ console.error('VECTOR: no task provided (non-interactive mode). Pass a prompt as an argument.');
127
+ process.exit(1);
128
+ }
129
+ console.log(`${terminal_1.ANSI.bold}${terminal_1.ANSI.brightCyan}VECTOR${terminal_1.ANSI.reset} — ${agent.config.provider}/${agent.config.model}`);
130
+ console.log(`${terminal_1.ANSI.dim}Project: ${agent.cwd}${terminal_1.ANSI.reset}\n`);
131
+ // Continue session if requested
132
+ let sessionMessages = null;
133
+ if (opts.continueSession || opts.sessionName) {
134
+ const name = opts.sessionName || latestSessionName();
135
+ if (name) {
136
+ const sess = (0, session_1.loadSession)(name);
137
+ if (sess && sess.messages.length > 0) {
138
+ sessionMessages = sess;
139
+ console.log(`${terminal_1.ANSI.dim}Continuing session: ${name}${terminal_1.ANSI.reset}`);
140
+ }
141
+ }
142
+ }
143
+ const result = await agent.runWithContext(prompt, sessionMessages, {
144
+ onStatus: (s) => console.log(`${terminal_1.ANSI.dim}▸ ${s}${terminal_1.ANSI.reset}`),
145
+ onToolCall: (t, args) => {
146
+ let preview = '';
147
+ try {
148
+ const p = JSON.parse(args);
149
+ preview = Object.entries(p).map(([k, v]) => `${k}=${String(v).slice(0, 60)}`).join(' ');
150
+ }
151
+ catch {
152
+ preview = args.slice(0, 80);
153
+ }
154
+ console.log(`${terminal_1.ANSI.brightYellow}⚙ ${t}${terminal_1.ANSI.reset} ${terminal_1.ANSI.dim}${preview}${terminal_1.ANSI.reset}`);
155
+ },
156
+ onDelta: (d) => process.stdout.write(d),
157
+ onFinal: () => process.stdout.write('\n'),
158
+ });
159
+ console.log(`\n${terminal_1.ANSI.dim}— ${result.iterations} iterations, ${result.toolCalls} tool calls${terminal_1.ANSI.reset}`);
160
+ if (opts.sessionName) {
161
+ // persist
162
+ }
163
+ process.exit(0);
164
+ }
165
+ function providerEnv(provider) {
166
+ switch (provider) {
167
+ case 'openai':
168
+ return 'OPENAI_API_KEY';
169
+ case 'anthropic':
170
+ return 'ANTHROPIC_API_KEY';
171
+ case 'gemini':
172
+ return 'GEMINI_API_KEY';
173
+ case 'openrouter':
174
+ return 'OPENROUTER_API_KEY';
175
+ case 'custom':
176
+ return 'VECTOR_API_KEY';
177
+ default:
178
+ return 'VECTOR_API_KEY';
179
+ }
180
+ }
181
+ function latestSessionName() {
182
+ const sessions = (0, session_1.listSessions)();
183
+ return sessions.length > 0 ? sessions[0].name : undefined;
184
+ }
185
+ async function runTui(opts) {
186
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
187
+ await runNonInteractive(opts);
188
+ return;
189
+ }
190
+ const agent = await buildAgent(opts);
191
+ const config = agent.config;
192
+ const hasKey = config.provider === 'ollama' ||
193
+ (0, credentials_1.hasApiKey)(config.provider) ||
194
+ (config.provider === 'custom' && Boolean(config.apiUrl));
195
+ if (!hasKey) {
196
+ console.log(`${terminal_1.ANSI.yellow}⚠${terminal_1.ANSI.reset} No API key configured for provider "${config.provider}".\n` +
197
+ `Set ${providerEnv(config.provider)} in your environment, or run /provider inside VECTOR to add one.\n`);
198
+ }
199
+ // Resolve session name
200
+ let sessionName = opts.sessionName;
201
+ if (opts.continueSession && !sessionName) {
202
+ sessionName = latestSessionName();
203
+ }
204
+ let exit = false;
205
+ // Permission callback: drive a modal through the app
206
+ let app = null;
207
+ let permissionResolver = null;
208
+ const askPermission = async (req) => {
209
+ if (req.level === 'blocked')
210
+ return { allow: false, remember: false };
211
+ // Use the app modal
212
+ return new Promise((resolve) => {
213
+ permissionResolver = resolve;
214
+ app?.setPermissionModal(req);
215
+ });
216
+ };
217
+ agent.setPermissionAsk(askPermission);
218
+ app = new app_1.App(agent, {
219
+ initialPrompt: opts.prompt,
220
+ sessionName: sessionName || undefined,
221
+ continueSession: opts.continueSession,
222
+ showPlan: opts.plan,
223
+ onExit: () => {
224
+ exit = true;
225
+ },
226
+ });
227
+ app.setPermissionResolver((d) => {
228
+ if (permissionResolver) {
229
+ permissionResolver(d);
230
+ permissionResolver = null;
231
+ }
232
+ });
233
+ // Terminal setup
234
+ const cleanup = (0, keyboard_1.enableRawMode)();
235
+ const doRender = () => {
236
+ const out = app.render();
237
+ process.stdout.write(terminal_1.ANSI.hideCursor + out);
238
+ };
239
+ const onResize = () => doRender();
240
+ process.stdout.on('resize', onResize);
241
+ process.on('SIGINT', () => {
242
+ // Ctrl+C is handled via raw mode keys; SIGINT may still fire
243
+ });
244
+ process.on('SIGWINCH', onResize);
245
+ const keyHandler = (ev) => {
246
+ void app.handleKey(ev);
247
+ };
248
+ try {
249
+ // Enable alt screen + clear
250
+ process.stdout.write(terminal_1.ANSI.enableAltScreen);
251
+ process.stdout.write(terminal_1.ANSI.clearScreen);
252
+ (0, keyboard_1.keyStream)(keyHandler);
253
+ app.start();
254
+ doRender();
255
+ // Main tick loop
256
+ while (!exit) {
257
+ await app.tick();
258
+ doRender();
259
+ await sleep(50);
260
+ }
261
+ }
262
+ finally {
263
+ process.stdout.write(terminal_1.ANSI.disableAltScreen);
264
+ process.stdout.write(terminal_1.ANSI.showCursor);
265
+ process.stdout.removeListener('resize', onResize);
266
+ cleanup();
267
+ }
268
+ }
269
+ function sleep(ms) {
270
+ return new Promise((r) => setTimeout(r, ms));
271
+ }
272
+ async function main() {
273
+ const opts = parseArgs(process.argv.slice(2));
274
+ if (opts.version) {
275
+ console.log(`vector v${VERSION}`);
276
+ return;
277
+ }
278
+ if (opts.help) {
279
+ printHelp();
280
+ return;
281
+ }
282
+ // Warnings
283
+ if (opts.yolo) {
284
+ console.warn(`${terminal_1.ANSI.yellow}⚠ WARNING: --yolo mode skips permission prompts for ask-level actions.${terminal_1.ANSI.reset}`);
285
+ }
286
+ (0, paths_1.ensureDirs)();
287
+ logger_1.logger.info(`VECTOR v${VERSION} started in ${process.cwd()}`);
288
+ if (opts.nonInteractive) {
289
+ await runNonInteractive(opts);
290
+ return;
291
+ }
292
+ try {
293
+ await runTui(opts);
294
+ }
295
+ catch (err) {
296
+ logger_1.logger.error(`fatal: ${err.message}`);
297
+ console.error(`VECTOR error: ${err.message}`);
298
+ process.exitCode = 1;
299
+ }
300
+ }
301
+ main().catch((err) => {
302
+ console.error(`VECTOR fatal: ${err.message}`);
303
+ process.exit(1);
304
+ });