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