Skip to content

fix: Show complete option help for every uloop command - #2016

Merged
hatayama merged 8 commits into
feature/cli-discoverability-integrationfrom
fix/pause-point-help-parity
Jul 26, 2026
Merged

fix: Show complete option help for every uloop command#2016
hatayama merged 8 commits into
feature/cli-discoverability-integrationfrom
fix/pause-point-help-parity

Conversation

@hatayama

@hatayama hatayama commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • uloop <command> --help now shows the real description of every option, the default value as it must be typed, and every flag the command actually accepts.
  • Each command's help ends with an instruction to load the agent skill that documents it.

User Impact

Inside a synced Unity project, --help was close to useless:

  • Every option read Parameter: <Name> and the tool summary line was missing entirely — 99 of the 102 cached options carried no information. The same command run outside a project showed real help, so the cache made things strictly worse.
  • simulate-keyboard --help reported default: 0 for --action, a value that has to be passed as Press.
  • enable-pause-point --help listed none of its six orchestration flags (--await, --captured-variables, --captured-variable-names, --expect, --trigger, --resume-play), while uloop list listed all six. An agent reading the help could not discover them at all.
  • Nothing in the help output revealed that a skill with the workflow rules, response shapes, and failure diagnoses exists.

After this change the same command reports its description, real per-option help, default: Press, all six pause-point flags, and closes with Load the uloop-simulate-keyboard skill before using options not listed above.

Changes

  • Pause-point CLI-only flags are defined once and read by both the --help renderer and uloop list, so the two listings cannot drift. A contract test asserts every flag the help table advertises is consumed by the runner's parsers — the dispatcher self-updates while the runner stays pinned, so a drifted table would advertise options that fail on use.
  • Enum defaults are converted from Unity's serialized ordinal to the member name in both listings. That conversion needs the enum to be zero-based and contiguous, so a Unity-side guard test asserts it for every enum a first-party schema exposes.
  • Placeholder descriptions fall back to the catalog embedded in the binary. Only description text is taken; type, enum, default, and required always stay as the cache reported them, since Unity is the authority on what the running Editor accepts. Authored descriptions and tools absent from the embedded catalog (custom commands) are left untouched. Both readers are wired, because list formats the raw response and never passes through the project-cache loader.
  • Command-to-skill mapping is a static table: the four pause-point commands all map to uloop-pause-point, and a command with no skill gets no line. A guard test asserts every skill named in the table is declared by a real SKILL.md.
  • simulate-keyboard --key now documents that digit keys are Digit0-Digit9 / Numpad0-Numpad9 and bare 0-9 is rejected; enable-pause-point --max-preview-elements documents that the value also caps later pause-point-status previews.

Verification

  • scripts/check-go-cli.sh: all four modules format/vet/lint clean (0 issues. x4) and all tests pass.
  • uloop compile: ErrorCount: 0.
  • uloop run-tests --filter-type regex --filter-value FirstPartyToolSchemaMetadataTests: 3/3 passed (2 existing + the new enum guard).
  • uloop enable-pause-point --help: all six CLI-only flags listed.
  • uloop list: the enable-pause-point entry is byte-identical to the previous output (descriptions, types, and full|names values compared against the base revision).
  • uloop simulate-keyboard --help: tool description present, --action shows default: Press, --key shows the digit rule, skill line present — verified both with a synced .uloop/tools.json and from a directory with no Unity project (embedded path).
  • uloop list --project-path <repo>: placeholder descriptions dropped from 99 to 0 for first-party tools; the three custom sample commands correctly pass through untouched.
  • Repository CI does not fire on pull requests targeting this integration branch (pull_request.branches is limited to main/v3-beta), so the checks above were run locally; the integration PR will exercise CI.

Review in cubic

hatayama added 5 commits July 27, 2026 00:39
enable-pause-point accepts six orchestration flags that exist only in the
CLI, so both option listings had to add them by hand and they drifted:
`uloop list` documented all six while `--help` documented none of them.
An agent reading `enable-pause-point --help` could not discover --await,
--trigger, or --resume-play at all.

Define the flags once in tooldocs and have both listings read that table,
following the --code-file precedent. The dispatcher self-updates while the
project runner stays pinned, so a new contract test asserts every flag the
help table advertises is actually consumed by the runner's parsers — a
drifted table would otherwise advertise options that fail on use.
Unity's schema cache serializes a C# enum default as its ordinal while
listing the members by name, so `simulate-keyboard --help` reported
"default: 0" for a parameter that only accepts "Press". `uloop list`
already converted the ordinal back to a name; --help did not.

Move that conversion into tooldocs so both listings share it. Indexing the
name list by ordinal only works while the enum is zero-based and
contiguous, so a Unity-side guard test now asserts that of every enum a
first-party schema exposes — an explicit member value or a [Flags] enum
would otherwise make --help name the wrong default.
`--help` can only list option names and one-line summaries, so an agent that
discovered a command there had no way to learn that a skill with the workflow
rules, response shapes, and failure diagnoses exists.

Close each command's help with an instruction to load its skill. The mapping
is a static table rather than a name derived from the command: the four
pause-point commands are all documented by uloop-pause-point, and a
uloop-enable-pause-point skill does not exist. Commands absent from the table
(custom commands) get no line at all. A guard test asserts every skill named
in the table is declared by a real SKILL.md.
Unity's schema generator emits "Parameter: <Name>" for every property with no
[Description] attribute, and the generated cache has no tool-level description
field at all. Inside a synced project that made `--help` and `uloop list`
strictly worse than having no cache: 99 of 102 cached options carried no
information, and the tool summary line disappeared entirely.

Fall back to the descriptions embedded in this binary when the cache's text is
a placeholder. Only description text is taken; type, enum, default, and
required always stay as the cache reported them, since Unity is the authority
on what the running Editor accepts. Authored descriptions and tools absent from
the embedded catalog (custom commands) are left untouched. Both readers are
wired up, because `list` formats the raw response and never passes through the
project-cache loader.

Also document that simulate-keyboard rejects bare digit keys, and that
enable-pause-point's --max-preview-elements caps later pause-point-status
previews too.
cli/common carries non-test source changes in this branch, so the dispatcher
and project-runner release triggers have to be restamped in the same PR or
check-release-triggers fails.
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The changes centralize tool descriptions, enum default rendering, pause-point CLI option definitions, and command-to-skill guidance. Catalog, list, help, schema generation, and pause-point parsing paths now consume these shared contracts with expanded validation coverage.

Changes

Tool metadata and catalog output

Layer / File(s) Summary
Description fallback and schema metadata
Packages/src/Editor/ToolContracts/*, cli/common/tools/*, cli/common/clicore/*, Assets/Tests/Editor/DynamicCodeToolTests/*
JSON property names are used for generated placeholders, embedded descriptions fill missing catalog text, and first-party enum schemas are validated as zero-based contiguous non-flags enums.
Enum defaults and list rendering
cli/common/tooldocs/*, cli/project-runner/internal/projectrunner/list_output*
Numeric enum defaults render as member names, empty-string defaults are omitted, and list/help output uses the shared description and option rendering logic.

Pause-point CLI contract

Layer / File(s) Summary
Pause-point option contract and parsing
cli/common/tooldocs/pause_point_cli_options*, cli/project-runner/internal/projectrunner/pause_point_*, cli/project-runner/internal/projectrunner/native_command_help*
CLI-only pause-point flags and values are centralized, consumed by enable/wait/status parsing and list/help generation, and checked by parser/documentation contract tests.

Command skill guidance

Layer / File(s) Summary
Skill guidance in command help
cli/common/tooldocs/skill_guidance*, cli/dispatcher/internal/dispatcher/*, cli/project-runner/internal/projectrunner/native_command_help*
Mapped commands append skill-loading guidance to help output, while unmapped commands omit it; dispatcher and runner help tests cover both cases.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CommandHelp
  participant SkillGuidanceLine
  participant ToolHelp
  participant User
  CommandHelp->>SkillGuidanceLine: resolve command skill
  SkillGuidanceLine-->>CommandHelp: guidance line or no mapping
  CommandHelp->>ToolHelp: append guidance after options
  ToolHelp-->>User: rendered command help
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: improving option help completeness for uloop commands.
Description check ✅ Passed The description is detailed and directly matches the help, list, and documentation changes in this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pause-point-help-parity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

hatayama added 3 commits July 27, 2026 01:16
Review follow-ups on the skill guidance line and option help formatting:

- The watch commands and `launch` now get the line too. Watch expressions are
  documented by the pause-point skill, and launch renders its own help, so it
  was the one command with a real skill that never mentioned it.
- The wording no longer refers to "options not listed above": commands such as
  focus-window have no options for that phrasing to point at. It now names what
  the skill adds instead.
- An empty-string default is no longer rendered, since Unity reports one for
  every unset string parameter and it printed a bare "default: " with nothing
  after it. `uloop list` already omitted it.
- Widen the option column so --captured-variable-names does not push its own
  description out of alignment.
- Route list's fallback call through the clicore facade like every other tool
  catalog access in that package.
- Build Unity's placeholder description from the JSON property name. The CLI
  recognizes the placeholder by comparing against the JSON name it received, so
  a [JsonProperty] rename would otherwise leave the placeholder in help output
  as if it were a real description.
The six CLI-only flags were parsed by six hand-written same-shaped branches,
pushing extractPausePointEnableAwaitFlags to 33 branches over 157 lines — past
the repository's complexity limit — right as this branch introduced a shared
table for the very same flags.

Keying the handlers by that table's flag names makes the two sets comparable,
so a flag can no longer be documented without being parsed or parsed without
being documented; a contract test now asserts the two key sets are equal. That
is the direction the previous subset check could not see, and it is how all six
flags came to be missing from --help.

Behavior-preserving: no existing test changed, and new tests pin the two edges
that the branch order encoded implicitly — --await has no =value form and
unrelated arguments pass through to schema parsing.
Unity reports an empty string as the default of every unset string parameter.
`--help` already suppresses that value, but list still emitted "Default": ""
for 20 options because encoding/json's omitempty cannot drop an empty string
held in an interface field. Reporting nil restores the help/list symmetry this
change set exists to guarantee.

Re-stamps the shared release inputs for the cli/common-adjacent change.
@hatayama
hatayama merged commit a0896f2 into feature/cli-discoverability-integration Jul 26, 2026
1 of 2 checks passed
@hatayama
hatayama deleted the fix/pause-point-help-parity branch July 26, 2026 16:28

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
cli/common/tooldocs/tool_option_help.go (1)

96-100: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Centralize the "omit empty-string default" rule in tooldocs.

Both --help and list independently re-implement "Unity's empty string means no default, so don't render it" — exactly the kind of contract this PR otherwise centralizes via tooldocs.EnumValueForNumericDefault. A future change to the omission rule (e.g. extending it to another sentinel) would need identical edits in both places to stay in sync.

  • cli/common/tooldocs/tool_option_help.go#L96-L100: extract the propertyDefault != nil && propertyDefault != "" check into a shared tooldocs helper (e.g. IsOmittedDefault(value any) bool) and use it here.
  • cli/project-runner/internal/projectrunner/list_output.go#L112-L118: use the same shared helper instead of the local defaultValue == "" check.
♻️ Proposed shared helper
// in cli/common/tooldocs/enum_defaults.go or a new file
func IsOmittedDefault(value any) bool {
	return value == nil || value == ""
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/common/tooldocs/tool_option_help.go` around lines 96 - 100, The
empty-string default omission rule is duplicated across help and list output;
centralize it in a shared tooldocs helper. Add an IsOmittedDefault helper
returning true for nil or empty-string values, use it in tool_option_help.go
around property.EffectiveDefault(), and replace the local defaultValue == ""
check in cli/project-runner/internal/projectrunner/list_output.go lines 112-118
with the helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@cli/common/tooldocs/tool_option_help.go`:
- Around line 96-100: The empty-string default omission rule is duplicated
across help and list output; centralize it in a shared tooldocs helper. Add an
IsOmittedDefault helper returning true for nil or empty-string values, use it in
tool_option_help.go around property.EffectiveDefault(), and replace the local
defaultValue == "" check in
cli/project-runner/internal/projectrunner/list_output.go lines 112-118 with the
helper.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 86de72f8-ea1c-4f6f-b90e-0aa3fde22ada

📥 Commits

Reviewing files that changed from the base of the PR and between 745d114 and c89a368.

📒 Files selected for processing (29)
  • Assets/Tests/Editor/DynamicCodeToolTests/FirstPartyToolSchemaMetadataTests.cs
  • Packages/src/Editor/ToolContracts/UnityCliLoopToolParameterSchemaGenerator.cs
  • cli/common/clicore/tool_catalog.go
  • cli/common/tooldocs/enum_defaults.go
  • cli/common/tooldocs/enum_defaults_test.go
  • cli/common/tooldocs/pause_point_cli_options.go
  • cli/common/tooldocs/pause_point_cli_options_test.go
  • cli/common/tooldocs/skill_guidance.go
  • cli/common/tooldocs/skill_guidance_test.go
  • cli/common/tooldocs/tool_option_help.go
  • cli/common/tools/catalog.go
  • cli/common/tools/default-tools.json
  • cli/common/tools/description_fallback.go
  • cli/common/tools/description_fallback_test.go
  • cli/dispatcher/internal/dispatcher/command_help.go
  • cli/dispatcher/internal/dispatcher/help_test.go
  • cli/dispatcher/internal/dispatcher/launch.go
  • cli/dispatcher/shared-inputs-stamp.json
  • cli/project-runner/internal/projectrunner/list_output.go
  • cli/project-runner/internal/projectrunner/list_output_test.go
  • cli/project-runner/internal/projectrunner/native_command_help.go
  • cli/project-runner/internal/projectrunner/native_command_help_test.go
  • cli/project-runner/internal/projectrunner/pause_point_captured_variables_mode.go
  • cli/project-runner/internal/projectrunner/pause_point_cli_options_contract_test.go
  • cli/project-runner/internal/projectrunner/pause_point_enable.go
  • cli/project-runner/internal/projectrunner/pause_point_expect.go
  • cli/project-runner/internal/projectrunner/pause_point_trigger.go
  • cli/project-runner/internal/projectrunner/pause_point_wait.go
  • cli/project-runner/shared-inputs-stamp.json

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.

1 participant