greentic_dev/
cli.rs

1use std::path::PathBuf;
2
3use crate::secrets_cli::SecretsCommand;
4use clap::{Args, Parser, Subcommand, ValueEnum};
5use greentic_component::cmd::{
6    build::BuildArgs as ComponentBuildArgs, doctor::DoctorArgs as ComponentDoctorArgs,
7    flow::FlowCommand as ComponentFlowCommand, hash::HashArgs as ComponentHashArgs,
8    inspect::InspectArgs as ComponentInspectArgs, new::NewArgs as ComponentNewArgs,
9    store::StoreCommand as ComponentStoreCommand,
10    templates::TemplatesArgs as ComponentTemplatesArgs,
11};
12
13#[derive(Parser, Debug)]
14#[command(name = "greentic-dev")]
15#[command(version)]
16#[command(about = "Greentic developer tooling CLI")]
17pub struct Cli {
18    #[command(subcommand)]
19    pub command: Command,
20}
21
22#[derive(Subcommand, Debug)]
23pub enum Command {
24    /// Flow tooling (validate, lint, bundle inspection)
25    #[command(subcommand)]
26    Flow(FlowCommand),
27    /// Pack tooling (delegates to packc for build/lint/sign/verify/new; uses greentic-pack for inspect/plan/events)
28    #[command(subcommand)]
29    Pack(PackCommand),
30    /// Component tooling (delegates to greentic-component built-ins + distributor add)
31    #[command(subcommand)]
32    Component(ComponentCommand),
33    /// Manage greentic-dev configuration
34    #[command(subcommand)]
35    Config(ConfigCommand),
36    /// MCP tooling
37    #[command(subcommand)]
38    Mcp(McpCommand),
39    /// GUI dev tooling (serve packs locally, stage dev packs)
40    #[command(subcommand)]
41    Gui(GuiCommand),
42    /// Secrets convenience wrappers
43    #[command(subcommand)]
44    Secrets(SecretsCommand),
45}
46
47#[derive(Subcommand, Debug)]
48pub enum FlowCommand {
49    /// Validate a flow YAML file and emit the canonical bundle JSON
50    Validate(FlowValidateArgs),
51    /// Add a configured component step to a flow via config-flow
52    AddStep(FlowAddStepArgs),
53}
54
55#[derive(Args, Debug)]
56pub struct FlowValidateArgs {
57    /// Path to the flow definition (YAML)
58    #[arg(short = 'f', long = "file")]
59    pub file: PathBuf,
60    /// Emit compact JSON instead of pretty-printing
61    #[arg(long = "json")]
62    pub json: bool,
63}
64
65#[derive(Args, Debug)]
66pub struct FlowAddStepArgs {
67    /// Path to component.manifest.json (defaults to ./component.manifest.json)
68    #[arg(long = "manifest")]
69    pub manifest: Option<PathBuf>,
70    /// Flow identifier inside manifest.dev_flows (default: default)
71    #[arg(long = "flow", default_value = "default")]
72    pub flow: String,
73    /// Flow identifier (maps to flows/<id>.ygtc)
74    pub flow_id: String,
75    /// Component coordinate (store://... or repo://...). If omitted, greentic-dev will prompt.
76    #[arg(long = "coordinate")]
77    pub coordinate: Option<String>,
78    /// Distributor profile to use (overrides GREENTIC_DISTRIBUTOR_PROFILE/env config)
79    #[arg(long = "profile")]
80    pub profile: Option<String>,
81    /// Config flow selection
82    #[arg(long = "mode", value_enum)]
83    pub mode: Option<ConfigFlowModeArg>,
84    /// Automatically append routing from an existing node (if provided)
85    #[arg(long = "after")]
86    pub after: Option<String>,
87}
88
89#[derive(Subcommand, Debug)]
90pub enum GuiCommand {
91    /// Serve GUI packs locally via greentic-gui
92    Serve(GuiServeArgs),
93    /// Stage a local GUI pack from static assets
94    PackDev(GuiPackDevArgs),
95}
96
97#[derive(Args, Debug)]
98pub struct GuiServeArgs {
99    /// Path to gui-dev.yaml (defaults to discovery order)
100    #[arg(long = "config")]
101    pub config: Option<PathBuf>,
102    /// Address to bind (default: 127.0.0.1:8080)
103    #[arg(long = "bind")]
104    pub bind: Option<String>,
105    /// Domain reported to greentic-gui (default: localhost:8080)
106    #[arg(long = "domain")]
107    pub domain: Option<String>,
108    /// Override greentic-gui binary path (otherwise PATH is used)
109    #[arg(long = "gui-bin")]
110    pub gui_bin: Option<PathBuf>,
111    /// Disable cargo fallback when greentic-gui binary is missing
112    #[arg(long = "no-cargo-fallback")]
113    pub no_cargo_fallback: bool,
114    /// Open a browser after the server starts
115    #[arg(long = "open-browser")]
116    pub open_browser: bool,
117}
118
119#[derive(Args, Debug, Clone)]
120pub struct GuiPackDevArgs {
121    /// Directory containing built/static assets to stage
122    #[arg(long = "dir")]
123    pub dir: PathBuf,
124    /// Output directory for the staged pack (must be empty or absent)
125    #[arg(long = "output")]
126    pub output: PathBuf,
127    /// Kind of GUI pack to generate (controls manifest shape)
128    #[arg(long = "kind", value_enum, default_value = "layout")]
129    pub kind: GuiPackKind,
130    /// Entrypoint HTML file (relative to assets) for layout/feature manifests
131    #[arg(long = "entrypoint", default_value = "index.html")]
132    pub entrypoint: String,
133    /// Optional manifest to copy instead of generating one
134    #[arg(long = "manifest")]
135    pub manifest: Option<PathBuf>,
136    /// Feature route (only used when kind=feature)
137    #[arg(long = "feature-route")]
138    pub feature_route: Option<String>,
139    /// Feature HTML file (relative to assets; kind=feature)
140    #[arg(long = "feature-html", default_value = "index.html")]
141    pub feature_html: String,
142    /// Mark the feature route as authenticated (kind=feature)
143    #[arg(long = "feature-authenticated")]
144    pub feature_authenticated: bool,
145    /// Optional build command to run before staging
146    #[arg(long = "build-cmd")]
147    pub build_cmd: Option<String>,
148    /// Skip running the build command even if provided
149    #[arg(long = "no-build")]
150    pub no_build: bool,
151}
152
153#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
154pub enum GuiPackKind {
155    Layout,
156    Feature,
157}
158
159#[derive(Subcommand, Debug)]
160pub enum PackCommand {
161    /// Delegate to packc build
162    Build(PackcArgs),
163    /// Delegate to packc lint
164    Lint(PackcArgs),
165    /// Delegate to packc new
166    New(PackcArgs),
167    /// Delegate to packc sign
168    Sign(PackcArgs),
169    /// Delegate to packc verify
170    Verify(PackcArgs),
171    /// Inspect a .gtpack (or directory via temporary build)
172    Inspect(PackInspectArgs),
173    /// Generate a deployment plan
174    Plan(PackPlanArgs),
175    /// Events helpers
176    #[command(subcommand)]
177    Events(PackEventsCommand),
178    /// Execute a pack locally with mocks/telemetry support
179    Run(PackRunArgs),
180    /// Initialize a pack workspace from a remote coordinate
181    Init(PackInitArgs),
182}
183
184#[derive(Args, Debug)]
185pub struct PackRunArgs {
186    /// Path to the pack (.gtpack) to execute
187    #[arg(short = 'p', long = "pack")]
188    pub pack: PathBuf,
189    /// Flow entry identifier override
190    #[arg(long = "entry")]
191    pub entry: Option<String>,
192    /// JSON payload to use as run input
193    #[arg(long = "input")]
194    pub input: Option<String>,
195    /// Emit JSON output
196    #[arg(long = "json")]
197    pub json: bool,
198    /// Offline mode (disable network/proxy)
199    #[arg(long = "offline")]
200    pub offline: bool,
201    /// Use mock executor (internal/testing)
202    #[arg(long = "mock-exec", hide = true)]
203    pub mock_exec: bool,
204    /// Allow external calls in mock executor (default: false)
205    #[arg(long = "allow-external", hide = true)]
206    pub allow_external: bool,
207    /// Return mocked external responses when external calls are allowed (mock exec only)
208    #[arg(long = "mock-external", hide = true)]
209    pub mock_external: bool,
210    /// Path to JSON payload used for mocked external responses (mock exec only)
211    #[arg(long = "mock-external-payload", hide = true)]
212    pub mock_external_payload: Option<PathBuf>,
213    /// Secrets seed file applied to the mock secrets store (mock exec only)
214    #[arg(long = "secrets-seed", hide = true)]
215    pub secrets_seed: Option<PathBuf>,
216    /// Enforcement policy for pack signatures
217    #[arg(long = "policy", default_value = "devok", value_enum)]
218    pub policy: RunPolicyArg,
219    /// OTLP collector endpoint (optional)
220    #[arg(long = "otlp")]
221    pub otlp: Option<String>,
222    /// Comma-separated list of allowed outbound hosts
223    #[arg(long = "allow")]
224    pub allow: Option<String>,
225    /// Mocks toggle
226    #[arg(long = "mocks", default_value = "on", value_enum)]
227    pub mocks: MockSettingArg,
228    /// Directory to persist run artifacts (transcripts, logs)
229    #[arg(long = "artifacts")]
230    pub artifacts: Option<PathBuf>,
231}
232
233#[derive(Args, Debug)]
234pub struct PackInitArgs {
235    /// Remote pack coordinate (e.g. pack://org/name@1.0.0)
236    pub from: String,
237    /// Distributor profile to use (overrides GREENTIC_DISTRIBUTOR_PROFILE/env config)
238    #[arg(long = "profile")]
239    pub profile: Option<String>,
240}
241
242#[derive(Args, Debug, Clone, Default)]
243#[command(disable_help_flag = true)]
244pub struct PackcArgs {
245    /// Arguments passed directly to the `packc` command
246    #[arg(
247        value_name = "ARGS",
248        trailing_var_arg = true,
249        allow_hyphen_values = true
250    )]
251    pub passthrough: Vec<String>,
252}
253
254#[derive(Args, Debug)]
255pub struct PackInspectArgs {
256    /// Path to the .gtpack file or pack directory
257    #[arg(value_name = "PATH")]
258    pub path: PathBuf,
259    /// Signature policy to enforce
260    #[arg(long, value_enum, default_value = "devok")]
261    pub policy: PackPolicyArg,
262    /// Emit JSON output
263    #[arg(long)]
264    pub json: bool,
265}
266
267#[derive(Subcommand, Debug)]
268pub enum PackEventsCommand {
269    /// List event providers declared in a pack
270    List(PackEventsListArgs),
271}
272
273#[derive(Args, Debug)]
274pub struct PackEventsListArgs {
275    /// Path to a .gtpack archive or pack source directory.
276    #[arg(value_name = "PATH")]
277    pub path: PathBuf,
278    /// Output format: table (default), json, yaml.
279    #[arg(long, value_enum, default_value = "table")]
280    pub format: PackEventsFormatArg,
281    /// When set, print additional diagnostics (for directory builds).
282    #[arg(long)]
283    pub verbose: bool,
284}
285
286#[derive(Args, Debug)]
287pub struct PackPlanArgs {
288    /// Path to a .gtpack archive or pack source directory.
289    #[arg(value_name = "PATH")]
290    pub input: PathBuf,
291    /// Tenant identifier to embed in the plan.
292    #[arg(long, default_value = "tenant-local")]
293    pub tenant: String,
294    /// Environment identifier to embed in the plan.
295    #[arg(long, default_value = "local")]
296    pub environment: String,
297    /// Emit compact JSON output instead of pretty-printing.
298    #[arg(long)]
299    pub json: bool,
300    /// When set, print additional diagnostics (for directory builds).
301    #[arg(long)]
302    pub verbose: bool,
303}
304
305#[derive(Subcommand, Debug, Clone)]
306pub enum ComponentCommand {
307    /// Add a remote component to the current workspace via the distributor
308    Add(ComponentAddArgs),
309    /// Scaffold a new Greentic component project
310    New(ComponentNewArgs),
311    /// List available component templates
312    Templates(ComponentTemplatesArgs),
313    /// Run component doctor checks
314    Doctor(ComponentDoctorArgs),
315    /// Inspect manifests and describe payloads
316    Inspect(ComponentInspectArgs),
317    /// Recompute manifest hashes
318    Hash(ComponentHashArgs),
319    /// Build component wasm + scaffold config flows
320    Build(ComponentBuildArgs),
321    /// Flow utilities (config flow scaffolding)
322    #[command(subcommand)]
323    Flow(ComponentFlowCommand),
324    /// Interact with the component store
325    #[command(subcommand)]
326    Store(ComponentStoreCommand),
327}
328
329#[derive(Args, Debug, Clone)]
330pub struct ComponentAddArgs {
331    /// Remote component coordinate (e.g. component://org/name@^1.0)
332    pub coordinate: String,
333    /// Distributor profile to use (overrides GREENTIC_DISTRIBUTOR_PROFILE/env config)
334    #[arg(long = "profile")]
335    pub profile: Option<String>,
336    /// Resolution intent (dev or runtime)
337    #[arg(long = "intent", default_value = "dev", value_enum)]
338    pub intent: DevIntentArg,
339}
340
341#[derive(Subcommand, Debug)]
342pub enum McpCommand {
343    /// Inspect MCP provider metadata
344    Doctor(McpDoctorArgs),
345}
346
347#[derive(Args, Debug)]
348pub struct McpDoctorArgs {
349    /// MCP provider identifier or config path
350    pub provider: String,
351    /// Emit compact JSON instead of pretty output
352    #[arg(long = "json")]
353    pub json: bool,
354}
355
356#[derive(Subcommand, Debug)]
357pub enum ConfigCommand {
358    /// Set a key in greentic-dev config (e.g. defaults.component.org)
359    Set(ConfigSetArgs),
360}
361
362#[derive(Args, Debug)]
363pub struct ConfigSetArgs {
364    /// Config key path (e.g. defaults.component.org)
365    pub key: String,
366    /// Value to assign to the key (stored as a string)
367    pub value: String,
368    /// Override config file path (default: $XDG_CONFIG_HOME/greentic-dev/config.toml)
369    #[arg(long = "file")]
370    pub file: Option<PathBuf>,
371}
372
373#[derive(Copy, Clone, Debug, ValueEnum)]
374pub enum PackSignArg {
375    Dev,
376    None,
377}
378
379#[derive(Copy, Clone, Debug, ValueEnum)]
380pub enum PackPolicyArg {
381    Devok,
382    Strict,
383}
384
385#[derive(Copy, Clone, Debug, ValueEnum)]
386pub enum RunPolicyArg {
387    Strict,
388    Devok,
389}
390
391#[derive(Copy, Clone, Debug, ValueEnum)]
392pub enum VerifyPolicyArg {
393    Strict,
394    Devok,
395}
396
397#[derive(Copy, Clone, Debug, ValueEnum)]
398pub enum MockSettingArg {
399    On,
400    Off,
401}
402
403#[derive(Copy, Clone, Debug, ValueEnum)]
404pub enum PackEventsFormatArg {
405    Table,
406    Json,
407    Yaml,
408}
409
410#[derive(Copy, Clone, Debug, ValueEnum)]
411pub enum ConfigFlowModeArg {
412    Default,
413    Custom,
414}
415#[derive(Copy, Clone, Debug, ValueEnum)]
416pub enum DevIntentArg {
417    Dev,
418    Runtime,
419}
420
421#[cfg(test)]
422mod tests {}