viben 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.
- package/README.md +181 -0
- package/bin/viben.js +386 -0
- package/dist/index.cjs +1142 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +94 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.js +1102 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1102 @@
|
|
|
1
|
+
// src/cli.ts
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
|
|
5
|
+
// src/commands/init.ts
|
|
6
|
+
import * as fs3 from "fs";
|
|
7
|
+
import * as path3 from "path";
|
|
8
|
+
import chalk2 from "chalk";
|
|
9
|
+
|
|
10
|
+
// src/types/index.ts
|
|
11
|
+
var CliError = class extends Error {
|
|
12
|
+
constructor(message, code, details) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.details = details;
|
|
16
|
+
this.name = "CliError";
|
|
17
|
+
}
|
|
18
|
+
toResponse() {
|
|
19
|
+
return {
|
|
20
|
+
success: false,
|
|
21
|
+
error: {
|
|
22
|
+
code: this.code,
|
|
23
|
+
message: this.message,
|
|
24
|
+
details: this.details
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/lib/config.ts
|
|
31
|
+
import * as fs2 from "fs";
|
|
32
|
+
import * as path2 from "path";
|
|
33
|
+
import * as yaml from "yaml";
|
|
34
|
+
|
|
35
|
+
// src/lib/scope.ts
|
|
36
|
+
import * as fs from "fs";
|
|
37
|
+
import * as path from "path";
|
|
38
|
+
import * as os from "os";
|
|
39
|
+
var WORKSPACE_DIR = ".viben";
|
|
40
|
+
var CONFIG_FILE = "config.yaml";
|
|
41
|
+
function getGlobalConfigDir() {
|
|
42
|
+
return process.env.VIBEN_STATE_DIR || path.join(os.homedir(), WORKSPACE_DIR);
|
|
43
|
+
}
|
|
44
|
+
var GLOBAL_CONFIG_DIR = getGlobalConfigDir();
|
|
45
|
+
function getStateDir() {
|
|
46
|
+
return getGlobalConfigDir();
|
|
47
|
+
}
|
|
48
|
+
function findWorkspaceRoot(startDir) {
|
|
49
|
+
let currentDir = path.resolve(startDir);
|
|
50
|
+
const root = path.parse(currentDir).root;
|
|
51
|
+
while (currentDir !== root) {
|
|
52
|
+
const vibenDir = path.join(currentDir, WORKSPACE_DIR);
|
|
53
|
+
const configPath = path.join(vibenDir, CONFIG_FILE);
|
|
54
|
+
if (fs.existsSync(configPath)) {
|
|
55
|
+
return currentDir;
|
|
56
|
+
}
|
|
57
|
+
currentDir = path.dirname(currentDir);
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function getWorkspaceDir() {
|
|
62
|
+
const workspaceRoot = findWorkspaceRoot(process.cwd());
|
|
63
|
+
if (workspaceRoot) {
|
|
64
|
+
return path.join(workspaceRoot, WORKSPACE_DIR);
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
function resolveScope(options = {}) {
|
|
69
|
+
if (options.global) {
|
|
70
|
+
return "global";
|
|
71
|
+
}
|
|
72
|
+
if (options.workspace) {
|
|
73
|
+
return "workspace";
|
|
74
|
+
}
|
|
75
|
+
const envScope = process.env.VIBEN_SCOPE;
|
|
76
|
+
if (envScope === "global" || envScope === "workspace") {
|
|
77
|
+
return envScope;
|
|
78
|
+
}
|
|
79
|
+
const workspaceDir = getWorkspaceDir();
|
|
80
|
+
if (workspaceDir) {
|
|
81
|
+
return "workspace";
|
|
82
|
+
}
|
|
83
|
+
return "global";
|
|
84
|
+
}
|
|
85
|
+
function getConfigPathForScope(scope) {
|
|
86
|
+
if (scope === "workspace") {
|
|
87
|
+
const workspaceDir = getWorkspaceDir();
|
|
88
|
+
if (!workspaceDir) {
|
|
89
|
+
throw new CliError(
|
|
90
|
+
'Not in a workspace. Use --global or run "viben init" first.',
|
|
91
|
+
"NO_WORKSPACE"
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return path.join(workspaceDir, CONFIG_FILE);
|
|
95
|
+
}
|
|
96
|
+
const globalDir = getGlobalConfigDir();
|
|
97
|
+
return path.join(globalDir, CONFIG_FILE);
|
|
98
|
+
}
|
|
99
|
+
function ensureDir(dirPath) {
|
|
100
|
+
if (!fs.existsSync(dirPath)) {
|
|
101
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/lib/config.ts
|
|
106
|
+
var DEFAULT_CONFIG = {
|
|
107
|
+
version: 1,
|
|
108
|
+
settings: {
|
|
109
|
+
editor: "code",
|
|
110
|
+
pager: "less",
|
|
111
|
+
color: "auto"
|
|
112
|
+
},
|
|
113
|
+
agents: [],
|
|
114
|
+
mcp: {
|
|
115
|
+
enabled: []
|
|
116
|
+
},
|
|
117
|
+
skills: {
|
|
118
|
+
enabled: []
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
function readConfigFile(filePath) {
|
|
122
|
+
if (!fs2.existsSync(filePath)) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
127
|
+
return yaml.parse(content);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
throw new CliError(
|
|
130
|
+
`Failed to read config file: ${filePath}`,
|
|
131
|
+
"CONFIG_READ_ERROR",
|
|
132
|
+
error
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function writeConfigFile(filePath, config) {
|
|
137
|
+
try {
|
|
138
|
+
const dirPath = path2.dirname(filePath);
|
|
139
|
+
ensureDir(dirPath);
|
|
140
|
+
const content = yaml.stringify(config, {
|
|
141
|
+
indent: 2,
|
|
142
|
+
lineWidth: 0
|
|
143
|
+
// Don't wrap lines
|
|
144
|
+
});
|
|
145
|
+
fs2.writeFileSync(filePath, content, "utf-8");
|
|
146
|
+
} catch (error) {
|
|
147
|
+
throw new CliError(
|
|
148
|
+
`Failed to write config file: ${filePath}`,
|
|
149
|
+
"CONFIG_WRITE_ERROR",
|
|
150
|
+
error
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function readScopedConfig(scope) {
|
|
155
|
+
const configPath = getConfigPathForScope(scope);
|
|
156
|
+
return readConfigFile(configPath);
|
|
157
|
+
}
|
|
158
|
+
function writeScopedConfig(scope, config) {
|
|
159
|
+
const configPath = getConfigPathForScope(scope);
|
|
160
|
+
writeConfigFile(configPath, config);
|
|
161
|
+
}
|
|
162
|
+
function getConfigValue(config, key) {
|
|
163
|
+
const parts = parseKey(key);
|
|
164
|
+
let current = config;
|
|
165
|
+
for (const part of parts) {
|
|
166
|
+
if (current === null || current === void 0) {
|
|
167
|
+
return void 0;
|
|
168
|
+
}
|
|
169
|
+
if (typeof current !== "object") {
|
|
170
|
+
return void 0;
|
|
171
|
+
}
|
|
172
|
+
if (part.isArrayIndex) {
|
|
173
|
+
if (!Array.isArray(current)) {
|
|
174
|
+
return void 0;
|
|
175
|
+
}
|
|
176
|
+
current = current[part.index];
|
|
177
|
+
} else {
|
|
178
|
+
current = current[part.key];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return current;
|
|
182
|
+
}
|
|
183
|
+
function setConfigValue(config, key, value) {
|
|
184
|
+
const parts = parseKey(key);
|
|
185
|
+
if (parts.length === 0) {
|
|
186
|
+
return config;
|
|
187
|
+
}
|
|
188
|
+
const newConfig = JSON.parse(JSON.stringify(config));
|
|
189
|
+
let current = newConfig;
|
|
190
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
191
|
+
const part = parts[i];
|
|
192
|
+
const nextPart = parts[i + 1];
|
|
193
|
+
if (part.isArrayIndex) {
|
|
194
|
+
const arr = current;
|
|
195
|
+
if (!Array.isArray(arr)) {
|
|
196
|
+
throw new CliError(`Cannot index non-array at ${key}`, "INVALID_KEY");
|
|
197
|
+
}
|
|
198
|
+
if (arr[part.index] === void 0) {
|
|
199
|
+
arr[part.index] = nextPart.isArrayIndex ? [] : {};
|
|
200
|
+
}
|
|
201
|
+
current = arr[part.index];
|
|
202
|
+
} else {
|
|
203
|
+
if (current[part.key] === void 0 || typeof current[part.key] !== "object") {
|
|
204
|
+
current[part.key] = nextPart.isArrayIndex ? [] : {};
|
|
205
|
+
}
|
|
206
|
+
current = current[part.key];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const lastPart = parts[parts.length - 1];
|
|
210
|
+
if (lastPart.isArrayIndex) {
|
|
211
|
+
const arr = current;
|
|
212
|
+
arr[lastPart.index] = value;
|
|
213
|
+
} else {
|
|
214
|
+
current[lastPart.key] = value;
|
|
215
|
+
}
|
|
216
|
+
return newConfig;
|
|
217
|
+
}
|
|
218
|
+
function deleteConfigValue(config, key) {
|
|
219
|
+
const parts = parseKey(key);
|
|
220
|
+
if (parts.length === 0) {
|
|
221
|
+
return config;
|
|
222
|
+
}
|
|
223
|
+
const newConfig = JSON.parse(JSON.stringify(config));
|
|
224
|
+
let current = newConfig;
|
|
225
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
226
|
+
const part = parts[i];
|
|
227
|
+
if (part.isArrayIndex) {
|
|
228
|
+
const arr = current;
|
|
229
|
+
if (!Array.isArray(arr) || arr[part.index] === void 0) {
|
|
230
|
+
return config;
|
|
231
|
+
}
|
|
232
|
+
current = arr[part.index];
|
|
233
|
+
} else {
|
|
234
|
+
if (current[part.key] === void 0 || typeof current[part.key] !== "object") {
|
|
235
|
+
return config;
|
|
236
|
+
}
|
|
237
|
+
current = current[part.key];
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const lastPart = parts[parts.length - 1];
|
|
241
|
+
if (lastPart.isArrayIndex) {
|
|
242
|
+
const arr = current;
|
|
243
|
+
if (Array.isArray(arr)) {
|
|
244
|
+
arr.splice(lastPart.index, 1);
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
delete current[lastPart.key];
|
|
248
|
+
}
|
|
249
|
+
return newConfig;
|
|
250
|
+
}
|
|
251
|
+
function parseKey(key) {
|
|
252
|
+
const parts = [];
|
|
253
|
+
const regex = /([^.\[\]]+)|\[(\d+)\]/g;
|
|
254
|
+
let match;
|
|
255
|
+
while ((match = regex.exec(key)) !== null) {
|
|
256
|
+
if (match[1] !== void 0) {
|
|
257
|
+
parts.push({ key: match[1], isArrayIndex: false });
|
|
258
|
+
} else if (match[2] !== void 0) {
|
|
259
|
+
parts.push({ key: "", isArrayIndex: true, index: parseInt(match[2], 10) });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return parts;
|
|
263
|
+
}
|
|
264
|
+
function flattenConfig(config) {
|
|
265
|
+
const items = [];
|
|
266
|
+
function flatten(obj, prefix) {
|
|
267
|
+
if (obj === null || obj === void 0) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (Array.isArray(obj)) {
|
|
271
|
+
obj.forEach((item, index) => {
|
|
272
|
+
const key = `${prefix}[${index}]`;
|
|
273
|
+
if (typeof item === "object" && item !== null) {
|
|
274
|
+
flatten(item, key);
|
|
275
|
+
} else {
|
|
276
|
+
items.push({ key, value: formatValue(item) });
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
} else if (typeof obj === "object") {
|
|
280
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
281
|
+
const key = prefix ? `${prefix}.${k}` : k;
|
|
282
|
+
if (typeof v === "object" && v !== null) {
|
|
283
|
+
flatten(v, key);
|
|
284
|
+
} else {
|
|
285
|
+
items.push({ key, value: formatValue(v) });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
items.push({ key: prefix, value: formatValue(obj) });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
flatten(config, "");
|
|
293
|
+
return items;
|
|
294
|
+
}
|
|
295
|
+
function formatValue(value) {
|
|
296
|
+
if (value === null || value === void 0) {
|
|
297
|
+
return "";
|
|
298
|
+
}
|
|
299
|
+
if (typeof value === "object") {
|
|
300
|
+
return JSON.stringify(value);
|
|
301
|
+
}
|
|
302
|
+
return String(value);
|
|
303
|
+
}
|
|
304
|
+
function getConfigWithOrigin() {
|
|
305
|
+
const items = [];
|
|
306
|
+
const seen = /* @__PURE__ */ new Set();
|
|
307
|
+
const workspaceDir = getWorkspaceDir();
|
|
308
|
+
if (workspaceDir) {
|
|
309
|
+
const workspaceConfigPath = path2.join(workspaceDir, CONFIG_FILE);
|
|
310
|
+
const workspaceConfig = readConfigFile(workspaceConfigPath);
|
|
311
|
+
if (workspaceConfig) {
|
|
312
|
+
const workspaceItems = flattenConfig(workspaceConfig);
|
|
313
|
+
for (const item of workspaceItems) {
|
|
314
|
+
items.push({ ...item, origin: "workspace" });
|
|
315
|
+
seen.add(item.key);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const globalDir = getGlobalConfigDir();
|
|
320
|
+
const globalConfigPath = path2.join(globalDir, CONFIG_FILE);
|
|
321
|
+
const globalConfig = readConfigFile(globalConfigPath);
|
|
322
|
+
if (globalConfig) {
|
|
323
|
+
const globalItems = flattenConfig(globalConfig);
|
|
324
|
+
for (const item of globalItems) {
|
|
325
|
+
if (!seen.has(item.key)) {
|
|
326
|
+
items.push({ ...item, origin: "global" });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return items;
|
|
331
|
+
}
|
|
332
|
+
function getEditor() {
|
|
333
|
+
if (process.env.VISUAL) {
|
|
334
|
+
return process.env.VISUAL;
|
|
335
|
+
}
|
|
336
|
+
if (process.env.EDITOR) {
|
|
337
|
+
return process.env.EDITOR;
|
|
338
|
+
}
|
|
339
|
+
const workspaceDir = getWorkspaceDir();
|
|
340
|
+
if (workspaceDir) {
|
|
341
|
+
const workspaceConfig = readConfigFile(path2.join(workspaceDir, CONFIG_FILE));
|
|
342
|
+
if (workspaceConfig?.settings?.editor) {
|
|
343
|
+
return workspaceConfig.settings.editor;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const globalDir = getGlobalConfigDir();
|
|
347
|
+
const globalConfig = readConfigFile(path2.join(globalDir, CONFIG_FILE));
|
|
348
|
+
if (globalConfig?.settings?.editor) {
|
|
349
|
+
return globalConfig.settings.editor;
|
|
350
|
+
}
|
|
351
|
+
return DEFAULT_CONFIG.settings?.editor || "vi";
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/lib/output.ts
|
|
355
|
+
import chalk from "chalk";
|
|
356
|
+
function successResponse(data) {
|
|
357
|
+
return {
|
|
358
|
+
success: true,
|
|
359
|
+
data
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function output(ctx, response, humanFn) {
|
|
363
|
+
if (ctx.json) {
|
|
364
|
+
console.log(JSON.stringify(response, null, 2));
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (ctx.quiet && response.success) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
humanFn();
|
|
371
|
+
}
|
|
372
|
+
function outputKeyValue(ctx, items) {
|
|
373
|
+
if (ctx.json) {
|
|
374
|
+
console.log(JSON.stringify(items, null, 2));
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
for (const item of items) {
|
|
378
|
+
if (item.origin) {
|
|
379
|
+
console.log(`${chalk.gray(item.origin + ":")} ${chalk.cyan(item.key)}=${item.value}`);
|
|
380
|
+
} else {
|
|
381
|
+
console.log(`${chalk.cyan(item.key)}=${item.value}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function outputTable(ctx, headers, rows) {
|
|
386
|
+
if (ctx.json) {
|
|
387
|
+
const data = rows.map((row) => {
|
|
388
|
+
const obj = {};
|
|
389
|
+
headers.forEach((header, index) => {
|
|
390
|
+
obj[header.toLowerCase()] = row[index];
|
|
391
|
+
});
|
|
392
|
+
return obj;
|
|
393
|
+
});
|
|
394
|
+
console.log(JSON.stringify(data, null, 2));
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const widths = headers.map((h, i) => {
|
|
398
|
+
const columnValues = [h, ...rows.map((r) => stripAnsi(r[i] || ""))];
|
|
399
|
+
return Math.max(...columnValues.map((v) => v.length));
|
|
400
|
+
});
|
|
401
|
+
const headerLine = headers.map((h, i) => h.padEnd(widths[i])).join(" ");
|
|
402
|
+
console.log(chalk.bold(headerLine));
|
|
403
|
+
const separator = widths.map((w) => "-".repeat(w)).join(" ");
|
|
404
|
+
console.log(chalk.gray(separator));
|
|
405
|
+
for (const row of rows) {
|
|
406
|
+
const rowLine = row.map((cell, i) => {
|
|
407
|
+
const stripped = stripAnsi(cell || "");
|
|
408
|
+
const padding = widths[i] - stripped.length;
|
|
409
|
+
return (cell || "") + " ".repeat(Math.max(0, padding));
|
|
410
|
+
}).join(" ");
|
|
411
|
+
console.log(rowLine);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function stripAnsi(str) {
|
|
415
|
+
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
416
|
+
}
|
|
417
|
+
function formatDate(dateStr) {
|
|
418
|
+
if (!dateStr) {
|
|
419
|
+
return chalk.gray("(unknown)");
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
const date = new Date(dateStr);
|
|
423
|
+
const now = /* @__PURE__ */ new Date();
|
|
424
|
+
const diff = now.getTime() - date.getTime();
|
|
425
|
+
if (diff < 24 * 60 * 60 * 1e3) {
|
|
426
|
+
const hours = Math.floor(diff / (60 * 60 * 1e3));
|
|
427
|
+
if (hours === 0) {
|
|
428
|
+
const minutes = Math.floor(diff / (60 * 1e3));
|
|
429
|
+
if (minutes === 0) {
|
|
430
|
+
return "just now";
|
|
431
|
+
}
|
|
432
|
+
return `${minutes}m ago`;
|
|
433
|
+
}
|
|
434
|
+
return `${hours}h ago`;
|
|
435
|
+
}
|
|
436
|
+
if (diff < 7 * 24 * 60 * 60 * 1e3) {
|
|
437
|
+
const days = Math.floor(diff / (24 * 60 * 60 * 1e3));
|
|
438
|
+
return `${days}d ago`;
|
|
439
|
+
}
|
|
440
|
+
return date.toLocaleDateString();
|
|
441
|
+
} catch {
|
|
442
|
+
return dateStr;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// src/commands/init.ts
|
|
447
|
+
function initWorkspace(targetDir, template) {
|
|
448
|
+
const vibenDir = path3.join(targetDir, WORKSPACE_DIR);
|
|
449
|
+
const configPath = path3.join(vibenDir, CONFIG_FILE);
|
|
450
|
+
if (fs3.existsSync(configPath)) {
|
|
451
|
+
throw new CliError(
|
|
452
|
+
'Workspace already initialized. Use "viben config" to modify settings.',
|
|
453
|
+
"WORKSPACE_EXISTS"
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
if (!fs3.existsSync(vibenDir)) {
|
|
457
|
+
fs3.mkdirSync(vibenDir, { recursive: true });
|
|
458
|
+
}
|
|
459
|
+
let config;
|
|
460
|
+
if (template) {
|
|
461
|
+
config = {
|
|
462
|
+
...DEFAULT_CONFIG,
|
|
463
|
+
version: 1
|
|
464
|
+
};
|
|
465
|
+
} else {
|
|
466
|
+
config = {
|
|
467
|
+
...DEFAULT_CONFIG,
|
|
468
|
+
version: 1
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
writeConfigFile(configPath, config);
|
|
472
|
+
const agentsDir = path3.join(vibenDir, "agents");
|
|
473
|
+
if (!fs3.existsSync(agentsDir)) {
|
|
474
|
+
fs3.mkdirSync(agentsDir, { recursive: true });
|
|
475
|
+
}
|
|
476
|
+
const mainAgentPath = path3.join(agentsDir, "main.yaml");
|
|
477
|
+
if (!fs3.existsSync(mainAgentPath)) {
|
|
478
|
+
const mainAgentConfig = `# Main agent configuration
|
|
479
|
+
id: main
|
|
480
|
+
name: Main Agent
|
|
481
|
+
description: Default workspace agent
|
|
482
|
+
|
|
483
|
+
# Model configuration (optional, uses defaults)
|
|
484
|
+
# model: claude-sonnet-4-20250514
|
|
485
|
+
# provider: anthropic
|
|
486
|
+
`;
|
|
487
|
+
fs3.writeFileSync(mainAgentPath, mainAgentConfig, "utf-8");
|
|
488
|
+
}
|
|
489
|
+
return config;
|
|
490
|
+
}
|
|
491
|
+
function registerInitCommand(program) {
|
|
492
|
+
program.command("init").description("Initialize a Viben workspace in the current directory").option("--from <template>", "Initialize from a template").action(async (options) => {
|
|
493
|
+
const ctx = {
|
|
494
|
+
json: program.opts().json || false,
|
|
495
|
+
verbose: program.opts().verbose || false,
|
|
496
|
+
quiet: program.opts().quiet || false
|
|
497
|
+
};
|
|
498
|
+
try {
|
|
499
|
+
const targetDir = process.cwd();
|
|
500
|
+
const existingWorkspace = findWorkspaceRoot(targetDir);
|
|
501
|
+
if (existingWorkspace && existingWorkspace !== targetDir) {
|
|
502
|
+
throw new CliError(
|
|
503
|
+
`Already inside workspace at ${existingWorkspace}`,
|
|
504
|
+
"NESTED_WORKSPACE"
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
const config = initWorkspace(targetDir, options.from);
|
|
508
|
+
output(
|
|
509
|
+
ctx,
|
|
510
|
+
successResponse({
|
|
511
|
+
workspaceDir: path3.join(targetDir, WORKSPACE_DIR),
|
|
512
|
+
configPath: path3.join(targetDir, WORKSPACE_DIR, CONFIG_FILE),
|
|
513
|
+
config
|
|
514
|
+
}),
|
|
515
|
+
() => {
|
|
516
|
+
console.log(chalk2.green("Workspace initialized successfully!"));
|
|
517
|
+
console.log();
|
|
518
|
+
console.log("Created:");
|
|
519
|
+
console.log(chalk2.gray(" .viben/config.yaml") + " - Workspace configuration");
|
|
520
|
+
console.log(chalk2.gray(" .viben/agents/main.yaml") + " - Default agent");
|
|
521
|
+
console.log();
|
|
522
|
+
console.log("Next steps:");
|
|
523
|
+
console.log(chalk2.cyan(" viben config list") + " - View configuration");
|
|
524
|
+
console.log(chalk2.cyan(" viben agent list") + " - List agents");
|
|
525
|
+
console.log(chalk2.cyan(" viben config set <key> <value>") + " - Modify settings");
|
|
526
|
+
}
|
|
527
|
+
);
|
|
528
|
+
} catch (error) {
|
|
529
|
+
if (error instanceof CliError) {
|
|
530
|
+
output(ctx, error.toResponse(), () => {
|
|
531
|
+
console.error(chalk2.red("Error:"), error.message);
|
|
532
|
+
});
|
|
533
|
+
process.exit(1);
|
|
534
|
+
}
|
|
535
|
+
throw error;
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// src/commands/config.ts
|
|
541
|
+
import { spawn } from "child_process";
|
|
542
|
+
import chalk3 from "chalk";
|
|
543
|
+
function parseValue(value) {
|
|
544
|
+
try {
|
|
545
|
+
return JSON.parse(value);
|
|
546
|
+
} catch {
|
|
547
|
+
return value;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
function formatValue2(value) {
|
|
551
|
+
if (value === null || value === void 0) {
|
|
552
|
+
return "";
|
|
553
|
+
}
|
|
554
|
+
if (typeof value === "object") {
|
|
555
|
+
return JSON.stringify(value);
|
|
556
|
+
}
|
|
557
|
+
return String(value);
|
|
558
|
+
}
|
|
559
|
+
function handleGet(ctx, scope, key) {
|
|
560
|
+
const config = readScopedConfig(scope) || DEFAULT_CONFIG;
|
|
561
|
+
const value = getConfigValue(config, key);
|
|
562
|
+
if (value === void 0) {
|
|
563
|
+
output(ctx, successResponse({ key, value: null }), () => {
|
|
564
|
+
});
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
output(ctx, successResponse({ key, value }), () => {
|
|
568
|
+
console.log(formatValue2(value));
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
function handleSet(ctx, scope, key, value) {
|
|
572
|
+
const config = readScopedConfig(scope) || { version: 1 };
|
|
573
|
+
const parsedValue = parseValue(value);
|
|
574
|
+
const newConfig = setConfigValue(config, key, parsedValue);
|
|
575
|
+
writeScopedConfig(scope, newConfig);
|
|
576
|
+
output(ctx, successResponse({ key, value: parsedValue, scope }), () => {
|
|
577
|
+
console.log(chalk3.green("OK") + ` Set ${chalk3.cyan(key)} = ${formatValue2(parsedValue)}`);
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
function handleUnset(ctx, scope, key) {
|
|
581
|
+
const config = readScopedConfig(scope);
|
|
582
|
+
if (!config) {
|
|
583
|
+
throw new CliError(
|
|
584
|
+
`No config file found for scope: ${scope}`,
|
|
585
|
+
"CONFIG_NOT_FOUND"
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
const newConfig = deleteConfigValue(config, key);
|
|
589
|
+
writeScopedConfig(scope, newConfig);
|
|
590
|
+
output(ctx, successResponse({ key, scope }), () => {
|
|
591
|
+
console.log(chalk3.green("OK") + ` Unset ${chalk3.cyan(key)}`);
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
function handleList(ctx, scope, showOrigin) {
|
|
595
|
+
if (showOrigin) {
|
|
596
|
+
const items2 = getConfigWithOrigin();
|
|
597
|
+
output(ctx, successResponse({ items: items2 }), () => {
|
|
598
|
+
outputKeyValue(ctx, items2);
|
|
599
|
+
});
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const config = readScopedConfig(scope) || DEFAULT_CONFIG;
|
|
603
|
+
const items = flattenConfig(config);
|
|
604
|
+
output(ctx, successResponse({ items, scope }), () => {
|
|
605
|
+
for (const item of items) {
|
|
606
|
+
console.log(`${chalk3.cyan(item.key)}=${item.value}`);
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
async function handleEdit(ctx, scope) {
|
|
611
|
+
const configPath = getConfigPathForScope(scope);
|
|
612
|
+
const editor = getEditor();
|
|
613
|
+
output(ctx, successResponse({ configPath, editor }), () => {
|
|
614
|
+
console.log(`Opening ${chalk3.cyan(configPath)} in ${chalk3.yellow(editor)}...`);
|
|
615
|
+
});
|
|
616
|
+
const child = spawn(editor, [configPath], {
|
|
617
|
+
stdio: "inherit",
|
|
618
|
+
shell: true
|
|
619
|
+
});
|
|
620
|
+
return new Promise((resolve2, reject) => {
|
|
621
|
+
child.on("error", (error) => {
|
|
622
|
+
reject(new CliError(
|
|
623
|
+
`Failed to open editor: ${error.message}`,
|
|
624
|
+
"EDITOR_ERROR",
|
|
625
|
+
error
|
|
626
|
+
));
|
|
627
|
+
});
|
|
628
|
+
child.on("close", () => {
|
|
629
|
+
resolve2();
|
|
630
|
+
});
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
function registerConfigCommand(program) {
|
|
634
|
+
const configCmd = program.command("config").description("Manage Viben configuration (git-style)");
|
|
635
|
+
configCmd.command("get <key>").description("Get a config value").option("-g, --global", "Use global config").option("-w, --workspace", "Use workspace config").action((key, options) => {
|
|
636
|
+
const ctx = {
|
|
637
|
+
json: program.opts().json || false,
|
|
638
|
+
verbose: program.opts().verbose || false,
|
|
639
|
+
quiet: program.opts().quiet || false
|
|
640
|
+
};
|
|
641
|
+
try {
|
|
642
|
+
const scope = resolveScope({
|
|
643
|
+
global: options.global || program.opts().global,
|
|
644
|
+
workspace: options.workspace || program.opts().workspace
|
|
645
|
+
});
|
|
646
|
+
handleGet(ctx, scope, key);
|
|
647
|
+
} catch (error) {
|
|
648
|
+
if (error instanceof CliError) {
|
|
649
|
+
output(ctx, error.toResponse(), () => {
|
|
650
|
+
console.error(chalk3.red("Error:"), error.message);
|
|
651
|
+
});
|
|
652
|
+
process.exit(1);
|
|
653
|
+
}
|
|
654
|
+
throw error;
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
configCmd.command("set <key> <value>").description("Set a config value").option("-g, --global", "Use global config").option("-w, --workspace", "Use workspace config").action((key, value, options) => {
|
|
658
|
+
const ctx = {
|
|
659
|
+
json: program.opts().json || false,
|
|
660
|
+
verbose: program.opts().verbose || false,
|
|
661
|
+
quiet: program.opts().quiet || false
|
|
662
|
+
};
|
|
663
|
+
try {
|
|
664
|
+
const scope = resolveScope({
|
|
665
|
+
global: options.global || program.opts().global,
|
|
666
|
+
workspace: options.workspace || program.opts().workspace
|
|
667
|
+
});
|
|
668
|
+
handleSet(ctx, scope, key, value);
|
|
669
|
+
} catch (error) {
|
|
670
|
+
if (error instanceof CliError) {
|
|
671
|
+
output(ctx, error.toResponse(), () => {
|
|
672
|
+
console.error(chalk3.red("Error:"), error.message);
|
|
673
|
+
});
|
|
674
|
+
process.exit(1);
|
|
675
|
+
}
|
|
676
|
+
throw error;
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
configCmd.command("unset <key>").description("Remove a config value").option("-g, --global", "Use global config").option("-w, --workspace", "Use workspace config").action((key, options) => {
|
|
680
|
+
const ctx = {
|
|
681
|
+
json: program.opts().json || false,
|
|
682
|
+
verbose: program.opts().verbose || false,
|
|
683
|
+
quiet: program.opts().quiet || false
|
|
684
|
+
};
|
|
685
|
+
try {
|
|
686
|
+
const scope = resolveScope({
|
|
687
|
+
global: options.global || program.opts().global,
|
|
688
|
+
workspace: options.workspace || program.opts().workspace
|
|
689
|
+
});
|
|
690
|
+
handleUnset(ctx, scope, key);
|
|
691
|
+
} catch (error) {
|
|
692
|
+
if (error instanceof CliError) {
|
|
693
|
+
output(ctx, error.toResponse(), () => {
|
|
694
|
+
console.error(chalk3.red("Error:"), error.message);
|
|
695
|
+
});
|
|
696
|
+
process.exit(1);
|
|
697
|
+
}
|
|
698
|
+
throw error;
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
configCmd.command("list").description("List all config values").option("-g, --global", "Use global config").option("-w, --workspace", "Use workspace config").option("--show-origin", "Show the origin of each config value").action((options) => {
|
|
702
|
+
const ctx = {
|
|
703
|
+
json: program.opts().json || false,
|
|
704
|
+
verbose: program.opts().verbose || false,
|
|
705
|
+
quiet: program.opts().quiet || false
|
|
706
|
+
};
|
|
707
|
+
try {
|
|
708
|
+
const scope = resolveScope({
|
|
709
|
+
global: options.global || program.opts().global,
|
|
710
|
+
workspace: options.workspace || program.opts().workspace
|
|
711
|
+
});
|
|
712
|
+
handleList(ctx, scope, options.showOrigin || false);
|
|
713
|
+
} catch (error) {
|
|
714
|
+
if (error instanceof CliError) {
|
|
715
|
+
output(ctx, error.toResponse(), () => {
|
|
716
|
+
console.error(chalk3.red("Error:"), error.message);
|
|
717
|
+
});
|
|
718
|
+
process.exit(1);
|
|
719
|
+
}
|
|
720
|
+
throw error;
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
configCmd.command("edit").description("Open config in editor").option("-g, --global", "Edit global config").option("-w, --workspace", "Edit workspace config").action(async (options) => {
|
|
724
|
+
const ctx = {
|
|
725
|
+
json: program.opts().json || false,
|
|
726
|
+
verbose: program.opts().verbose || false,
|
|
727
|
+
quiet: program.opts().quiet || false
|
|
728
|
+
};
|
|
729
|
+
try {
|
|
730
|
+
const scope = resolveScope({
|
|
731
|
+
global: options.global || program.opts().global,
|
|
732
|
+
workspace: options.workspace || program.opts().workspace
|
|
733
|
+
});
|
|
734
|
+
await handleEdit(ctx, scope);
|
|
735
|
+
} catch (error) {
|
|
736
|
+
if (error instanceof CliError) {
|
|
737
|
+
output(ctx, error.toResponse(), () => {
|
|
738
|
+
console.error(chalk3.red("Error:"), error.message);
|
|
739
|
+
});
|
|
740
|
+
process.exit(1);
|
|
741
|
+
}
|
|
742
|
+
throw error;
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// src/commands/agent/index.ts
|
|
748
|
+
import chalk7 from "chalk";
|
|
749
|
+
|
|
750
|
+
// src/commands/agent/list.ts
|
|
751
|
+
import * as fs4 from "fs";
|
|
752
|
+
import * as path4 from "path";
|
|
753
|
+
import * as yaml2 from "yaml";
|
|
754
|
+
import chalk4 from "chalk";
|
|
755
|
+
function readAgentsFromDir(agentsDir, source) {
|
|
756
|
+
if (!fs4.existsSync(agentsDir)) {
|
|
757
|
+
return [];
|
|
758
|
+
}
|
|
759
|
+
const agents = [];
|
|
760
|
+
const files = fs4.readdirSync(agentsDir);
|
|
761
|
+
for (const file of files) {
|
|
762
|
+
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) {
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
const filePath = path4.join(agentsDir, file);
|
|
766
|
+
try {
|
|
767
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
768
|
+
const parsed = yaml2.parse(content);
|
|
769
|
+
const stat = fs4.statSync(filePath);
|
|
770
|
+
const agent = {
|
|
771
|
+
id: parsed.id || path4.basename(file, path4.extname(file)),
|
|
772
|
+
name: parsed.name || void 0,
|
|
773
|
+
description: parsed.description || void 0,
|
|
774
|
+
model: parsed.model || void 0,
|
|
775
|
+
provider: parsed.provider || void 0,
|
|
776
|
+
createdAt: stat.birthtime.toISOString(),
|
|
777
|
+
updatedAt: stat.mtime.toISOString()
|
|
778
|
+
};
|
|
779
|
+
agents.push(agent);
|
|
780
|
+
} catch (error) {
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return agents;
|
|
785
|
+
}
|
|
786
|
+
function listAgents(ctx) {
|
|
787
|
+
const agents = [];
|
|
788
|
+
const workspaceDir = getWorkspaceDir();
|
|
789
|
+
if (workspaceDir) {
|
|
790
|
+
const workspaceAgentsDir = path4.join(workspaceDir, "agents");
|
|
791
|
+
const workspaceAgents = readAgentsFromDir(workspaceAgentsDir, "workspace");
|
|
792
|
+
for (const agent of workspaceAgents) {
|
|
793
|
+
agents.push({ ...agent, source: "workspace" });
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
const globalAgentsDir = path4.join(getStateDir(), "agents");
|
|
797
|
+
const globalAgents = readAgentsFromDir(globalAgentsDir, "global");
|
|
798
|
+
for (const agent of globalAgents) {
|
|
799
|
+
if (!agents.find((a) => a.id === agent.id)) {
|
|
800
|
+
agents.push({ ...agent, source: "global" });
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
output(
|
|
804
|
+
ctx,
|
|
805
|
+
successResponse({ agents, count: agents.length }),
|
|
806
|
+
() => {
|
|
807
|
+
if (agents.length === 0) {
|
|
808
|
+
console.log(chalk4.gray("No agents found."));
|
|
809
|
+
console.log();
|
|
810
|
+
console.log("Create an agent with:");
|
|
811
|
+
console.log(chalk4.cyan(" viben agent create -n <agent-id>"));
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
outputTable(
|
|
815
|
+
ctx,
|
|
816
|
+
["ID", "Name", "Model", "Source"],
|
|
817
|
+
agents.map((a) => [
|
|
818
|
+
a.id,
|
|
819
|
+
a.name || chalk4.gray("(unnamed)"),
|
|
820
|
+
a.model || chalk4.gray("(default)"),
|
|
821
|
+
a.source
|
|
822
|
+
])
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// src/commands/agent/create.ts
|
|
829
|
+
import * as fs5 from "fs";
|
|
830
|
+
import * as path5 from "path";
|
|
831
|
+
import * as yaml3 from "yaml";
|
|
832
|
+
import chalk5 from "chalk";
|
|
833
|
+
function getAgentsDir(scope) {
|
|
834
|
+
if (scope === "workspace") {
|
|
835
|
+
const workspaceDir = getWorkspaceDir();
|
|
836
|
+
if (!workspaceDir) {
|
|
837
|
+
throw new CliError(
|
|
838
|
+
'Not in a workspace. Use --global or run "viben init" first.',
|
|
839
|
+
"NO_WORKSPACE"
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
return path5.join(workspaceDir, "agents");
|
|
843
|
+
}
|
|
844
|
+
return path5.join(getStateDir(), "agents");
|
|
845
|
+
}
|
|
846
|
+
function validateAgentId(id) {
|
|
847
|
+
if (!id || id.trim() === "") {
|
|
848
|
+
throw new CliError("Agent ID cannot be empty", "INVALID_ID");
|
|
849
|
+
}
|
|
850
|
+
if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(id)) {
|
|
851
|
+
throw new CliError(
|
|
852
|
+
"Agent ID must start with a letter and contain only letters, numbers, underscores, and hyphens",
|
|
853
|
+
"INVALID_ID"
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
if (id.length > 64) {
|
|
857
|
+
throw new CliError("Agent ID must be 64 characters or less", "INVALID_ID");
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
function createAgent(ctx, options) {
|
|
861
|
+
const id = options.name;
|
|
862
|
+
validateAgentId(id);
|
|
863
|
+
const scope = resolveScope({
|
|
864
|
+
global: options.global,
|
|
865
|
+
workspace: options.workspace
|
|
866
|
+
});
|
|
867
|
+
const agentsDir = getAgentsDir(scope);
|
|
868
|
+
const agentPath = path5.join(agentsDir, `${id}.yaml`);
|
|
869
|
+
if (fs5.existsSync(agentPath)) {
|
|
870
|
+
throw new CliError(
|
|
871
|
+
`Agent "${id}" already exists`,
|
|
872
|
+
"AGENT_EXISTS"
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
if (!fs5.existsSync(agentsDir)) {
|
|
876
|
+
fs5.mkdirSync(agentsDir, { recursive: true });
|
|
877
|
+
}
|
|
878
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
879
|
+
const agent = {
|
|
880
|
+
id,
|
|
881
|
+
name: options.description ? void 0 : id,
|
|
882
|
+
description: options.description,
|
|
883
|
+
model: options.model,
|
|
884
|
+
provider: options.provider,
|
|
885
|
+
createdAt: now,
|
|
886
|
+
updatedAt: now
|
|
887
|
+
};
|
|
888
|
+
const agentConfig = {
|
|
889
|
+
id: agent.id
|
|
890
|
+
};
|
|
891
|
+
if (agent.name) {
|
|
892
|
+
agentConfig.name = agent.name;
|
|
893
|
+
}
|
|
894
|
+
if (agent.description) {
|
|
895
|
+
agentConfig.description = agent.description;
|
|
896
|
+
}
|
|
897
|
+
if (agent.model) {
|
|
898
|
+
agentConfig.model = agent.model;
|
|
899
|
+
}
|
|
900
|
+
if (agent.provider) {
|
|
901
|
+
agentConfig.provider = agent.provider;
|
|
902
|
+
}
|
|
903
|
+
const content = yaml3.stringify(agentConfig, { indent: 2 });
|
|
904
|
+
fs5.writeFileSync(agentPath, content, "utf-8");
|
|
905
|
+
output(
|
|
906
|
+
ctx,
|
|
907
|
+
successResponse({
|
|
908
|
+
agent,
|
|
909
|
+
path: agentPath,
|
|
910
|
+
scope
|
|
911
|
+
}),
|
|
912
|
+
() => {
|
|
913
|
+
console.log(chalk5.green("OK") + ` Created agent "${chalk5.cyan(id)}"`);
|
|
914
|
+
console.log();
|
|
915
|
+
console.log("Agent file:", chalk5.gray(agentPath));
|
|
916
|
+
console.log();
|
|
917
|
+
console.log("Next steps:");
|
|
918
|
+
console.log(chalk5.cyan(` viben agent show -n ${id}`) + " - View agent details");
|
|
919
|
+
}
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// src/commands/agent/show.ts
|
|
924
|
+
import * as fs6 from "fs";
|
|
925
|
+
import * as path6 from "path";
|
|
926
|
+
import * as yaml4 from "yaml";
|
|
927
|
+
import chalk6 from "chalk";
|
|
928
|
+
function findAgent(id) {
|
|
929
|
+
const workspaceDir = getWorkspaceDir();
|
|
930
|
+
if (workspaceDir) {
|
|
931
|
+
const workspaceAgentPath = path6.join(workspaceDir, "agents", `${id}.yaml`);
|
|
932
|
+
if (fs6.existsSync(workspaceAgentPath)) {
|
|
933
|
+
const content = fs6.readFileSync(workspaceAgentPath, "utf-8");
|
|
934
|
+
const parsed = yaml4.parse(content);
|
|
935
|
+
const stat = fs6.statSync(workspaceAgentPath);
|
|
936
|
+
return {
|
|
937
|
+
agent: {
|
|
938
|
+
id: parsed.id || id,
|
|
939
|
+
name: parsed.name,
|
|
940
|
+
description: parsed.description,
|
|
941
|
+
model: parsed.model,
|
|
942
|
+
provider: parsed.provider,
|
|
943
|
+
createdAt: stat.birthtime.toISOString(),
|
|
944
|
+
updatedAt: stat.mtime.toISOString()
|
|
945
|
+
},
|
|
946
|
+
path: workspaceAgentPath,
|
|
947
|
+
source: "workspace"
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
const globalAgentPath = path6.join(getStateDir(), "agents", `${id}.yaml`);
|
|
952
|
+
if (fs6.existsSync(globalAgentPath)) {
|
|
953
|
+
const content = fs6.readFileSync(globalAgentPath, "utf-8");
|
|
954
|
+
const parsed = yaml4.parse(content);
|
|
955
|
+
const stat = fs6.statSync(globalAgentPath);
|
|
956
|
+
return {
|
|
957
|
+
agent: {
|
|
958
|
+
id: parsed.id || id,
|
|
959
|
+
name: parsed.name,
|
|
960
|
+
description: parsed.description,
|
|
961
|
+
model: parsed.model,
|
|
962
|
+
provider: parsed.provider,
|
|
963
|
+
createdAt: stat.birthtime.toISOString(),
|
|
964
|
+
updatedAt: stat.mtime.toISOString()
|
|
965
|
+
},
|
|
966
|
+
path: globalAgentPath,
|
|
967
|
+
source: "global"
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
return null;
|
|
971
|
+
}
|
|
972
|
+
function showAgent(ctx, id) {
|
|
973
|
+
const result = findAgent(id);
|
|
974
|
+
if (!result) {
|
|
975
|
+
throw new CliError(
|
|
976
|
+
`Agent "${id}" not found`,
|
|
977
|
+
"AGENT_NOT_FOUND"
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
const { agent, path: agentPath, source } = result;
|
|
981
|
+
output(
|
|
982
|
+
ctx,
|
|
983
|
+
successResponse({
|
|
984
|
+
agent,
|
|
985
|
+
path: agentPath,
|
|
986
|
+
source
|
|
987
|
+
}),
|
|
988
|
+
() => {
|
|
989
|
+
console.log(chalk6.bold.underline(`Agent: ${agent.id}`));
|
|
990
|
+
console.log();
|
|
991
|
+
const printField = (label, value, defaultVal) => {
|
|
992
|
+
const displayValue = value || chalk6.gray(defaultVal || "(not set)");
|
|
993
|
+
console.log(` ${chalk6.cyan(label.padEnd(12))} ${displayValue}`);
|
|
994
|
+
};
|
|
995
|
+
printField("Name", agent.name);
|
|
996
|
+
printField("Description", agent.description);
|
|
997
|
+
printField("Model", agent.model, "(default)");
|
|
998
|
+
printField("Provider", agent.provider, "(default)");
|
|
999
|
+
printField("Source", source);
|
|
1000
|
+
console.log();
|
|
1001
|
+
printField("Created", formatDate(agent.createdAt));
|
|
1002
|
+
printField("Updated", formatDate(agent.updatedAt));
|
|
1003
|
+
console.log();
|
|
1004
|
+
console.log(chalk6.gray("Config file:"), agentPath);
|
|
1005
|
+
}
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// src/commands/agent/index.ts
|
|
1010
|
+
function registerAgentCommand(program) {
|
|
1011
|
+
const agentCmd = program.command("agent").description("Manage agents");
|
|
1012
|
+
agentCmd.command("list").description("List all agents").action(() => {
|
|
1013
|
+
const ctx = {
|
|
1014
|
+
json: program.opts().json || false,
|
|
1015
|
+
verbose: program.opts().verbose || false,
|
|
1016
|
+
quiet: program.opts().quiet || false
|
|
1017
|
+
};
|
|
1018
|
+
try {
|
|
1019
|
+
listAgents(ctx);
|
|
1020
|
+
} catch (error) {
|
|
1021
|
+
if (error instanceof CliError) {
|
|
1022
|
+
output(ctx, error.toResponse(), () => {
|
|
1023
|
+
console.error(chalk7.red("Error:"), error.message);
|
|
1024
|
+
});
|
|
1025
|
+
process.exit(1);
|
|
1026
|
+
}
|
|
1027
|
+
throw error;
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1030
|
+
agentCmd.command("create").description("Create a new agent").requiredOption("-n, --name <id>", "Agent ID (required)").option("-d, --description <text>", "Agent description").option("-m, --model <model>", "Model to use").option("-p, --provider <provider>", "Provider name").option("-g, --global", "Create in global scope").option("-w, --workspace", "Create in workspace scope").action((options) => {
|
|
1031
|
+
const ctx = {
|
|
1032
|
+
json: program.opts().json || false,
|
|
1033
|
+
verbose: program.opts().verbose || false,
|
|
1034
|
+
quiet: program.opts().quiet || false
|
|
1035
|
+
};
|
|
1036
|
+
try {
|
|
1037
|
+
if (!options.name) {
|
|
1038
|
+
throw new CliError("Agent ID is required (-n, --name)", "MISSING_ID");
|
|
1039
|
+
}
|
|
1040
|
+
createAgent(ctx, {
|
|
1041
|
+
name: options.name,
|
|
1042
|
+
description: options.description,
|
|
1043
|
+
model: options.model,
|
|
1044
|
+
provider: options.provider,
|
|
1045
|
+
global: options.global || program.opts().global,
|
|
1046
|
+
workspace: options.workspace || program.opts().workspace
|
|
1047
|
+
});
|
|
1048
|
+
} catch (error) {
|
|
1049
|
+
if (error instanceof CliError) {
|
|
1050
|
+
output(ctx, error.toResponse(), () => {
|
|
1051
|
+
console.error(chalk7.red("Error:"), error.message);
|
|
1052
|
+
});
|
|
1053
|
+
process.exit(1);
|
|
1054
|
+
}
|
|
1055
|
+
throw error;
|
|
1056
|
+
}
|
|
1057
|
+
});
|
|
1058
|
+
agentCmd.command("show").description("Show agent details").requiredOption("-n, --name <id>", "Agent ID (required)").action((options) => {
|
|
1059
|
+
const ctx = {
|
|
1060
|
+
json: program.opts().json || false,
|
|
1061
|
+
verbose: program.opts().verbose || false,
|
|
1062
|
+
quiet: program.opts().quiet || false
|
|
1063
|
+
};
|
|
1064
|
+
try {
|
|
1065
|
+
if (!options.name) {
|
|
1066
|
+
throw new CliError("Agent ID is required (-n, --name)", "MISSING_ID");
|
|
1067
|
+
}
|
|
1068
|
+
showAgent(ctx, options.name);
|
|
1069
|
+
} catch (error) {
|
|
1070
|
+
if (error instanceof CliError) {
|
|
1071
|
+
output(ctx, error.toResponse(), () => {
|
|
1072
|
+
console.error(chalk7.red("Error:"), error.message);
|
|
1073
|
+
});
|
|
1074
|
+
process.exit(1);
|
|
1075
|
+
}
|
|
1076
|
+
throw error;
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// src/cli.ts
|
|
1082
|
+
var require2 = createRequire(import.meta.url);
|
|
1083
|
+
var packageJson = require2("../package.json");
|
|
1084
|
+
function createProgram() {
|
|
1085
|
+
const program = new Command();
|
|
1086
|
+
program.name("viben").description("Viben CLI - Orchestrate AI agent clusters in your local workspace").version(packageJson.version, "-v, --version", "Display version number");
|
|
1087
|
+
program.option("--json", "Output in JSON format for machine consumption").option("-g, --global", "Use global scope").option("-w, --workspace", "Use workspace scope").option("--verbose", "Enable verbose output").option("-q, --quiet", "Suppress non-essential output");
|
|
1088
|
+
registerInitCommand(program);
|
|
1089
|
+
registerConfigCommand(program);
|
|
1090
|
+
registerAgentCommand(program);
|
|
1091
|
+
return program;
|
|
1092
|
+
}
|
|
1093
|
+
async function run(args) {
|
|
1094
|
+
const program = createProgram();
|
|
1095
|
+
await program.parseAsync(args || process.argv);
|
|
1096
|
+
}
|
|
1097
|
+
export {
|
|
1098
|
+
CliError,
|
|
1099
|
+
createProgram,
|
|
1100
|
+
run
|
|
1101
|
+
};
|
|
1102
|
+
//# sourceMappingURL=index.js.map
|