fix(cli): wheels packages help now documents add as canonical install verb#2729
Conversation
…tall verb LuCLI auto-introspection emitted a help row advertising `install <name> [--force] Install a package`, but LuCLI's own built-in extension installer intercepts the literal `install` verb across all modules before dispatch reaches `Module.cfc::packages()`. The advertised verb is unreachable — same trap that hit `wheels browser install` (renamed to `wheels browser setup` in #2345). Fix: own the help text directly. `wheels packages help`, `wheels packages --help`, and `wheels packages -h` now short-circuit to a hand-written `$packagesHelp()` that documents `add` as the canonical install verb, lists every reachable sub-verb, and includes a Note paragraph explaining why `install` does not work. Short-circuit fires before any network call (registry, manifest fetch), so help is always fast and offline. Spec at `cli/lucli/tests/specs/commands/PackagesCommandSpec.cfc` asserts the help output documents `add`, lists every canonical sub-verb, mentions LuCLI interception, and does not advertise an `install <name> [--force] Install a package` row. Fixes #2713 Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Wheels Bot — No doc updatesReviewed this PR's diff and found no docs that need updating (the guide pages under |
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR: The implementation is correct and well-reasoned — $packagesHelp() cleanly owns the help text, the short-circuit fires before any network call, and all three invocation forms (help, --help, -h) are wired up. However, the PR has a hard blocker: the TDD Gate CI check is already failing because the spec lives at cli/lucli/tests/specs/commands/PackagesCommandSpec.cfc, which the gate's path regex does not cover. There is also a dead-code clause in the guard condition and the Signed-off-by identity does not match the required bot identity. The correctness and cross-engine story are sound; these process issues need fixing before merge.
Correctness
Dead-code guard clause — sub == "-h" at Module.cfc line 2065
if (opts.help ?: false || sub == "help" || sub == "-h") {$packagesStripFlags unconditionally strips -h before this line runs (it adds its own if (a == "-h") { i++; continue; } guard in this same PR), so -h can never appear in positional and therefore sub can never equal "-h". The condition evaluates correctly because opts.help ?: false handles the -h case via $packagesArgsToOptions, but the third clause is misleading dead code. A future maintainer could read it as "positional -h is a supported form" and be confused when tracing the path.
Concrete fix: remove || sub == "-h" from the guard and add a comment that -h reaches $packagesHelp() via opts.help, set by $packagesArgsToOptions.
CI / TDD Gate (blocks merge)
The "Bot PR TDD Gate" required check is already failing:
##[error]Bot PRs must include a failing-then-passing spec under
tests/specs/ or vendor/wheels/tests/specs/
The gate's path pattern is '^(tests/specs/|vendor/wheels/tests/specs/)'. The new spec is at:
cli/lucli/tests/specs/commands/PackagesCommandSpec.cfc
This path matches neither prefix, so spec_changes is empty and the gate exits 1. The spec itself is correct and should remain at cli/lucli/tests/specs/ — that is the right home for CLI unit tests (all existing command specs live there). The fix needed is to the gate's pattern in .github/workflows/bot-tdd-gate.yml:
# current
grep -E '^(tests/specs/|vendor/wheels/tests/specs/)'
# fix — add the CLI test path
grep -E '^(tests/specs/|vendor/wheels/tests/specs/|cli/lucli/tests/specs/)'That one-line change should be included in this PR or in a pre-merge fixup commit.
DCO / Commit Trailer Identity
The commit trailer is:
Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
The required identity per _shared-rails.md is:
Signed-off-by: wheels-bot[bot] <wheels-bot[bot]@users.noreply.github.com>
The DCO GitHub App is a required status check. If it validates that the Signed-off-by identity matches the configured bot user, this will block merge. Add a fixup commit with the correct trailer.
Tests
The spec at cli/lucli/tests/specs/commands/PackagesCommandSpec.cfc is structurally sound: it extends wheels.wheelstest.system.BaseSpec, uses BDD describe/it, scaffolds the module with TestHelper, and asserts the right things. Two minor gaps:
-
No
-htest. The spec exercises["help"]and["--help"]but not["-h"]directly. Given that-his explicitly mentioned in the PR description and the help text documents it, addingmod.__arguments = ["-h"]; expect(Len(mod.packages())).toBeGT(0);would close the loop. -
REFindNoCasereturn-value assertion is semantically correct but reads ambiguously. The form:expect(REFindNoCase("install[[:space:]]+<name>...", out)).toBe(0);relies on the reader knowing
REFindNoCasereturns0when not found. A clearer equivalent:expect(out).notToInclude("install <name> [--force]");This is a readability nit — the regex check correctly guards the historic row. No correctness issue.
Verdict
The implementation logic is clean and the fix is correct. Two items block merge:
- Fix the TDD gate path regex in
bot-tdd-gate.ymlto includecli/lucli/tests/specs/. - Fix the
Signed-off-bytrailer to use thewheels-bot[bot]identity.
The dead-code guard clause and missing -h test case are worth addressing in this PR but are not merge-blockers.
Wheels Bot — Reviewer B (round 1)A correctly identified the hard blocker (TDD gate) and the dead-code nit, and the overall verdict of "request changes" is right. One finding is a false positive with wrong remediation guidance: the DCO concern. Convergence is appropriate on the real changes. SycophancyNone detected. A's praise ("implementation is correct and well-reasoned") is backed by the correctness analysis, not handed out freely. False positivesDCO finding — partially wrong reasoning and wrong fix. A claims the
The real concern is a policy violation — Missed issuesNone material. A caught the TDD gate blocker (confirmed: One minor observation A did not raise: if the TDD gate regex is expanded to include Verdict alignmentA's COMMENTED verdict (neither APPROVE nor REQUEST_CHANGES at the API level) is consistent with the findings — two items it names as merge blockers are real (TDD gate failing) and meaningful (policy: wrong bot identity). Appropriate. ConvergenceAligned. Changes are needed. The actionable fix set:
DCO / Signed-off-by should NOT be in the fix set: the existing trailer correctly matches the commit author; changing it to |
Wheels Bot — Address Review (round 1)Applied consensus findings from Reviewer A and Reviewer B's convergence (round 1):
Intentionally skipped (not part of the consensus):
Test runner — The new commit will trigger a fresh Reviewer A run on the updated SHA. Convergence loop continues until reviewers align on |
…i specs Addresses Reviewer A & B consensus from PR #2729 (round 1): - `.github/workflows/bot-tdd-gate.yml`: add `cli/lucli/tests/specs/` to both the spec regex and the `impl_changes` exclusion. The packages help spec lives there, so the gate currently rejects the PR with "Bot PRs must include a failing-then-passing spec" even though the spec exists. Excluding the path from `impl_changes` also avoids double-counting it as both spec and implementation, which B flagged as a latent risk. - `cli/lucli/Module.cfc`: remove the dead `|| sub == "-h"` clause from the packages() help short-circuit. `-h` is consumed by $packagesArgsToOptions (sets opts.help = true) and stripped from positionals by $packagesStripFlags before `sub` is read, so the third clause is unreachable. Added a comment documenting the data flow and parenthesized the Elvis expression for clarity. - `cli/lucli/tests/specs/commands/PackagesCommandSpec.cfc`: add a third test case exercising `[\"-h\"]` directly. Asserts the short flag reaches the same hand-written help body as `help` and `--help` (verified by the presence of "wheels packages add" in the output). Skipped per B's round-1 analysis: the DCO/Signed-off-by trailer change A proposed. The existing trailer matches the commit author, which is what the DCO App validates — rewriting it would create a mismatch. There is also no DCO check in the current required-checks list. Refs #2713
There was a problem hiding this comment.
Wheels Bot -- Reviewer A
TL;DR: This is the fixup commit addressing all three round-1 blockers. The dead guard clause is removed with a data-flow comment; the TDD gate path regex is extended in both the spec-detection and impl-exclusion expressions; the missing -h test case is added. No new correctness, cross-engine, or security issues were introduced. Verdict: approve (submitted as comment because GitHub does not allow self-approval on bot-authored PRs).
Correctness
All round-1 blockers are resolved:
-
Dead guard clause removed. cli/lucli/Module.cfc line 2070 is now:
if ((opts.help ?: false) || sub == "help") {The previous clause || sub == "-h" was unreachable: packagesArgsToOptions consumes -h into opts.help, and packagesStripFlags removes it from positional before sub is read. The added comment at lines 2066-2069 documents that data flow for future maintainers.
-
TDD gate path regex. Both the spec-detection grep (line 61) and the impl-exclusion grep (line 73) in .github/workflows/bot-tdd-gate.yml now include cli/lucli/tests/specs/. The PR spec file (PackagesCommandSpec.cfc) registers as a spec change; Module.cfc registers as implementation -- gate passes cleanly.
-
Elvis precedence. Parenthesizing (opts.help ?: false) is correct and explicit. Without parens, ?: could bind to the full right side of || depending on CFML engine operator-precedence rules.
Tests
The -h test case (PackagesCommandSpec.cfc lines 37-45) asserts both that output is non-empty and that it contains "wheels packages add", confirming the short flag routes to the same hand-written help body.
Minor nit: expect(Len(out)).toBeGT(0) on line 38 is redundant -- the following toInclude assertion also fails on empty output. No correctness impact.
Commits
Commit 81caf1a is authored by Peter Amiri with no Signed-off-by trailer. If the DCO App validates human-authored commits it would need Signed-off-by: Peter Amiri peter@alurium.com. The commit body notes the DCO App is not in the required-checks list, consistent with Reviewer B's round-1 finding. Flagging for awareness; not a merge blocker.
Wheels Bot — Reviewer B (round 1)A's review of the fixup commit is accurate and well-grounded. All three round-1 blockers are verifiably resolved (CI confirms: Bot PR TDD Gate now passes), the verdict of approve is consistent with the findings, and A's two remaining observations (the redundant assertion nit and the DCO awareness flag) are correctly scoped. Aligned on approve. SycophancyNone detected. A's approval is earned — it is supported by line-specific verification of each resolved blocker, not free-handed. False positivesNone. Spot-checking A's specific claims against the diff:
Missed issuesNone material. The diff touches only Verdict alignmentA's COMMENTED/approve verdict is consistent with the findings: blockers are gone, CI is green, and the two remaining observations are correctly classified as nits rather than blockers. ConvergenceAligned. A's verdict (approve) is the right call on this SHA. No changes are needed. |
…s-packages-help-advertises-install-but-the-wo # Conflicts: # CHANGELOG.md
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR: This is the third pass on PR #2729 (head SHA 88654fa60). The merge commit brings in develop cleanly; the substantive changes from commits 33321057f (initial implementation) and 81caf1a09 (fixup: dead guard clause removed, TDD gate extended, -h test added) are all addressed and correct. Round-1 and round-2 blockers are resolved. No correctness, cross-engine, or security issues in the new state. Two minor nits remain — one test redundancy and the DCO trailer situation on the human-authored commit — neither blocks merge.
Correctness
Implementation logic is clean. The data flow through $packagesArgsToOptions -> $packagesStripFlags -> packages() is consistent:
--helpand-hboth setopts.help = truein$packagesArgsToOptions(cli/lucli/Module.cfcline 2189).$packagesStripFlagscorrectly excludes--helpfrom the boolean-skip branch (it is in the booleans list at line 2207) and explicitly skips-hvia its own guard (lines 2213-2215).- The guard
if ((opts.help ?: false) || sub == "help")at line 2070 fires before the switch and before any network call. Elvis precedence is correctly parenthesised. $packagesHelp()is a clean string accumulator with no branching or external dependencies — always fast and offline.
The install case retained in the switch (lines 2103-2115) as a documentation marker with a friendly redirect is a good defensive measure for the day LuCLI stops intercepting the verb.
Tests
cli/lucli/tests/specs/commands/PackagesCommandSpec.cfc — one redundancy nit.
Line 40: expect(Len(out)).toBeGT(0) in the -h test is unreachable as a distinct failure mode because line 43 (expect(out).toInclude("wheels packages add")) already fails on empty output. Not wrong; just noise.
The REFindNoCase("install[[:space:]]+<name>...", out).toBe(0) negative assertion at line 57 is correct — POSIX [[:space:]] is supported by both Lucee and Adobe CF (confirmed by existing usage in vendor/wheels/Model.cfc:30 and vendor/wheels/tests/specs/cli/LinuxPackageStagingSpec.cfc:82). The assertion properly guards against the old row being re-added.
TDD Gate (.github/workflows/bot-tdd-gate.yml)
Both the spec-detection pattern (line 61) and the impl-exclusion pattern (line 73) now include cli/lucli/tests/specs/. With this PR's files:
cli/lucli/tests/specs/commands/PackagesCommandSpec.cfcregisters as speccli/lucli/Module.cfcregisters as implementation.github/workflows/bot-tdd-gate.ymlandCHANGELOG.mdare excluded from both buckets
Gate passes cleanly.
Commits
Three commits in this PR:
-
33321057f—fix(cli): wheels packages help now documents add as canonical install verb— valid conventional commit. HasSigned-off-by: claude[bot]. Identity mismatch vs. requiredwheels-bot[bot]was flagged in round 1; round 2 confirmed DCO is not in the required-checks list — not a merge blocker. -
81caf1a09—fix(cli): drop dead -h guard clause and unblock TDD gate for cli/lucli specs— valid conventional commit. NoSigned-off-bytrailer for the human author (bpamiri). Same DCO note — not a required check. -
88654fa60— Merge commit incorporating origin/develop. Standard merge commit format; no conventional-commit requirement.
Docs
CHANGELOG [Unreleased] entry is present and accurate. The PR checklist correctly leaves Framework Docs, AI Reference Docs, and CLAUDE.md unchecked — this is a pure CLI/help-text fix with no framework API or convention changes.
No blocking issues. The nits above (redundant Len check, DCO trailers) are carry-overs already acknowledged in prior rounds and below the merge-threshold. Change is ready.
Wheels Bot — Reviewer B (round 1)A's review of the post-merge SHA is accurate and concise. The prior-round convergence (approve on SycophancyNone detected. A's "change is ready" conclusion is backed by line-specific verification of each resolved blocker, not handed out freely. False positivesNone. Spot-checking A's specific claims:
Missed issuesNone material. A notes the only two carry-over nits (redundant Verdict alignmentA's COMMENTED/ready verdict is consistent with the findings: no blockers remain, CI is green, and the outstanding observations are acknowledged nits from prior rounds. ConvergenceAligned. A's assessment (approve, no blocking issues) is the right call on this SHA. No changes are needed. |
…s-packages-help-advertises-install-but-the-wo # Conflicts: # CHANGELOG.md
…s-packages-help-advertises-install-but-the-wo # Conflicts: # CHANGELOG.md
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR: This is a fourth pass triggered by the merge-to-develop commit (48e534997). The PR's own substantive changes — $packagesHelp(), the short-circuit guard, the TDD gate path extension, and the spec — are all intact and unchanged from the last reviewed SHA (88654fa60). The merge introduced one real diff: a CHANGELOG.md conflict between the PR entry and several develop entries (#2712, #2714, #2717, CORS fixes). The conflict was resolved correctly — all entries from both sides are present and no PR entry was dropped. No new correctness, cross-engine, or security issues. Verdict: comment (carry-over nits only; nothing blocks merge).
Correctness
Merge conflict resolution is correct.
The CHANGELOG.md conflict (noted in the merge commit message) was resolved by preserving the PR's #2713 Fixed entry and inserting the develop-side entries (#2714, #2717, CORS #2707/#2704, and #2712 Added) around it. No entry was duplicated or dropped. Verified by diffing 88654fa60..48e534997 -- CHANGELOG.md.
PR implementation files are intact.
The $packagesHelp() function, the short-circuit guard if ((opts.help ?: false) || sub == "help"), and the $packagesArgsToOptions/$packagesStripFlags additions are all present in the merged state at their original line numbers. The .github/workflows/bot-tdd-gate.yml path additions for cli/lucli/tests/specs/ are also intact.
Develop-side changes in the merge commit are unrelated to this PR.
cli/lucli/Module.cfc received one additional hunk (the paginationLinks() upgrade-check grep from #2714), and vendor/wheels/PackageLoader.cfc received the CFML-mapping feature from #2732. Neither touches the packages help path.
Tests
No new test changes. The PackagesCommandSpec.cfc spec is intact. Carry-over nit from round 3 remains:
cli/lucli/tests/specs/commands/PackagesCommandSpec.cfcline 38:expect(Len(out)).toBeGT(0)in the-htest is redundant — the followingtoIncludeassertion already fails on empty output. Minor noise; no correctness impact.
Commits
New commits since last reviewed SHA:
a575c521b—fix(middleware): emit Vary: Origin from Cors when reflecting allowed origins (#2724)— valid, from develop.f9a4312b6—fix(middleware): Cors no longer emits raw comma list as Access-Control-Allow-Origin (#2725)— valid, from develop.ce47c8024—fix(view): paginationNav rejects unknown sub-helper arguments (#2726)— valid, from develop.221612c29— merge commit.151feb319—feat(config): auto-register per-package CFML mapping in PackageLoader (#2732)— valid, from develop.48e534997— merge commit (conflict inCHANGELOG.md, resolved correctly).
All conventional-commit subjects are within 100 chars and conform to the commitlint.config.js allowlist. The DCO situation on the human-authored 81caf1a09 (Peter Amiri, no Signed-off-by) is unchanged from round 2 — DCO is not a required status check on this repo, so it does not block merge.
No blocking issues. The merge is clean and the PR is ready for a human approving review.
Wheels Bot — Reviewer B (round 1)A's fourth-pass review is accurate and appropriately scoped. Prior convergence (approve on SycophancyNone detected. A's conclusion ("no blocking issues, ready for human approving review") is backed by line-specific verification of the CHANGELOG conflict resolution and implementation file integrity — not handed out freely. False positivesNone. Spot-checking A's specific claims against the diff:
Missed issuesNone material. A correctly scopes this pass to what the merge commit changed. The two carry-over nits from prior rounds — the redundant Verdict alignmentA's COMMENTED verdict (nothing blocks merge) is consistent with the findings. The PR's blockers were resolved two SHAs ago; this pass confirms the merge did not reintroduce them. ConvergenceAligned. A's assessment matches B's prior convergence on |
…s-packages-help-advertises-install-but-the-wo # Conflicts: # CHANGELOG.md
There was a problem hiding this comment.
Wheels Bot — Reviewer A
TL;DR: Fifth pass on PR #2729 (head SHA 41c981a9a). The new merge commit pulls in #2734 (anatomy-of-a-package blog post + manifest doc fixes: dependencies to requires / replaces / suggests in CLAUDE.md and both guide snapshots). The PR own implementation -- $packagesHelp(), the short-circuit guard, the TDD gate path extension, and the spec -- is unchanged from the last reviewed SHA (48e534997). CHANGELOG conflict resolved correctly, all round-1/2 blockers long since closed. One minor carry-over nit remains. Verdict: comment (nothing blocks merge).
Correctness
Merge conflict resolution is correct. The CHANGELOG.md conflict between the PR #2713 Fixed entry and the #2734 Fixed entry from develop was resolved by preserving both entries. No entry was dropped or duplicated. Confirmed via git show 41c981a9a -- CHANGELOG.md.
#2734 content is correct on its own terms. CLAUDE.md now shows requires, replaces, and suggests in the example manifest (replacing the legacy dependencies key), and adds a prose paragraph describing the three fields. This matches the PackageLoader / ModuleGraph.cfc behaviour described in existing CLAUDE.md text. No inconsistency introduced.
PR implementation is intact. The $packagesHelp() accumulator, the if ((opts.help ?: false) || sub == "help") short-circuit at cli/lucli/Module.cfc line 2070, the --help / -h parsing additions to $packagesArgsToOptions and $packagesStripFlags, and the bot-tdd-gate.yml path extensions for cli/lucli/tests/specs/ are all unchanged from the round-3 reviewed SHA (88654fa60).
Tests
One carry-over nit -- redundant Len check in the -h test. cli/lucli/tests/specs/commands/PackagesCommandSpec.cfc line 40: expect(Len(out)).toBeGT(0) is unreachable as a distinct failure mode -- an empty out will fail on the toInclude at line 43 first. Minor noise, no correctness impact. Carried over from round 3.
Commits
New commit since last reviewed SHA: 41c981a9a -- merge commit (origin/develop into PR branch), conflict in CHANGELOG.md resolved correctly. All earlier commits (33321057f, 81caf1a09) were reviewed in rounds 1 and 2. DCO trailer situation on 81caf1a09 (human-authored, no Signed-off-by) is unchanged and confirmed non-blocking (DCO App is not a required status check on this repo).
No blocking issues. The PR is clean and ready for a human approving review on develop.
Wheels Bot — Reviewer B (round 1)A's fifth-pass review is accurate and concise. Prior convergence (approve on `48e534997`) holds: the head SHA `41c981a9a` is a merge commit from develop carrying only a CHANGELOG conflict resolution and unrelated `#2734` develop-side changes, with no new implementation delta. A's verification of each resolved blocker and the carry-over nit is correct. Aligned on approve. SycophancyNone detected. A's "nothing blocks merge" conclusion is backed by line-specific verification of each claim, not handed out freely. False positivesNone. Spot-checking A's specific claims against the diff:
Missed issuesNone material. The diff touches only Verdict alignmentA's COMMENTED verdict (nothing blocks merge, ready for human approving review) is consistent with the findings: all round-1 blockers are long resolved, CI is green, and the outstanding nit is correctly scoped as non-blocking. ConvergenceAligned. A's verdict (approve, no blocking issues) is the right call on this SHA. No changes are needed. |
…spatch (#2786) * fix(cli): make `wheels packages install` a real alias for `add` in dispatch The `case "install":` branch in `Module.cfc::packages()` previously printed a warning to stdout and returned an empty string instead of installing anything. That was wrong for every caller path that actually reaches module dispatch — the stdio MCP server, scripted in-process clients, and the spec suite — because `PackagesMainCli.install()` itself has been a transparent alias for `add()` since #2729. The dispatch layer was the only place where the alias broke. The shell-facing `wheels packages install <name>` is still intercepted by LuCLI's built-in extension installer upstream of module dispatch and remains broken on that path (documented in the module-owned `--help` text). This change only fixes the paths that LuCLI does NOT intercept. Both verbs now share a single fall-through case body so validation, error shape, and install behavior cannot drift apart again. Fixes #2785 Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> * docs(web/guides): clarify install-as-alias behavior in packages CLI section Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> * fix(cli): address Reviewer A/B consensus findings (round 1) - PackagesCommandSpec: add `expect(installResult.type).toBe(addResult.type)` after the existing `.notToBe("")` assertion so the equivalence claim in the surrounding comment is actually enforced. A regression where `install` throws at argument validation (before the registry call) would have satisfied `.notToBe("")` but diverged from `add`'s shape; the new assertion pins it. - CHANGELOG: terminal period on the new `[Unreleased] / ### Fixed` entry for consistency with surrounding entries. Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> * fix(web/guides): address Reviewer A/B consensus findings (round 2) - web/sites/guides/src/content/docs/v4-0-0/digging-deeper/packages.mdx (line 320) — scope the install-as-alias note to v4.0.1+. The previous wording asserted the alias was transparent on MCP / in-process paths, but that's only true after this PR (which targets v4.0.1). On v4.0.0 itself, MCP also no-ops; the versioned v4.0.0 docs now say so explicitly and point readers to the v4.0.1 snapshot for the alias behavior. The v4-0-1-snapshot/ copy was already correct and is untouched. Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> --------- Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Signed-off-by: Peter Amiri <peter@alurium.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Peter Amiri <peter@alurium.com>
Documents
addas the canonical install verb inwheels packages --help/wheels packages help/wheels packages -h, and explains why typinginstalldoes not work. LuCLI's built-in extension installer intercepts the literalinstallverb across all modules before dispatch reachesModule.cfc::packages(), so the previously-advertisedinstall <name> [--force] Install a packagerow pointed at a verb that could never run. Same trap that hitwheels browser install(renamed towheels browser setupin #2345).Fixes #2713
Summary
$packagesHelp()method onModule.cfcemits a hand-written help string the module owns directly (rather than relying on auto-introspection that drifted from the real CLI surface).wheels packages help(positional),wheels packages --help, andwheels packages -hall short-circuit to it before any network call — help is fast and offline.list,search,show,add,update,remove,registry refresh,registry info), documentsaddas the canonical install verb, and includes a Note paragraph explaining the LuCLI interception so users typing the oldinstallform understand what is happening.Files
cli/lucli/Module.cfc— short-circuit inpackages(), new$packagesHelp(),--help/-hparsing in$packagesArgsToOptions/$packagesStripFlags.cli/lucli/tests/specs/commands/PackagesCommandSpec.cfc— failing-then-passing spec asserts help documentsadd, lists every sub-verb, mentions LuCLI interception, and does not advertise the deadinstall <name> [--force] Install a packagerow.CHANGELOG.md— Fixed entry under[Unreleased].Checklist
cli/lucli/tests/specs/commands/PackagesCommandSpec.cfc)bash tools/test-cli-local.shexecution was blocked by sandbox permissions in this run, so the spec-then-impl flow is left for CI (bot-tdd-gate.ymlverifies both spec and impl diffs exist;pr.ymlruns the suite end-to-end).