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