tickmarkr 1.90.2 → 1.90.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -28,4 +28,16 @@ export declare function applyRunnerIgnore(cwd: string): {
|
|
|
28
28
|
action: "none" | "wrote" | "manual";
|
|
29
29
|
detail: string;
|
|
30
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Q140s(b) / operator directive (D-OBS-10): the repo's package manager must resolve in
|
|
33
|
+
* THIS process's environment — the same class of environment the daemon spawns gates
|
|
34
|
+
* into. Dossier run 1: pnpm lived only in the operator's interactive PATH; every gate
|
|
35
|
+
* died on `Unable to find package manager binary` / `spawnSync pnpm ENOENT`, the reds
|
|
36
|
+
* were baseline-forgiven, and no suite ever ran. A which(1)-grade check at doctor time
|
|
37
|
+
* turns that into preflight information.
|
|
38
|
+
*/
|
|
39
|
+
export declare function packageManagerFinding(cwd: string, probe?: (pm: string) => boolean): {
|
|
40
|
+
verdict: "pass" | "fail";
|
|
41
|
+
detail: string;
|
|
42
|
+
} | undefined;
|
|
31
43
|
export declare function doctor(_argv: string[], cwd?: string, adapters?: WorkerAdapter[], opts?: DoctorOpts): Promise<string>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
2
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
4
|
import { join } from "node:path";
|
|
5
|
+
import { detectPackageManager } from "../../gates/baseline.js";
|
|
4
6
|
import { allAdapters, binaryShadowWarnings, detectCandidateClis, flagDriftWarnings, modelAliasExclusions, modelAliasLine, probeAll, probeModels, servableExclusions, servabilityLine, writeDoctor } from "../../adapters/registry.js";
|
|
5
7
|
import { CLAUDE_ALIAS_IDENTITY_STAMPS, claudeCode, resolveClaudeAliasIdentity } from "../../adapters/claude-code.js";
|
|
6
8
|
import { BANNER, dim, fail, kvRow, legend, ok, rule, statusRow, title } from "../../brand.js";
|
|
@@ -149,6 +151,25 @@ export function applyRunnerIgnore(cwd) {
|
|
|
149
151
|
writeFileSync(file, original); // verify failed: never leave a half-fix behind
|
|
150
152
|
return { action: "manual", detail: finding.detail };
|
|
151
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Q140s(b) / operator directive (D-OBS-10): the repo's package manager must resolve in
|
|
156
|
+
* THIS process's environment — the same class of environment the daemon spawns gates
|
|
157
|
+
* into. Dossier run 1: pnpm lived only in the operator's interactive PATH; every gate
|
|
158
|
+
* died on `Unable to find package manager binary` / `spawnSync pnpm ENOENT`, the reds
|
|
159
|
+
* were baseline-forgiven, and no suite ever ran. A which(1)-grade check at doctor time
|
|
160
|
+
* turns that into preflight information.
|
|
161
|
+
*/
|
|
162
|
+
export function packageManagerFinding(cwd, probe = (pm) => spawnSync(pm, ["--version"], { stdio: "ignore", shell: false }).error === undefined) {
|
|
163
|
+
if (!existsSync(join(cwd, "package.json")))
|
|
164
|
+
return undefined;
|
|
165
|
+
const pm = detectPackageManager(cwd);
|
|
166
|
+
if (probe(pm))
|
|
167
|
+
return { verdict: "pass", detail: `${pm} (detected) resolves in this environment` };
|
|
168
|
+
return {
|
|
169
|
+
verdict: "fail",
|
|
170
|
+
detail: `repo uses ${pm} but the binary does not resolve in this environment — every auto-detected gate command will die before running any suite (fix: corepack enable, or install ${pm} on the daemon's PATH)`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
152
173
|
export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(), opts = {}) {
|
|
153
174
|
if (_argv.length === 1 && _argv[0] === "--refresh-catalog") {
|
|
154
175
|
const refreshed = await refreshCatalogCommand({ repoRoot: cwd, now: opts.catalogNow });
|
|
@@ -240,6 +261,10 @@ export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(
|
|
|
240
261
|
rows.push(alignedStatusRow(runnerIgnore.verdict, "test-runner", runnerIgnore.detail));
|
|
241
262
|
}
|
|
242
263
|
}
|
|
264
|
+
// Q140s(b): the repo's package manager must resolve where gates spawn (D-OBS-10).
|
|
265
|
+
const pmRow = packageManagerFinding(cwd);
|
|
266
|
+
if (pmRow)
|
|
267
|
+
rows.push(alignedStatusRow(pmRow.verdict, "package-manager", pmRow.detail));
|
|
243
268
|
// v1.22 T5: workspace-trust pre-flight — per installed adapter: trusted | seeded | action-required | n/a.
|
|
244
269
|
// action-required names the exact one-time command (or dialog) the operator must run once.
|
|
245
270
|
rows.push(legend("workspace trust:"));
|
package/dist/gates/baseline.d.ts
CHANGED
|
@@ -37,6 +37,15 @@ export declare function freshFailures(entry: BaselineCommand | undefined, raw: s
|
|
|
37
37
|
failing: string[];
|
|
38
38
|
unreadable: boolean;
|
|
39
39
|
};
|
|
40
|
+
export type PackageManager = "npm" | "pnpm" | "yarn" | "bun";
|
|
41
|
+
/**
|
|
42
|
+
* Operator directive 2026-08-12 (D-OBS-10): tickmarkr is package-manager agnostic.
|
|
43
|
+
* The `packageManager` field is authoritative (corepack's own contract); lockfiles
|
|
44
|
+
* break ties; npm is only the LAST resort. Dossier exhibit: auto-detected
|
|
45
|
+
* `npm run -s test` on a repo whose engines said `"npm": "please-use-pnpm"` — turbo
|
|
46
|
+
* then failed to resolve pnpm and every gate died on environment, not code.
|
|
47
|
+
*/
|
|
48
|
+
export declare function detectPackageManager(repoRoot: string): PackageManager;
|
|
40
49
|
export declare function detectGateCommands(repoRoot: string, cfg: TickmarkrConfig): Record<string, string>;
|
|
41
50
|
/**
|
|
42
51
|
* The ceiling a battery runs under: whichever is larger of the shipped constant and the slack applied
|
package/dist/gates/baseline.js
CHANGED
|
@@ -123,17 +123,50 @@ export function freshFailures(entry, raw) {
|
|
|
123
123
|
const fresh = current.filter((f) => !known.has(f) && (!FAIL_ANCHOR_RE.test(f) || f.startsWith("FAIL ")));
|
|
124
124
|
return { failing: fresh.filter((f) => f !== UNRECOGNIZED_FAILURE), unreadable: current.includes(UNRECOGNIZED_FAILURE) };
|
|
125
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Operator directive 2026-08-12 (D-OBS-10): tickmarkr is package-manager agnostic.
|
|
128
|
+
* The `packageManager` field is authoritative (corepack's own contract); lockfiles
|
|
129
|
+
* break ties; npm is only the LAST resort. Dossier exhibit: auto-detected
|
|
130
|
+
* `npm run -s test` on a repo whose engines said `"npm": "please-use-pnpm"` — turbo
|
|
131
|
+
* then failed to resolve pnpm and every gate died on environment, not code.
|
|
132
|
+
*/
|
|
133
|
+
export function detectPackageManager(repoRoot) {
|
|
134
|
+
const pkgPath = join(repoRoot, "package.json");
|
|
135
|
+
try {
|
|
136
|
+
const pm = JSON.parse(readFileSync(pkgPath, "utf8")).packageManager;
|
|
137
|
+
const name = pm?.split("@")[0];
|
|
138
|
+
if (name === "pnpm" || name === "yarn" || name === "bun" || name === "npm")
|
|
139
|
+
return name;
|
|
140
|
+
}
|
|
141
|
+
catch { /* no or unparseable manifest: lockfiles decide */ }
|
|
142
|
+
if (existsSync(join(repoRoot, "pnpm-lock.yaml")))
|
|
143
|
+
return "pnpm";
|
|
144
|
+
if (existsSync(join(repoRoot, "yarn.lock")))
|
|
145
|
+
return "yarn";
|
|
146
|
+
if (existsSync(join(repoRoot, "bun.lock")) || existsSync(join(repoRoot, "bun.lockb")))
|
|
147
|
+
return "bun";
|
|
148
|
+
return "npm";
|
|
149
|
+
}
|
|
150
|
+
// Silent where the manager supports it everywhere (npm -s; pnpm's global -s), bare
|
|
151
|
+
// where a flag would break a major version (yarn berry rejects run -s; bun is quiet).
|
|
152
|
+
const RUN_PREFIX = {
|
|
153
|
+
npm: "npm run -s",
|
|
154
|
+
pnpm: "pnpm -s run",
|
|
155
|
+
yarn: "yarn run",
|
|
156
|
+
bun: "bun run",
|
|
157
|
+
};
|
|
126
158
|
export function detectGateCommands(repoRoot, cfg) {
|
|
127
159
|
const out = {};
|
|
128
160
|
const pkgPath = join(repoRoot, "package.json");
|
|
129
161
|
const scripts = existsSync(pkgPath)
|
|
130
162
|
? (JSON.parse(readFileSync(pkgPath, "utf8")).scripts ?? {})
|
|
131
163
|
: {};
|
|
164
|
+
const runPrefix = RUN_PREFIX[detectPackageManager(repoRoot)];
|
|
132
165
|
for (const name of ["build", "test", "lint"]) {
|
|
133
166
|
if (cfg.gates[name])
|
|
134
167
|
out[name] = cfg.gates[name];
|
|
135
168
|
else if (scripts[name])
|
|
136
|
-
out[name] =
|
|
169
|
+
out[name] = `${runPrefix} ${name}`;
|
|
137
170
|
}
|
|
138
171
|
return out;
|
|
139
172
|
}
|