1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
use std::path::{Path, PathBuf};
use clap::{Args, Parser, Subcommand, ValueEnum};
use tau_proto::SessionId;
use tau_session_inspect::{
default_agents_dir, default_session_id, default_sessions_dir, default_state_dir,
};
#[cfg(test)]
mod tests;
#[derive(Parser)]
#[command(
name = "tau",
about = "Unix-native LLM agent harness",
disable_version_flag = true
)]
pub struct Cli {
/// Print version, build revision, and build date.
#[arg(short = 'V', long = "version", global = true)]
pub version: bool,
#[command(flatten)]
pub harness: HarnessArgs,
#[command(flatten)]
pub run: RunArgs,
#[command(subcommand)]
pub command: Option<Command>,
}
#[derive(Args)]
pub struct HarnessArgs {
#[command(flatten)]
pub role_overrides: RoleOverrideArgs,
#[command(flatten)]
pub extension_overrides: ExtensionOverrideArgs,
/// Select the startup/rendered role.
#[arg(short = 'r', long = "role")]
pub role: Option<String>,
/// Select comma-separated configuration profiles before CLI overrides.
#[arg(long = "profile", value_name = "PROFILE")]
pub profile: Option<String>,
/// Override one harness config key after all config files are loaded.
#[arg(
long = "harness-config",
value_name = "KEY=VALUE",
require_equals = true
)]
pub harness_config: Vec<tau_config::settings::HarnessConfigCliOverride>,
/// Override one provider alias for this harness startup.
///
/// `TAU_PROVIDER_ALIASES` supplies a JSON object of lower-precedence
/// environment overrides.
#[arg(long = "provider-alias", value_name = "FROM=TO")]
pub provider_alias: Vec<tau_config::settings::ProviderAliasCliOverride>,
/// Override one exact model-name alias for this harness startup.
///
/// `TAU_MODEL_ALIASES` supplies a JSON object of lower-precedence
/// environment overrides.
#[arg(long = "model-alias", value_name = "FROM=TO")]
pub model_alias: Vec<tau_config::settings::ModelAliasCliOverride>,
}
#[derive(Args)]
pub struct RoleOverrideArgs {
/// Enable a configured role after all config files are loaded.
#[arg(long = "enable-role")]
pub enable_role: Vec<String>,
/// Disable a configured role after all config files are loaded.
#[arg(long = "disable-role")]
pub disable_role: Vec<String>,
/// Disable every configured role before later CLI role overrides.
#[arg(long = "disable-roles-all", action = clap::ArgAction::Count)]
pub disable_roles_all: u8,
}
#[derive(Args)]
pub struct ExtensionOverrideArgs {
/// Enable non-test configured extensions before later CLI extension
/// overrides. The built-in test-dummy fixture still requires explicit
/// `--enable-extension test-dummy`.
#[arg(long = "enable-extensions-all", action = clap::ArgAction::Count)]
pub enable_extensions_all: u8,
/// Disable every configured extension before later CLI extension overrides.
#[arg(long = "disable-extensions-all", action = clap::ArgAction::Count)]
pub disable_extensions_all: u8,
/// Enable a configured extension after all config files are loaded.
///
/// `TAU_ENABLE_EXTENSIONS=NAME[,NAME...]` additively enables exact
/// configured names before ordered CLI overrides. Space/tab around
/// names is allowed; malformed or unknown names fail startup. CLI
/// enable/disable flags win.
#[arg(long = "enable-extension")]
pub enable_extension: Vec<String>,
/// Disable a configured extension after all config files are loaded.
#[arg(long = "disable-extension")]
pub disable_extension: Vec<String>,
}
#[derive(Args)]
/// Options shared by new, attach, and resume session startup.
pub struct RunArgs {
/// Deprecated legacy extension config path; use `--harness-config`
/// overrides instead.
#[arg(long, hide = true)]
pub config: Option<PathBuf>,
/// Read one prompt from stdin, submit it, print final output, and exit.
///
/// Answers go to stdout; reasoning, headers, and errors go to stderr.
/// Each destination's terminal state is checked independently, and dynamic
/// bodies are sanitized only when that destination is a terminal. Pipes and
/// files retain semantic UTF-8 bytes and existing framing.
#[arg(long = "prompt-stdin")]
pub prompt_stdin: bool,
/// Run without writing session membership, session metadata, session debug
/// events, per-session logs, session-scoped extension data, or the terminal
/// UI log to disk.
///
/// Agent transcripts, provider state, credentials, user/cache extension
/// data, runtime sockets, and configuration state keep their normal
/// persistence behavior.
#[arg(long)]
pub ephemeral: bool,
}
#[derive(Subcommand)]
pub enum Command {
/// Run an interactive agent session.
///
/// `tau` spawns a new harness daemon and attaches for this process.
#[command(hide = true)]
Run(RunArgs),
/// Attach to a running session without taking daemon ownership.
Attach {
/// Running session id; omit to choose interactively.
session: Option<SessionId>,
},
/// Resume a persisted session in a new harness daemon.
Resume {
/// Persisted session id; omission auto-selects the sole unlocked
/// target, otherwise shows the eligible-session picker.
session: Option<SessionId>,
},
/// Serve one fixed session in the foreground without an initial UI.
Serve {
/// Exact session id to create or resume.
#[arg(long)]
session: SessionId,
/// Require the session directory to be completely absent, then create
/// it.
#[arg(
long,
required_unless_present_any = ["existing", "create_or_existing"],
conflicts_with_all = ["existing", "create_or_existing"]
)]
create: bool,
/// Require and strictly resume valid existing session state.
#[arg(
long,
required_unless_present_any = ["create", "create_or_existing"],
conflicts_with_all = ["create", "create_or_existing"]
)]
existing: bool,
/// Resume valid existing state or atomically create an absent session.
#[arg(
long,
required_unless_present_any = ["create", "existing"],
conflicts_with_all = ["create", "existing"]
)]
create_or_existing: bool,
/// Read one literal bootstrap prompt from this UTF-8 file after
/// startup.
///
/// `-` reads stdin through EOF once. The paired bootstrap id makes this
/// submission durable and at-most-once across restarts.
#[arg(
long = "bootstrap-prompt-file",
value_name = "PATH",
requires = "bootstrap_id"
)]
bootstrap_prompt_file: Option<PathBuf>,
/// Durable bootstrap generation id.
#[arg(
long = "bootstrap-id",
value_name = "ID",
requires = "bootstrap_prompt_file"
)]
bootstrap_id: Option<tau_harness::BootstrapId>,
/// Mirror framed, escaped extension stderr to this process's stderr.
///
/// Private per-session extension log files remain authoritative. Custom
/// extension stderr is unredacted and may reach a wider journal
/// audience.
#[arg(long)]
mirror_extension_stderr: bool,
},
/// Inspect sessions.
Session {
#[command(subcommand)]
command: SessionCommand,
},
/// Inspect agents.
Agent {
#[command(subcommand)]
command: AgentCommand,
},
/// Copy sample config files to ~/.config/tau/
Init {
/// Overwrite existing config files
#[arg(long)]
force: bool,
},
/// Manage LLM providers (add, remove, list)
Provider {
/// Subcommand and arguments (e.g. `add`, `remove <name>`, `list`)
#[arg(trailing_var_arg = true)]
args: Vec<String>,
},
/// Developer-only commands.
#[command(hide = true, hide_possible_values = true)]
Dev {
#[command(subcommand)]
command: DevCommand,
},
/// Run a bundled Tau component as a standalone process.
///
/// Bundled extensions are components too, but not every component is an
/// extension; for example, the harness is a component.
Component {
/// Component name (harness or a bundled extension such as ext-shell,
/// ext-provider-builtin, ext-websearch, ext-rhai,
/// ext-std-notifications, or ext-test-dummy)
name: String,
/// Use stdin/stdout as the initial UI connection before starting
/// harness extensions. Only valid with `tau component harness`.
#[arg(long, hide = true)]
initial_ui_stdio: bool,
},
}
#[derive(Subcommand)]
pub enum SessionCommand {
/// Inspect durable cache accounting and private capture coverage offline.
Cache(SessionCacheArgs),
/// Gracefully shut down one exact running session.
Kill {
/// Exact session identifier to shut down
session_id: tau_proto::SessionId,
},
/// List currently running sessions.
List(SessionListArgs),
/// Show a single session's history.
Show {
/// Session identifier
#[arg(
long,
default_value_t = tau_proto::SessionId::parse(default_session_id())
.expect("configured default session id must be valid")
)]
session_id: tau_proto::SessionId,
/// Path to per-session storage root (`<state-dir>/sessions/`)
#[arg(long, default_value_os_t = default_sessions_dir())]
sessions_dir: PathBuf,
},
/// Print exact durable activity accounting for one session as TOON.
Stats {
/// Session identifier to account.
#[arg(long)]
session: tau_proto::SessionId,
/// Path to per-session storage root (`<state-dir>/sessions/`).
#[arg(long, default_value_os_t = default_sessions_dir())]
sessions_dir: PathBuf,
},
}
/// Output and exact-directory filters for `tau session list`.
#[derive(Args, Clone, Debug, Default)]
pub struct SessionListArgs {
/// List only harnesses whose canonical startup root is this directory.
#[arg(long, value_name = "DIR", value_parser = parse_canonical_directory)]
pub dir: Option<PathBuf>,
/// Emit one JSON array with session id and canonical project root fields.
#[arg(long)]
pub json: bool,
}
/// Canonicalizes one existing directory during CLI parsing.
fn parse_canonical_directory(value: &str) -> Result<PathBuf, String> {
let canonical = Path::new(value)
.canonicalize()
.map_err(|error| format!("cannot access directory `{value}`: {error}"))?;
if !canonical.is_dir() {
return Err(format!("path is not a directory: `{value}`"));
}
Ok(canonical)
}
#[derive(Subcommand)]
pub enum AgentCommand {
/// Inspect durable cache accounting and private capture coverage offline.
Cache(AgentCacheArgs),
/// Export a durable agent artifact for offline use.
Export(AgentExportArgs),
/// List agents known to a running session.
List(AgentListArgs),
/// Unload one idle saved agent without deleting its transcript or session
/// history.
///
/// Only durable agents are supported. After the request is sent, timeout or
/// disconnect is indeterminate; retrying the same session and agent is
/// safe.
Unload(AgentUnloadArgs),
/// Project a validated durable agent snapshot (defaults to compact TOON
/// lite).
Trace(AgentTraceArgs),
}
/// Options for `tau agent export`.
#[derive(Args, Clone)]
pub struct AgentExportArgs {
/// Artifact to export.
#[command(subcommand)]
pub command: AgentExportCommand,
}
/// Durable agent artifacts available for export.
#[derive(Subcommand, Clone)]
pub enum AgentExportCommand {
/// Export user prompts and agent responses from the selected branch.
Chat(AgentExportChatArgs),
}
/// Options for `tau agent export chat`.
#[derive(Args, Clone)]
pub struct AgentExportChatArgs {
/// Durable agent journal to export.
pub agent_id: tau_proto::AgentId,
/// Emit the existing strict TOON serialization instead of Markdown.
#[arg(long, conflicts_with = "markdown")]
pub toons: bool,
/// Explicitly select Markdown, which is already the default.
#[arg(long, conflicts_with = "toons")]
pub markdown: bool,
/// Durable agent journal root.
#[arg(long, default_value_os_t = default_agents_dir())]
pub agents_dir: PathBuf,
}
/// Offline cache report options shared by agent and session scopes.
#[derive(Args, Clone)]
pub struct CacheArgs {
/// Emit a compact summary or internal version-zero JSON Lines.
#[arg(long, value_enum, default_value_t)]
pub format: CacheFormat,
/// Restrict canonical responses to this exact local prompt.
#[arg(long)]
pub prompt: Option<tau_proto::AgentPromptId>,
/// Include observations at or after this absolute RFC3339 timestamp.
#[arg(long, value_parser = parse_cache_since)]
pub since: Option<u64>,
/// Include observations at or before this absolute RFC3339 timestamp.
#[arg(long, value_parser = parse_cache_until)]
pub until: Option<u64>,
/// Restrict evidence to this exact configured or effective model.
#[arg(long)]
pub model: Option<String>,
/// Restrict evidence to one closed provider operation.
#[arg(long, value_enum)]
pub operation: Option<CacheOperation>,
/// Restrict evidence to one logical/provider attempt ordinal.
#[arg(long)]
pub attempt: Option<u64>,
/// Exclude exact comparisons without a proven captured response-chain edge.
#[arg(long)]
pub require_exact_chain: bool,
/// Geometry grouping dimensions, as a comma-separated list.
#[arg(
long,
value_enum,
value_delimiter = ',',
default_value = "model,backend,controls"
)]
pub group_by: Vec<CacheGroup>,
/// Select summary, attribution, continuity, geometry, or gap evidence.
#[arg(long, value_enum, default_value_t)]
pub view: CacheView,
/// Existing Tau state root; inspection never creates it.
#[arg(long, default_value_os_t = tau_session_inspect::default_state_dir())]
pub state_dir: PathBuf,
/// Inclusive compressed byte limit per capture.
#[arg(long, default_value_t = 16 * 1024 * 1024)]
pub max_compressed_bytes: u64,
/// Inclusive decompressed byte limit per capture.
#[arg(long, default_value_t = 64 * 1024 * 1024)]
pub max_decompressed_bytes: u64,
/// Inclusive cumulative decompressed capture bytes.
#[arg(long, default_value_t = 1024 * 1024 * 1024)]
pub max_total_bytes: u64,
/// Capture parser and report working-memory budget in bytes.
#[arg(long, default_value_t = 512 * 1024 * 1024)]
pub max_memory_bytes: u64,
/// Replace one disposable owner-private index for later comparisons.
#[arg(long, value_name = "PATH")]
pub index: Option<PathBuf>,
}
/// Closed cache operation accepted by offline selection.
#[derive(Clone, Copy, ValueEnum)]
pub enum CacheOperation {
/// Ordinary provider inference.
Inference,
/// Provider-backed standalone compaction.
StandaloneCompaction,
/// Provider cache refresh or prewarm work.
CacheRefresh,
}
/// Closed empirical geometry grouping dimension.
#[derive(Clone, Copy, ValueEnum)]
pub enum CacheGroup {
/// Effective provider model.
Model,
/// Backend adapter and transport.
Backend,
/// Reasoning, tool, tier, and cache controls.
Controls,
}
/// Parses an inclusive lower RFC3339 bound, rounding toward later observations.
pub(crate) fn parse_cache_since(value: &str) -> Result<u64, String> {
let nanos = parse_cache_rfc3339_nanos(value)?;
u64::try_from((nanos + 999) / 1_000)
.map_err(|_| "timestamp exceeds the supported Unix-microsecond range".to_owned())
}
/// Parses an inclusive upper RFC3339 bound, rounding toward earlier
/// observations.
pub(crate) fn parse_cache_until(value: &str) -> Result<u64, String> {
let nanos = parse_cache_rfc3339_nanos(value)?;
u64::try_from(nanos / 1_000)
.map_err(|_| "timestamp exceeds the supported Unix-microsecond range".to_owned())
}
/// Parses one nonnegative absolute RFC3339 timestamp at nanosecond precision.
fn parse_cache_rfc3339_nanos(value: &str) -> Result<i128, String> {
use time::format_description::well_known::Rfc3339;
let timestamp = time::OffsetDateTime::parse(value, &Rfc3339)
.map_err(|error| format!("must be an absolute RFC3339 timestamp: {error}"))?;
let nanos = timestamp.unix_timestamp_nanos();
if nanos < 0 {
return Err("timestamp must not precede the Unix epoch".to_owned());
}
Ok(nanos)
}
/// Offline cache evidence projection.
#[derive(Clone, Copy, Debug, Default, ValueEnum)]
pub enum CacheView {
/// Canonical accounting and overall capture coverage.
#[default]
Summary,
/// Provider-reported per-item attribution evidence.
Attribution,
/// Attempt, dispatch, anchor, connection, and repair facts.
Continuity,
/// Empirical reported-token distributions by scalar regime.
Geometry,
/// Encountered evidence gaps only.
Gaps,
}
/// Available first-delivery content-free cache report encodings.
#[derive(Clone, Copy, Default, ValueEnum)]
pub enum CacheFormat {
/// Counts and explicit evidence gaps.
#[default]
Summary,
/// Canonical per-response facts, recorded costs, and coverage.
Jsonl,
}
/// Offline cache inspection rooted at one durable agent.
#[derive(Args, Clone)]
pub struct AgentCacheArgs {
/// Existing durable agent identity.
pub agent_id: tau_proto::AgentId,
/// Include authenticated creator descendants recursively.
#[arg(long)]
pub include_descendants: bool,
/// Shared read-only report controls.
#[command(flatten)]
pub options: CacheArgs,
}
/// Offline cache inspection of durable session membership.
#[derive(Args, Clone)]
pub struct SessionCacheArgs {
/// Existing durable session identity.
pub session_id: tau_proto::SessionId,
/// Shared read-only report controls.
#[command(flatten)]
pub options: CacheArgs,
}
/// Options for `tau agent unload`.
#[derive(Args, Clone)]
pub struct AgentUnloadArgs {
/// Running session to mutate.
pub session_id: SessionId,
/// Saved agent to unload.
pub agent_id: tau_proto::AgentId,
}
/// Options for `tau agent trace`.
#[derive(Args, Clone)]
pub struct AgentTraceArgs {
/// Durable agent journal to export.
pub agent_id: tau_proto::AgentId,
/// Recursively include agents created by the requested workflow.
#[arg(long)]
pub include_descendants: bool,
/// Machine-readable export format.
#[arg(long, value_enum, default_value_t)]
pub format: AgentTraceFormat,
/// Compact semantic text and tool-output detail.
#[arg(long, value_enum, default_value_t)]
pub mode: AgentTraceMode,
/// Durable agent journal root.
#[arg(long, default_value_os_t = default_agents_dir())]
pub agents_dir: PathBuf,
}
/// Machine-readable agent trace export format.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, ValueEnum)]
pub enum AgentTraceFormat {
/// Complete canonical Tau JSON Lines.
TauJsonl,
/// Lossy OTLP/OpenInference JSON visualization adapter.
OtlpJson,
/// Compact assistant, message, reasoning, and tool-call timeline as JSON
/// Lines.
AgentToolsJsonl,
/// Compact assistant, message, reasoning, and tool-call timeline as TOON.
#[default]
AgentToolsToon,
/// Content-free provider, tool, wait, outer-turn, and compaction accounting
/// as JSON Lines.
AgentPerformanceJsonl,
}
/// Content detail for compact semantic trace formats.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, ValueEnum)]
pub enum AgentTraceMode {
/// Report complete metrics and at most 4 KiB of each text/output item.
#[default]
Lite,
/// Report complete metrics and complete semantic text/normalized output.
Full,
}
/// Filters for `tau agent list`.
#[derive(Args, Clone)]
pub struct AgentListArgs {
/// Running session to query.
pub session_id: SessionId,
/// Include suspended live agents.
#[arg(long)]
pub include_suspended: bool,
/// Include current unavailable agents and rows with missing, invalid, or
/// unreadable creation facts.
#[arg(long)]
pub include_unavailable: bool,
/// Include previously loaded and now-unloaded agents.
#[arg(long)]
pub include_unloaded: bool,
/// Include every supported agent category.
#[arg(long)]
pub all: bool,
}
#[derive(Subcommand)]
pub enum DevCommand {
/// Send one line to a running session.
Send {
/// Running session identifier.
session_id: SessionId,
/// Line to submit. Commands are interpreted like the TUI.
#[arg(required = true, trailing_var_arg = true)]
line: Vec<String>,
},
/// Dump the initial provider prompt built from local config.
DumpInitialPrompt {
/// Output path.
#[arg(long, default_value = "tmp/initial_prompt.txt")]
out: PathBuf,
/// Synthetic first user message.
#[arg(long, default_value = "hello")]
message: String,
},
/// Print the effective provider-visible prompt context.
///
/// Configures ordinary extensions, initializes one fresh ephemeral agent,
/// and waits boundedly for its context without calling a provider.
/// Extensions retain ordinary persistent state access and side effects.
/// Omitting `--role` uses the configured startup role.
PrintPrompt {
/// Include harness-injected AGENTS.md context.
#[arg(long = "enable-agents-md", default_value_t = true, action = clap::ArgAction::Set)]
enable_agents_md: bool,
},
/// Print only the rendered system prompt for a role.
///
/// Uses a stable fake agent id as the explicit `agent_id` input for custom
/// templates; built-in templates intentionally omit agent identity.
PrintSystemPrompt,
/// Print the effective tool definitions.
///
/// Uses the same fresh ephemeral-agent lifecycle and effective model/tool
/// snapshot as `print-prompt`, without calling a provider. Extensions
/// retain ordinary persistent state access and side effects. Omitting
/// `--role` uses the configured startup role.
PrintTools,
/// Print the effective skills available to a role.
///
/// Uses the same fresh ephemeral-agent lifecycle and effective context
/// snapshot as `print-prompt`, without calling a provider. Extensions
/// retain ordinary persistent state access and side effects. Omitting
/// `--role` uses the configured startup role.
PrintSkills {
/// Output encoding for the effective skill list.
#[arg(long, value_enum, default_value_t)]
format: SkillOutputFormat,
},
/// Preview config-derived declarations from explicitly opted-in extensions.
///
/// Does not start a harness, read extension state or credentials, or
/// discover agent context. Unsupported or incomplete inventories exit
/// unsuccessfully.
PreviewDeclarations,
/// Inspect or clear reports recorded by the standard papercut reporter.
Papercut {
/// Papercut operation to run.
#[command(subcommand)]
command: PapercutCommand,
},
/// Manage a manual Tau end-to-end session in a private tmux server.
Tmux {
/// Tmux helper action to run.
#[command(subcommand)]
command: DevTmuxCommand,
},
}
/// Encodings supported by `tau dev print-skills`.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, ValueEnum)]
pub enum SkillOutputFormat {
/// Human-readable Markdown document.
#[default]
Markdown,
/// Machine-readable JSON array.
Json,
}
/// Commands that inspect or clear the standard papercut reporter's records.
#[derive(Subcommand)]
pub enum PapercutCommand {
/// List recorded papercut reports.
List {
/// Render the reports as copyable Markdown.
#[arg(long)]
markdown: bool,
/// Tau state directory containing the standard reporter's records.
#[arg(long, default_value_os_t = default_state_dir())]
state_dir: PathBuf,
},
/// Archive every active papercut report at this command's serialized clear
/// boundary and print the preserved file's path.
Clear {
/// Tau state directory containing the standard reporter's records.
#[arg(long, default_value_os_t = default_state_dir())]
state_dir: PathBuf,
},
}
/// Hidden tmux helper subcommands for manual Tau end-to-end sessions.
#[derive(Subcommand)]
pub enum DevTmuxCommand {
/// Start Tau in an isolated scratch environment inside tmux.
Start(DevTmuxStartArgs),
/// Capture the current tmux pane contents.
Capture(DevTmuxTargetArgs),
/// Send text to the tmux pane, followed by Enter by default.
Send(DevTmuxSendArgs),
/// Stop the private tmux server.
Stop(DevTmuxStopArgs),
}
/// Shared tmux target arguments used by the manual E2E helper.
#[derive(Args)]
pub struct DevTmuxCommonArgs {
/// Scratch root containing the tmux socket and isolated Tau environment.
/// When omitted, `start` generates a fresh temporary root; target commands
/// use the historical static fallback root.
#[arg(long = "scratch-root", visible_alias = "root")]
pub scratch_root: Option<PathBuf>,
/// Private tmux session name.
#[arg(long, default_value = "tau-e2e")]
pub session: String,
}
/// Arguments for starting a new isolated Tau tmux session.
#[derive(Args)]
pub struct DevTmuxStartArgs {
/// Shared tmux socket/session selection.
#[command(flatten)]
pub common: DevTmuxCommonArgs,
/// Tau binary to run inside tmux.
#[arg(long)]
pub tau_bin: Option<PathBuf>,
/// Working directory for Tau and core-shell.
#[arg(long)]
pub workdir: Option<PathBuf>,
/// Initial tmux pane width.
#[arg(long, default_value_t = 120)]
pub width: u16,
/// Initial tmux pane height.
#[arg(long, default_value_t = 40)]
pub height: u16,
}
/// Arguments that identify an existing Tau tmux session.
#[derive(Args)]
pub struct DevTmuxTargetArgs {
/// Shared tmux socket/session selection.
#[command(flatten)]
pub common: DevTmuxCommonArgs,
}
/// Arguments for sending literal input to an existing Tau tmux session.
#[derive(Args)]
pub struct DevTmuxSendArgs {
/// Existing tmux session to receive input.
#[command(flatten)]
pub target: DevTmuxTargetArgs,
/// Do not send Enter after the text.
#[arg(long)]
pub no_enter: bool,
/// Text to send literally to the Tau prompt.
#[arg(required = true, trailing_var_arg = true)]
pub text: Vec<String>,
}
/// Arguments for stopping an existing Tau tmux session.
#[derive(Args)]
pub struct DevTmuxStopArgs {
/// Existing tmux session to stop.
#[command(flatten)]
pub target: DevTmuxTargetArgs,
/// Remove the scratch root after stopping tmux.
#[arg(long)]
pub remove_scratch: bool,
}