Summary
Setup scripts duplicate an identical which(cmd) helper that resolves a CLI binary path via command -v. Extract it to a shared module so future setup scripts import one implementation.
Deferred from PR #361 review (#361 (comment)).
Current duplication
The same function appears in:
scripts/setup-full.mjs
scripts/setup-kit.mjs
scripts/setup-email-dns.mjs
function which(cmd) {
if (!/^[a-zA-Z0-9_.-]+$/.test(cmd)) return '';
const r = spawnSync('sh', ['-c', `command -v "${cmd}" 2>/dev/null`], {
encoding: 'utf8',
});
return r.status === 0 ? (r.stdout || '').trim().split('\n')[0] : '';
}
Proposed change
- Add
scripts/lib/setup-shell.mjs (or another existing shared CLI util module if one is a better fit) exporting which(cmd).
- Replace local copies in the three setup scripts with imports.
- Add a small unit test in
scripts/__tests__/ and register it in test:unit:scripts if not already covered.
Acceptance criteria
Out of scope (optional follow-up)
ghAuthOk() is also duplicated in setup-kit.mjs and setup-email-dns.mjs — can be extracted in the same module if convenient, but not required for this issue.
Summary
Setup scripts duplicate an identical
which(cmd)helper that resolves a CLI binary path viacommand -v. Extract it to a shared module so future setup scripts import one implementation.Deferred from PR #361 review (#361 (comment)).
Current duplication
The same function appears in:
scripts/setup-full.mjsscripts/setup-kit.mjsscripts/setup-email-dns.mjsProposed change
scripts/lib/setup-shell.mjs(or another existing shared CLI util module if one is a better fit) exportingwhich(cmd).scripts/__tests__/and register it intest:unit:scriptsif not already covered.Acceptance criteria
which()implementation used by all setup scripts listed abovecommand -vresolution, same empty-string fallback)npm run test:unit:scripts)Out of scope (optional follow-up)
ghAuthOk()is also duplicated insetup-kit.mjsandsetup-email-dns.mjs— can be extracted in the same module if convenient, but not required for this issue.