- 1
use std::path::PathBuf; - 2
- 3
use clap::{Parser, Subcommand, ValueEnum}; - 4
- 5
#[derive(Parser, Debug)] - 6
// `VAK_VERSION` is composed by build.rs: the release, plus the commit it was - 7
// built from when one was supplied. The commit used to be recorded ONLY in - 8
// install.json — the right place for it, and not the place anyone looks. - 9
// "Which build is this?" is answered by `--version`, and a bare `2.0.1` - 10
// cannot tell two binaries on the same release line apart, which is exactly - 11
// the question that matters when asking whether a fix is in the thing you - 12
// are running. - 13
#[command(name = "vak", version = env!("VAK_VERSION"), about = "Vakyartha — an agent harness")] - 14
pub(crate) struct Cli { - 15
/// Target a specific workspace directory instead of the current working directory. - 16
#[arg(long, short = 'C', global = true)] - 17
pub(crate) workspace: Option<PathBuf>, - 18
- 19
#[command(subcommand)] - 20
pub(crate) command: Option<Command>, - 21
} - 22
- 23
/// Which web surface `vak open` targets. - 24
#[derive(ValueEnum, Clone, Copy, Debug, PartialEq, Eq)] - 25
pub(crate) enum OpenSurface { - 26
/// The workspace client: run tasks, review diffs, answer approvals. - 27
App, - 28
/// The operations console: sessions, receipts, services, incidents. - 29
Admin, - 30
} - 31
- 32
#[derive(Subcommand, Debug)] - 33
pub(crate) enum Command { - 34
/// Managed release lifecycle (docs/design/32): install/status/sync - 35
Self_ { - 36
#[command(subcommand)] - 37
action: SelfAction, - 38
}, - 39
/// Run one prompt headless and print the result - 40
Exec { - 41
prompt: String, - 42
/// Target a specific specialist agent (e.g. 'researcher', 'writer', or custom ID; defaults to built-in 'vak') - 43
#[arg(long)] - 44
agent: Option<String>, - 45
#[arg(long)] - 46
model: Option<String>, - 47
#[arg(long)] - 48
provider: Option<String>, - 49
#[arg(long, default_value_t = 40)] - 50
max_turns: usize, - 51
#[arg(long)] - 52
json: bool, - 53
#[arg(long)] - 54
yes: bool, - 55
#[arg(long)] - 56
permission_mode: Option<String>, - 57
/// Permit direct `write` and `edit` calls only for these workspace - 58
/// paths during this run. Repeat the flag for more than one path. - 59
#[arg(long = "write-path")] - 60
write_paths: Vec<PathBuf>, - 61
/// Run in an isolated git worktree off HEAD - 62
#[arg(long)] - 63
worktree: bool, - 64
/// Resume an existing session instead of starting a new one - 65
#[arg(long)] - 66
session: Option<String>, - 67
/// Acknowledge that prompt layers changed since the resumed session - 68
/// was created, and run its frozen prompt anyway - 69
#[arg(long)] - 70
accept_drift: bool, - 71
/// Track this run as a durable managed work contract - 72
#[arg(long)] - 73
managed: bool, - 74
/// Durable objective for goal mode (docs/design/42-managed-work-contracts.md): - 75
/// completion is audited against --criteria, never self-reported. - 76
#[arg(long)] - 77
goal: Option<String>, - 78
/// Acceptance criteria, comma-separated. Prefix `verify:` to run a - 79
/// criterion as a shell command; others are judged from evidence. - 80
#[arg(long, value_delimiter = ',')] - 81
criteria: Vec<String>, - 82
/// Trust this workspace's project config and secret scope - 83
#[arg(long)] - 84
trust: bool, - 85
}, - 86
/// Launch the rich modern terminal console (docs/design/55-rich-terminal-surface.md) - 87
Term { - 88
/// Target a running vak server (e.g. http://127.0.0.1:8901 or remote) - 89
#[arg(long)] - 90
server: Option<String>, - 91
/// Server bearer authentication token - 92
#[arg(long)] - 93
token: Option<String>, - 94
/// Connect to or resume an existing session id - 95
#[arg(long)] - 96
session: Option<String>, - 97
}, - 98
/// Show the effective composed configuration - 99
Config { - 100
#[command(subcommand)] - 101
action: Option<ConfigAction>, - 102
}, - 103
/// Inspect and edit the layered system prompt - 104
Prompts { - 105
#[command(subcommand)] - 106
action: PromptsAction, - 107
}, - 108
/// List recorded sessions for this project - 109
Sessions, - 110
/// Static flow DAGs: list, check, run - 111
Flow { - 112
#[command(subcommand)] - 113
action: FlowAction, - 114
}, - 115
/// Plan and execute an open-ended task with a dynamic planner - 116
Plan { - 117
task: String, - 118
#[arg(long)] - 119
yes: bool, - 120
/// Permission mode for this run only, same as `vak exec`. A planner - 121
/// dispatches model-generated shell through the same engine as any - 122
/// other turn, so it needs the same boundary control. - 123
#[arg(long)] - 124
permission_mode: Option<String>, - 125
/// Permit direct `write` and `edit` calls only for these workspace - 126
/// paths during this run. Repeat the flag for more than one path. - 127
#[arg(long = "write-path")] - 128
write_paths: Vec<PathBuf>, - 129
/// Run in an isolated git worktree off HEAD - 130
#[arg(long)] - 131
worktree: bool, - 132
/// Trust this workspace's project config and secret scope - 133
#[arg(long)] - 134
trust: bool, - 135
}, - 136
/// Run the built-in eval suite - 137
Eval { - 138
/// Write JSON report to this path - 139
#[arg(long)] - 140
report: Option<PathBuf>, - 141
/// Run the live suite against the configured provider (needs API key) - 142
#[arg(long)] - 143
live: bool, - 144
#[arg(long)] - 145
provider: Option<String>, - 146
#[arg(long)] - 147
model: Option<String>, - 148
}, - 149
/// Serve the agent over HTTP+SSE, including the web client at /app - 150
Serve { - 151
#[arg(long, default_value_t = 8901)] - 152
port: u16, - 153
/// Interface to bind. Defaults to loopback, and anything else - 154
/// requires `[server] trusted_hosts` to be set — binding a - 155
/// shell-capable agent to the network is never implicit - 156
/// (docs/design/48-web-client.md §4.2). - 157
#[arg(long)] - 158
host: Option<String>, - 159
/// Enable gateway surface routing regardless of config - 160
/// (docs/design/22-gateway.md) - 161
#[arg(long)] - 162
gateway: bool, - 163
/// Trust this workspace's project config and secret scope - 164
#[arg(long)] - 165
trust: bool, - 166
}, - 167
/// Open a running server's web surface in a browser, already signed in - 168
Open { - 169
/// Which surface: `app` (the workspace) or `admin` (operations). - 170
#[arg(value_enum, default_value_t = OpenSurface::App)] - 171
surface: OpenSurface, - 172
/// Port the server is listening on. Defaults to the configured one. - 173
#[arg(long)] - 174
port: Option<u16>, - 175
/// Print the URL instead of opening a browser. - 176
#[arg(long)] - 177
print: bool, - 178
}, - 179
/// Explain how a request would be read, and what the runtime would do - 180
/// about it (docs/design/47-commitment-kernel.md) - 181
Intent { - 182
#[command(subcommand)] - 183
action: IntentAction, - 184
}, - 185
/// Word, Excel and PowerPoint files from a script: read, apply ops, - 186
/// compare and verify, as JSON (docs/design/72-openxml-documents.md) - 187
Office { - 188
#[command(subcommand)] - 189
action: OfficeAction, - 190
}, - 191
/// Durable commitments: what this agent owes, and what closed it - 192
Commit { - 193
#[command(subcommand)] - 194
action: CommitAction, - 195
}, - 196
/// Delegate authority to a commitment for a bounded time and scope - 197
Grant { - 198
/// Commitment id or unique prefix. - 199
id: String, - 200
/// Workspace-relative path globs the grant covers (repeatable). - 201
#[arg(long = "path")] - 202
paths: Vec<String>, - 203
/// Tool names the grant covers (repeatable). - 204
#[arg(long = "tool")] - 205
tools: Vec<String>, - 206
/// Lifetime spend the grant permits without asking again. - 207
#[arg(long)] - 208
spend_usd: Option<f64>, - 209
/// Hours until the grant lapses. Omit for no expiry. - 210
#[arg(long)] - 211
hours: Option<i64>, - 212
/// Cap the permission mode while the grant is live. Never raises it. - 213
#[arg(long, default_value = "workspace-write")] - 214
permission: String, - 215
/// What happens to a deferred question nobody answers: - 216
/// wait | assume | abandon. - 217
#[arg(long, default_value = "wait")] - 218
on_silence: String, - 219
/// Hours before `on_silence` applies. - 220
#[arg(long, default_value_t = 24)] - 221
after_hours: u32, - 222
}, - 223
/// Withdraw a grant. Takes effect immediately. - 224
Revoke { - 225
/// Commitment id or unique prefix. - 226
id: String, - 227
}, - 228
/// Workspace checkpoints: list or restore - 229
Checkpoints { - 230
#[command(subcommand)] - 231
action: CheckpointAction, - 232
}, - 233
/// Durable memory notes for this workspace: list / add / forget / amend / consolidate - 234
Memory { - 235
#[command(subcommand)] - 236
action: Option<MemoryAction>, - 237
}, - 238
/// Manage the semantic entity knowledge graph: list / search / get / delete - 239
Entities { - 240
#[command(subcommand)] - 241
action: Option<EntitiesAction>, - 242
}, - 243
/// Manage persistent agent specialists and domain templates: list / templates / init - 244
Agents { - 245
#[command(subcommand)] - 246
action: Option<AgentsAction>, - 247
}, - 248
/// Export session transcript or living outcome canvas to standalone HTML or markdown - 249
Export { - 250
/// Session ID to export - 251
session_id: String, - 252
/// Export as an interactive self-contained HTML document - 253
#[arg(long)] - 254
html: bool, - 255
/// Optional destination path to write output (defaults to stdout) - 256
#[arg(long, short)] - 257
out: Option<PathBuf>, - 258
}, - 259
/// Review proposed skills: list / promote / reject - 260
SkillsReview { - 261
#[command(subcommand)] - 262
action: SkillsReviewAction, - 263
}, - 264
/// Validate Agent Skills files in a project or explicit path - 265
Skills { - 266
#[command(subcommand)] - 267
action: SkillsAction, - 268
}, - 269
/// Inspect and manage immutable, disabled-by-default plugin packages - 270
Plugins { - 271
#[command(subcommand)] - 272
action: PluginAction, - 273
}, - 274
/// Bridge a Telegram bot to a running gateway (docs/design/22-gateway.md) - 275
Telegram { - 276
/// Gateway base URL, e.g. http://127.0.0.1:8901 - 277
#[arg(long)] - 278
server: String, - 279
/// Gateway bearer token (overrides VAK_GATEWAY_TOKEN; the - 280
/// env var is the normal path so secrets never appear in `ps`) - 281
#[arg(long)] - 282
token: Option<String>, - 283
/// Run as this bot identity instead of the legacy single - 284
/// `TELEGRAM_BOT_TOKEN` slot (multi-bot-per-channel, docs/design/34). - 285
/// Its token is read from the env var recorded for this id in the - 286
/// admin console's Bots list. Run one `vak telegram --bot-id ...` - 287
/// process per bot to have more than one Telegram bot live at once. - 288
#[arg(long)] - 289
bot_id: Option<String>, - 290
}, - 291
/// Bridge a Discord bot to a running gateway (docs/design/34 Phase 3) - 292
Discord { - 293
/// Gateway base URL, e.g. http://127.0.0.1:8901 - 294
#[arg(long)] - 295
server: String, - 296
/// Gateway bearer token (overrides VAK_GATEWAY_TOKEN; the - 297
/// env var is the normal path so secrets never appear in `ps`) - 298
#[arg(long)] - 299
token: Option<String>, - 300
/// See `telegram --bot-id`. - 301
#[arg(long)] - 302
bot_id: Option<String>, - 303
}, - 304
/// Bridge a Slack bot to a running gateway (docs/design/34 Phase 3) - 305
Slack { - 306
/// Gateway base URL, e.g. http://127.0.0.1:8901 - 307
#[arg(long)] - 308
server: String, - 309
/// Gateway bearer token (overrides VAK_GATEWAY_TOKEN; the - 310
/// env var is the normal path so secrets never appear in `ps`) - 311
#[arg(long)] - 312
token: Option<String>, - 313
/// See `telegram --bot-id`. - 314
#[arg(long)] - 315
bot_id: Option<String>, - 316
}, - 317
/// Guided first run: choose a workspace, connect a model, activate services - 318
Setup { - 319
#[command(subcommand)] - 320
action: Option<SetupAction>, - 321
/// Print the URL and wait instead of opening a browser - 322
#[arg(long)] - 323
no_browser: bool, - 324
/// Print the URL for tunnelling to a headless host, then wait - 325
#[arg(long)] - 326
print_url: bool, - 327
/// Run the whole flow as terminal prompts, with no browser - 328
#[arg(long)] - 329
terminal: bool, - 330
/// Take every choice from the environment and fail on any missing - 331
/// one, instead of prompting - 332
#[arg(long)] - 333
non_interactive: bool, - 334
}, - 335
/// Diagnose provider auth, config warnings, and extensions - 336
Doctor { - 337
/// Trust this workspace's project config and secret scope - 338
#[arg(long)] - 339
trust: bool, - 340
/// Act on failing checks that have a known fix, then re-check - 341
#[arg(long)] - 342
repair: bool, - 343
}, - 344
/// Backup your vak home: export or import a directory copy - 345
Backup { - 346
#[command(subcommand)] - 347
action: BackupAction, - 348
}, - 349
/// Usage digest over cost ledger, memory notes, and skill proposals - 350
Digest { - 351
/// Days of history to fold in (clamped to 1..=90) - 352
#[arg(long, default_value_t = 7)] - 353
days: u32, - 354
}, - 355
/// Scheduled tasks stored as tasks.json in the sessions home: CRUD without the server - 356
Tasks { - 357
#[command(subcommand)] - 358
action: TasksAction, - 359
}, - 360
/// Durable attention inbox (gateway pushes): list / show / ack / count - 361
Inbox { - 362
#[command(subcommand)] - 363
action: Option<InboxAction>, - 364
}, - 365
} - 366
- 367
#[derive(Clone, Copy, Debug, ValueEnum)] - 368
pub(crate) enum PluginScopeArg { - 369
User, - 370
Workspace, - 371
} - 372
- 373
#[derive(Subcommand, Debug)] - 374
pub(crate) enum SkillsAction { - 375
/// Validate one SKILL.md or every SKILL.md below a directory - 376
Validate { - 377
path: Option<PathBuf>, - 378
#[arg(long)] - 379
json: bool, - 380
}, - 381
} - 382
- 383
#[derive(Clone, Copy, Debug, ValueEnum)] - 384
pub(crate) enum MarketplaceTrustArg { - 385
ManualReview, - 386
PinnedCommit, - 387
LocalOnly, - 388
} - 389
- 390
#[derive(Subcommand, Debug)] - 391
pub(crate) enum PluginAction { - 392
/// Inspect and hash a Codex, Claude, Copilot, or Cursor catalog snapshot - 393
CatalogInspect { - 394
path: PathBuf, - 395
#[arg(long)] - 396
json: bool, - 397
}, - 398
/// Register a catalog snapshot for explicit review; it starts disabled - 399
CatalogRegister { - 400
path: PathBuf, - 401
#[arg(long, default_value = "local catalog")] - 402
label: String, - 403
#[arg(long, value_enum, default_value_t = MarketplaceTrustArg::ManualReview)] - 404
trust: MarketplaceTrustArg, - 405
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 406
scope: PluginScopeArg, - 407
#[arg(long)] - 408
json: bool, - 409
}, - 410
/// List registered catalog snapshots and their trust state - 411
CatalogSources { - 412
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 413
scope: PluginScopeArg, - 414
#[arg(long)] - 415
json: bool, - 416
}, - 417
/// Search entries in registered marketplace catalog snapshots - 418
CatalogSearch { - 419
query: String, - 420
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 421
scope: PluginScopeArg, - 422
#[arg(long)] - 423
json: bool, - 424
}, - 425
/// Materialize and install one explicitly selected catalog entry. - 426
CatalogInstall { - 427
catalog: PathBuf, - 428
name: String, - 429
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 430
scope: PluginScopeArg, - 431
#[arg(long)] - 432
allow_unlicensed: bool, - 433
#[arg(long)] - 434
json: bool, - 435
}, - 436
/// Inspect a local package without installing or executing it - 437
Inspect { - 438
path: PathBuf, - 439
#[arg(long)] - 440
json: bool, - 441
}, - 442
/// Copy a reviewed local package into immutable storage, disabled - 443
Install { - 444
path: PathBuf, - 445
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 446
scope: PluginScopeArg, - 447
/// Proceed when the package declares no license - 448
#[arg(long)] - 449
allow_unlicensed: bool, - 450
#[arg(long)] - 451
json: bool, - 452
}, - 453
/// Inspect and stage a new immutable generation without activating it - 454
Update { - 455
path: PathBuf, - 456
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 457
scope: PluginScopeArg, - 458
#[arg(long)] - 459
allow_unlicensed: bool, - 460
#[arg(long)] - 461
json: bool, - 462
}, - 463
/// Enable the currently selected generation - 464
Enable { - 465
name: String, - 466
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 467
scope: PluginScopeArg, - 468
#[arg(long)] - 469
json: bool, - 470
}, - 471
/// Disable a plugin without uninstalling it - 472
Disable { - 473
name: String, - 474
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 475
scope: PluginScopeArg, - 476
#[arg(long)] - 477
json: bool, - 478
}, - 479
/// Select the previous immutable generation and leave it disabled - 480
Rollback { - 481
name: String, - 482
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 483
scope: PluginScopeArg, - 484
#[arg(long)] - 485
json: bool, - 486
}, - 487
/// List every immutable generation retained for a plugin - 488
Versions { - 489
name: String, - 490
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 491
scope: PluginScopeArg, - 492
#[arg(long)] - 493
json: bool, - 494
}, - 495
/// List installed packages for one scope - 496
List { - 497
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 498
scope: PluginScopeArg, - 499
#[arg(long)] - 500
json: bool, - 501
}, - 502
/// Show append-only install and removal provenance - 503
Audit { - 504
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 505
scope: PluginScopeArg, - 506
#[arg(long)] - 507
json: bool, - 508
}, - 509
/// Unregister and remove one immutable package generation - 510
Remove { - 511
name: String, - 512
#[arg(long, value_enum, default_value_t = PluginScopeArg::User)] - 513
scope: PluginScopeArg, - 514
#[arg(long)] - 515
json: bool, - 516
}, - 517
} - 518
- 519
#[derive(Subcommand, Debug)] - 520
pub(crate) enum SelfAction { - 521
/// Digest every durable file the state registry declares, or check a - 522
/// snapshot taken before an update against what is on disk now - 523
State { - 524
/// Compare the current state against this snapshot and report any - 525
/// entry the update contract forbids changing - 526
#[arg(long)] - 527
verify: Option<std::path::PathBuf>, - 528
}, - 529
/// Place this build into the managed prefix, with a manifest - 530
Install { - 531
/// Managed prefix (default: platform application location) - 532
#[arg(long)] - 533
prefix: Option<PathBuf>, - 534
/// Reinstall even when the prefix already holds this exact build - 535
#[arg(long)] - 536
force: bool, - 537
}, - 538
/// Clear the prefix and place this build fresh - 539
Reinstall { - 540
#[arg(long)] - 541
prefix: Option<PathBuf>, - 542
#[arg(long)] - 543
yes: bool, - 544
}, - 545
/// Check every installed component against the manifest digests - 546
Verify { - 547
#[arg(long)] - 548
prefix: Option<PathBuf>, - 549
}, - 550
/// Regenerate + reload service units onto the installed binary - 551
ServicesSync { - 552
#[arg(long)] - 553
prefix: Option<PathBuf>, - 554
/// Service names (default: all); unknown names are reported - 555
names: Vec<String>, - 556
}, - 557
/// Drift matrix: build vs manifest vs per-service units - 558
Status { - 559
#[arg(long)] - 560
prefix: Option<PathBuf>, - 561
}, - 562
/// Reverse of install; --purge also deletes the data home (confirmed) - 563
Uninstall { - 564
#[arg(long)] - 565
prefix: Option<PathBuf>, - 566
#[arg(long)] - 567
yes: bool, - 568
#[arg(long)] - 569
purge: bool, - 570
}, - 571
/// Opt-in pull-and-replace from a release feed URL - 572
Update { - 573
#[arg(long)] - 574
prefix: Option<PathBuf>, - 575
/// Release feed URL (default: the [update] url in config) - 576
#[arg(long)] - 577
url: Option<String>, - 578
#[arg(long)] - 579
yes: bool, - 580
/// Report what would change without installing anything - 581
#[arg(long)] - 582
dry_run: bool, - 583
}, - 584
} - 585
- 586
#[derive(Subcommand, Debug)] - 587
pub(crate) enum InboxAction { - 588
/// List entries: unread by default, acked included with --all - 589
List { - 590
/// Include already-acked entries - 591
#[arg(long)] - 592
all: bool, - 593
/// Maximum rows to print - 594
#[arg(long, default_value_t = 50)] - 595
limit: usize, - 596
}, - 597
/// Print one entry's body and session/task refs by unique id prefix - 598
Show { id_prefix: String }, - 599
/// Mark an entry read (idempotent tombstone append) - 600
Ack { id_prefix: String }, - 601
/// One-line unread total - 602
Count, - 603
} - 604
- 605
/// `vak setup` actions. Bare `vak setup` runs the guided flow; `status` - 606
/// is the read-only projection every surface shares - 607
/// (docs/design/46-stabilization-install-and-onboarding.md). - 608
#[derive(Subcommand, Debug)] - 609
pub(crate) enum SetupAction { - 610
/// Install the Shared starter skills and plugins into ~/vak-home - 611
Seed, - 612
/// What is configured, what is not, and the one repair for each gap - 613
Status { - 614
/// Emit the readiness projection as JSON instead of a report - 615
#[arg(long)] - 616
json: bool, - 617
/// Inspect a managed install at this prefix - 618
#[arg(long)] - 619
prefix: Option<std::path::PathBuf>, - 620
}, - 621
} - 622
- 623
#[derive(Subcommand, Debug)] - 624
pub(crate) enum BackupAction { - 625
/// Copy sessions/memory/checkpoints/ledgers into a directory - 626
Export { - 627
/// Destination directory (must not be the vak home itself) - 628
dir: PathBuf, - 629
/// Include the encrypted credential store, when this host uses one - 630
/// (a WARNING.txt travels beside it; a host using the OS keychain - 631
/// has nothing here to include) - 632
#[arg(long)] - 633
include_secrets: bool, - 634
}, - 635
/// Restore a backup directory into the vak home - 636
Import { - 637
/// Source directory containing manifest.json - 638
dir: PathBuf, - 639
/// What to do when a file already exists: skip or rename - 640
#[arg(long, default_value = "skip")] - 641
conflict: String, - 642
}, - 643
} - 644
- 645
#[derive(Subcommand, Debug)] - 646
pub(crate) enum TasksAction { - 647
/// List tasks with schedule and next-fire preview - 648
List, - 649
/// Add a task, or expand a built-in preset (prompt XOR script; - 650
/// interval XOR cron) - 651
Add { - 652
/// Task name (with --preset: overrides the preset's default name) - 653
#[arg(long, required_unless_present = "preset")] - 654
name: Option<String>, - 655
/// Prompt dispatched to the model on each run - 656
#[arg(long, conflicts_with = "preset")] - 657
prompt: Option<String>, - 658
/// Watchdog shell one-liner (zero tokens while stdout stays empty) - 659
#[arg(long, conflicts_with = "preset")] - 660
script: Option<String>, - 661
/// Seconds between runs (default 3600 when no cron given) - 662
#[arg(long, conflicts_with = "preset")] - 663
every: Option<u64>, - 664
/// 5-field cron expression (`m h dom mon dow`, local time) - 665
#[arg(long, conflicts_with = "preset")] - 666
cron: Option<String>, - 667
/// Working directory for runs (default: this directory) - 668
#[arg(long)] - 669
cwd: Option<PathBuf>, - 670
/// Delivery surface for run summaries, e.g. telegram:12345 - 671
#[arg(long)] - 672
deliver: Option<String>, - 673
/// Pin this task to a model id (never escalates) - 674
#[arg(long)] - 675
model: Option<String>, - 676
/// Expand a built-in preset: weekly-digest (Mondays 09:00, - 677
/// runs `digest --days 7` via this binary) - 678
#[arg(long)] - 679
preset: Option<String>, - 680
}, - 681
/// Remove a task by id - 682
Remove { id: String }, - 683
/// Enable a task - 684
Enable { id: String }, - 685
/// Disable a task without deleting it - 686
Disable { id: String }, - 687
} - 688
- 689
#[derive(Subcommand, Debug)] - 690
pub(crate) enum MemoryAction { - 691
/// Remove abandoned lock/temp artifacts without deleting notes - 692
Clean { - 693
/// Only remove artifacts older than this many seconds - 694
#[arg(long, default_value_t = 86_400)] - 695
older_than_secs: u64, - 696
}, - 697
/// List memory notes (ids are usable with forget/amend) - 698
List { - 699
/// Read the global USER.md profile tier instead of this workspace - 700
#[arg(long)] - 701
profile: bool, - 702
}, - 703
/// Remove exactly one note by id - 704
Forget { - 705
id: String, - 706
/// Target the global USER.md profile tier instead of this workspace - 707
#[arg(long)] - 708
profile: bool, - 709
}, - 710
/// Replace one note's body, keeping its provenance header - 711
Amend { - 712
id: String, - 713
text: String, - 714
/// Target the global USER.md profile tier instead of this workspace - 715
#[arg(long)] - 716
profile: bool, - 717
}, - 718
/// Append a note - 719
Add { - 720
text: String, - 721
/// Note kind, e.g. decision / preference / fact - 722
#[arg(long, default_value = "note")] - 723
kind: String, - 724
#[arg(long, default_value = "")] - 725
tag: String, - 726
/// Append to the global USER.md profile tier instead of this workspace - 727
#[arg(long)] - 728
profile: bool, - 729
}, - 730
/// Run autonomous memory consolidation: promotes recurring procedures to invariants and detects conflicts - 731
Consolidate, - 732
} - 733
- 734
#[derive(Subcommand, Debug)] - 735
pub(crate) enum EntitiesAction { - 736
/// List entities in this workspace (or global) - 737
List { - 738
/// Read the global entities tier instead of this workspace - 739
#[arg(long)] - 740
global: bool, - 741
}, - 742
/// Search entities across names, summaries, attributes, and relations - 743
Search { - 744
query: String, - 745
/// Filter by entity category (e.g. 'service', 'database', 'person') - 746
#[arg(long)] - 747
entity_type: Option<String>, - 748
/// Search the global entities tier instead of this workspace - 749
#[arg(long)] - 750
global: bool, - 751
}, - 752
/// Get details of an entity by id - 753
Get { - 754
id: String, - 755
/// Target the global entities tier instead of this workspace - 756
#[arg(long)] - 757
global: bool, - 758
}, - 759
/// Delete an entity by id - 760
Delete { - 761
id: String, - 762
/// Target the global entities tier instead of this workspace - 763
#[arg(long)] - 764
global: bool, - 765
}, - 766
} - 767
- 768
#[derive(Subcommand, Debug)] - 769
pub(crate) enum AgentsAction { - 770
/// List configured agent specialists - 771
List { - 772
/// Target the global agents tier (~/vak-home/.vak/agents.json) - 773
#[arg(long)] - 774
global: bool, - 775
}, - 776
/// List available built-in domain specialist templates (researcher, writer, operator, analyst) - 777
Templates, - 778
/// Instantiate a new specialist agent from a domain template - 779
Init { - 780
/// Template ID to instantiate: researcher | writer | operator | analyst - 781
#[arg(long)] - 782
template: String, - 783
/// Unique ID for the new agent (e.g. 'data-lead') - 784
#[arg(long)] - 785
id: String, - 786
/// Optional custom display name - 787
#[arg(long)] - 788
name: Option<String>, - 789
/// Target the global agents tier (~/vak-home/.vak/agents.json) - 790
#[arg(long)] - 791
global: bool, - 792
}, - 793
} - 794
- 795
#[derive(Subcommand, Debug)] - 796
pub(crate) enum FlowAction { - 797
/// List discovered flows - 798
List, - 799
/// Convert proven work into a flow file (docs/design/10-flows.md): - 800
/// --from accepts a flow-run/plan ledger JSON path or a session id. - 801
Adopt { - 802
/// Ledger JSON path, or a session id whose green bash commands - 803
/// become a chained bash flow. - 804
from: String, - 805
/// Name for the adopted flow (written to .vak/flows/) - 806
#[arg(long)] - 807
name: String, - 808
/// Overwrite an existing flow file of the same name - 809
#[arg(long)] - 810
force: bool, - 811
}, - 812
/// Deterministic run-vs-run diff over two ledger JSONs (no model) - 813
Diff { - 814
/// Path to first run/plan ledger JSON - 815
a: std::path::PathBuf, - 816
/// Path to second run/plan ledger JSON - 817
b: std::path::PathBuf, - 818
}, - 819
/// Validate a flow without running it - 820
Check { name: String }, - 821
/// Run a flow (optionally resuming a previous run) - 822
Run { - 823
name: String, - 824
#[arg(long)] - 825
resume: bool, - 826
/// Acknowledge live-file drift and resume the frozen snapshot - 827
#[arg(long)] - 828
accept_drift: bool, - 829
#[arg(long)] - 830
yes: bool, - 831
#[arg(long)] - 832
provider: Option<String>, - 833
#[arg(long)] - 834
model: Option<String>, - 835
/// Trust this workspace's project config and secret scope - 836
#[arg(long)] - 837
trust: bool, - 838
}, - 839
} - 840
- 841
#[derive(Subcommand, Debug)] - 842
pub(crate) enum ConfigAction { - 843
/// Print the effective composed configuration - 844
Dump, - 845
/// Show the permission mode, approval mode, and rule lists in force - 846
Permissions, - 847
/// Set the permission mode, persisted to a config layer - 848
SetMode { - 849
/// read-only | workspace-write | full-access - 850
mode: String, - 851
#[arg(long, default_value = "project")] - 852
scope: PromptScope, - 853
}, - 854
/// Set how `Ask` decisions are resolved, persisted to a config layer - 855
SetApproval { - 856
/// ask | approve-safe | auto-approve - 857
mode: String, - 858
#[arg(long, default_value = "project")] - 859
scope: PromptScope, - 860
}, - 861
} - 862
- 863
/// Scope for a prompt edit. Matches the wire names the admin API uses; - 864
/// interfaces label these "Shared" and "This project" (docs/design/05-config.md). - 865
#[derive(ValueEnum, Clone, Copy, Debug, PartialEq, Eq)] - 866
pub(crate) enum PromptScope { - 867
/// ~/vak-home — the baseline every project inherits - 868
User, - 869
/// this workspace only - 870
Project, - 871
} - 872
- 873
#[derive(Subcommand, Debug)] - 874
pub(crate) enum PromptsAction { - 875
/// Print the assembled prompt, or one layer's own text - 876
Show { - 877
/// Show only this block (identity, operating-rules, guardrails) - 878
block: Option<String>, - 879
/// Print the layer's own text instead of the assembled result - 880
#[arg(long)] - 881
scope: Option<PromptScope>, - 882
/// Annotate each contributing layer - 883
#[arg(long)] - 884
provenance: bool, - 885
}, - 886
/// Open a block in $EDITOR and save it to the chosen scope - 887
Edit { - 888
block: String, - 889
#[arg(long, default_value = "project")] - 890
scope: PromptScope, - 891
}, - 892
/// Set a block from a file or stdin (`-`), non-interactively - 893
Set { - 894
block: String, - 895
/// File to read; `-` reads stdin - 896
from: String, - 897
#[arg(long, default_value = "project")] - 898
scope: PromptScope, - 899
}, - 900
/// Delete this layer's block and resume inheritance - 901
Reset { - 902
block: String, - 903
#[arg(long, default_value = "project")] - 904
scope: PromptScope, - 905
}, - 906
/// Show what this workspace changed against the shipped default - 907
Diff, - 908
/// Render the exact prompt a given surface and role would receive - 909
Preview { - 910
/// cli, desktop, server, background, worker, or a chat channel - 911
#[arg(long, default_value = "cli")] - 912
surface: String, - 913
/// Named agent role to apply - 914
#[arg(long)] - 915
role: Option<String>, - 916
}, - 917
/// List named agent roles defined for this workspace - 918
Roles, - 919
} - 920
- 921
#[derive(Subcommand, Debug)] - 922
pub(crate) enum SkillsReviewAction { - 923
/// List pending proposals - 924
List, - 925
/// Promote a proposal into user-level skills - 926
Promote { id: String }, - 927
/// Discard a proposal - 928
Reject { id: String }, - 929
} - 930
- 931
#[derive(Subcommand, Debug)] - 932
pub(crate) enum CheckpointAction { - 933
/// List checkpoints for the latest session in this project - 934
List { - 935
#[arg(long)] - 936
session: Option<String>, - 937
}, - 938
/// Restore a checkpoint into the workspace - 939
Restore { session: String, seq: u32 }, - 940
} - 941
- 942
#[cfg(test)] - 943
mod tests { - 944
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] - 945
use super::*; - 946
use clap::CommandFactory; - 947
- 948
fn parse(args: &[&str]) -> Command { - 949
Cli::try_parse_from(std::iter::once("vak").chain(args.iter().copied())) - 950
.unwrap_or_else(|e| panic!("parse {args:?} failed: {e}")) - 951
.command - 952
.expect("subcommand present") - 953
} - 954
- 955
#[test] - 956
fn cli_definition_is_well_formed() { - 957
Cli::command().debug_assert(); - 958
} - 959
- 960
#[test] - 961
fn doctor_parses_with_and_without_trust() { - 962
assert!(!matches!( - 963
parse(&["doctor"]), - 964
Command::Doctor { trust: true, .. } - 965
)); - 966
assert!(matches!( - 967
parse(&["doctor", "--trust"]), - 968
Command::Doctor { trust: true, .. } - 969
)); - 970
} - 971
- 972
#[test] - 973
fn doctor_parses_repair() { - 974
assert!(matches!( - 975
parse(&["doctor", "--repair"]), - 976
Command::Doctor { repair: true, .. } - 977
)); - 978
assert!(!matches!( - 979
parse(&["doctor"]), - 980
Command::Doctor { repair: true, .. } - 981
)); - 982
} - 983
- 984
#[test] - 985
fn backup_export_flags_parse() { - 986
match parse(&["backup", "export", "/tmp/bk"]) { - 987
Command::Backup { - 988
action: - 989
BackupAction::Export { - 990
dir, - 991
include_secrets, - 992
}, - 993
} => { - 994
assert_eq!(dir, PathBuf::from("/tmp/bk")); - 995
assert!(!include_secrets); - 996
} - 997
other => panic!("unexpected: {other:?}"), - 998
} - 999
match parse(&["backup", "export", "/tmp/bk", "--include-secrets"]) { - 1000
Command::Backup {
Indexing the workspace…
Vakyartha documentation is discovering safe artifacts, anchors, and source references.