Skip to content

fix(cli): normalize Windows drive-letter paths in wheels new#2835

Merged
bpamiri merged 4 commits into
developfrom
peter/fix-windows-drive-letter-uri-misparse
Jun 5, 2026
Merged

fix(cli): normalize Windows drive-letter paths in wheels new#2835
bpamiri merged 4 commits into
developfrom
peter/fix-windows-drive-letter-uri-misparse

Conversation

@bpamiri
Copy link
Copy Markdown
Collaborator

@bpamiri bpamiri commented May 29, 2026

Summary

wheels new <name> fails on a fresh Scoop install with:

[ERROR] Error: lucee.runtime.exp.NativeException:
there is no Resource provider available with the name [c],
available resource providers are [ftp, zip, tar, tgz, http, https, ram, s3]

…before any module output appears. Draft because Windows verification is pending — patched Module.cfc is being hand-delivered to risto for drop-in test against his Scoop install. Will mark ready once that confirms.

Root cause

Module.init() receives cwd from LuCLI as the JVM's user.dir, which on Windows is C:\Users\<name> with backslashes. The early wheels new code path then constructs paths like:

targetDir = variables.cwd & "/" & appName
          = "C:\Users\cy" & "/" & "blog"
          = "C:\Users\cy/blog"   // mixed slashes

Lucee 7's ResourceUtil runs a URI scheme-detection regex (^[a-zA-Z][a-zA-Z0-9+.-]*:) before its Windows drive-letter special case in some code paths. The regex matches c: from C:\Users\cy/blog, extracts c as the scheme, looks for a matching resource provider, finds none (only ftp, zip, tar, tgz, http, https, ram, s3), and throws.

The same call with pure backslashes (C:\Users\cy\blog) works because Lucee's Windows path detection kicks in earlier; the same call with pure forward slashes (C:/Users/cy/blog) works for the same reason. Only the mixed-slash form crashes.

The other suspect site is resolveProjectRoot() / resolveFrameworkSource(), where java.io.File.getCanonicalPath() returns backslashes (Windows native) and we concat with /vendor/wheels — same crash.

Mac/Linux are unaffected (no <letter>: prefix). This has been latent in the Scoop install since day one but was masked by a separate routing bug (the missing -Dlucli.binary.name=wheels flag in scoop-wheels/bucket/wheels.json, fixed in wheels-dev/scoop-wheels@30ea6e5). With that gone, the first Windows user to install fresh and run wheels new hits this immediately.

Fix — two-layer defense

  1. $normalizePath() replaces backslashes with forward slashes. Called on variables.cwd in init() and on every File.getCanonicalPath() result in resolveProjectRoot() and resolveFrameworkSource(). Forward-slash-only paths bypass the URI scheme ambiguity because C:/... matches Lucee's Windows-path detection before the URI regex.

  2. $safeDirExists() / $safeFileExists() wrap their built-in counterparts with a java.io.File-based fallback. If anything still slips through normalization (a user-provided WHEELS_FRAMEWORK_PATH, a mapping, etc.), directoryExists() throws the same NativeException — we catch it and fall back to new java.io.File(path).isDirectory(), which is OS-native and has no concept of URI schemes.

Layer 1 prevents the throw; layer 2 catches anything layer 1 misses. Both no-op on Mac/Linux.

Scope

Limited to the pre-scaffold path: init(), resolveProjectRoot(), scaffoldNewApp()'s first directoryExists, and resolveFrameworkSource()'s three directoryExists calls. Once targetDir is forward-slash-normalized, the recursive copyTemplateDir() loop inherits clean paths — no additional changes needed.

Test plan

  • Smoke-tested on macOS by overwriting ~/.wheels/modules/wheels/Module.cfc with the patched version. wheels new test-patched scaffolded successfully with identical output to unpatched.
  • Windows drop-in test on risto's Scoop install — patched Module.cfc to be copied to C:\Users\cy\.wheels\modules\wheels\Module.cfc, then wheels new blog re-run.
  • After merge: confirm wheels new still works on Mac stable channel after the next 4.0.3 (or follow-on snapshot) reaches Scoop. Requires AUTO_MERGE_PAT scopes also resolved (separate Bug A — see scoop-wheels CI history).

…`wheels new`

`wheels new <name>` fails on a fresh Scoop install with:

    [ERROR] Error: lucee.runtime.exp.NativeException:
    there is no Resource provider available with the name [c],
    available resource providers are [ftp, zip, tar, tgz, http, https, ram, s3]

before any module output appears. Reported by Risto on Windows 11 via
Scoop after #2766 + the manifest fix landed (which unblocked `wheels new`
routing to the wheels module in the first place — see scoop-wheels
direct-push 30ea6e5 + 6cf06fc, 2026-05-28).

## Root cause

`Module.init()` receives `cwd` from LuCLI as the JVM's `user.dir`,
which on Windows is `C:\Users\<name>` with backslashes. The early
`wheels new` code path then constructs paths like:

    targetDir = variables.cwd & "/" & appName
              = "C:\Users\cy" & "/" & "blog"
              = "C:\Users\cy/blog"   # mixed slashes!

Lucee 7's `ResourceUtil` runs a URI scheme-detection regex
(`^[a-zA-Z][a-zA-Z0-9+.-]*:`) BEFORE its Windows drive-letter
special case in this code path. The regex matches `c:` from
`C:\Users\cy/blog`, extracts "c" as the scheme, looks for a
matching resource provider, finds none (only `ftp`, `zip`,
`tar`, `tgz`, `http`, `https`, `ram`, `s3` are registered),
and throws. The same call with pure backslashes (`C:\Users\cy\blog`)
works because Lucee's Windows path detection kicks in earlier;
the same call with pure forward slashes (`C:/Users/cy/blog`)
works for the same reason.

The other suspect site is `resolveProjectRoot()` /
`resolveFrameworkSource()`, where `java.io.File.getCanonicalPath()`
returns backslashes (Windows native) and we concat with
`/vendor/wheels`. Same mixed-slash pattern, same crash.

Mac/Linux are unaffected because paths never start with a
letter-colon prefix. The bug has been latent in the Scoop install
since day one but was masked by the `-Dlucli.binary.name=wheels`
omission (scoop-wheels 30ea6e5) — until that was fixed, no
Windows `wheels new` invocation reached the wheels module at all.

## Fix

Two-layer defense:

1. **Normalize all backslashes to forward slashes** before any
   path-existence check. `$normalizePath()` runs on `variables.cwd`
   in `init()` and on every `File.getCanonicalPath()` result in
   `resolveProjectRoot()` and `resolveFrameworkSource()`. Forward-
   slash-only paths bypass the URI scheme ambiguity because
   `C:/...` matches Lucee's Windows-path detection before its
   URI-scheme regex.

2. **`$safeDirExists()` / `$safeFileExists()` wrappers** with a
   `java.io.File`-based fallback. If anything still slips through
   normalization (a path the user provided via `WHEELS_FRAMEWORK_PATH`,
   a mapping, etc.), `directoryExists()` throws the same
   NativeException — we catch it and fall back to
   `new java.io.File(path).isDirectory()`, which is OS-native and
   has no concept of URI schemes.

Layer 1 prevents the throw in the first place; layer 2 prevents the
crash if layer 1 is incomplete. Both layers are no-ops on Mac/Linux
(no backslashes to normalize; `directoryExists` doesn't throw).

## Scope

Limited to the early `wheels new` path: `init()`,
`resolveProjectRoot()`, `scaffoldNewApp()`'s first `directoryExists`,
and `resolveFrameworkSource()`'s three `directoryExists` calls. Once
the framework source is located and we enter the recursive
`copyTemplateDir()` loop, paths are constructed from the (already
normalized) `targetDir` so they inherit forward slashes — no
additional changes needed there.

## Verification

- Smoke-tested on macOS by overwriting `~/.wheels/modules/wheels/Module.cfc`
  with the patched version: `wheels new test-patched` scaffolded
  successfully with identical output to unpatched.
- Windows verification pending — patched Module.cfc to be hand-delivered
  to Risto for drop-in test against his Scoop install before this PR
  merges.

Refs scoop-wheels manifest update 30ea6e5 (binary.name fix that
unmasked this bug).

Signed-off-by: Peter Amiri <peter@alurium.com>
@bpamiri bpamiri marked this pull request as ready for review June 4, 2026 02:31
Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

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: Solid, well-motivated fix for a real Windows Scoop regression. The two-layer defence ($normalizePath + $safeDirExists) is the right approach, and the root-cause write-up in the PR body is excellent. One dead-code finding, a missing CHANGELOG entry, and a gap in unit tests. Commenting rather than requesting changes — none of these block the fix from being correct.


Correctness

$safeFileExists is defined but never called.

cli/lucli/Module.cfc lines 84-92 add:

private boolean function $safeFileExists(required string p) {
    try {
        return fileExists(arguments.p);
    } catch (any e) {
        return createObject("java", "java.io.File").init(arguments.p).isFile();
    }
}

A search of every fileExists() call in the file confirms none was updated to $safeFileExists(). The PR description says scope is limited to the pre-scaffold directory-existence path — that is consistent, but then the helper should either be removed or have a comment explaining it is reserved for a follow-up. Dead helpers add maintenance surface for no current benefit.

Suggestion: Remove $safeFileExists now and re-introduce it in the PR that wires it up, or add an explicit note so reviewers know it is not accidentally orphaned.


Tests

No spec covers $normalizePath directly. The function is a pure string transformation and is trivially testable on macOS/Linux. Because $normalizePath is private, a thin public accessor or integration-level test through the module is needed. This is the highest-value regression guard given this is the exact failure mode the PR is fixing.

The $safeDirExists fallback arm (the catch that calls java.io.File.isDirectory()) is also untested. A stub that forces directoryExists() to throw would cover that path.


Docs

No [Unreleased] CHANGELOG entry for this fix. The section currently lists only the version-switcher docs change from #2827. A ### Fixed item should be added.


Commits

Single commit: fix(cli): normalize Windows drive-letter paths to forward slashes in wheels new — header is ~80 chars, within the 100-char limit. Type fix, scope cli, both valid. No issues.

@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented Jun 4, 2026

Wheels Bot -- Reviewer B (round 1)

A review 4424204651 body is 'Test body via field' with no line comments -- a placeholder, not a real review.

Sycophancy: None detected (no claims made).

False positives: None detected (no claims made).

Missed issues:

  • DRAFT status ignored. PR says Windows verification is pending; a real verdict should wait.
  • safeFileExists unused. Diff adds the function but zero call sites appear -- dead code or missed replacement.
  • Test coverage unmentioned. CLAUDE.md mandates tools/test-cli-local.sh for any cli/lucli change.
  • Silent exception swallow. Both safe helpers use catch(any e) with no logging.

Verdict alignment: COMMENTED is incidentally fine for a draft but not reached by analysis.

Convergence: Not aligned -- A must submit a real review covering (1) DRAFT/Windows context, (2) unused safeFileExists, (3) CLI test status.

Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

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 analysis rests on an incorrect premise — the review evaluated (ID 4424204651 with body "Test body via field") is not my substantive initial review. My actual review (PRR_kwDOABMUH88AAAABB7QBaw, submitted at 2026-06-04T02:37:13Z) already covered $safeFileExists dead code and test coverage gaps. I concede one genuine miss — the silent exception swallow in both safe helpers — and defend the rest of my findings against B's characterization.

Conceded points

  • Silent exception swallow: B is correct that I missed this. Both $safeDirExists and $safeFileExists use catch(any e) with no logging. On Windows — exactly the platform this fix targets — swallowing exceptions silently makes debugging harder. If the fallback java.io.File call itself fails (e.g., security manager, permissions), the caller sees false and gets a misleading "directory does not exist" result rather than an actionable error. The catch blocks should at minimum log to writeLog() or systemOutput() before returning the fallback value.

Defended points

  • B's premise that my review was a placeholder: B evaluated review ID 4424204651 with body "Test body via field." The review the GitHub API returns for this SHA from wheels-bot is PRR_kwDOABMUH88AAAABB7QBaw with a full, substantive body covering correctness, tests, docs, and commit conventions. B appears to have evaluated a different review ID — possibly an artifact of an earlier test submission not visible in the current review list. My actual review is not a placeholder.

  • $safeFileExists unused: B listed this as a missed issue, but my review's Correctness section flagged it explicitly, including the suggestion to either remove it now or document it as reserved for a follow-up PR.

  • Test coverage: B says "Test coverage unmentioned." My actual review's Tests section calls out exactly this — that $normalizePath needs a unit spec (it is a pure string transformation, trivially testable) and that the $safeDirExists fallback catch arm has no spec exercising the throw path. The CLAUDE.md tools/test-cli-local.sh mandate is worth noting as a checklist item, but the coverage gap was already in my review.

  • DRAFT status and verdict: B says a real verdict should wait due to DRAFT status. gh pr view 2835 --json isDraft returns false — the author has moved the PR out of draft and it is open for review. What I did not flag, and should have, is the unchecked "[ ] Windows drop-in test on risto's Scoop install" item in the test plan. That remains open and is worth a comment, but since the fix logic and root cause analysis are sound, it does not change my verdict.

Updated verdict

Still COMMENT — the core fix is correct, the root cause analysis is thorough, and the open items (dead $safeFileExists, silent exception swallow in both helpers, missing unit spec for $normalizePath, unchecked Windows manual verification checkbox) are robustness and polish gaps, not correctness blockers.

Address wheels-bot review on #2835:

- $safeFileExists was added beside $safeDirExists but never wired to a
  call site; the Windows path fix only checks directories. Remove the
  dead helper; re-add with its java.io.File fallback if a file-existence
  check ever needs it.
- Add an [Unreleased] "### Fixed" CHANGELOG entry for the drive-letter
  URI misparse fix.

$safeDirExists and its 8 call sites are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
@github-actions github-actions Bot added the docs label Jun 4, 2026
Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

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: Follow-up review for the updated head (f487bf54b). The previous review covered SHA 7e04f014a; the PR has since gained a refactor commit and a rebase on develop. The two issues the author addressed — removing the dead $safeFileExists helper and adding the CHANGELOG entry — are done cleanly. Two gaps from the prior cycle remain: the silent catch(any e) in $safeDirExists and the absence of a unit test for $normalizePath. Neither is a correctness blocker, so verdict is still comment.


Correctness

The core fix is sound. The two-layer defense — normalize backslashes to forward slashes in $normalizePath(), fall back to java.io.File.isDirectory() in $safeDirExists() — addresses the root cause cleanly. Each loop iteration in resolveProjectRoot and resolveFrameworkSource re-normalizes the result of File.init(dir).getCanonicalPath() before concatenation, so the traversal stays clean on Windows even after java.io.File.getParent() returns a backslash-separated string internally.

UNC paths are silently mishandled. $normalizePath converts \\server\share to //server/share, which is not a valid path on any platform. This is an edge case that cannot appear from a Scoop user.dir (user.dir is always a local path), so it is not a blocker. The function docblock should note the limitation to prevent future misuse:

/**
 * Normalize a LOCAL filesystem path to forward slashes for safe handoff
 * to Lucee file APIs on Windows. UNC paths (\\server\share) are not
 * supported and must not be passed here.
 */

Tests

$normalizePath still has no spec. This is a pure string transformation with no framework or file-system dependency — exactly the function that broke on Windows — and it is trivially testable. Since the function is private, the cleanest path is an integration-level check via the module's resolved cwd:

// cli/lucli/tests/specs/commands/NewCommandSpec.cfc — new describe block
describe("path normalization", () => {
    it("converts backslash cwd to forward slashes on all platforms", () => {
        // Simulates what Windows LuCLI hands Module.init()
        var m = new cli.lucli.Module(cwd="C:\Users\cy");
        expect(m.variables.cwd).toBe("C:/Users/cy");
    });
});

The $safeDirExists fallback catch arm is still untested. The path where Lucee throws and java.io.File.isDirectory() takes over is exercised only by the manual Windows drop-in noted in the PR checklist.


Conventions

Silent exception swallow in $safeDirExists. Flagged in round 1 of the prior review cycle but not addressed. From cli/lucli/Module.cfc:

private boolean function $safeDirExists(required string p) {
    try {
        return directoryExists(arguments.p);
    } catch (any e) {
        return createObject("java", "java.io.File").init(arguments.p).isDirectory();
        // e is discarded with no log
    }
}

If directoryExists() throws for a reason unrelated to the URI-scheme issue (permissions, a null path, etc.), the caller gets false with no diagnostic. On Windows — the exact platform this fix targets — that silent false could cause wheels new to pass directory checks while masking a real I/O failure. A systemOutput() before the fallback return surfaces this in CLI output without breaking UX:

} catch (any e) {
    systemOutput("[wheels] directoryExists fallback for: #arguments.p# (#e.message#)", true);
    return createObject("java", "java.io.File").init(arguments.p).isDirectory();
}

Commits

Both commits are well-formed:

Commit Header Chars DCO
7e04f014a fix(cli): normalize Windows drive-letter paths to forward slashes in wheels new ~73 Author signed
ef03ef777 refactor(cli): drop unused $safeFileExists, document #2835 in CHANGELOG ~61 Author signed

Minor nit: the refactor commit mixes two concerns (dead-code removal + CHANGELOG). Not a lint violation under squash-merge — just noted for history readability.


PR Checklist

The [ ] Windows drop-in test on risto's Scoop install checkbox in the PR description remains unchecked, yet the PR is no longer marked draft. If the Windows test passed, please tick the box or post a confirmation comment so the release pipeline and future reviewers can confirm test status without reading the full discussion.

Copy link
Copy Markdown
Contributor

@wheels-bot wheels-bot Bot left a comment

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: Round 3 / new head b8bfea85a — a merge-from-develop rebase only. The two PR-own commits (7e04f014a, ef03ef777) are byte-for-byte identical to those reviewed in round 2. The develop merge brought in the main() dispatcher fix (#2842) and its MainCommandSpec.cfc, neither of which belong to this diff. The core normalization fix remains correct. Three items from round 2 are still open; verdict holds at comment (no correctness blockers).


Correctness

The fix is sound and unchanged — $normalizePath() eliminates the mixed-slash form before any Lucee ResourceUtil call; $safeDirExists() adds a Java-level fallback for any path that still reaches directoryExists(). No new correctness issues.


Conventions

Silent exception swallow in $safeDirExists — still unresolved (raised in rounds 1 and 2, cli/lucli/Module.cfc lines 75–81):

private boolean function $safeDirExists(required string p) {
    try {
        return directoryExists(arguments.p);
    } catch (any e) {
        return createObject("java", "java.io.File").init(arguments.p).isDirectory();
        // e discarded silently — no log
    }
}

When directoryExists() throws for a reason unrelated to the drive-letter scheme (permissions, null path), the caller gets false with zero diagnostics. On Windows — the exact platform this fix targets — a silent false can cause wheels new to bypass directory-exists guards while masking a real I/O failure. A single systemOutput() before the fallback return makes the diagnosis visible without breaking UX:

} catch (any e) {
    systemOutput("[wheels] directoryExists() fallback for: " & arguments.p & " (" & e.message & ")", true);
    return createObject("java", "java.io.File").init(arguments.p).isDirectory();
}

Tests

$normalizePath still has no spec — pure string transformation, no framework or file-system dependency, trivially testable. cli/lucli/tests/specs/commands/NewCommandSpec.cfc has no coverage for backslash-to-forward-slash conversion. Suggested addition inside the existing NewCommandSpec:

describe("path normalization", () => {
    it("converts backslash cwd to forward slashes", () => {
        // Simulate what Windows LuCLI hands Module.init()
        var m = new cli.lucli.Module(cwd="C:\Users\cy");
        expect(m.variables.cwd).toBe("C:/Users/cy");
    });
});

$safeDirExists fallback path still untested. The catch arm that calls java.io.File.isDirectory() has no spec exercising the throw.


PR Checklist

The [ ] Windows drop-in test on risto's Scoop install checkbox in the PR description remains unchecked and the PR is not in draft. If the Windows test passed, please tick the box or post a brief confirmation so the next reviewer does not have to re-read the full discussion thread.


Commits

Commit Header Chars Type
7e04f014a fix(cli): normalize Windows drive-letter paths to forward slashes in wheels new ~73 valid
ef03ef777 refactor(cli): drop unused $safeFileExists, document #2835 in CHANGELOG ~61 valid

Both within the 100-char limit. Squash target is the PR title (fix(cli): normalize Windows drive-letter paths in wheels new), also valid.

@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented Jun 5, 2026

Wheels Bot — Reviewer B (round 1)

A's round 3 review on b8bfea85a is accurate and well-scoped. A correctly identifies this head as a merge-from-develop with no substantive diff change to the two PR-own commits, carries forward the two remaining advisory items (silent exception swallow, missing tests) that have been open since round 1, validates both commits against conventionalcommit rules, and keeps the verdict at COMMENTED rather than upgrading to APPROVED while those items are unresolved. I agree on all counts and am aligning on converged-changes.

Sycophancy

None detected. A held COMMENTED and enumerated concrete open items instead of saying LGTM.

False positives

None detected. I verified the two claims against the actual diff:

  • Silent exception swallow: the catch (any e) block in $safeDirExists() discards e with no systemOutput() or logging. A's reading is correct.
  • Missing test coverage: the diff touches only CHANGELOG.md and cli/lucli/Module.cfc. No test file appears anywhere in the diff. A's reading is correct.

Missed issues

None detected. A covered the relevant ground for a CLI-only change: correctness of the two-layer normalization approach, diagnostics gap in the fallback path, test coverage against CLAUDE.md's tools/test-cli-local.sh mandate, conventional-commit compliance for both commits, and the unchecked Windows verification checkbox. The CHANGELOG diff adds only the #2835 entry; the other bullets are context lines from develop, and A correctly left them unremarked.

Verdict alignment

COMMENTED is consistent with the findings. The open items are advisory (no correctness blocker) but real, and approving before they are addressed would be premature.

Convergence

Aligned. A and I agree on two actionable changes:

  1. Log in the $safeDirExists catch arm — add systemOutput("[wheels] directoryExists() fallback for: " & arguments.p & " (" & e.message & ")", true); before the java.io.File.isDirectory() fallback return.
  2. Add specs for $normalizePath and the $safeDirExists fallback path — A supplied a concrete snippet for cli/lucli/tests/specs/commands/NewCommandSpec.cfc; the fallback-path spec requires triggering it with a known-bad path.

Both are concrete and addressable by the bot. The Windows verification checkbox ([ ] Windows drop-in test on risto's Scoop install) is a human gate — the PR author needs to tick it or post confirmation.

@bpamiri bpamiri merged commit ab6d80c into develop Jun 5, 2026
7 checks passed
@bpamiri bpamiri deleted the peter/fix-windows-drive-letter-uri-misparse branch June 5, 2026 00:20
@wheels-bot
Copy link
Copy Markdown
Contributor

wheels-bot Bot commented Jun 5, 2026

Wheels Bot — Address Review held (PR already merged)

Reviewer A and Reviewer B converged on changes at b8bfea85a7dab5914b972b15543e0b76da9a369a at 2026-06-05T00:19:58Z, but the PR was merged 29 seconds later at 2026-06-05T00:20:27Z (merge commit ab6d80cfaa1547f633f14d8f3fc43e31e651a040). Pushing follow-up commits to this branch would not change what landed on develop, so the address-review loop exits without modifying code.

The two consensus advisory items remain valid and should be picked up in a follow-up PR against develop:

  1. Log in the \$safeDirExists catch arm — the fallback path in cli/lucli/Module.cfc discards the caught NativeException silently. Add a systemOutput(...) line before the java.io.File.isDirectory() fallback so the diagnostic that motivated the two-layer defense is visible when the second layer actually fires.
  2. Add specs for \$normalizePath and the \$safeDirExists fallback path — the merged change has no CLI test coverage. CLAUDE.md mandates tools/test-cli-local.sh for any cli/lucli/** change. A concrete starting point is cli/lucli/tests/specs/commands/NewCommandSpec.cfc; the fallback-path spec needs to trigger the catch arm with a known-bad path.

Skipped (not in consensus): the unchecked Windows verification checkbox is a human gate, not a bot-actionable finding.

bpamiri added a commit that referenced this pull request Jun 5, 2026
…-argument

Resolve CHANGELOG.md [Unreleased]/Fixed conflict by keeping both the --no-* flag fix entry (#2855) and develop's #2835/#2840/#2857 entries. cli/lucli/Module.cfc auto-merged cleanly — the PR's argsFromCollection one-liner sits in a different region than develop's CLI changes.

Signed-off-by: Peter Amiri <peter@alurium.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant