---
phase: 27-global-flag-infrastructure
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - cargo-pmcp/src/main.rs
  - cargo-pmcp/src/commands/loadtest/mod.rs
  - cargo-pmcp/src/commands/loadtest/run.rs
  - cargo-pmcp/src/loadtest/display.rs
  - cargo-pmcp/src/loadtest/summary.rs
  - cargo-pmcp/src/commands/test/mod.rs
  - cargo-pmcp/src/commands/test/check.rs
  - cargo-pmcp/src/commands/test/run.rs
  - cargo-pmcp/src/commands/test/generate.rs
  - cargo-pmcp/src/commands/test/list.rs
  - cargo-pmcp/src/commands/test/download.rs
  - cargo-pmcp/src/commands/test/upload.rs
  - cargo-pmcp/src/commands/deploy/mod.rs
  - cargo-pmcp/src/commands/deploy/deploy.rs
  - cargo-pmcp/src/commands/secret/mod.rs
  - cargo-pmcp/src/commands/schema.rs
  - cargo-pmcp/src/commands/validate.rs
  - cargo-pmcp/src/commands/preview.rs
  - cargo-pmcp/src/commands/app.rs
  - cargo-pmcp/src/commands/connect.rs
  - cargo-pmcp/src/commands/dev.rs
  - cargo-pmcp/src/commands/new.rs
  - cargo-pmcp/src/commands/add.rs
  - cargo-pmcp/src/commands/landing/mod.rs
autonomous: true
requirements:
  - FLAG-08
must_haves:
  truths:
    - "User can pass --no-color to any cargo pmcp command and all terminal output is plain text (no ANSI escape codes)"
    - "Both --no-color and --quiet are accepted as global flags (before or after subcommand)"
    - "NO_COLOR environment variable automatically disables color without passing --no-color"
    - "Piped output (non-TTY stdout) automatically disables color"
    - "Loadtest run command no longer has its own local --no-color flag (removed in favor of the global one)"
  artifacts:
    - path: "cargo-pmcp/src/main.rs"
      provides: "GlobalFlags struct definition, CLI args for --no-color and --quiet, pre-dispatch color suppression, GlobalFlags passed to all command handlers"
      contains: "struct GlobalFlags"
    - path: "cargo-pmcp/src/commands/loadtest/mod.rs"
      provides: "Loadtest command without local --no-color flag"
  key_links:
    - from: "cargo-pmcp/src/main.rs (Cli struct)"
      to: "cargo-pmcp/src/main.rs (execute_command)"
      via: "GlobalFlags constructed from Cli fields, passed as parameter"
      pattern: "GlobalFlags"
    - from: "cargo-pmcp/src/main.rs (execute_command)"
      to: "all command handler functions"
      via: "GlobalFlags parameter added to every handler call"
      pattern: "global_flags"
    - from: "cargo-pmcp/src/main.rs (pre-dispatch)"
      to: "colored::control::set_override"
      via: "Called before execute_command when no_color, NO_COLOR, or non-TTY"
      pattern: "set_override"
---

<objective>
Define the `GlobalFlags` struct, add `--no-color` and `--quiet` as global CLI flags, wire `GlobalFlags` through all command dispatch paths, and implement `--no-color` color suppression globally.

Purpose: Establish the global flag infrastructure that all subsequent Phase 27-32 work builds on. This plan handles the mechanical wiring (every handler gets `GlobalFlags`) and the `--no-color` behavior (color suppression across `colored`, `console`, and `indicatif` crates).

Output: Every `cargo pmcp` command accepts `--no-color` globally, color is suppressed when the flag is set, the `NO_COLOR` env var is set, or stdout is not a TTY. The loadtest-local `--no-color` flag is removed. `--quiet` is accepted but not yet behaviorally implemented (that is Plan 02).
</objective>

<execution_context>
@./.claude/get-shit-done/workflows/execute-plan.md
@./.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/27-global-flag-infrastructure/27-CONTEXT.md
@cargo-pmcp/src/main.rs
@cargo-pmcp/src/commands/mod.rs
@cargo-pmcp/src/commands/loadtest/mod.rs
@cargo-pmcp/src/commands/loadtest/run.rs
@cargo-pmcp/src/loadtest/display.rs
@cargo-pmcp/src/loadtest/summary.rs

<interfaces>
<!-- Current Cli struct and execute_command in main.rs -->
<!-- Executor must understand the current dispatch pattern to wire GlobalFlags -->

From cargo-pmcp/src/main.rs:
```rust
struct Cli {
    #[arg(long, short, global = true)]
    verbose: bool,
    #[command(subcommand)]
    command: Commands,
}

fn execute_command(command: Commands) -> Result<()> {
    match command {
        Commands::New { name, path } => commands::new::execute(name, path, None)?,
        // ... each arm calls handler functions with destructured fields
    }
}
```

From cargo-pmcp/src/commands/loadtest/mod.rs:
```rust
pub enum LoadtestCommand {
    Run {
        // ... includes `no_color: bool` as a LOCAL flag — MUST be removed
    },
}
impl LoadtestCommand {
    pub fn execute(self) -> Result<()> { ... }
}
```

From cargo-pmcp/src/loadtest/display.rs:
```rust
impl LiveDisplay {
    pub fn new(no_color: bool) -> Self {
        // Sets colored::control::set_override(false) locally
    }
}
```

Color crate usage across codebase:
- `colored` (3.x): 22+ files — use `colored::control::set_override(false)` to suppress
- `console` (0.16): 2 files (validate.rs, schema.rs) — use `console::set_colors_enabled(false)` to suppress
- `indicatif` (0.18): 1 file (display.rs) — respects colored's override; use `ProgressBar::set_draw_target(ProgressDrawTarget::hidden())` for no-color or use plain style
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Define GlobalFlags struct and wire through all command dispatch</name>
  <files>cargo-pmcp/src/main.rs, cargo-pmcp/src/commands/loadtest/mod.rs, cargo-pmcp/src/commands/loadtest/run.rs, cargo-pmcp/src/commands/test/mod.rs, cargo-pmcp/src/commands/test/check.rs, cargo-pmcp/src/commands/test/run.rs, cargo-pmcp/src/commands/test/generate.rs, cargo-pmcp/src/commands/test/list.rs, cargo-pmcp/src/commands/test/download.rs, cargo-pmcp/src/commands/test/upload.rs, cargo-pmcp/src/commands/deploy/mod.rs, cargo-pmcp/src/commands/deploy/deploy.rs, cargo-pmcp/src/commands/secret/mod.rs, cargo-pmcp/src/commands/schema.rs, cargo-pmcp/src/commands/validate.rs, cargo-pmcp/src/commands/preview.rs, cargo-pmcp/src/commands/app.rs, cargo-pmcp/src/commands/connect.rs, cargo-pmcp/src/commands/dev.rs, cargo-pmcp/src/commands/new.rs, cargo-pmcp/src/commands/add.rs, cargo-pmcp/src/commands/landing/mod.rs</files>
  <action>
  1. In `cargo-pmcp/src/main.rs`:

     a. Define a `GlobalFlags` struct (derive Clone, Debug) with three pub fields: `verbose: bool`, `no_color: bool`, `quiet: bool`. Place it near the top of the file, after the mod declarations.

     b. Add `--no-color` and `--quiet` as global args to the `Cli` struct:
        ```rust
        /// Suppress colored output
        #[arg(long, global = true)]
        no_color: bool,

        /// Suppress all non-error output
        #[arg(long, global = true)]
        quiet: bool,
        ```

     c. In `main()`, after parsing the CLI, construct `GlobalFlags` from the three Cli fields (verbose, no_color, quiet). Also set env vars for subprocess consumption: `PMCP_NO_COLOR=1` if no_color, `PMCP_QUIET=1` if quiet (keep existing `PMCP_VERBOSE=1` for verbose).

     d. Change `execute_command` signature from `fn execute_command(command: Commands) -> Result<()>` to `fn execute_command(command: Commands, global_flags: &GlobalFlags) -> Result<()>`.

     e. In every `Commands::*` match arm, pass `global_flags` (or relevant fields) to the handler. For handlers that are methods on command structs (Test, Deploy, Secret, Loadtest, Schema, Validate, App, Landing), add `global_flags: &GlobalFlags` parameter to their `execute()` methods. For handlers called as free functions (new, add, dev, connect, preview), add `_global_flags: &GlobalFlags` parameter (underscore-prefixed since quiet behavior comes in Plan 02).

  2. For each subcommand module, update the `execute()` method signature to accept `global_flags: &GlobalFlags`:

     - `commands::test::TestCommand::execute()` — receives and passes down to sub-handlers (check, run, generate, list, download, upload). Each sub-handler function adds `_global_flags: &GlobalFlags` parameter.
     - `commands::deploy::DeployCommand::execute()` — passes down.
     - `commands::secret::SecretCommand::execute()` — passes down.
     - `commands::loadtest::LoadtestCommand::execute()` — passes down. **IMPORTANT:** Remove the `no_color: bool` field from `LoadtestCommand::Run` variant. Instead, `execute_run()` receives `global_flags.no_color` from the GlobalFlags.
     - `commands::schema::SchemaCommand::execute()` — passes down.
     - `commands::validate::ValidateCommand::execute()` — passes down.
     - `commands::app::AppCommand::execute()` — passes down.
     - `commands::landing::LandingCommand::execute()` — passes down (note: it also takes `project_root`).
     - Free functions: `commands::new::execute()`, `commands::add::server/tool/workflow()`, `commands::dev::execute()`, `commands::connect::execute()`, `commands::preview::execute()` — add `_global_flags: &GlobalFlags` parameter.

  3. Import `GlobalFlags` in each command module. Since `GlobalFlags` is defined in `main.rs`, either:
     - Move it to a small `cli.rs` module and re-export from `main.rs` (Claude's discretion per CONTEXT.md), OR
     - Define it in `commands/mod.rs` so subcommand modules can use it without circular imports (PREFERRED — keeps it in the commands module tree).

     The recommended approach: define `GlobalFlags` in `cargo-pmcp/src/commands/mod.rs` and import it in `main.rs` via `use commands::GlobalFlags;`. This avoids circular dependency issues.

  4. In `cargo-pmcp/src/commands/loadtest/run.rs`: Remove the `no_color: bool` parameter from `execute_run()`. Replace references to local `no_color` with the value from the global flags parameter. Update the call to `LoadTestEngine::with_no_color()` and `LiveDisplay::new()` accordingly.

  5. In `cargo-pmcp/src/commands/loadtest/mod.rs`: Remove the `no_color` field and `#[arg(long)]` from `LoadtestCommand::Run`. Update the match arm in `execute()` to no longer destructure `no_color` — instead pass `global_flags.no_color` to `execute_run`.

  The key principle: this task does the MECHANICAL wiring. Every handler gets the GlobalFlags parameter. Handlers that don't use it yet prefix with underscore. Plan 02 will activate the quiet behavior.
  </action>
  <verify>
    <automated>cd /Users/guy/Development/mcp/sdk/rust-mcp-sdk && cargo check -p cargo-pmcp 2>&1 | tail -20</automated>
  </verify>
  <done>
  - `GlobalFlags` struct defined in `commands/mod.rs` with `verbose`, `no_color`, `quiet` fields
  - `--no-color` and `--quiet` accepted as global flags on the `Cli` struct
  - `execute_command()` takes `&GlobalFlags` parameter
  - Every command handler receives `&GlobalFlags` (or `_global_flags` if not yet used)
  - Loadtest `Run` variant no longer has a local `--no-color` field
  - `cargo check -p cargo-pmcp` passes with zero errors
  </done>
</task>

<task type="auto">
  <name>Task 2: Implement global color suppression (--no-color, NO_COLOR env, TTY auto-detect)</name>
  <files>cargo-pmcp/src/main.rs, cargo-pmcp/src/commands/loadtest/run.rs, cargo-pmcp/src/loadtest/display.rs, cargo-pmcp/src/loadtest/summary.rs, cargo-pmcp/src/commands/schema.rs, cargo-pmcp/src/commands/validate.rs</files>
  <action>
  1. In `cargo-pmcp/src/main.rs`, in `main()` BEFORE calling `execute_command()`, add global color suppression logic:

     ```rust
     // Determine effective no_color: explicit flag, NO_COLOR env (no-color.org), or non-TTY
     let effective_no_color = global_flags.no_color
         || std::env::var("NO_COLOR").is_ok()
         || !std::io::stdout().is_terminal();

     if effective_no_color {
         // Suppress colored crate output globally
         colored::control::set_override(false);
         // Suppress console crate output globally
         console::set_colors_enabled(false);
         console::set_colors_enabled_stderr(false);
     }
     ```

     Add `use std::io::IsTerminal;` at the top of main.rs.

     Update the `GlobalFlags` construction to use `effective_no_color` for the `no_color` field (so downstream code sees the resolved value, not just the CLI flag):
     ```rust
     let global_flags = GlobalFlags {
         verbose: cli.verbose,
         no_color: effective_no_color,
         quiet: cli.quiet,
     };
     ```

  2. In `cargo-pmcp/src/commands/loadtest/run.rs`: Remove the local color suppression block (lines ~97-100 that call `colored::control::set_override(false)` and check `is_terminal()`). This is now handled globally in `main()`. Keep the `render_summary` and `println!` calls as-is — they will automatically respect the global override.

  3. In `cargo-pmcp/src/loadtest/display.rs`: Remove the local `colored::control::set_override(false)` call in `LiveDisplay::new()`. Remove the `std::io::IsTerminal` import if no longer needed. The color override is now set globally before any command executes. Keep the `_no_color` field if it's used for indicatif draw target decisions (hiding progress bars when no color).

  4. In `cargo-pmcp/src/loadtest/summary.rs`: Remove the `colored::control::set_override(false)` in tests if present — the global override handles this. Keep the `StripAnsi` wrapper since it's useful for test assertions.

  5. In `cargo-pmcp/src/commands/schema.rs` and `cargo-pmcp/src/commands/validate.rs`: These use `console::style()`. The global `console::set_colors_enabled(false)` call in main() handles suppression. No changes needed to these files beyond confirming they work (the global call covers them).

  6. Verify `NO_COLOR=1 cargo pmcp --help` produces no ANSI escape codes. Verify `--no-color` flag is accepted globally.
  </action>
  <verify>
    <automated>cd /Users/guy/Development/mcp/sdk/rust-mcp-sdk && cargo check -p cargo-pmcp 2>&1 | tail -5 && cargo test -p cargo-pmcp 2>&1 | tail -10</automated>
  </verify>
  <done>
  - Global color suppression fires in main() before any command executes
  - `colored::control::set_override(false)` called once globally (not per-command)
  - `console::set_colors_enabled(false)` called once globally
  - `NO_COLOR` environment variable honored automatically
  - Non-TTY stdout auto-disables color
  - Loadtest display.rs and run.rs no longer have local color suppression (replaced by global)
  - All existing tests pass
  </done>
</task>

</tasks>

<verification>
1. `cargo check -p cargo-pmcp` passes with zero errors and zero clippy warnings
2. `cargo test -p cargo-pmcp` passes — no regressions from signature changes
3. `cargo pmcp --help` shows `--no-color` and `--quiet` as global options
4. `cargo pmcp loadtest run --help` does NOT show a local `--no-color` flag
5. `NO_COLOR=1 cargo pmcp --help 2>&1 | grep -P '\x1b\['` produces no output (no ANSI codes)
6. `cargo pmcp --no-color --help 2>&1 | grep -P '\x1b\['` produces no output (no ANSI codes)
</verification>

<success_criteria>
- GlobalFlags struct is defined and wired through every command dispatch path
- `--no-color` flag works globally on any command
- `NO_COLOR` env var and non-TTY auto-detection work
- Loadtest no longer has its own local `--no-color` flag
- Zero clippy warnings, all tests pass
</success_criteria>

<output>
After completion, create `.planning/phases/27-global-flag-infrastructure/27-01-SUMMARY.md`
</output>
