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/providers)
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 components (sync pack.yaml from components/)
166    Components(PackcArgs),
167    /// Delegate to packc update (sync pack.yaml components + flows)
168    Update(PackcArgs),
169    /// Delegate to packc new
170    New(PackcArgs),
171    /// Delegate to packc sign
172    Sign(PackcArgs),
173    /// Delegate to packc verify
174    Verify(PackcArgs),
175    /// Delegate to packc gui helpers
176    Gui(PackcArgs),
177    /// Inspect a .gtpack (or directory via temporary build)
178    Inspect(PackInspectArgs),
179    /// Generate a deployment plan
180    Plan(PackPlanArgs),
181    /// Events helpers (legacy)
182    #[command(subcommand)]
183    Events(PackEventsCommand),
184    /// Delegate to packc config (resolve config with provenance)
185    Config(PackcArgs),
186    /// Execute a pack locally with mocks/telemetry support
187    Run(PackRunArgs),
188    /// Initialize a pack workspace from a remote coordinate
189    Init(PackInitArgs),
190    /// Register or update a provider declaration in the pack manifest extension
191    NewProvider(PackNewProviderArgs),
192}
193
194#[derive(Args, Debug)]
195pub struct PackRunArgs {
196    /// Path to the pack (.gtpack) to execute
197    #[arg(short = 'p', long = "pack")]
198    pub pack: PathBuf,
199    /// Flow entry identifier override
200    #[arg(long = "entry")]
201    pub entry: Option<String>,
202    /// JSON payload to use as run input
203    #[arg(long = "input")]
204    pub input: Option<String>,
205    /// Emit JSON output
206    #[arg(long = "json")]
207    pub json: bool,
208    /// Offline mode (disable network/proxy)
209    #[arg(long = "offline")]
210    pub offline: bool,
211    /// Use mock executor (internal/testing)
212    #[arg(long = "mock-exec", hide = true)]
213    pub mock_exec: bool,
214    /// Allow external calls in mock executor (default: false)
215    #[arg(long = "allow-external", hide = true)]
216    pub allow_external: bool,
217    /// Return mocked external responses when external calls are allowed (mock exec only)
218    #[arg(long = "mock-external", hide = true)]
219    pub mock_external: bool,
220    /// Path to JSON payload used for mocked external responses (mock exec only)
221    #[arg(long = "mock-external-payload", hide = true)]
222    pub mock_external_payload: Option<PathBuf>,
223    /// Secrets seed file applied to the mock secrets store (mock exec only)
224    #[arg(long = "secrets-seed", hide = true)]
225    pub secrets_seed: Option<PathBuf>,
226    /// Enforcement policy for pack signatures
227    #[arg(long = "policy", default_value = "devok", value_enum)]
228    pub policy: RunPolicyArg,
229    /// OTLP collector endpoint (optional)
230    #[arg(long = "otlp")]
231    pub otlp: Option<String>,
232    /// Comma-separated list of allowed outbound hosts
233    #[arg(long = "allow")]
234    pub allow: Option<String>,
235    /// Mocks toggle
236    #[arg(long = "mocks", default_value = "on", value_enum)]
237    pub mocks: MockSettingArg,
238    /// Directory to persist run artifacts (transcripts, logs)
239    #[arg(long = "artifacts")]
240    pub artifacts: Option<PathBuf>,
241}
242
243#[derive(Args, Debug)]
244pub struct PackInitArgs {
245    /// Remote pack coordinate (e.g. pack://org/name@1.0.0)
246    pub from: String,
247    /// Distributor profile to use (overrides GREENTIC_DISTRIBUTOR_PROFILE/env config)
248    #[arg(long = "profile")]
249    pub profile: Option<String>,
250}
251
252#[derive(Args, Debug)]
253pub struct PackNewProviderArgs {
254    /// Path to a pack source directory, manifest.cbor, or .gtpack archive
255    #[arg(long = "pack")]
256    pub pack: PathBuf,
257    /// Provider identifier (stored as provider_type)
258    #[arg(long = "id")]
259    pub id: String,
260    /// Runtime reference in the form component_ref::export@world
261    #[arg(long = "runtime")]
262    pub runtime: String,
263    /// Optional provider kind (stored in capabilities)
264    #[arg(long = "kind")]
265    pub kind: Option<String>,
266    /// Optional external manifest/config reference (relative path)
267    #[arg(long = "manifest")]
268    pub manifest: Option<PathBuf>,
269    /// When set, do not write changes to disk
270    #[arg(long = "dry-run")]
271    pub dry_run: bool,
272    /// Overwrite an existing provider with the same id
273    #[arg(long = "force")]
274    pub force: bool,
275    /// Emit JSON for the resulting provider declaration
276    #[arg(long = "json")]
277    pub json: bool,
278    /// Scaffold provider manifest files if supported
279    #[arg(long = "scaffold-files")]
280    pub scaffold_files: bool,
281}
282
283#[derive(Args, Debug, Clone, Default)]
284#[command(disable_help_flag = true)]
285pub struct PackcArgs {
286    /// Arguments passed directly to the `packc` command
287    #[arg(
288        value_name = "ARGS",
289        trailing_var_arg = true,
290        allow_hyphen_values = true
291    )]
292    pub passthrough: Vec<String>,
293}
294
295#[derive(Args, Debug)]
296pub struct PackInspectArgs {
297    /// Path to the .gtpack file or pack directory
298    #[arg(value_name = "PATH")]
299    pub path: PathBuf,
300    /// Signature policy to enforce
301    #[arg(long, value_enum, default_value = "devok")]
302    pub policy: PackPolicyArg,
303    /// Emit JSON output
304    #[arg(long)]
305    pub json: bool,
306}
307
308#[derive(Subcommand, Debug)]
309pub enum PackEventsCommand {
310    /// List event providers declared in a pack
311    List(PackEventsListArgs),
312}
313
314#[derive(Args, Debug)]
315pub struct PackEventsListArgs {
316    /// Path to a .gtpack archive or pack source directory.
317    #[arg(value_name = "PATH")]
318    pub path: PathBuf,
319    /// Output format: table (default), json, yaml.
320    #[arg(long, value_enum, default_value = "table")]
321    pub format: PackEventsFormatArg,
322    /// When set, print additional diagnostics (for directory builds).
323    #[arg(long)]
324    pub verbose: bool,
325}
326
327#[derive(Args, Debug)]
328pub struct PackPlanArgs {
329    /// Path to a .gtpack archive or pack source directory.
330    #[arg(value_name = "PATH")]
331    pub input: PathBuf,
332    /// Tenant identifier to embed in the plan.
333    #[arg(long, default_value = "tenant-local")]
334    pub tenant: String,
335    /// Environment identifier to embed in the plan.
336    #[arg(long, default_value = "local")]
337    pub environment: String,
338    /// Emit compact JSON output instead of pretty-printing.
339    #[arg(long)]
340    pub json: bool,
341    /// When set, print additional diagnostics (for directory builds).
342    #[arg(long)]
343    pub verbose: bool,
344}
345
346#[derive(Subcommand, Debug, Clone)]
347pub enum ComponentCommand {
348    /// Add a remote component to the current workspace via the distributor
349    Add(ComponentAddArgs),
350    /// Scaffold a new Greentic component project
351    New(ComponentNewArgs),
352    /// List available component templates
353    Templates(ComponentTemplatesArgs),
354    /// Run component doctor checks
355    Doctor(ComponentDoctorArgs),
356    /// Inspect manifests and describe payloads
357    Inspect(ComponentInspectArgs),
358    /// Recompute manifest hashes
359    Hash(ComponentHashArgs),
360    /// Build component wasm + scaffold config flows
361    Build(ComponentBuildArgs),
362    /// Flow utilities (config flow scaffolding)
363    #[command(subcommand)]
364    Flow(ComponentFlowCommand),
365    /// Interact with the component store
366    #[command(subcommand)]
367    Store(ComponentStoreCommand),
368}
369
370#[derive(Args, Debug, Clone)]
371pub struct ComponentAddArgs {
372    /// Remote component coordinate (e.g. component://org/name@^1.0)
373    pub coordinate: String,
374    /// Distributor profile to use (overrides GREENTIC_DISTRIBUTOR_PROFILE/env config)
375    #[arg(long = "profile")]
376    pub profile: Option<String>,
377    /// Resolution intent (dev or runtime)
378    #[arg(long = "intent", default_value = "dev", value_enum)]
379    pub intent: DevIntentArg,
380}
381
382#[derive(Subcommand, Debug)]
383pub enum McpCommand {
384    /// Inspect MCP provider metadata
385    Doctor(McpDoctorArgs),
386}
387
388#[derive(Args, Debug)]
389pub struct McpDoctorArgs {
390    /// MCP provider identifier or config path
391    pub provider: String,
392    /// Emit compact JSON instead of pretty output
393    #[arg(long = "json")]
394    pub json: bool,
395}
396
397#[derive(Subcommand, Debug)]
398pub enum ConfigCommand {
399    /// Set a key in greentic-dev config (e.g. defaults.component.org)
400    Set(ConfigSetArgs),
401}
402
403#[derive(Args, Debug)]
404pub struct ConfigSetArgs {
405    /// Config key path (e.g. defaults.component.org)
406    pub key: String,
407    /// Value to assign to the key (stored as a string)
408    pub value: String,
409    /// Override config file path (default: $XDG_CONFIG_HOME/greentic-dev/config.toml)
410    #[arg(long = "file")]
411    pub file: Option<PathBuf>,
412}
413
414#[derive(Copy, Clone, Debug, ValueEnum)]
415pub enum PackSignArg {
416    Dev,
417    None,
418}
419
420#[derive(Copy, Clone, Debug, ValueEnum)]
421pub enum PackPolicyArg {
422    Devok,
423    Strict,
424}
425
426#[derive(Copy, Clone, Debug, ValueEnum)]
427pub enum RunPolicyArg {
428    Strict,
429    Devok,
430}
431
432#[derive(Copy, Clone, Debug, ValueEnum)]
433pub enum VerifyPolicyArg {
434    Strict,
435    Devok,
436}
437
438#[derive(Copy, Clone, Debug, ValueEnum)]
439pub enum MockSettingArg {
440    On,
441    Off,
442}
443
444#[derive(Copy, Clone, Debug, ValueEnum)]
445pub enum PackEventsFormatArg {
446    Table,
447    Json,
448    Yaml,
449}
450
451#[derive(Copy, Clone, Debug, ValueEnum)]
452pub enum ConfigFlowModeArg {
453    Default,
454    Custom,
455}
456#[derive(Copy, Clone, Debug, ValueEnum)]
457pub enum DevIntentArg {
458    Dev,
459    Runtime,
460}
461
462#[cfg(test)]
463mod tests {}