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 greentic-pack; greentic-runner for pack run)
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 via greentic-flow doctor
50    Validate(FlowValidateArgs),
51    /// Doctor (preferred) validates a flow YAML file via greentic-flow doctor
52    Doctor(FlowValidateArgs),
53    /// Add a configured component step to a flow via config-flow
54    AddStep(Box<FlowAddStepArgs>),
55}
56
57#[derive(Args, Debug)]
58pub struct FlowValidateArgs {
59    /// Path to the flow definition (YAML)
60    #[arg(short = 'f', long = "file")]
61    pub file: PathBuf,
62    /// Emit JSON output (greentic-flow doctor --json)
63    #[arg(long = "json")]
64    pub json: bool,
65}
66
67#[derive(Args, Debug)]
68pub struct FlowAddStepArgs {
69    /// Flow file to modify (e.g., flows/main.ygtc)
70    #[arg(long = "flow")]
71    pub flow_path: PathBuf,
72    /// Optional anchor node id; defaults to entrypoint or first node.
73    #[arg(long = "after")]
74    pub after: Option<String>,
75    /// Mode for add-step (default or config)
76    #[arg(long = "mode", value_enum, default_value = "default")]
77    pub mode: FlowAddStepMode,
78    /// Component id (default mode).
79    #[arg(long = "component")]
80    pub component_id: Option<String>,
81    /// Optional pack alias for the new node.
82    #[arg(long = "pack-alias")]
83    pub pack_alias: Option<String>,
84    /// Optional operation for the new node.
85    #[arg(long = "operation")]
86    pub operation: Option<String>,
87    /// Payload JSON for the new node (default mode).
88    #[arg(long = "payload", default_value = "{}")]
89    pub payload: String,
90    /// Optional routing JSON for the new node (default mode).
91    #[arg(long = "routing")]
92    pub routing: Option<String>,
93    /// Config flow file to execute (config mode).
94    #[arg(long = "config-flow")]
95    pub config_flow: Option<PathBuf>,
96    /// Answers JSON for config mode.
97    #[arg(long = "answers")]
98    pub answers: Option<String>,
99    /// Answers file (JSON) for config mode.
100    #[arg(long = "answers-file")]
101    pub answers_file: Option<PathBuf>,
102    /// Allow cycles/back-edges during insertion.
103    #[arg(long = "allow-cycles")]
104    pub allow_cycles: bool,
105    /// Write back to the flow file instead of stdout.
106    #[arg(long = "write")]
107    pub write: bool,
108    /// Validate only without writing output.
109    #[arg(long = "validate-only")]
110    pub validate_only: bool,
111    /// Optional component manifest paths for catalog validation.
112    #[arg(long = "manifest")]
113    pub manifests: Vec<PathBuf>,
114    /// Optional explicit node id hint.
115    #[arg(long = "node-id")]
116    pub node_id: Option<String>,
117    /// Verbose passthrough logging.
118    #[arg(long = "verbose")]
119    pub verbose: bool,
120}
121
122#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)]
123pub enum FlowAddStepMode {
124    Default,
125    Config,
126}
127
128#[derive(Subcommand, Debug)]
129pub enum GuiCommand {
130    /// Serve GUI packs locally via greentic-gui
131    Serve(GuiServeArgs),
132    /// Stage a local GUI pack from static assets
133    PackDev(GuiPackDevArgs),
134}
135
136#[derive(Args, Debug)]
137pub struct GuiServeArgs {
138    /// Path to gui-dev.yaml (defaults to discovery order)
139    #[arg(long = "config")]
140    pub config: Option<PathBuf>,
141    /// Address to bind (default: 127.0.0.1:8080)
142    #[arg(long = "bind")]
143    pub bind: Option<String>,
144    /// Domain reported to greentic-gui (default: localhost:8080)
145    #[arg(long = "domain")]
146    pub domain: Option<String>,
147    /// Override greentic-gui binary path (otherwise PATH is used)
148    #[arg(long = "gui-bin")]
149    pub gui_bin: Option<PathBuf>,
150    /// Disable cargo fallback when greentic-gui binary is missing
151    #[arg(long = "no-cargo-fallback")]
152    pub no_cargo_fallback: bool,
153    /// Open a browser after the server starts
154    #[arg(long = "open-browser")]
155    pub open_browser: bool,
156}
157
158#[derive(Args, Debug, Clone)]
159pub struct GuiPackDevArgs {
160    /// Directory containing built/static assets to stage
161    #[arg(long = "dir")]
162    pub dir: PathBuf,
163    /// Output directory for the staged pack (must be empty or absent)
164    #[arg(long = "output")]
165    pub output: PathBuf,
166    /// Kind of GUI pack to generate (controls manifest shape)
167    #[arg(long = "kind", value_enum, default_value = "layout")]
168    pub kind: GuiPackKind,
169    /// Entrypoint HTML file (relative to assets) for layout/feature manifests
170    #[arg(long = "entrypoint", default_value = "index.html")]
171    pub entrypoint: String,
172    /// Optional manifest to copy instead of generating one
173    #[arg(long = "manifest")]
174    pub manifest: Option<PathBuf>,
175    /// Feature route (only used when kind=feature)
176    #[arg(long = "feature-route")]
177    pub feature_route: Option<String>,
178    /// Feature HTML file (relative to assets; kind=feature)
179    #[arg(long = "feature-html", default_value = "index.html")]
180    pub feature_html: String,
181    /// Mark the feature route as authenticated (kind=feature)
182    #[arg(long = "feature-authenticated")]
183    pub feature_authenticated: bool,
184    /// Optional build command to run before staging
185    #[arg(long = "build-cmd")]
186    pub build_cmd: Option<String>,
187    /// Skip running the build command even if provided
188    #[arg(long = "no-build")]
189    pub no_build: bool,
190}
191
192#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
193pub enum GuiPackKind {
194    Layout,
195    Feature,
196}
197
198#[derive(Subcommand, Debug)]
199pub enum PackCommand {
200    /// Delegate to greentic-pack build
201    Build(PackcArgs),
202    /// Delegate to greentic-pack lint
203    Lint(PackcArgs),
204    /// Delegate to greentic-pack components (sync pack.yaml from components/)
205    Components(PackcArgs),
206    /// Delegate to greentic-pack update (sync pack.yaml components + flows)
207    Update(PackcArgs),
208    /// Delegate to greentic-pack new
209    New(PackcArgs),
210    /// Delegate to greentic-pack sign
211    Sign(PackcArgs),
212    /// Delegate to greentic-pack verify
213    Verify(PackcArgs),
214    /// Delegate to greentic-pack gui helpers
215    Gui(PackcArgs),
216    /// Inspect a .gtpack (or directory via temporary build)
217    Inspect(PackInspectArgs),
218    /// Generate a deployment plan
219    Plan(PackPlanArgs),
220    /// Events helpers (legacy)
221    #[command(subcommand)]
222    Events(PackEventsCommand),
223    /// Delegate to greentic-pack config (resolve config with provenance)
224    Config(PackcArgs),
225    /// Execute a pack locally with mocks/telemetry support
226    Run(PackRunArgs),
227    /// Initialize a pack workspace from a remote coordinate
228    Init(PackInitArgs),
229    /// Register or update a provider declaration in the pack manifest extension
230    NewProvider(PackNewProviderArgs),
231}
232
233#[derive(Args, Debug)]
234pub struct PackRunArgs {
235    /// Path to the pack (.gtpack) to execute
236    #[arg(short = 'p', long = "pack")]
237    pub pack: PathBuf,
238    /// Flow entry identifier override
239    #[arg(long = "entry")]
240    pub entry: Option<String>,
241    /// JSON payload to use as run input
242    #[arg(long = "input")]
243    pub input: Option<String>,
244    /// Emit JSON output
245    #[arg(long = "json")]
246    pub json: bool,
247    /// Offline mode (disable network/proxy)
248    #[arg(long = "offline")]
249    pub offline: bool,
250    /// Use mock executor (internal/testing)
251    #[arg(long = "mock-exec", hide = true)]
252    pub mock_exec: bool,
253    /// Allow external calls in mock executor (default: false)
254    #[arg(long = "allow-external", hide = true)]
255    pub allow_external: bool,
256    /// Return mocked external responses when external calls are allowed (mock exec only)
257    #[arg(long = "mock-external", hide = true)]
258    pub mock_external: bool,
259    /// Path to JSON payload used for mocked external responses (mock exec only)
260    #[arg(long = "mock-external-payload", hide = true)]
261    pub mock_external_payload: Option<PathBuf>,
262    /// Secrets seed file applied to the mock secrets store (mock exec only)
263    #[arg(long = "secrets-seed", hide = true)]
264    pub secrets_seed: Option<PathBuf>,
265    /// Enforcement policy for pack signatures
266    #[arg(long = "policy", default_value = "devok", value_enum)]
267    pub policy: RunPolicyArg,
268    /// OTLP collector endpoint (optional)
269    #[arg(long = "otlp")]
270    pub otlp: Option<String>,
271    /// Comma-separated list of allowed outbound hosts
272    #[arg(long = "allow")]
273    pub allow: Option<String>,
274    /// Mocks toggle
275    #[arg(long = "mocks", default_value = "on", value_enum)]
276    pub mocks: MockSettingArg,
277    /// Directory to persist run artifacts (transcripts, logs)
278    #[arg(long = "artifacts")]
279    pub artifacts: Option<PathBuf>,
280}
281
282#[derive(Args, Debug)]
283pub struct PackInitArgs {
284    /// Remote pack coordinate (e.g. pack://org/name@1.0.0)
285    pub from: String,
286    /// Distributor profile to use (overrides GREENTIC_DISTRIBUTOR_PROFILE/env config)
287    #[arg(long = "profile")]
288    pub profile: Option<String>,
289}
290
291#[derive(Args, Debug)]
292pub struct PackNewProviderArgs {
293    /// Path to a pack source directory, manifest.cbor, or .gtpack archive
294    #[arg(long = "pack")]
295    pub pack: PathBuf,
296    /// Provider identifier (stored as provider_type)
297    #[arg(long = "id")]
298    pub id: String,
299    /// Runtime reference in the form component_ref::export@world
300    #[arg(long = "runtime")]
301    pub runtime: String,
302    /// Optional provider kind (stored in capabilities)
303    #[arg(long = "kind")]
304    pub kind: Option<String>,
305    /// Optional external manifest/config reference (relative path)
306    #[arg(long = "manifest")]
307    pub manifest: Option<PathBuf>,
308    /// When set, do not write changes to disk
309    #[arg(long = "dry-run")]
310    pub dry_run: bool,
311    /// Overwrite an existing provider with the same id
312    #[arg(long = "force")]
313    pub force: bool,
314    /// Emit JSON for the resulting provider declaration
315    #[arg(long = "json")]
316    pub json: bool,
317    /// Scaffold provider manifest files if supported
318    #[arg(long = "scaffold-files")]
319    pub scaffold_files: bool,
320}
321
322#[derive(Args, Debug, Clone, Default)]
323#[command(disable_help_flag = true)]
324pub struct PackcArgs {
325    /// Arguments passed directly to the `greentic-pack` command
326    #[arg(
327        value_name = "ARGS",
328        trailing_var_arg = true,
329        allow_hyphen_values = true
330    )]
331    pub passthrough: Vec<String>,
332}
333
334#[derive(Args, Debug)]
335pub struct PackInspectArgs {
336    /// Path to the .gtpack file or pack directory
337    #[arg(value_name = "PATH")]
338    pub path: PathBuf,
339    /// Signature policy to enforce
340    #[arg(long, value_enum, default_value = "devok")]
341    pub policy: PackPolicyArg,
342    /// Emit JSON output
343    #[arg(long)]
344    pub json: bool,
345}
346
347#[derive(Subcommand, Debug)]
348pub enum PackEventsCommand {
349    /// List event providers declared in a pack
350    List(PackEventsListArgs),
351}
352
353#[derive(Args, Debug)]
354pub struct PackEventsListArgs {
355    /// Path to a .gtpack archive or pack source directory.
356    #[arg(value_name = "PATH")]
357    pub path: PathBuf,
358    /// Output format: table (default), json, yaml.
359    #[arg(long, value_enum, default_value = "table")]
360    pub format: PackEventsFormatArg,
361    /// When set, print additional diagnostics (for directory builds).
362    #[arg(long)]
363    pub verbose: bool,
364}
365
366#[derive(Args, Debug)]
367pub struct PackPlanArgs {
368    /// Path to a .gtpack archive or pack source directory.
369    #[arg(value_name = "PATH")]
370    pub input: PathBuf,
371    /// Tenant identifier to embed in the plan.
372    #[arg(long, default_value = "tenant-local")]
373    pub tenant: String,
374    /// Environment identifier to embed in the plan.
375    #[arg(long, default_value = "local")]
376    pub environment: String,
377    /// Emit compact JSON output instead of pretty-printing.
378    #[arg(long)]
379    pub json: bool,
380    /// When set, print additional diagnostics (for directory builds).
381    #[arg(long)]
382    pub verbose: bool,
383}
384
385#[derive(Subcommand, Debug, Clone)]
386pub enum ComponentCommand {
387    /// Add a remote component to the current workspace via the distributor
388    Add(ComponentAddArgs),
389    /// Scaffold a new Greentic component project
390    New(ComponentNewArgs),
391    /// List available component templates
392    Templates(ComponentTemplatesArgs),
393    /// Run component doctor checks
394    Doctor(ComponentDoctorArgs),
395    /// Inspect manifests and describe payloads
396    Inspect(ComponentInspectArgs),
397    /// Recompute manifest hashes
398    Hash(ComponentHashArgs),
399    /// Build component wasm + scaffold config flows
400    Build(ComponentBuildArgs),
401    /// Flow utilities (config flow scaffolding)
402    #[command(subcommand)]
403    Flow(ComponentFlowCommand),
404    /// Interact with the component store
405    #[command(subcommand)]
406    Store(ComponentStoreCommand),
407}
408
409#[derive(Args, Debug, Clone)]
410pub struct ComponentAddArgs {
411    /// Remote component coordinate (e.g. component://org/name@^1.0)
412    pub coordinate: String,
413    /// Distributor profile to use (overrides GREENTIC_DISTRIBUTOR_PROFILE/env config)
414    #[arg(long = "profile")]
415    pub profile: Option<String>,
416    /// Resolution intent (dev or runtime)
417    #[arg(long = "intent", default_value = "dev", value_enum)]
418    pub intent: DevIntentArg,
419}
420
421#[derive(Subcommand, Debug)]
422pub enum McpCommand {
423    /// Inspect MCP provider metadata
424    Doctor(McpDoctorArgs),
425}
426
427#[derive(Args, Debug)]
428pub struct McpDoctorArgs {
429    /// MCP provider identifier or config path
430    pub provider: String,
431    /// Emit compact JSON instead of pretty output
432    #[arg(long = "json")]
433    pub json: bool,
434}
435
436#[derive(Subcommand, Debug)]
437pub enum ConfigCommand {
438    /// Set a key in greentic-dev config (e.g. defaults.component.org)
439    Set(ConfigSetArgs),
440}
441
442#[derive(Args, Debug)]
443pub struct ConfigSetArgs {
444    /// Config key path (e.g. defaults.component.org)
445    pub key: String,
446    /// Value to assign to the key (stored as a string)
447    pub value: String,
448    /// Override config file path (default: $XDG_CONFIG_HOME/greentic-dev/config.toml)
449    #[arg(long = "file")]
450    pub file: Option<PathBuf>,
451}
452
453#[derive(Copy, Clone, Debug, ValueEnum)]
454pub enum PackSignArg {
455    Dev,
456    None,
457}
458
459#[derive(Copy, Clone, Debug, ValueEnum)]
460pub enum PackPolicyArg {
461    Devok,
462    Strict,
463}
464
465#[derive(Copy, Clone, Debug, ValueEnum)]
466pub enum RunPolicyArg {
467    Strict,
468    Devok,
469}
470
471#[derive(Copy, Clone, Debug, ValueEnum)]
472pub enum VerifyPolicyArg {
473    Strict,
474    Devok,
475}
476
477#[derive(Copy, Clone, Debug, ValueEnum)]
478pub enum MockSettingArg {
479    On,
480    Off,
481}
482
483#[derive(Copy, Clone, Debug, ValueEnum)]
484pub enum PackEventsFormatArg {
485    Table,
486    Json,
487    Yaml,
488}
489
490#[derive(Copy, Clone, Debug, ValueEnum)]
491pub enum ConfigFlowModeArg {
492    Default,
493    Custom,
494}
495#[derive(Copy, Clone, Debug, ValueEnum)]
496pub enum DevIntentArg {
497    Dev,
498    Runtime,
499}
500
501#[cfg(test)]
502mod tests {}