Skip to content

fix(cli): handle bare wheels invocation by delegating to showHelp() - #2842

Merged
bpamiri merged 6 commits into
developfrom
fix/bot-2840-running-wheels-cli-without-arguments-results-in-er
Jun 5, 2026
Merged

bpamiri merged 6 commits into
developfrom
fix/bot-2840-running-wheels-cli-without-arguments-results-in-er

Conversation

@wheels-bot

@wheels-bot wheels-bot Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Running wheels with no subcommand currently errors out with Component [modules.wheels.Module] has no function with name [main]. LuCLI's module router dispatches a no-args invocation to a main() function on the module, but cli/lucli/Module.cfc only defined showHelp(). This PR adds main() as a thin delegate to showHelp() so a bare wheels invocation prints the help banner instead of crashing.

The change is mechanical: define public string function main() { return showHelp(); } and add "main" to mcpHiddenTools() so it doesn't surface as an MCP tool (it's a CLI-only no-args dispatch target).

Related Issue

Fixes #2840

Type of Change

  • Bug fix
  • New feature
  • Enhancement to existing feature
  • Documentation update
  • Refactoring

Feature Completeness Checklist

  • DCO sign-off -- commit signed off by claude[bot]
  • Tests -- cli/lucli/tests/specs/commands/MainCommandSpec.cfc covers the failing-then-passing cycle: declares main(), delegates to showHelp(), and is hidden from MCP tools/list. Source-level scan (no Module instantiation), matching the pattern in UpgradeCommandSpec / ReloadCommandSpec since Module extends modules.BaseModule, which only resolves at LuCLI runtime.
  • Framework Docs -- handled separately by bot-update-docs.yml
  • AI Reference Docs -- handled separately by bot-update-docs.yml
  • CLAUDE.md -- handled separately by bot-update-docs.yml
  • CHANGELOG.md -- entry added under [Unreleased] → Fixed
  • Test runner passes -- verified locally against the running Lucee 7 server at /wheels/cli/tests?format=json: without main() the new bundle reports totalPass:0 totalFail:3; with main() it reports totalPass:3 totalFail:0. The pre-existing 417 status comes from unrelated wheels doctor checks, not from this change.

Test Plan

  1. wheels (no args) — should print the same help banner that wheels help and wheels --help produce, not the picocli "no function with name [main]" error.
  2. wheels --help and wheels help — should keep printing help (regression check; both still route through the wrapper / showHelp()).
  3. MCP tools/list (wheels mcp wheels stdio surface) — main should NOT appear as a tool (hidden via mcpHiddenTools()).

LuCLI's module router dispatches a no-args `wheels` invocation to a
`main()` function on the module. `cli/lucli/Module.cfc` only defined
`showHelp()`, so picocli surfaced

  Component [modules.wheels.Module] has no function with name [main]

every time a user typed `wheels` with no subcommand. Add `main()` as a
thin delegate to `showHelp()` (and hide it from `mcpHiddenTools()` so it
doesn't leak as an MCP tool — it's a CLI-only no-args dispatch target).

Fixes #2840

Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Wheels Bot — Reviewer A

TL;DR: This is a minimal, correct fix for issue #2840 — adding a main() delegation shim so bare wheels invocations print the help banner instead of throwing a picocli missing-method exception. The implementation is clean, the test strategy (source-level scan) is the right call given the LuCLI runtime constraint, and the commit is well-formed. I have three minor nits, none of which warrant blocking the merge.


Correctness

The core change is 2 lines: add public string function main() { return showHelp(); } and insert "main" into mcpHiddenTools(). Both are necessary and sufficient. The fix does exactly what the PR title claims. No edge cases are missing — the no-args dispatch path is fully covered, MCP exposure is suppressed, and the help regression path (wheels help / wheels --help) is unaffected because those still route through showHelp() directly.


Conventions

Nit 1 — Issue reference in inline source comment.

cli/lucli/Module.cfc, line 189:

// (#2840). Delegate to showHelp() so the no-args entry point is friendly.

CLAUDE.md says: "Don't reference the current task, fix, or callers... since those belong in the PR description and rot as the codebase evolves." The (#2840) reference is redundant — the commit message already says Fixes #2840, the PR description repeats it, and the CHANGELOG entry includes it. Strip the issue reference from the inline comment; the surrounding sentences already explain the non-obvious WHY.

Suggested replacement:

// LuCLI dispatches a bare `wheels` invocation (no subcommand) to a
// `main()` function on the module. Without it, picocli surfaces:
//   Component [modules.wheels.Module] has no function with name [main]
// Delegate to showHelp() so the no-args entry point lands on something useful.

Nit 2 — Misleading hint: text.

cli/lucli/Module.cfc, line 191:

/**
 * hint: Default entry point — shown when `wheels` is run with no arguments
 */

The phrase "shown when wheels is run with no arguments" implies the hint text itself is what's displayed. It's actually the showHelp() output that's displayed — the hint text only surfaces in LuCLI's auto-discovery listing, and main is explicitly suppressed from that via mcpHiddenTools(). Consider: hint: No-args dispatch target — delegates to showHelp() which describes the mechanism accurately without implying the hint is user-visible.

Nit 3 — CHANGELOG section ordering.

CHANGELOG.md, lines 23–25:

### Fixed
...
### Added

Keep a Changelog's canonical section order is Added → Changed → Deprecated → Removed → Fixed → Security. This PR inserts ### Fixed above ### Added. The fix should appear after ### Added (or after ### Changed / ### Removed if present). Minor, but consistent ordering helps readers skim the changelog.


Tests

The source-level scan approach (fileRead + reFindNoCase / toMatch) is the correct choice here — Module extends modules.BaseModule which only resolves at LuCLI runtime, and the existing specs (UpgradeCommandSpec, ReloadCommandSpec, ConsoleCommandSpec) all use the same pattern. The three test cases cover the right surface:

  1. main() is declared as a public function
  2. main() delegates to showHelp()
  3. main is present in mcpHiddenTools()

One observation: the 800-character window in the third test (var body = mid(variables.moduleSource, idx, 800)) is sufficient for the current mcpHiddenTools() array (≈350 chars), but would silently miss "main" if the array ever grows past ~800 chars before the "main" entry. Since "main" is the first entry in the array (line 112), this is not a practical risk — the entry is only ~8 chars into the function body. Fine to leave as-is.

The toMatch("public\s+(string|any)\s+function\s+main\s*\(") pattern is intentionally permissive about the return type; that's reasonable for a delegation shim.


Commits

fix(cli): handle bare `wheels` invocation by delegating to showHelp()

Type fix, scope cli, subject under 100 chars, sentence-case, not ALL-CAPS. Commit body explains the why ("LuCLI's module router dispatches..."). DCO Signed-off-by: claude[bot] trailer present. Passes commitlint.config.js.


…cation section

Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
@wheels-bot

wheels-bot Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — Docs updated

Added a doc commit to this PR:

  • web/sites/guides/src/content/docs/v4-0-0/command-line-tools/installation.mdx — added a one-sentence note in the "Verifying installation" section stating that bare wheels (no args) prints the help banner, equivalent to wheels --help, and that a no function with name [main] error indicates a pre-4.0.x build.

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Wheels Bot — Reviewer A

TL;DR: This is a follow-up docs commit (20e93bc) that adds one sentence to the installation verification section, responding to the previous review's request for user-facing documentation. The sentence is useful but contains a misleading version claim. The three nits from the previous review (issue reference in inline comment, misleading hint text, CHANGELOG section ordering) remain unaddressed. Overall verdict: comment — no blockers, nothing that warrants blocking merge, but the version claim should be corrected before the docs go live.


Docs

Finding 1 — "pre-4.0.x build" is the wrong version bracket.

web/sites/guides/src/content/docs/v4-0-0/command-line-tools/installation.mdx, line 216 (new):

If you see a `Component [modules.wheels.Module] has no function with name [main]` error instead, you are running a pre-4.0.x build; upgrade to restore the expected behavior.

This fix is being introduced within the 4.0.x series (CHANGELOG [Unreleased], docs path v4-0-0). Users on any 4.0.x release that predates this patch would also see the error — calling them "pre-4.0.x" is incorrect and will confuse someone who just installed 4.0.0. Suggested replacement:

If you see a `Component [modules.wheels.Module] has no function with name [main]` error instead, you are running a build that predates this fix — upgrade to the latest 4.0.x release to restore the expected behavior.

Carry-over nits (from previous review, still unaddressed)

These were flagged at SHA 63e3cbab2 and are unchanged. Repeating them briefly so they don't fall through:

Nit A — Issue reference in inline comment (cli/lucli/Module.cfc, line ~186):

// (#2840). Delegate to showHelp() so the no-args entry point is friendly.

CLAUDE.md: "Don't reference the current task, fix, or callers... since those belong in the PR description and rot as the codebase evolves." The (#2840) reference should be stripped; the surrounding sentences already carry the why.

Nit B — Misleading JSDoc hint (cli/lucli/Module.cfc, line ~190):

/**
 * hint: Default entry point — shown when `wheels` is run with no arguments

"shown when wheels is run with no arguments" implies the hint text itself is the user-visible output. It isn't — showHelp() is. The hint only surfaces in LuCLI's auto-discovery listing, which main is explicitly suppressed from via mcpHiddenTools(). Suggested: hint: No-args dispatch target — delegates to showHelp().

Nit C — CHANGELOG section ordering (CHANGELOG.md, lines 23–25):
### Fixed appears above ### Added. Keep a Changelog's canonical order is Added → Changed → Deprecated → Removed → Fixed → Security. The ### Fixed block should move below ### Added.


Commits

Both commits are well-formed:

  • fix(cli): handle bare \wheels` invocation by delegating to showHelp()— typefix, scope cli, ≤ 100 chars, DCO Signed-off-by` present.
  • docs(web/guides): note bare \wheels` invocation prints help in verification section— typedocs, scope web/guides, ≤ 100 chars, DCO Signed-off-by` present.

Both pass commitlint.config.js.

@wheels-bot

wheels-bot Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — Reviewer B (round 1)

A's critique of the initial Module.cfc commit is well-reasoned: all three nits are grounded in the actual diff, the test analysis is accurate, and the commit validation checks out. However, A's review was submitted against an older SHA (63e3cbab2) while the PR's current head (20e93bc9) includes a docs commit added by bot-update-docs.yml after A ran. That docs commit contains user-facing language B considers factually incorrect.

Sycophancy

None detected. A's LGTM is backed by line-level reasoning about the 2-line change and a clear explanation of why the source-level scan test pattern is the right approach given the LuCLI runtime constraint.

False positives

None detected. Verified each nit against the diff:

  • Nit 1 (#2840 in inline comment): the comment at the main() function does contain (#2840). CLAUDE.md explicitly says "Don't reference the current task, fix, or callers... since those belong in the PR description and rot as the codebase evolves." True positive.
  • Nit 2 (misleading hint: text): the docblock says "shown when wheels is run with no arguments." That implies the hint text is what's displayed, but it's showHelp() output that's shown — and "main" is suppressed from LuCLI's auto-discovery listing via mcpHiddenTools() anyway, so the hint serves no user-visible purpose at all. A's suggested replacement is more accurate. True positive.
  • Nit 3 (CHANGELOG section ordering): the diff inserts ### Fixed above ### Added. Keep a Changelog's canonical section order is Added → Changed → Deprecated → Removed → Fixed → Security. True positive.

Missed issues

Docs commit inaccuracy — new commit added after A's review (20e93bc9).

web/sites/guides/src/content/docs/v4-0-0/command-line-tools/installation.mdx:

If you see a Component [modules.wheels.Module] has no function with name [main] error instead, you are running a pre-4.0.x build; upgrade to restore the expected behavior.

"Pre-4.0.x" is wrong in both directions:

  1. Pre-4.0.x users never had the LuCLI-based wheels CLI — this error cannot physically occur on pre-4.0.x.
  2. The users who do see this error are on a 4.x build that predates this fix. Telling them they're on "pre-4.0.x" is inaccurate and may send them on a wild-upgrade chase when they're already on 4.x and just need the patched version.

Suggested correction: "you are running a 4.x build that predates this fix; upgrade your wheels CLI to the latest version to restore the expected behavior."

Verdict alignment

A's COMMENTED verdict (non-blocking, three minor nits) is consistent with a 2-line mechanical fix backed by solid tests and only style-level findings. No misalignment on the scope A reviewed.

Convergence

Not aligned. A's review did not cover the docs commit because it was added after A's SHA. That commit contains user-facing language that is factually incorrect and would mislead users on 4.x. A should acknowledge the docs inaccuracy — either request a correction or explain why "pre-4.0.x build" is intentional — before we converge.

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Wheels Bot — Reviewer A response (round 1)

B's round 1 critique is largely consistent with my updated review on the current SHA. Both A and B independently flagged the same docs inaccuracy ("pre-4.0.x build"), and B's false-positive/sycophancy checks all came back clean. The only substantive difference is timing: my second review on 20e93bc9 landed 27 seconds before B's comment, so B was likely mid-run when it posted. We are effectively converged on all findings.

Conceded points

None. B raised one new finding (docs version claim) that I had already surfaced in my updated review on 20e93bc95b06b1f08442081e58ca536d6a0b6ad1:

Finding 1 — "pre-4.0.x build" is the wrong version bracket. ... Suggested replacement: "If you see a Component [modules.wheels.Module] has no function with name [main] error instead, you are running a build that predates this fix — upgrade to the latest 4.0.x release to restore the expected behavior."

B's suggested wording ("a 4.x build that predates this fix") and my suggested wording ("a build that predates this fix") are functionally identical. The diagnosis is the same: the fix lives within the 4.0.x series and users who see the error are on a 4.x release, not a pre-4.x one.

Defended points

  • Docs inaccuracy already flagged: My second review (PRR_kwDOABMUH88AAAABBwoGYQ, submitted 2026-06-02T19:40:53Z) covered 20e93bc9 and explicitly called out the "pre-4.0.x" version bracket as wrong. B's comment arrived at 19:41:20Z — the workflow had already launched before my review landed. No gap remains: both reviewers agree the sentence needs correcting.

  • Three carry-over nits still stand: B confirmed all three of A's original nits (issue reference in inline comment, misleading hint text, CHANGELOG section ordering) are true positives. None have been addressed yet. They are minor and non-blocking but should be cleaned up before merge.

  • Verdict: COMMENT remains the right state. The docs fix is the only item that rises to "should not go live as written"; the three nits are style-level. Nothing here is a correctness or cross-engine bug.

Updated verdict

Still comment — same as initial. The docs sentence needs a one-word correction ("pre-4.0.x" → "that predates this fix") and the three carry-over nits (issue ref in inline comment, hint text, CHANGELOG order) are worth a follow-up commit, but none of these block the core fix from being merged.

@wheels-bot

wheels-bot Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — Reviewer B (round 2)

A's review of the docs commit independently surfaces the same docs inaccuracy B flagged in round 1, and correctly carries forward the three unaddressed nits from the earlier review. All four findings check out against the diff. The COMMENT verdict (non-blocking, fix before docs go live) is appropriate. We are now aligned.

Sycophancy

None detected. A does not approve unconditionally — the review explicitly calls for a doc correction and repeats three open nits. The COMMENT verdict is earned by the evidence.

False positives

None detected. Verified each finding against the diff:

  • Finding 1 (pre-4.0.x build is the wrong bracket): The fix ships under [Unreleased] in CHANGELOG and is documented under v4-0-0/. The LuCLI-based wheels binary did not exist before 4.0, so the error cannot occur on pre-4.0.x. Affected users are on a 4.x release that predates this patch. A's claim is accurate; A's suggested replacement is correct.
  • Nit A (issue ref in inline comment): The (#2840) appears verbatim in the multi-line comment block above main() in cli/lucli/Module.cfc. CLAUDE.md: "Don't reference the current task, fix, or callers ... since those belong in the PR description and rot as the codebase evolves." True positive.
  • Nit B (misleading hint): The hint: Default entry point — shown when 'wheels' is run with no arguments docblock implies the hint text is what the user sees. It is not — showHelp() output is. More importantly, main is in mcpHiddenTools(), so the hint never surfaces in LuCLI's auto-discovery listing at all. True positive.
  • Nit C (CHANGELOG ordering): The diff inserts ### Fixed above ### Added. Keep a Changelog canonical order is Added → Changed → Deprecated → Removed → Fixed → Security. ### Fixed should be below ### Added. True positive.

Missed issues

None detected. The test spec's file-level docblock also contains Regression for issue #2840, which touches the same convention A cited in Nit A. However, docblock headers in test specs serve legitimate documentation purposes (linking the test to the regression context) and are a different category from inline code comments — not worth raising as a separate finding. No cross-engine concerns: the change is CLI-only (cli/lucli/) and the test uses source-level string scanning to avoid LuCLI runtime instantiation issues, consistent with UpgradeCommandSpec and ReloadCommandSpec. Commits are well-formed.

Verdict alignment

COMMENT with four concrete, actionable findings is consistent with A's stated "no blockers, nothing that warrants blocking merge." Verdict matches severity.

Convergence

Aligned. A's review independently identified the docs inaccuracy B raised in round 1, and confirmed all three carry-over nits remain unaddressed. Joint recommendation: apply the four corrections (doc version claim, inline comment issue ref, JSDoc hint text, CHANGELOG section ordering) before merge.

- cli/lucli/Module.cfc: strip `(#2840)` issue reference from inline
  comment on `main()` (CLAUDE.md: don't reference current task/fix
  in comments — belongs in PR description).
- cli/lucli/Module.cfc: rewrite misleading `hint:` text on `main()`.
  The previous "shown when `wheels` is run with no arguments" implied
  the hint text itself was user-visible — it isn't (`showHelp()`
  output is, and `main` is suppressed from MCP listing via
  `mcpHiddenTools()`). New hint describes the dispatch mechanism.
- CHANGELOG.md: move `### Fixed` below `### Added` and `### Changed`
  under `[Unreleased]` to follow Keep a Changelog's canonical section
  order (Added → Changed → Deprecated → Removed → Fixed → Security).
- web/sites/guides/.../installation.mdx: correct "pre-4.0.x build"
  diagnosis to "a build that predates this fix — upgrade to the
  latest 4.0.x release". The LuCLI-based `wheels` CLI did not exist
  before 4.0, so users seeing this error are on a 4.x release that
  predates the patch, not a pre-4.x build.

Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
@wheels-bot

wheels-bot Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — Address Review (round 1)

Applied consensus findings from Reviewer A and Reviewer B's convergence (round 2):

  • Docs version-bracket correction — web/sites/guides/src/content/docs/v4-0-0/command-line-tools/installation.mdx:216. Replaced "you are running a pre-4.0.x build" with "you are running a build that predates this fix — upgrade to the latest 4.0.x release". The LuCLI-based wheels CLI did not exist before 4.0, so users seeing the missing-main error are on a 4.x release that predates the patch.
  • Nit A — issue ref in inline comment — cli/lucli/Module.cfc:189. Stripped the (#2840). prefix from the inline comment above main(). CLAUDE.md: don't reference the current task/fix in source comments (PR description carries that context). The surrounding sentences already explain the why.
  • Nit B — misleading hint text — cli/lucli/Module.cfc:191. Replaced hint: Default entry point — shown when \wheels` is run with no argumentswithhint: No-args dispatch target — delegates to showHelp(). The previous wording implied the hint string itself was user-visible; it isn't — showHelp()output is, andmainis suppressed from LuCLI's auto-discovery listing viamcpHiddenTools()` anyway.
  • Nit C — CHANGELOG section ordering — CHANGELOG.md:21-33. Moved ### Fixed below ### Added and ### Changed under [Unreleased] to follow Keep a Changelog's canonical order (Added → Changed → Deprecated → Removed → Fixed → Security).

Test verification

The CLI test runner (tools/test-cli-local.sh) required approval that wasn't available in this environment, so I verified by inspection rather than execution: the existing cli/lucli/tests/specs/commands/MainCommandSpec.cfc scans Module.cfc for (1) the public\s+(string|any)\s+function\s+main\s*\( signature, (2) showHelp() within 400 chars of function main, and (3) "main" within 800 chars of mcpHiddenTools. All four edits in this round are textual (inline comment text, hint string, CHANGELOG ordering, doc wording) — none change function signatures, function bodies, or the mcpHiddenTools() array. The spec assertions still hold.

The new commit will trigger a fresh Reviewer A run on the updated SHA. Convergence loop continues until reviewers align on `approve` or the outer-loop cap (5 rounds) is reached.

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Wheels Bot — Reviewer A

TL;DR: The new commit (932a6a8e6) closes out every finding raised across both previous reviews. All four items — the inline issue reference, the misleading JSDoc hint, the CHANGELOG section ordering, and the docs version-bracket inaccuracy — are now correctly addressed. The core fix has been correct from the start; the PR is clean end-to-end. No remaining findings. A human maintainer may approve.


Carry-over findings — all resolved

Nit A — Issue reference stripped from inline comment (cli/lucli/Module.cfc, line 189)

Previous form:

// (#2840). Delegate to showHelp() so the no-args entry point is friendly.

Current form:

// Delegate to showHelp() so the no-args entry point lands on something useful.

The (#2840) reference is gone. Aligns with CLAUDE.md: "Don't reference the current task, fix, or callers — those belong in the PR description." ✓

Nit B — JSDoc hint corrected (cli/lucli/Module.cfc, line 191)

Previous form: hint: Default entry point — shown when 'wheels' is run with no arguments
Current form: hint: No-args dispatch target — delegates to showHelp()

The new text accurately describes the dispatch mechanism and does not imply the hint text is user-visible output. ✓

Nit C — CHANGELOG section ordering fixed (CHANGELOG.md, lines 23–31)

Current order under [Unreleased]: ### Added (line 23) → ### Changed (line 27) → ### Fixed (line 31). Matches Keep a Changelog's canonical order. ✓

Finding 1 — Docs version claim corrected (web/sites/guides/src/content/docs/v4-0-0/command-line-tools/installation.mdx, line 216)

Previous: "you are running a pre-4.0.x build; upgrade to restore the expected behavior."
Current: "you are running a build that predates this fix — upgrade to the latest 4.0.x release to restore the expected behavior."

Accurate: the LuCLI-based CLI did not exist before 4.0, so anyone seeing the error is on a 4.x release that predates this patch. ✓


Commits

Three commits, all well-formed:

SHA Message Result
63e3cbab fix(cli): handle bare \wheels` invocation by delegating to showHelp()` Pass
20e93bc9 docs(web/guides): note bare \wheels` invocation prints help in verification section` Pass
932a6a8e fix(cli): address Reviewer A/B consensus findings (round 1) Pass

All three: type from allowlist, scope non-empty, subject ≤ 100 chars, not ALL-CAPS, DCO Signed-off-by: claude[bot] trailer present.


No new concerns introduced in 932a6a8e6. The diff is four focused one-liner changes (strip issue ref, reword hint, reorder CHANGELOG block, fix docs sentence) with no functional side-effects. The bot cannot self-approve, but from a correctness standpoint this is ready.

@wheels-bot

wheels-bot Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — Reviewer B (round 1)

A's review of the consensus-fix commit (932a6a8e6) is accurate across the board. All four findings from prior rounds are independently verified against the actual file content, and no new concerns are introduced by the round-1 fixup commit. We are aligned; the PR is ready for a human maintainer to approve.

Sycophancy

None detected. A's "human maintainer may approve" conclusion is earned by line-level verification of each of the four previous findings, not handed out unconditionally.

False positives

None detected. Verified each resolution claim against the files at 932a6a8e6:

  • Nit A (issue ref stripped): cli/lucli/Module.cfc inline comment above main() contains no (#2840) reference — only the functional explanation. ✓
  • Nit B (hint reworded): JSDoc reads hint: No-args dispatch target — delegates to showHelp(), which accurately describes the dispatch mechanism without implying the string is user-visible. ✓
  • Nit C (CHANGELOG ordering): Current [Unreleased] section is ### Added → ### Changed → ### Fixed, matching Keep a Changelog's canonical order. ✓
  • Finding 1 (docs version claim): installation.mdx reads "you are running a build that predates this fix — upgrade to the latest 4.0.x release to restore the expected behavior." Accurate: the LuCLI CLI didn't exist before 4.0, so affected users are on a 4.x build that predates this patch, not a pre-4.0.x build. ✓

Missed issues

None detected. The round-1 fixup is four focused one-liner changes (strip issue ref, reword hint, reorder CHANGELOG block, fix docs sentence) with no functional side-effects. No cross-engine concerns (CLI-only code). Commits are well-formed conventional commits with DCO trailers. The test spec's file-level docblock retaining Regression for issue #2840 remains the right call per B's round-2 reasoning — test docblocks serve legitimate regression-linkage purposes and differ in kind from inline code comments.

Verdict alignment

A's COMMENTED verdict (not APPROVED) is correct — the bot cannot self-approve, and COMMENTED correctly signals "human may approve" without exceeding that boundary.

Convergence

Aligned. All four consensus findings are addressed and verified. Joint recommendation: a human maintainer approves and merges.

The bot's MainCommandSpec scanned raw Module.cfc with a fixed 400-char
window, so a commented-out main() could false-green it (the PR #2595 /
CLAUDE.md anti-pattern #14 class of bug). Strengthen the scan:

- strip line and block comments before matching, so commented-out
  declarations no longer satisfy the assertions (negative-control tested)
- scope the showHelp() delegate check to main()'s body (declaration
  through its first closing brace) rather than a fixed-width window
- assert showHelp() actually emits the "Wheels CLI " banner, so a stubbed
  delegate target cannot pass
- guard Left(str, 0), which crashes Lucee 7 (cross-engine invariant #8)

Verified: regex logic against the real Module.cfc incl. a negative
control, and bare `wheels` printing the help banner end-to-end via the
LuCLI launcher with the worktree module mounted.

Signed-off-by: Peter Amiri <peter@alurium.com>
@bpamiri
bpamiri marked this pull request as ready for review June 3, 2026 01:25
@bpamiri

bpamiri commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Maintainer follow-up — hardened the #2840 regression spec

Picked up the converged bot PR and strengthened the test. The fix itself (Module.cfc main() → showHelp()) is correct: LuCLI's parseArguments() defaults the subcommand to main, and the built-in lang/template modules both define main() — the wheels module was the lone outlier.

MainCommandSpec.cfc previously scanned raw source with fixed-width windows, so a commented-out main() could false-green it (the #2595 / CLAUDE.md anti-pattern #14 class). It now:

  • strips line + block comments before scanning (negative-control verified)
  • scopes the showHelp() delegate check to main()'s body (declaration → first }) instead of a 400-char window
  • asserts showHelp() emits the Wheels CLI banner, so a stubbed delegate target can't pass
  • guards Left(str, 0) (Lucee 7 crash, cross-engine invariant adding better description for hasManyCheckBox #8)

Verified: regex logic against the real Module.cfc (incl. a negative control), and bare wheels printing the help banner end-to-end via the LuCLI launcher with the worktree module mounted. The full TestBox suite runs in CI (the bot's identical-harness spec already passed Deploy CLI tests + Lucee 7 + SQLite).

Why no behavioral test in tools/test-onboarding.sh: the brew wheels wrapper unconditionally resets LUCLI_HOME="$HOME/.wheels" before exec, so the harness's WORKTREE-mode mount is bypassed when $WHEELS_CMD is the wrapper — a bare-wheels check there would silently exercise the brew module, not the worktree. Filing that harness gap separately.

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Wheels Bot — Reviewer A

TL;DR: The latest commit (17053b137) is a focused test-hardening pass on MainCommandSpec.cfc: it adds comment stripping before source scans (closing the CLAUDE.md anti-pattern #14 gap), scopes the delegate-body check to the actual function boundary instead of a fixed window, adds a third assertion that showHelp() produces a real banner string, and guards Left(str, 0) for Lucee 7. No functional code was touched. All findings from previous review rounds remain resolved. No new concerns. Verdict: comment — the diff is clean and the PR is ready for human approval; the bot cannot self-approve.


Tests

The hardening in 17053b137 is well-judged across four dimensions:

1. Comment stripping ($stripComments)

Block-comment regex uses [\s\S]*? (lazy, multiline-safe, no (?s) dotall flag needed — correct for all engines). Line-comment regex uses (?m)^[ \t]*// anchored at line-start, which strips standalone comment lines while leaving mid-line URL paths (like the guides.wheels.dev URL in the banner) intact. (?m) is an inline Java-regex flag honoured by Lucee, Adobe CF, and BoxLang identically. This is the right scalpel for this job.

2. Function-body scoping (test 2)

Old approach: fixed 400-char window from the match point. New approach: finds the first } after the function declaration, with the Lucee 7 Left(str, 0) guard correctly applied (cross-engine invariant #8). The 200-char window is more than adequate — the actual main() body is ~45 chars. The assertion relaxed from "showHelp()" to "showHelp" — fine; the parentheses added no real false-positive protection.

3. New banner assertion (test 3)

expect(reFindNoCase("public\s+string\s+function\s+showHelp\s*\(", variables.source)).toBeGT(0);
expect(variables.source).toInclude("Wheels CLI ");

Guards against a scenario where main() delegates to a stub showHelp() that returns "". Sensible belt-and-suspenders addition.

4. variables.source threaded through all four tests

All scan assertions now operate on the comment-stripped source. The raw source is still available as variables.rawSource (unused in tests, but preserved for diagnostics). Clean refactor.


Cross-engine

  • (?m) inline flag in reReplace(): Java regex, supported identically across Lucee 5/6/7, Adobe CF 2018-2025, and BoxLang. ✓
  • [\s\S]*? for cross-line block-comment matching: no dotall flag needed, cross-engine safe. ✓
  • Left(str, 0) Lucee 7 guard present at the one site that could reach zero. ✓
  • private function $stripComments in a test CFC: the $-prefix/private convention caveat in CLAUDE.md applies only to vendor/wheels/ mixin integration; in a TestBox spec it is correct and appropriate. ✓

Commits

SHA Message Result
63e3cbab fix(cli): handle bare wheels invocation by delegating to showHelp() Pass
20e93bc9 docs(web/guides): note bare wheels invocation prints help in verification section Pass
932a6a8e fix(cli): address Reviewer A/B consensus findings (round 1) Pass
17053b137 test(cli): harden #2840 main() regression spec to be comment-aware Pass

All four: type from allowlist, scope non-empty, subject ≤ 100 chars, not ALL-CAPS. 17053b137 is authored by Peter Amiri with Signed-off-by: Peter Amiri <peter@alurium.com> — the DCO trailer matches the commit author identity. ✓


No new findings. The PR is clean end-to-end.

@wheels-bot

wheels-bot Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — Reviewer B (round 1)

A's review of the 17053b137 test-hardening commit checks out across every claim. All four concrete assertions were verified against the diff; no false positives, no missed issues, and the COMMENTED verdict is correct given that the bot cannot self-approve. We are aligned; the PR is ready for a human maintainer to approve and merge.

Sycophancy

None detected. A's "no new concerns" conclusion is earned: each claim traces back to an actual diff line (regex patterns, Lucee 7 guard, mcpHiddenTools entry, commit metadata). No unconditional LGTM.

False positives

None detected. Verified each of A's assertions against the diff:

  • Block-comment regex reReplace(s, "/\*[\s\S]*?\*/", "", "all") — [\s\S]*? is lazy and cross-line-safe without a dotall flag; correct on all CFML engines. ✓
  • Line-comment regex reReplace(s, "(?m)^[ \t]*//.*", "", "all") — (?m) inline flag makes ^ match per-line; strips standalone comment lines while leaving mid-string URL slashes intact. A's cross-engine claim is accurate (all CFML engines delegate to Java's regex engine). ✓
  • Function-body scoping — mid(variables.source, startIdx, 200) then find("}", rest) with the Lucee 7 guard braceAt > 0 ? left(rest, braceAt) : rest. The actual main() body is ~40 chars; 200 chars is adequate. ✓
  • """main""" in mcpHiddenTools check — "main" is the first entry in the return [...] array in Module.cfc; the 800-char window reaches it easily even after comment stripping. ✓
  • $stripComments private convention — A's caveat that CLAUDE.md's $-prefix/private rule applies only to vendor/wheels/ mixin integration, not test specs, is correct. In a TestBox spec, private prevents TestBox from treating it as a test case. ✓
  • DCO on 17053b137 — git show confirms author Peter Amiri <peter@alurium.com> and trailer Signed-off-by: Peter Amiri <peter@alurium.com>. Author identity matches trailer. ✓

Missed issues

None detected. The change is a test-only, CLI-only hardening pass with no functional side-effects. No cross-engine concerns (test CFC, no vendor/wheels/ mixin paths). All four prior-round consensus findings remain resolved per verification in round 2 of the prior SHA. The CHANGELOG and docs changes in the cumulative diff are from earlier commits already reviewed and approved.

Verdict alignment

COMMENTED is the correct state — the bot cannot approve its own work; this is how "human may approve" is expressed. Consistent with A's finding of zero new concerns.

Convergence

Aligned. A independently verified all four prior-round consensus findings as resolved, confirmed no new concerns in the hardening commit, and correctly deferred final approval to a human maintainer. Joint recommendation: human approval and merge.

The previous revision stripped comments with a global non-greedy
reReplace over the ~6k-line Module.cfc before scanning. That match hung
the Lucee 7 CLI test suite (5-minute timeout, HTTP 000000) while passing
on the Lucee that LuCLI 0.3.7 bundles — a classic engine-dependent
catastrophic backtrack.

Same comment-robustness, no whole-file scan: match declarations with
(?m)^[ \t]*public ... function main, which a commented-out
`// public ... main()` or a ` * ...` docblock line cannot satisfy. The
check is now a cheap first-match reFind with no backtracking-prone
construct.

Logic re-verified against the real Module.cfc incl. //-comment and
docblock negative controls.

Signed-off-by: Peter Amiri <peter@alurium.com>

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Wheels Bot — Reviewer A

TL;DR: The fifth and final commit (c06c5065d) is a focused, well-motivated refactor of MainCommandSpec.cfc: it swaps the whole-file comment-stripping approach (which caused catastrophic backtracking on Lucee 7) for line-anchored regex patterns. The approach change is correct, well-documented, and adds no regression risk. All prior findings are resolved. Verdict: comment — no blockers; the PR is clean and ready for a human maintainer to approve.


Correctness

The line-anchoring approach is sound. Every pattern changes from an unanchored scan like:

reFindNoCase("public\s+(string|any)\s+function\s+main\s*\(", source)

to a line-start-anchored scan:

reFindNoCase("(?m)^[ \t]*public\s+(string|any)\s+function\s+main\s*\(", source)

The (?m)^[ \t]* prefix means the match can only begin at the start of a line after optional tabs/spaces. A commented-out // public string function main() cannot satisfy it (the line starts with //). A JSDoc line * public string function main() cannot satisfy it (the line starts with *, not whitespace). The docblock in the spec correctly enumerates both negative controls and states they were verified against the real Module.cfc. ✓

Test 2's startIdx now points to the start of the public string function main() { line (including leading whitespace). mid(source, startIdx, 200) therefore captures the full single-delegation body; find("}", rest) always hits the closing brace of main() (no inner braces in a one-liner), and left(rest, braceAt) preserves the showHelp call. ✓


Cross-engine

  • (?m) inline flag in reFindNoCase(): Java MULTILINE mode; supported identically on Lucee 5/6/7, Adobe CF 2018/2021/2023/2025, and BoxLang. ✓
  • [ \t] in a character class: passed through as the two-character regex literal in CFML strings (CFML does not process backslash escapes); Java regex interprets \t as the tab character inside []. ✓
  • No [\s\S]*? over large source: the catastrophic-backtrack root cause (the $stripComments helper in 17053b137) is fully removed. ✓
  • No Left(str, 0) risk: test 2 retains the braceAt > 0 ? guard from the prior revision (cross-engine invariant 8). ✓

Tests

All four test cases remain intact with the same logical coverage:

  1. main() is declared as a public function (line-anchored, comment-resistant) ✓
  2. main() delegates to showHelp() (body scan scoped to function boundary) ✓
  3. showHelp() is a real implementation, not a stub (checks declaration and banner literal) ✓
  4. "main" is in mcpHiddenTools() (800-char window from function start; "main" is the first array entry, so the window is adequate) ✓

The removal of variables.rawSource and the now-unnecessary $stripComments helper simplifies the spec by 44 lines with no coverage loss.


Commits

Five commits, all well-formed:

SHA Message Result
63e3cbab fix(cli): handle bare wheels invocation by delegating to showHelp() Pass
20e93bc9 docs(web/guides): note bare wheels invocation prints help in verification section Pass
932a6a8e fix(cli): address Reviewer A/B consensus findings (round 1) Pass
17053b13 test(cli): harden #2840 main() regression spec to be comment-aware Pass
c06c5065 test(cli): line-anchor the #2840 spec instead of stripping comments Pass

All five: type from allowlist, scope non-empty, subject ≤ 100 chars, not ALL-CAPS. DCO Signed-off-by trailers present and match commit author identities. ✓


No new concerns introduced in c06c5065d. The diff is 25 insertions / 44 deletions of test infrastructure, net simpler, with equivalent correctness guarantees and better runtime behavior on Lucee 7.

@wheels-bot

wheels-bot Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — Reviewer B (round 1)

A's review of the c06c5065d test-refactor commit is accurate and well-supported. Every regex claim was verified against the actual diff; all prior-round findings remain resolved; COMMENTED is the correct verdict for bot-authored work. We are aligned.

Sycophancy

None detected. A's "no new concerns" conclusion is backed by line-level evidence for each regex pattern, the cross-engine flag analysis, the Lucee 7 backtracking root-cause, and the commit metadata table. No unconditional LGTM.

False positives

None detected. Verified each claim:

  • Line-anchoring robustness ((?m)^[ \t]*public\s+...): the (?m)^ anchor means only a real declaration that opens a line (after optional indent) can satisfy it. A // public ... main() line starts with //; a JSDoc * public line starts with * — neither matches. ✓
  • [ \t] in CFML string: CFML does not process \t as a tab in double-quoted string literals; the two characters \ + t are passed verbatim to Java's regex engine, which interprets \t as tab inside a character class. ✓
  • startIdx in test 2: reFindNoCase(...) returns the 1-based position of the match start (first char of the leading whitespace or of public). mid(source, startIdx, 200) captures the one-liner main() body fully. ✓
  • braceAt > 0 ? guard: Present at body = braceAt > 0 ? left(rest, braceAt) : rest. Guards Left(str, 0), which crashes Lucee 7 (cross-engine invariant 8). ✓
  • """main""" in test 4: In CFML, """main""" evaluates to "main" (literal double-quote embedded). The first entry of mcpHiddenTools() is "main" — the 800-char window reaches it. ✓
  • No catastrophic-backtrack risk: Diff confirms removal of variables.rawSource and the $stripComments helper that triggered the Lucee 7 timeout. ✓

Missed issues

None detected. The c06c5065d commit is a test-spec refactor with no functional side-effects. DCO on c06c5065 confirmed: Signed-off-by: Peter Amiri <peter@alurium.com> matches author identity. All four prior-round consensus findings (docs version bracket, inline issue-ref comment, JSDoc hint text, CHANGELOG section order) remain resolved per the 932a6a8e address-review commit and verified by B in the 17053b13 round. No cross-engine concerns: CLI-only test code.

Verdict alignment

COMMENTED is correct — the bot cannot self-approve; this is how "human may approve" is expressed. Consistent with A's finding of zero new concerns.

Convergence

Aligned. A independently confirmed all prior consensus findings remain resolved and found no new concerns in the test-refactor commit. Joint recommendation: a human maintainer approves and merges.

@bpamiri
bpamiri merged commit 6318bee into develop Jun 5, 2026
12 checks passed
@bpamiri
bpamiri deleted the fix/bot-2840-running-wheels-cli-without-arguments-results-in-er branch June 5, 2026 00:05

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Wheels Bot — Reviewer A

TL;DR: The only change since the last review (SHA c06c5065d) is a develop-merge commit (d5091882b) that incorporated upstream develop changes into the fix branch before final merge — no modifications to the fix, the test spec, or the docs. The PR has since been merged. All findings from previous review rounds remain resolved; no new concerns introduced. This review is a completeness pass only.


What changed at d509188

git show --stat d5091882b confirms this is a merge commit (Merge: c06c5065d f4ed63759) authored by Peter Amiri that pulled develop into the branch. The files it touches (.ai/, .github/workflows/, CHANGELOG.md, CLI templates, test infrastructure) are all from other PRs landing on develop — not from this fix. The PR diff (gh pr diff 2842) is unchanged from what was reviewed at c06c5065d:

  • cli/lucli/Module.cfc: main() delegation shim + mcpHiddenTools() entry — clean.
  • cli/lucli/tests/specs/commands/MainCommandSpec.cfc: line-anchored source-scan spec — clean.
  • CHANGELOG.md: entry under [Unreleased] → Fixed — clean.
  • web/sites/guides/src/content/docs/v4-0-0/command-line-tools/installation.mdx: docs sentence with accurate version language — clean.

No findings. The PR is merged.

@wheels-bot

wheels-bot Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — Reviewer B (round 1)

A's completeness pass on the merge commit (d5091882b) is accurate. The PR is merged, the diff is identical to what was reviewed at c06c5065d, and A's claim that the merge commit only brings in upstream develop changes (not touching the fix) is correct. No new concerns, no prior findings reopened.

Sycophancy

None detected. A explicitly identifies this as a completeness pass triggered by the develop-merge commit, explains the merge parents, and confirms the substantive diff is unchanged — not a blanket LGTM.

False positives

None detected. A's claim that the four files in the PR diff (Module.cfc, MainCommandSpec.cfc, CHANGELOG.md, installation.mdx) are unchanged from c06c5065d is consistent with gh pr diff 2842 showing the same four-file diff reviewed in prior rounds.

Missed issues

None detected. The PR is already merged; the review is post-merge housekeeping only. All four prior-round consensus findings (docs version bracket, inline issue-ref comment, JSDoc hint text, CHANGELOG section ordering) were resolved in the 932a6a8e address-review commit and remain intact in the final merged state. No cross-engine concerns in the develop-merge delta.

Verdict alignment

COMMENTED is correct — the bot cannot self-approve, and there is nothing substantive to flag on a pure merge commit. Consistent with A's "no findings" body.

Convergence

Aligned. The fix, its test spec, the docs correction, and the CHANGELOG entry all landed cleanly. A's post-merge completeness pass confirms no regression from the develop merge. Joint recommendation: no action needed; PR is merged and all prior findings are resolved.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Running wheels cli without arguments results in error

1 participant