From 0d286b991b12d2a15abd7c46f88c50354d7a79cf Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Mon, 27 Apr 2026 23:42:51 +0200 Subject: [PATCH] sf snapshot: pre-dispatch, uncommitted changes after 2902m inactivity --- package.json | 1 + src/resources/extensions/sf/detection.ts | 46 ++++++++++++------- .../extensions/sf/tests/detection.test.ts | 10 ++-- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index ab26698e1..edea38694 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "test:unit": "npm run test:compile && node --import ./scripts/dist-test-resolve.mjs --experimental-test-isolation=process --test-timeout=30000 --test-reporter=./scripts/test-reporter-compact.mjs --test \"dist-test/src/tests/*.test.js\" \"dist-test/src/resources/extensions/sf/tests/*.test.js\" \"dist-test/src/resources/extensions/sf/tests/*.test.mjs\" \"dist-test/src/resources/extensions/shared/tests/*.test.js\" \"dist-test/src/resources/extensions/claude-code-cli/tests/*.test.js\" \"dist-test/src/resources/extensions/github-sync/tests/*.test.js\" \"dist-test/src/resources/extensions/universal-config/tests/*.test.js\" \"dist-test/src/resources/extensions/voice/tests/*.test.js\" \"dist-test/src/resources/extensions/mcp-client/tests/*.test.js\"", "test:packages": "node --test packages/pi-coding-agent/dist/core/*.test.js packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js", "test:marketplace": "node scripts/with-env.mjs SF_TEST_CLONE_MARKETPLACES=1 -- node --import ./src/resources/extensions/sf/tests/resolve-ts.mjs --experimental-strip-types --test src/resources/extensions/sf/tests/claude-import-tui.test.ts src/resources/extensions/sf/tests/plugin-importer-live.test.ts src/tests/marketplace-discovery.test.ts", + "test:sf-light": "node --max-old-space-size=2048 --import ./src/resources/extensions/sf/tests/resolve-ts.mjs --experimental-strip-types --test-timeout=30000 --test \"src/resources/extensions/sf/tests/*.test.ts\"", "test:coverage": "c8 --reporter=text --reporter=lcov --exclude=\"src/resources/extensions/sf/tests/**\" --exclude=\"src/tests/**\" --exclude=\"scripts/**\" --exclude=\"native/**\" --exclude=\"node_modules/**\" --check-coverage --statements=40 --lines=40 --branches=20 --functions=20 node --import ./src/resources/extensions/sf/tests/resolve-ts.mjs --experimental-strip-types --experimental-test-isolation=process --test src/resources/extensions/sf/tests/*.test.ts src/resources/extensions/sf/tests/*.test.mjs src/tests/*.test.ts src/resources/extensions/shared/tests/*.test.ts", "test:integration": "node --import ./src/resources/extensions/sf/tests/resolve-ts.mjs --experimental-strip-types --test \"src/tests/integration/*.test.ts\" \"src/resources/extensions/sf/tests/integration/*.test.ts\" \"src/resources/extensions/async-jobs/*.test.ts\" \"src/resources/extensions/browser-tools/tests/*.test.mjs\"", "pretest": "npm run typecheck:extensions", diff --git a/src/resources/extensions/sf/detection.ts b/src/resources/extensions/sf/detection.ts index 8c24d6243..2574b02f5 100644 --- a/src/resources/extensions/sf/detection.ts +++ b/src/resources/extensions/sf/detection.ts @@ -659,39 +659,51 @@ function detectVerificationCommands( if (detectedFiles.includes("package.json")) { const scripts = readPackageJsonScripts(basePath); if (scripts) { - // Test commands (highest priority) - if (scripts.test && scripts.test !== "echo \"Error: no test specified\" && exit 1") { - commands.push(pm === "npm" ? "npm test" : `${pm} test`); - } - // Build commands - if (scripts.build) { - commands.push(`${run} build`); - } - // Lint commands - if (scripts.lint) { - commands.push(`${run} lint`); - } - // Typecheck commands - if (scripts.typecheck) { + // Typecheck first — fast, no worker processes + if (scripts["typecheck:extensions"]) { + commands.push(`${run} typecheck:extensions`); + } else if (scripts.typecheck) { commands.push(`${run} typecheck`); } else if (scripts.tsc) { commands.push(`${run} tsc`); } + // Build (compile check when no dedicated typecheck exists) + if (scripts.build) { + commands.push(`${run} build`); + } + // Lint + if (scripts.lint) { + commands.push(`${run} lint`); + } + // Prefer a light test target over the full suite. + // npm test / yarn test can spawn many worker processes and saturate + // CPUs (especially when paired with c8 coverage or test isolation). + // Use a *-light variant when present, otherwise fall back to npm test. + if (scripts["test:sf-light"]) { + commands.push(`${run} test:sf-light`); + } else if (scripts["test:light"]) { + commands.push(`${run} test:light`); + } else if (scripts.test && scripts.test !== "echo \"Error: no test specified\" && exit 1") { + commands.push(pm === "npm" ? "npm test" : `${pm} test`); + } } } if (detectedFiles.includes("Cargo.toml")) { - commands.push("cargo test"); + // Limit test threads so Rust tests don't saturate all CPUs. + commands.push("cargo test -- --test-threads=2"); commands.push("cargo clippy"); } if (detectedFiles.includes("go.mod")) { - commands.push("go test ./..."); + // Limit parallelism: Go's default is GOMAXPROCS which can be very high. + commands.push("go test -parallel 2 ./..."); commands.push("go vet ./..."); } if (detectedFiles.includes("pyproject.toml") || detectedFiles.includes("setup.py") || detectedFiles.includes("requirements.txt")) { - commands.push("pytest"); + // Single-process pytest by default; -x stops on first failure (fast feedback). + commands.push("pytest -x"); } if (detectedFiles.includes("Gemfile")) { diff --git a/src/resources/extensions/sf/tests/detection.test.ts b/src/resources/extensions/sf/tests/detection.test.ts index 00d6d9372..00f236255 100644 --- a/src/resources/extensions/sf/tests/detection.test.ts +++ b/src/resources/extensions/sf/tests/detection.test.ts @@ -190,7 +190,7 @@ test("detectProjectSignals: Node.js project", (t) => { assert.equal(signals.isGitRepo, true); assert.equal(signals.packageManager, "npm"); assert.ok(signals.verificationCommands.includes("npm test")); - assert.ok(signals.verificationCommands.some(c => c.includes("build"))); + assert.ok(signals.verificationCommands.some(c => c.includes("build") || c.includes("typecheck"))); assert.ok(signals.verificationCommands.some(c => c.includes("lint"))); }); @@ -202,7 +202,7 @@ test("detectProjectSignals: Rust project", (t) => { const signals = detectProjectSignals(dir); assert.ok(signals.detectedFiles.includes("Cargo.toml")); assert.equal(signals.primaryLanguage, "rust"); - assert.ok(signals.verificationCommands.includes("cargo test")); + assert.ok(signals.verificationCommands.includes("cargo test -- --test-threads=2")); assert.ok(signals.verificationCommands.includes("cargo clippy")); }); @@ -214,7 +214,7 @@ test("detectProjectSignals: Go project", (t) => { const signals = detectProjectSignals(dir); assert.ok(signals.detectedFiles.includes("go.mod")); assert.equal(signals.primaryLanguage, "go"); - assert.ok(signals.verificationCommands.includes("go test ./...")); + assert.ok(signals.verificationCommands.includes("go test -parallel 2 ./...")); }); test("detectProjectSignals: Python project", (t) => { @@ -225,7 +225,7 @@ test("detectProjectSignals: Python project", (t) => { const signals = detectProjectSignals(dir); assert.ok(signals.detectedFiles.includes("pyproject.toml")); assert.equal(signals.primaryLanguage, "python"); - assert.ok(signals.verificationCommands.includes("pytest")); + assert.ok(signals.verificationCommands.includes("pytest -x")); }); test("detectProjectSignals: monorepo detection via workspaces", (t) => { @@ -601,7 +601,7 @@ test("detectProjectSignals: requirements.txt sets Python language", () => { const signals = detectProjectSignals(dir); assert.ok(signals.detectedFiles.includes("requirements.txt")); assert.equal(signals.primaryLanguage, "python"); - assert.ok(signals.verificationCommands.includes("pytest"), "should suggest pytest for requirements.txt projects"); + assert.ok(signals.verificationCommands.includes("pytest -x"), "should suggest pytest for requirements.txt projects"); } finally { cleanup(dir); }