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