prompter/cli.rs
1//! CLI argument types and mode resolution.
2use crate::profile::FamilyName;
3use clap::{Parser, Subcommand};
4use std::path::PathBuf;
5pub use tftio_lib::MetaCommand;
6use tftio_lib::agent::AgentSubcommand;
7
8#[derive(Parser, Debug)]
9#[command(name = "prompter")]
10#[command(about = "Compose reusable prompt snippets from profile definitions")]
11#[command(long_about = None)]
12/// CLI argument struct for the prompter tool.
13#[command(version)]
14pub struct Cli {
15 /// Subcommand to execute
16 #[command(subcommand)]
17 pub command: Commands,
18
19 /// Override configuration file path
20 #[arg(short = 'c', long, value_name = "FILE", global = true)]
21 pub config: Option<PathBuf>,
22
23 /// Output in JSON format
24 #[arg(short = 'j', long, global = true)]
25 pub json: bool,
26}
27
28/// Available subcommands for the prompter CLI.
29///
30/// Each variant represents a different operation mode of the tool.
31#[derive(Subcommand, Debug)]
32pub enum Commands {
33 /// Shared metadata commands.
34 Meta {
35 /// Shared metadata command to execute.
36 #[command(subcommand)]
37 command: MetaCommand,
38 },
39 /// Initialize default config and library
40 Init,
41 /// List available profiles
42 List,
43 /// Show dependency tree for profiles
44 Tree,
45 /// Validate configuration and library references
46 Validate,
47 /// Render one or more profiles (concatenated file contents with deduplication)
48 Run {
49 /// Profile name(s) to render
50 #[arg(required = true)]
51 profiles: Vec<String>,
52 /// Model family used to select fragment variants
53 #[arg(long, value_name = "NAME")]
54 family: Option<FamilyName>,
55 /// Separator between files
56 #[arg(short, long)]
57 separator: Option<String>,
58 /// Pre-prompt text to inject at the beginning
59 #[arg(short = 'p', long)]
60 pre_prompt: Option<String>,
61 /// Post-prompt text to inject at the end
62 #[arg(short = 'P', long)]
63 post_prompt: Option<String>,
64 /// Emit only the deduplicated fragments, suppressing the auto-injected
65 /// default pre-prompt, the date/system context, and the post-prompt
66 /// (an explicit --pre-prompt/--post-prompt still applies)
67 #[arg(short = 'b', long)]
68 bare: bool,
69 },
70 /// Render the always-on invariant base (the agent system prompt).
71 ///
72 /// Emits the `core.base` profile, optionally followed by additional
73 /// profiles. Intended for harness/launcher startup: stamp the output into
74 /// each agent's system-prompt site (Codex AGENTS.md, Claude
75 /// --append-system-prompt, pi SYSTEM.md). Skills inject task context with
76 /// `run` and do NOT re-emit this base.
77 System {
78 /// Additional profile(s) to append after the system base
79 profiles: Vec<String>,
80 /// Separator between files
81 #[arg(short, long)]
82 separator: Option<String>,
83 /// Pre-prompt text to inject at the beginning
84 #[arg(short = 'p', long)]
85 pre_prompt: Option<String>,
86 /// Post-prompt text to inject at the end
87 #[arg(short = 'P', long)]
88 post_prompt: Option<String>,
89 /// Emit only the deduplicated fragments, suppressing the auto-injected
90 /// default pre-prompt, the date/system context, and the post-prompt
91 /// (an explicit --pre-prompt/--post-prompt still applies)
92 #[arg(short = 'b', long)]
93 bare: bool,
94 },
95}
96
97/// Whether rendered output carries its auto-injected framing context.
98///
99/// `Full` wraps the deduplicated fragment bodies in the pre-prompt, the
100/// date/system prefix, and the post-prompt. `Bare` suppresses everything
101/// auto-injected — the default pre-prompt, the date/system stamp, and the
102/// default/config post-prompt — leaving only the fragment bodies, for callers
103/// that want just the composed prompt (e.g. a token-frugal local model that
104/// caches the whole system prompt and is defeated by the per-call date line).
105///
106/// In both modes an explicit `--pre-prompt`/`--post-prompt` is honored: `Bare`
107/// drops defaults, not text the user asked for. The date/system stamp is never
108/// user-supplied, so `Bare` always omits it.
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110pub enum Framing {
111 /// Surround fragments with the pre-prompt, system prefix, and post-prompt.
112 Full,
113 /// Emit only the deduplicated fragment bodies.
114 Bare,
115}
116
117impl Framing {
118 /// Convert a `--bare` flag into a framing mode.
119 #[must_use]
120 pub const fn from_bare_flag(bare: bool) -> Self {
121 if bare { Self::Bare } else { Self::Full }
122 }
123
124 /// Whether this mode emits the surrounding framing context.
125 #[must_use]
126 pub const fn is_full(self) -> bool {
127 matches!(self, Self::Full)
128 }
129}
130
131/// Application execution modes after parsing command-line arguments.
132///
133/// This enum represents the resolved execution mode after processing
134/// both subcommands and direct profile arguments.
135#[derive(Debug)]
136pub enum AppMode {
137 /// Render one or more profiles with optional separator and pre-prompt
138 Run {
139 /// Profile name(s) to render
140 profiles: Vec<String>,
141 /// Optional model family used to select fragment variants
142 family: Option<FamilyName>,
143 /// Optional separator between concatenated files
144 separator: Option<String>,
145 /// Optional custom pre-prompt text
146 pre_prompt: Option<String>,
147 /// Optional custom post-prompt text
148 post_prompt: Option<String>,
149 /// Whether to wrap fragments in framing context or emit bare bodies
150 framing: Framing,
151 /// Optional configuration file override
152 config: Option<PathBuf>,
153 /// Output in JSON format
154 json: bool,
155 },
156 /// List all available profiles using an optional config override
157 List {
158 /// Optional configuration file override
159 config: Option<PathBuf>,
160 /// Output in JSON format
161 json: bool,
162 },
163 /// Show dependency tree for profiles
164 Tree {
165 /// Optional configuration file override
166 config: Option<PathBuf>,
167 /// Output in JSON format
168 json: bool,
169 },
170 /// Validate configuration and library references with an optional config override
171 Validate {
172 /// Optional configuration file override
173 config: Option<PathBuf>,
174 /// Output in JSON format
175 json: bool,
176 },
177 /// Initialize default configuration and library
178 Init,
179 /// Show version information
180 Version {
181 /// Output in JSON format
182 json: bool,
183 },
184 /// Show license information
185 License,
186 /// Show help information
187 Help,
188 /// Generate shell completion scripts
189 Completions {
190 /// Shell to generate completions for
191 shell: clap_complete::Shell,
192 },
193 /// Check health and configuration status
194 Doctor {
195 /// Output in JSON format
196 json: bool,
197 },
198 /// Agent-skill artifact subcommand (routed through cli-common).
199 Agent {
200 /// Subcommand to dispatch.
201 command: AgentSubcommand,
202 },
203}