chore(yun-tianming): share the query-string helper between storage and metrics
Summary
src/commands/storage.tsandsrc/commands/metrics.tseach carried a privateqs()that was byte-for-byte identical (build aURLSearchParamsfrom a record, skippingundefinedand'', and prefix?when non-empty). The storage copy was added in 8919437 (2026-08) as a second copy of the metrics one from 50fc2e4 (2026-06). The one surviving copy now lives insrc/commands/services.tsbesideq(), the branch-only form of the same helper that nine command modules already import from there, and both callers import it.
Safety
- Duplicate collapse — the two bodies were identical (confirmed with a 4-line-window scan across
src/, which reported this as the only cross-file duplicate). The survivor is exercised bytest/storage.test.ts(objectsPath,objectDownloadPath, including the encoding and omitted-param cases) andtest/logs-deploy.test.ts(deployEventsPath, including the omitted-param case), all of which ran before and after. No exported symbol of storage.ts or metrics.ts changed name, signature or behaviour; services.ts gains one export (qs) and introduces no import cycle (it imports only../api.jsand../util.js). No command or flag changed, soskills/insta/cli-reference.mdhas nothing to mirror.
Verification
npm run typecheck # tsc -p tsconfig.json --noEmit
npm test # vitest run
- Baseline (untouched
main@ d075ab7): typecheck exit 0; 107 test files passed, 2124 tests passed, 136 skipped, 31.3s. - Final tree: typecheck exit 0; 107 test files passed, 2124 tests passed, 136 skipped, 33.0s.
There is no lint script in package.json; CI (.github/workflows/ci.yml) runs exactly tsc --noEmit and vitest run on ubuntu and windows, both of which this machine reproduces (ubuntu side). The 136 skipped tests are pre-existing. No suite needs a database, Docker or credentials.
Deferred findings
src/commands/project.ts:57-58—resolveProjectNamesplitscwdandhomedir()on/only. On Windowsprocess.cwd()isC:\Users\me\my-app, so a bareinsta project createwould slugify the whole path toc-users-me-my-app, and the "cwd is the home directory" guard can never match.test/create-name.test.tsonly passes POSIX strings, so the Windows CI job does not catch it. A fix (path.basename) changes the project name the command provisions, so it is a product call; not changed.src/commands/postgres.ts:26-33, 126-133, 228-233, 258-263, 292-297— five identical blocks build the?branch=&group=suffix by hand indbAlwaysOn,dbLimits,dbStats,dbVolumeanddbRestart; the new sharedqs({ branch, group: service })would express each in one line. Only the pure line-builders in that module are tested, not these command bodies, so left as is.src/commands/compute.ts(lifecycle,computeStatus,serviceAlwaysOn,computeScale,computeRemoveInstance,serviceVolume,serviceLimits) — the same load-client / require-project / GET services /resolveSoleServiceprologue is repeated eight times;storage.tsalready has astorageTargethelper for exactly this shape. These command bodies have no test coverage, so not refactored.src/commands/deploy.ts:140and:229— the identical 409 rewrap.catch(repoConnectedHint) appears inprepareSource(tested) anddeploy()(not tested). Not extracted because one of the two call sites is uncovered.src/commands/auth.ts:71-75and:87-91—loginOauthandloginDeviceend in the same five lines (set session, GET/me, set session with user, persist, print). Only the dispatch layer is tested (test/login-dispatch.test.ts); left.src/commands/mcp.ts:363-371—installForparses the existing JSON config once for the "already configured" check andrenderJsonConfigparses it again. Harmless; folding them would change the exportedrenderJsonConfigcontract, so left.
Opened by Yun Tianming: nightly sweep 2026-10-29-1000, model claude-fable-5-1, 40 turns, 3 files, 24 changed lines.
Verification: npm run typecheck (tsc --noEmit) and npm test (vitest run: 107 files, 2124 passed, 136 skipped) both passed on the final tree, matching baseline; no lint script exists.
🤖 Generated with Claude Code
Summary by cubic
Shares the query-string helper between storage.ts and metrics.ts, which each carried an identical private copy, by exporting a single qs from services.ts with no behavior change to any command. Both callers import it alongside the existing q helper.
A comment and new cases in test/services.test.ts directly pin the shared contract: skipping undefined and '' (but keeping '0'), returning '' instead of a bare ?, insertion order, URLSearchParams encoding, and the exact characters where qs diverges from q (space, !, ', (, ), ~).