---
phase: 06-make-secrets-specific-to-installed-claws-not-global
plan: 02
type: execute
wave: 2
depends_on:
  - 06-01
files_modified:
  - src/clawrium/cli/secret.py
  - tests/test_cli_secret.py
autonomous: true
requirements:
  - PSEC-02
  - PSEC-03

must_haves:
  truths:
    - "User sets secrets with clm secret set <clawname> KEY"
    - "User lists secrets grouped by claw with missing required secrets"
    - "Secrets can only be set for installed claws"
    - "Remove command requires claw name"
  artifacts:
    - path: "src/clawrium/cli/secret.py"
      provides: "CLI commands for per-claw secret management"
      exports: ["secret_app"]
    - path: "tests/test_cli_secret.py"
      provides: "Tests for per-claw CLI commands"
      contains: "test_secret_set_with_claw"
  key_links:
    - from: "src/clawrium/cli/secret.py"
      to: "src/clawrium/core/secrets.py"
      via: "set_instance_secret, get_instance_secrets"
      pattern: "set_instance_secret\\("
---

<objective>
Update CLI secret commands to use per-claw scoping with claw name as first positional argument.

Purpose: Users need to manage secrets per claw instance, not globally. Commands change to `clm secret set <clawname> KEY` and list shows secrets grouped by claw.

Output: Updated cli/secret.py with per-claw commands and tests.
</objective>

<execution_context>
@/home/devashish/workspace/ric03uec/clawrium/.claude/get-shit-done/workflows/execute-plan.md
@/home/devashish/workspace/ric03uec/clawrium/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/06-make-secrets-specific-to-installed-claws-not-global/06-CONTEXT.md
@.planning/phases/06-make-secrets-specific-to-installed-claws-not-global/06-01-SUMMARY.md

<interfaces>
<!-- From 06-01-PLAN, the new secrets module functions -->

From src/clawrium/core/secrets.py (after 06-01):
```python
def get_instance_key(host: str, claw_type: str, claw_name: str) -> str:
    """Returns 'host:claw_type:claw_name'"""

def get_installed_claw(claw_name: str) -> tuple[str, str, str]:
    """Returns (hostname, claw_type, claw_name) for installed claw.
    Raises ClawNotFoundError if claw not found."""

def get_instance_secrets(instance_key: str) -> dict[str, SecretEntry]: ...
def set_instance_secret(instance_key: str, key: str, value: str, description: str = "") -> bool: ...
def remove_instance_secret(instance_key: str, key: str) -> bool: ...
def list_instances_with_secrets() -> list[str]: ...

class ClawNotFoundError(Exception): ...
```

From src/clawrium/core/registry.py:
```python
def get_required_secrets(claw_name: str) -> list[SecretDefinition]:
    """Returns [{"key": "OPENAI_API_KEY", "description": "..."}]"""
```

From src/clawrium/core/hosts.py:
```python
def load_hosts() -> list[dict]:
    """Each host has 'claws' dict: {"openclaw": {"status": "installed", "user": "opc-work"}}"""
```

CLI signature per D-10:
- `clm secret set <clawname> KEY` - claw name first, then key
- `clm secret list` - shows all claws grouped with their secrets
- `clm secret remove <clawname> KEY` - remove specific key from claw
</interfaces>
</context>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: Update CLI set command for per-claw secrets</name>
  <files>src/clawrium/cli/secret.py, tests/test_cli_secret.py</files>
  <read_first>
    - src/clawrium/cli/secret.py (current CLI to modify)
    - src/clawrium/core/secrets.py (new instance functions from 06-01)
    - tests/test_cli_secret.py (existing test patterns)
    - tests/conftest.py (fixtures for hosts/config)
  </read_first>
  <behavior>
    - Test: `clm secret set opc-work OPENAI_API_KEY` prompts for value and stores for that claw
    - Test: `clm secret set nonexistent-claw KEY` shows error "Claw 'nonexistent-claw' not found"
    - Test: `clm secret set opc-work INVALID-KEY` shows key validation error
    - Test: Update with confirmation flow works for per-claw secrets
  </behavior>
  <action>
    1. Update imports in cli/secret.py:
       - Remove: get_secret, set_secret, remove_secret, list_secrets
       - Add: get_installed_claw, get_instance_key, get_instance_secrets, set_instance_secret, remove_instance_secret, list_instances_with_secrets, ClawNotFoundError

    2. Modify set_cmd signature per D-10:
       ```python
       @secret_app.command(name="set")
       def set_cmd(
           claw_name: str = typer.Argument(..., help="Claw name (e.g., opc-work)"),
           key: str = typer.Argument(..., help="Secret key name (e.g., OPENAI_API_KEY)"),
           description: Optional[str] = typer.Option(None, "--description", "-d", help="Description"),
           yes: bool = typer.Option(False, "--yes", "-y", help="Skip overwrite confirmation"),
       ) -> None:
       ```

    3. Update set_cmd implementation:
       - Validate claw exists using get_installed_claw(claw_name)
       - Catch ClawNotFoundError and show "[red]Error:[/red] Claw 'X' not found. Only installed claws can have secrets."
       - Get instance_key from get_instance_key(*get_installed_claw(claw_name))
       - Check existing secret with get_instance_secrets(instance_key).get(key)
       - Prompt for value with getpass (same pattern)
       - Call set_instance_secret(instance_key, key, value, description)
       - Show success message with claw name

    4. Update tests/test_cli_secret.py:
       - Update test fixtures to create a host with installed claw first
       - Add test_secret_set_with_claw: creates secret for installed claw
       - Add test_secret_set_claw_not_found: error when claw doesn't exist
       - Update existing tests to use new command signature
  </action>
  <verify>
    <automated>cd /home/devashish/workspace/ric03uec/clawrium && python -m pytest tests/test_cli_secret.py -v -k "set"</automated>
  </verify>
  <acceptance_criteria>
    - src/clawrium/cli/secret.py contains `claw_name: str = typer.Argument`
    - src/clawrium/cli/secret.py contains `get_installed_claw(claw_name)`
    - src/clawrium/cli/secret.py contains `set_instance_secret(`
    - tests/test_cli_secret.py contains `test_secret_set_with_claw`
    - tests/test_cli_secret.py contains `test_secret_set_claw_not_found`
    - `pytest tests/test_cli_secret.py -k "set"` exits with code 0
  </acceptance_criteria>
  <done>
    - set command takes claw_name as first positional argument
    - set validates claw exists before accepting secrets
    - set stores secret under instance key
    - Error shown for non-existent claw
    - Tests pass
  </done>
</task>

<task type="auto" tdd="true">
  <name>Task 2: Update CLI list and remove commands</name>
  <files>src/clawrium/cli/secret.py, tests/test_cli_secret.py</files>
  <read_first>
    - src/clawrium/cli/secret.py (after Task 1)
    - src/clawrium/core/secrets.py (instance functions)
    - src/clawrium/core/registry.py (get_required_secrets)
    - tests/test_cli_secret.py (after Task 1)
  </read_first>
  <behavior>
    - Test: `clm secret list` shows secrets grouped by claw instance
    - Test: `clm secret list` shows missing required secrets per claw
    - Test: `clm secret remove opc-work OPENAI_API_KEY` removes from that claw only
    - Test: `clm secret remove nonexistent-claw KEY` shows error
  </behavior>
  <action>
    1. Modify list_cmd to show per-claw grouping per D-12:
       ```python
       @secret_app.command(name="list")
       def list_cmd() -> None:
       ```
       - Load all hosts with load_hosts()
       - For each host, for each installed claw:
         - Get instance_key
         - Get secrets with get_instance_secrets(instance_key)
         - Get required_secrets from manifest with get_required_secrets(claw_type)
         - Create Rich table with claw name, host, secrets, and missing secrets
       - Output format:
         ```
         Claw: opc-work (wolf)
         +------------------+------------------+------------+
         | Key              | Description      | Updated    |
         +------------------+------------------+------------+
         | OPENAI_API_KEY   | OpenAI API key   | 2026-03-22 |
         +------------------+------------------+------------+
         Missing: ANTHROPIC_API_KEY (Anthropic API key for Claude models)
         ```
       - If no claws installed: "No claws installed. Run 'clm install' first."
       - If no secrets for any claw: show each claw with "No secrets set" and missing list

    2. Modify remove_cmd to require claw_name per D-10 pattern:
       ```python
       @secret_app.command(name="remove")
       def remove_cmd(
           claw_name: str = typer.Argument(..., help="Claw name"),
           key: str = typer.Argument(..., help="Secret key to remove"),
           force: bool = typer.Option(False, "--force", "-f", help="Skip confirmation"),
       ) -> None:
       ```
       - Validate claw exists using get_installed_claw(claw_name)
       - Get instance_key
       - Check secret exists with get_instance_secrets(instance_key).get(key)
       - Confirm and call remove_instance_secret(instance_key, key)

    3. Update tests/test_cli_secret.py:
       - Add test_secret_list_grouped_by_claw
       - Add test_secret_list_shows_missing_required
       - Add test_secret_list_no_claws_installed
       - Add test_secret_remove_with_claw
       - Add test_secret_remove_claw_not_found
       - Update existing remove tests for new signature
  </action>
  <verify>
    <automated>cd /home/devashish/workspace/ric03uec/clawrium && python -m pytest tests/test_cli_secret.py -v</automated>
  </verify>
  <acceptance_criteria>
    - src/clawrium/cli/secret.py list_cmd iterates over hosts and claws
    - src/clawrium/cli/secret.py list_cmd calls get_required_secrets
    - src/clawrium/cli/secret.py remove_cmd has claw_name as first argument
    - tests/test_cli_secret.py contains `test_secret_list_grouped_by_claw`
    - tests/test_cli_secret.py contains `test_secret_list_shows_missing_required`
    - tests/test_cli_secret.py contains `test_secret_remove_with_claw`
    - `pytest tests/test_cli_secret.py` exits with code 0
  </acceptance_criteria>
  <done>
    - list shows secrets grouped by claw with missing required secrets
    - remove requires claw_name as first argument
    - remove validates claw exists
    - All CLI secret tests pass
  </done>
</task>

</tasks>

<verification>
```bash
cd /home/devashish/workspace/ric03uec/clawrium
make test
make lint
```
</verification>

<success_criteria>
- `clm secret set <clawname> KEY` works with claw name first
- `clm secret list` shows secrets grouped by claw with missing required secrets
- `clm secret remove <clawname> KEY` removes from specific claw
- Secrets only settable for installed claws
- All tests pass
- Code passes linting
</success_criteria>

<output>
After completion, create `.planning/phases/06-make-secrets-specific-to-installed-claws-not-global/06-02-SUMMARY.md`
</output>
