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/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