Skip to content

settings: generate CLI settings from SDK schema#580

Open
neubig wants to merge 10 commits intomainfrom
openhands/issue-2228-cli-settings-schema
Open

settings: generate CLI settings from SDK schema#580
neubig wants to merge 10 commits intomainfrom
openhands/issue-2228-cli-settings-schema

Conversation

@neubig
Copy link
Contributor

@neubig neubig commented Mar 8, 2026

Summary

  • replace the CLI's settings definitions with schema-driven rendering from AgentSettings.export_schema()
  • generate slash commands and help text from the shared SDK settings metadata instead of CLI definitions
  • point pyproject.toml directly at the software-agent-sdk issue branch so the PR can consume the new schema before a release is cut
  • resolve follow-up review feedback by removing implicit API-key merge-on-save behavior, switching to direct SDK imports, and sharing argument-hint formatting across CLI/ACP settings commands

Dependencies

  • depends on the SDK schema changes in OpenHands/software-agent-sdk
  • installs openhands-sdk, openhands-tools, and openhands-workspace from OpenHands/software-agent-sdk@openhands/issue-2228-sdk-settings-schema

Testing

  • uv run pytest tests/shared/test_settings_commands.py tests/stores/test_programmatic_settings.py tests/acp/test_slash_commands.py -q
  • make lint
  • make test
  • make test-binary

Evidence

I replaced the earlier helper-only evidence with real CLI runs on this PR branch.

1) Actual Textual CLI run exercised a schema-derived settings command

I ran the real TTY entrypoint (uv run openhands --exit-without-confirmation) on this branch and sent a schema-generated slash command inside the live TUI. Relevant transcript from the running CLI:

$ OPENHANDS_PERSISTENCE_DIR=/tmp/cli580-tui uv run openhands --exit-without-confirmation
OpenHands CLI v1.13.1

All set up!
Initialized conversation 91b3678b5cca43b7ab4a4ab256452937

What do you want to build?
1. Ask questions, edit files, or run commands.
2. Use @ to look up a file in the folder structure
3. Type /help for help, /feedback to leave anonymous feedback, or / to scroll through available commands

/critic on
Settings Updated
Enable critic
set to enabled

Goodbye! 👋
Conversation ID: 91b3678b5cca43b7ab4a4ab256452937

That is the real CLI/TUI, not a direct Python helper call, and it shows a schema-derived slash command being recognized and applied in the running app.

2) Actual CLI command created a live OpenHands Cloud conversation

The local sandbox LLM_MODEL / LLM_API_KEY env pair was not directly usable for a local BYO run here (openhands/... provider mismatch, then auth failure after forcing an openai/ prefix), so I used the authenticated cloud entrypoint to get a genuine live run from this PR branch:

$ OPENHANDS_PERSISTENCE_DIR=/tmp/cli580-cloud uv run openhands cloud -t "Reply with the exact text CLI_CLOUD_OK and nothing else."
Warning: Environment variable(s) LLM_API_KEY, LLM_MODEL detected but will be ignored.
Use --override-with-envs flag to apply them.
Detected repository: OpenHands/OpenHands-CLI
Detected branch: openhands/issue-2228-cli-settings-schema
Creating cloud conversation...
Conversation ID: 6d40b6a27ba2497194d532efa0a37925
View in browser: https://app.all-hands.dev/conversations/6d40b6a27ba2497194d532efa0a37925
Cloud conversation created successfully! 🚀

Conversation link: https://app.all-hands.dev/conversations/6d40b6a27ba2497194d532efa0a37925

Fixes

Fixes OpenHands/software-agent-sdk#2228

Checklist

  • CI passing
  • Tests are minimal and pass
  • No unnecessary code
  • Evidence from live run
  • All review comments resolved
  • Documentation updated (if applicable)

🚀 Try this PR

uvx --python 3.12 git+https://github.com/OpenHands/OpenHands-CLI.git@openhands/issue-2228-cli-settings-schema

Co-authored-by: openhands <openhands@all-hands.dev>
Copy link
Collaborator

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taste Rating: 🟡 Acceptable - Solves a real problem (settings duplication) with a pragmatic schema-driven approach. Core logic is sound, but there are data ownership concerns and some unnecessary complexity around API key preservation.

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands_cli/acp_impl
   slash_commands.py68198%57
openhands_cli/acp_impl/agent
   base_agent.py2613287%143, 186, 227, 338–340, 411, 418–419, 424, 445–448, 452–459, 484, 567–568, 571–572, 653, 729–731, 760
   local_agent.py85495%81, 145, 155, 169
   remote_agent.py1476357%85–86, 92–93, 97–98, 102, 104–108, 111–113, 123–124, 127–129, 133–135, 145–146, 161–163, 167, 169–170, 176, 180–181, 183, 192–193, 198–200, 211–212, 217, 219–220, 224, 231–232, 234–235, 238–240, 242, 250, 252–253, 287, 352–354, 360–361
openhands_cli/acp_impl/utils
   mcp.py25484%69–72
openhands_cli/shared
   settings_commands.py1112280%53, 65–73, 107, 116, 128–129, 135, 151, 161, 165, 175–178
openhands_cli/stores
   __init__.py40100% 
   programmatic_settings.py370100% 
openhands_cli/tui/core
   commands.py49295%38, 61
openhands_cli/tui/modals/settings/components
   cli_settings_tab.py320100% 
openhands_cli/tui/widgets
   input_area.py802963%75, 77–97, 101–102, 108, 118, 135–136, 152
openhands_cli/tui/widgets/user_input
   input_field.py1721193%288, 303, 306–307, 316–317, 319, 350–351, 372–373
TOTAL685594386% 

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
@neubig neubig marked this pull request as draft March 9, 2026 03:07
neubig pushed a commit that referenced this pull request Mar 9, 2026
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
@neubig neubig force-pushed the openhands/issue-2228-cli-settings-schema branch from 05e7cda to acb7530 Compare March 9, 2026 18:02
Co-authored-by: openhands <openhands@all-hands.dev>
@neubig neubig marked this pull request as ready for review March 16, 2026 13:44
Copy link
Collaborator

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taste Rating: 🟡 Acceptable - Solves a real problem (settings duplication) with a pragmatic schema-driven approach. Core logic is sound, but there are testing gaps and data ownership concerns that should be addressed before merging.


Key Observations:

  1. ✅ Previous review feedback addressed well (type safety, DRY violations, import patterns)
  2. ⚠️ Dual-persistence model adds complexity (manual sync between AgentStore and CliSettings)
  3. ⚠️ Minimal testing for a significant architectural change
  4. ✅ Schema-driven approach eliminates duplication elegantly

@neubig neubig marked this pull request as draft March 19, 2026 04:37
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: openhands <openhands@all-hands.dev>
@neubig neubig marked this pull request as ready for review March 26, 2026 09:12
Copy link
Collaborator

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taste Rating: 🟡 Acceptable - Solves a real problem (settings duplication) with clean schema-driven architecture. Previous review feedback was addressed thoroughly. Core design is sound, manual glue code between CLI and SDK settings is pragmatic.


Assessment:

Data Structures: Excellent choice - schema export as single source of truth eliminates duplication and special cases

Real Problem: Yes - removes settings definitions duplicated between CLI and SDK

Testing: Tests verify real behavior (save/load cycles, value parsing, nested updates) rather than just mocking calls

Evidence: PR description includes actual CLI runs showing schema-driven commands working in the real TUI

Simplicity: Core idea is clean. The manual CliSettingsAgentSettings mapping (programmatic_settings.py:97-122) is unavoidable glue code - it's pragmatic, not elegant, but necessary.

Previous Reviews Addressed: Type safety improved with SettingsFieldSchema, DRY violation fixed by extracting format_setting_argument_hint(), implicit API key merge removed


Minor Notes:

See inline comments for two low-priority observations on error messages and schema validation assumptions.


VERDICT:
Worth merging - Fundamentally sound architecture, tests verify real behavior, known trade-offs are documented and acceptable

KEY INSIGHT:
Schema-driven settings eliminate an entire class of synchronization bugs by making the SDK schema the single source of truth.

field_key: str,
field: SettingsFieldSchema,
argument: str,
) -> Any:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Acceptable - Error Handling: The boolean parsing accepts many variants (on, true, enabled, etc.), which is good UX. The error message could theoretically expose all valid values to an attacker probing the system, but this is a CLI tool where users control input, not a web API. Pragmatic choice.

def compose(self) -> ComposeResult:
"""Compose the CLI settings tab content."""
cli_fields = [
field
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Acceptable - Schema Assumption: This assumes export_schema() returns well-formed sections with a cli key. If the SDK schema is malformed, this will fail at runtime. That's acceptable (fail-fast), but worth noting that there's no defensive validation here. Consider adding a schema validation test if SDK schema changes are frequent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prepare settings for ingestion to programmatic settings menu in CLI and GUI

3 participants