From 857251f4e069d56942542b44ddf3e5d72d4a2220 Mon Sep 17 00:00:00 2001 From: NagyVikt Date: Fri, 17 Apr 2026 15:23:43 +0200 Subject: [PATCH] Keep gx doctor repair-first and normalize worktree status checks Users reported gx doctor failing hard on package script mismatches even when repair could resolve them. Root cause was a duplicate legacy doctor definition overriding the active repair-first flow in the command path. This patch keeps the repair-first doctor as the only active doctor command, and aligns worktree dirty detection with normal untracked-file mode using git status --porcelain --untracked-files=normal --. Constraint: Preserve existing doctor command UX and output contract for guarded repos Rejected: Delete legacy doctor audit block entirely in this hotfix | larger refactor than needed for immediate behavior fix Confidence: high Scope-risk: narrow Reversibility: clean Directive: Do not introduce duplicate top-level command handlers with the same function name Tested: node --check bin/multiagent-safety.js; npm test; node bin/multiagent-safety.js doctor --target /home/deadpool/Documents/recodee Not-tested: npm publish/install path for this patch version --- README.md | 2 ++ bin/multiagent-safety.js | 11 +++++++++-- test/metadata.test.js | 16 +++++++++++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ec8bee1..3d6aa2c 100644 --- a/README.md +++ b/README.md @@ -375,6 +375,8 @@ npm pack --dry-run ### v5.0.16 - Fixed `gx doctor` runtime crash (`parseDoctorArgs is not defined`) by restoring the doctor argument parser for `--target` and `--strict`. +- Fixed `gx doctor` command routing so the repair-first doctor flow remains the active command path (duplicate legacy doctor definition no longer overrides it). +- Updated worktree change detection to run `git status --porcelain --untracked-files=normal --` for consistent normal untracked-file behavior. - Added regression coverage that asserts the doctor parser function exists in `bin/multiagent-safety.js`. - Bumped package version from `5.0.15` to `5.0.16`. diff --git a/bin/multiagent-safety.js b/bin/multiagent-safety.js index 1aa45c0..14aed11 100755 --- a/bin/multiagent-safety.js +++ b/bin/multiagent-safety.js @@ -2117,7 +2117,14 @@ function mapWorktreePathsByBranch(repoRoot) { } function hasSignificantWorkingTreeChanges(worktreePath) { - const result = run('git', ['-C', worktreePath, 'status', '--porcelain']); + const result = run('git', [ + '-C', + worktreePath, + 'status', + '--porcelain', + '--untracked-files=normal', + '--', + ]); if (result.status !== 0) { return true; } @@ -4659,7 +4666,7 @@ function initWorkspace(rawArgs) { } } -function doctor(rawArgs) { +function doctorAudit(rawArgs) { const options = parseDoctorArgs(rawArgs); const repoRoot = resolveRepoRoot(options.target); const failures = []; diff --git a/test/metadata.test.js b/test/metadata.test.js index 7ca4b1e..084eddf 100644 --- a/test/metadata.test.js +++ b/test/metadata.test.js @@ -80,5 +80,19 @@ test('doctor CLI parser exists to prevent runtime ReferenceError regressions', ( const cliPath = path.join(repoRoot, 'bin', 'multiagent-safety.js'); const cliSource = fs.readFileSync(cliPath, 'utf8'); assert.match(cliSource, /function parseDoctorArgs\(rawArgs\)/); - assert.match(cliSource, /const options = parseDoctorArgs\(rawArgs\);/); + assert.match(cliSource, /function doctorAudit\(rawArgs\)/); +}); + +test('active doctor command remains single-source and runs the repair-first path', () => { + const cliPath = path.join(repoRoot, 'bin', 'multiagent-safety.js'); + const cliSource = fs.readFileSync(cliPath, 'utf8'); + const doctorDefs = cliSource.match(/function doctor\(rawArgs\)/g) || []; + assert.equal(doctorDefs.length, 1, 'doctor() must not be duplicated'); + assert.match(cliSource, /printOperations\('Doctor\/fix', fixPayload, options\.dryRun\);/); +}); + +test('worktree-change detection uses normal untracked-file mode', () => { + const cliPath = path.join(repoRoot, 'bin', 'multiagent-safety.js'); + const cliSource = fs.readFileSync(cliPath, 'utf8'); + assert.match(cliSource, /'status',\s*'--porcelain',\s*'--untracked-files=normal',\s*'--'/s); });