refactor: hard-fork GSD -> MSD (Make Software Done)

Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
This commit is contained in:
Jakub Zych
2026-10-06 01:47:40 +02:00
parent fe069b2a56
commit a9a7a328e6
2763 changed files with 78465 additions and 78434 deletions

View File

@@ -1,5 +1,5 @@
/**
* GSD Tools Test Helpers
* MSD Tools Test Helpers
*/
const { execFileSync } = require('child_process');
@@ -10,12 +10,12 @@ const { createFixture } = require('./fixtures/index.cjs');
const processSeam = require('./helpers/process-seam.cjs');
const { SEAM_DEFAULT_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
const TOOLS_PATH = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
const TOOLS_PATH = path.join(__dirname, '..', 'msd-core', 'bin', 'msd-tools.cjs');
// Session-IDENTITY vars. Blanked so a child cannot inherit the developer's
// terminal/agent session and key shared state off it.
const SESSION_IDENTITY_ENV_KEYS = [
'GSD_SESSION_KEY',
'MSD_SESSION_KEY',
'CODEX_THREAD_ID',
'CLAUDE_SESSION_ID',
'CLAUDE_CODE_SESSION_ID',
@@ -45,16 +45,16 @@ let _builtLib = null;
function builtLib() {
if (_builtLib) return _builtLib;
try {
const { runtimes } = require('../gsd-core/bin/lib/capability-registry.cjs');
const { runtimes } = require('../msd-core/bin/lib/capability-registry.cjs');
const {
NON_REGISTRY_CONFIG_HOME_DESCRIPTORS,
GSD_LOCATION_ENV_KEYS,
} = require('../gsd-core/bin/lib/runtime-homes.cjs');
_builtLib = { runtimes, NON_REGISTRY_CONFIG_HOME_DESCRIPTORS, GSD_LOCATION_ENV_KEYS };
MSD_LOCATION_ENV_KEYS,
} = require('../msd-core/bin/lib/runtime-homes.cjs');
_builtLib = { runtimes, NON_REGISTRY_CONFIG_HOME_DESCRIPTORS, MSD_LOCATION_ENV_KEYS };
} catch (cause) {
throw new Error(
'tests/helpers.cjs derives the config-location scrub set from the built runtime '
+ 'lib (gsd-core/bin/lib), which is not present. Run `npm run build:lib` first — '
+ 'lib (msd-core/bin/lib), which is not present. Run `npm run build:lib` first — '
+ '`npm test` does this for you via its pretest script.',
{ cause },
);
@@ -65,9 +65,9 @@ function builtLib() {
// Config-location vars that are neither in the registry nor descriptor-shaped,
// each with its reader:
// GROK_AGENTS_HOME — hardcoded `grok` branch in getGlobalConfigDir (src/runtime-homes.cts)
// GSD_RUNTIME — selects WHICH runtime home resolves (src/model-resolver.cts)
// GSD_PROJECT — planningDir() project segment (src/planning-workspace.cts)
// GSD_WORKSTREAM — planningDir() workstream segment (src/planning-workspace.cts)
// MSD_RUNTIME — selects WHICH runtime home resolves (src/model-resolver.cts)
// MSD_PROJECT — planningDir() project segment (src/planning-workspace.cts)
// MSD_WORKSTREAM — planningDir() workstream segment (src/planning-workspace.cts)
//
// #2665 round 3: this list shrinks as sources become enumerable, and that direction
// is the point. KIMI_SHARE_DIR was NOT added here — it now derives from
@@ -75,12 +75,12 @@ function builtLib() {
// names is precisely what reopened this bug three times.
const NON_REGISTRY_CONFIG_LOCATION_ENV_KEYS = [
'GROK_AGENTS_HOME',
'GSD_RUNTIME',
'GSD_PROJECT',
'GSD_WORKSTREAM',
// #3245: host-session signals GSD now reads (host-runtime-detection.cts's
'MSD_RUNTIME',
'MSD_PROJECT',
'MSD_WORKSTREAM',
// #3245: host-session signals MSD now reads (host-runtime-detection.cts's
// detectHostRuntime / resolveReportedRuntime). Scrubbed for the same reason
// GSD_RUNTIME is — an ambiently-set CODEX_SANDBOX / (this repo's test suite
// MSD_RUNTIME is — an ambiently-set CODEX_SANDBOX / (this repo's test suite
// running from inside a Codex session, or any host that happens to export
// these) would non-deterministically flip the detected runtime for every
// test that does not explicitly pass them. Tests that WANT them set still
@@ -93,7 +93,7 @@ const NON_REGISTRY_CONFIG_LOCATION_ENV_KEYS = [
// Write-escape PERMISSIONS — deliberately its own family, and deliberately NOT
// folded into any of the four rungs below.
//
// #2665 round 5: GSD_ALLOW_SYMLINKED_DEST is boolean and names no path, so it is
// #2665 round 5: MSD_ALLOW_SYMLINKED_DEST is boolean and names no path, so it is
// not a config-location var by any honest reading. But install-engine.cts reads it
// env-first (`:214`) and threads it as `allowOptInFollow` into the symlink-escape
// guard at four call sites, each gating a write (`:361/:367`, `:416/:424`,
@@ -104,7 +104,7 @@ const NON_REGISTRY_CONFIG_LOCATION_ENV_KEYS = [
// Blanking is fail-safe in the only direction that matters: '' is neither '1' nor
// 'true', so a blanked value makes the guard STRICTER, never looser. That asymmetry
// is why this can be scrubbed wholesale without reasoning about each call site.
const WRITE_ESCAPE_PERMISSION_ENV_KEYS = ['GSD_ALLOW_SYMLINKED_DEST'];
const WRITE_ESCAPE_PERMISSION_ENV_KEYS = ['MSD_ALLOW_SYMLINKED_DEST'];
// Config-LOCATION vars — distinct in kind from the session-identity vars above:
// these decide WHERE a child writes, so leaving one ambient lets a test that
@@ -120,7 +120,7 @@ const WRITE_ESCAPE_PERMISSION_ENV_KEYS = ['GSD_ALLOW_SYMLINKED_DEST'];
let _configLocationEnvKeys = null;
function configLocationEnvKeys() {
if (_configLocationEnvKeys) return _configLocationEnvKeys;
const { runtimes, NON_REGISTRY_CONFIG_HOME_DESCRIPTORS, GSD_LOCATION_ENV_KEYS } = builtLib();
const { runtimes, NON_REGISTRY_CONFIG_HOME_DESCRIPTORS, MSD_LOCATION_ENV_KEYS } = builtLib();
_configLocationEnvKeys = [
...new Set([
// 1. Every runtime descriptor the capability registry carries — including
@@ -140,9 +140,9 @@ function configLocationEnvKeys() {
...(d?.env ?? []),
...(d?.skillsHome?.env ?? []),
]),
// 3. GSD's OWN location vars — a different family: they decide where GSD keeps
// user-owned state ($GSD_HOME/.gsd/), not where a runtime keeps its config.
...GSD_LOCATION_ENV_KEYS,
// 3. MSD's OWN location vars — a different family: they decide where MSD keeps
// user-owned state ($MSD_HOME/.msd/), not where a runtime keeps its config.
...MSD_LOCATION_ENV_KEYS,
// 4. The residue that is neither registry-carried nor descriptor-shaped.
...NON_REGISTRY_CONFIG_LOCATION_ENV_KEYS,
// 5. Write-escape permissions — NOT locations. Same mechanism because the
@@ -170,7 +170,7 @@ function testEnvBase() {
* installer IN-PROCESS — `install(true, 'claude')` — resolves through the same
* env-first `getGlobalConfigDir`, so an ambient CLAUDE_CONFIG_DIR beats a
* sandboxed `process.env.HOME` and a complete global install (agents/, commands/,
* skills/, gsd-core/, manifest, settings) lands in the developer's live config
* skills/, msd-core/, manifest, settings) lands in the developer's live config
* dir. No child-env scrub can reach that call; only clearing the parent's env can.
*
* Pair with a HOME sandbox, not instead of one: HOME covers the home-derived
@@ -195,16 +195,16 @@ function scrubConfigLocationEnv() {
}
/**
* Run gsd-tools command.
* Run msd-tools command.
*
* @param {string|string[]} args - Command string (shell-interpreted) or array
* of arguments (shell-bypassed via execFileSync, safe for JSON and dollar signs).
* @param {string} cwd - Working directory.
* @param {object} [env] - Optional env overrides merged on top of process.env.
* Pass { HOME: cwd } to sandbox ~/.gsd/ lookups in tests that assert concrete
* Pass { HOME: cwd } to sandbox ~/.msd/ lookups in tests that assert concrete
* config values that could be overridden by a developer's defaults.json.
*/
function runGsdTools(args, cwd = process.cwd(), env = {}) {
function runMsdTools(args, cwd = process.cwd(), env = {}) {
// Resolve argv once so both the first attempt and the retry use the same vector.
const childEnv = { ...process.env, ...testEnvBase(), ...env };
const argv = Array.isArray(args)
@@ -216,7 +216,7 @@ function runGsdTools(args, cwd = process.cwd(), env = {}) {
// typed { outcome, exitCode, stdout, stderr, timedOut, signal, killed, code }
// result — never throws for a kill/timeout/buffer-overflow/spawn-failure.
// This adapter is the ONLY place that retries and the ONLY place that
// reconstructs runGsdTools's legacy { success, output, error, exitCode }
// reconstructs runMsdTools's legacy { success, output, error, exitCode }
// shape, so all 136 callers keep their existing contract byte-identically.
//
// `processSeam.runNode` is looked up on the module object (not destructured
@@ -233,8 +233,8 @@ function runGsdTools(args, cwd = process.cwd(), env = {}) {
function throwResourceStarvation(result) {
throw new Error(
`[runGsdTools: resource-starvation / subprocess-kill after retry] ` +
`gsd-tools was killed before completion ` +
`[runMsdTools: resource-starvation / subprocess-kill after retry] ` +
`msd-tools was killed before completion ` +
`(signal=${result.signal}, code=${result.code}, killed=${result.killed}). ` +
`This indicates host OOM or scheduler contention, not a product bug. ` +
`stdout=${(result.stdout || '').trim()} ` +
@@ -260,7 +260,7 @@ function runGsdTools(args, cwd = process.cwd(), env = {}) {
// note so CI logs show "stderr: (empty)" rather than silently losing
// the fact that the child process produced no error output — empty
// stderr with a non-zero exit code is a signal of OS-level crash (OOM
// kill, worker thread fatal error) rather than a gsd-tools application
// kill, worker thread fatal error) rather than a msd-tools application
// error.
const commandLine = [process.execPath, TOOLS_PATH, ...argv].join(' ');
const error = stderrRaw
@@ -296,7 +296,7 @@ function runGsdTools(args, cwd = process.cwd(), env = {}) {
return {
success: false,
output: (result.stdout || '').trim(),
error: `gsd-tools output exceeded the subprocess buffer limit (code=${result.code})`,
error: `msd-tools output exceeded the subprocess buffer limit (code=${result.code})`,
exitCode: 1,
};
}
@@ -311,7 +311,7 @@ function runGsdTools(args, cwd = process.cwd(), env = {}) {
return {
success: false,
output: (result.stdout || '').trim(),
error: `gsd-tools was killed by signal (signal=${result.signal}, code=${result.code})`,
error: `msd-tools was killed by signal (signal=${result.signal}, code=${result.code})`,
exitCode: null,
};
}
@@ -332,7 +332,7 @@ function runGsdTools(args, cwd = process.cwd(), env = {}) {
return {
success: false,
output: (result.stdout || '').trim(),
error: `gsd-tools failed to spawn (code=${result.code})`,
error: `msd-tools failed to spawn (code=${result.code})`,
exitCode: 1,
};
}
@@ -359,17 +359,17 @@ function runGsdTools(args, cwd = process.cwd(), env = {}) {
}
// Create a bare temp directory (no .planning/ structure)
function createTempDir(prefix = 'gsd-test-') {
function createTempDir(prefix = 'msd-test-') {
return fs.mkdtempSync(path.join(require('os').tmpdir(), prefix));
}
// Create temp directory structure
function createTempProject(prefix = 'gsd-test-') {
function createTempProject(prefix = 'msd-test-') {
return createFixture({ prefix, planning: true, git: false });
}
// Create temp directory with initialized git repo and at least one commit
function createTempGitProject(prefix = 'gsd-test-') {
function createTempGitProject(prefix = 'msd-test-') {
return createFixture({ prefix, planning: true, git: true, projectDoc: true });
}
@@ -716,7 +716,7 @@ async function suppressFdAsync(captureFd, fn) {
*
* ADR-1671's workflow fragmentization (#2930/#2932/#2993 et al.) moves whole
* sections out of a host workflow (e.g. `plan-phase.md`) into lazily-loaded
* step files under `gsd-core/workflows/<name>/steps/*.md`. A structural or
* step files under `msd-core/workflows/<name>/steps/*.md`. A structural or
* drift guard that reads the host file alone goes blind the moment a
* section it cares about moves out — this is exactly the shape #2650's own
* regression tests hit when #2993 relocated plan-phase.md's chunked-planning
@@ -804,10 +804,10 @@ function parseFrontmatter(content) {
}
// #3026 CR: shared `--help` output check used by bug-1818 + bug-3019 tests.
// Render-on-help shape is `Usage: gsd-tools …\nCommands: …` — both lines
// Render-on-help shape is `Usage: msd-tools …\nCommands: …` — both lines
// must be present; structural test, not prose substring matching.
function isUsageOutput(text) {
return /Usage:\s*gsd-tools/.test(text) && /Commands:/.test(text);
return /Usage:\s*msd-tools/.test(text) && /Commands:/.test(text);
}
/**
@@ -1047,8 +1047,8 @@ async function waitFor(predicate, { timeoutMs = 10000, stepMs = 25, message = 'w
* `_resetRuntimeWarningCacheForTests` wrappers in individual test files.
*/
function resetRuntimeWarningCaches() {
const configLoader = require('../gsd-core/bin/lib/config-loader.cjs');
const modelResolver = require('../gsd-core/bin/lib/model-resolver.cjs');
const configLoader = require('../msd-core/bin/lib/config-loader.cjs');
const modelResolver = require('../msd-core/bin/lib/model-resolver.cjs');
configLoader._resetRuntimeWarningCacheForTests();
modelResolver._resetModelPolicyWarningCacheForTests();
modelResolver._resetModelOverrideWarningCacheForTests();
@@ -1060,16 +1060,16 @@ function resetRuntimeWarningCaches() {
* Single source of truth for tests that need a deterministic, session-key-free
* and workstream/project-free process.env — save/clear before, restore after.
* Union of the sets previously hand-duplicated in
* tests/active-workstream-store.unit.test.cjs and tests/gsd-statusline.test.cjs
* tests/active-workstream-store.unit.test.cjs and tests/msd-statusline.test.cjs
* (#2850 code review finding: the two copies had already silently diverged).
*/
const SESSION_ENV_KEYS = [
'GSD_SESSION_KEY', 'CODEX_THREAD_ID', 'CLAUDE_SESSION_ID', 'CLAUDE_CODE_SESSION_ID',
'MSD_SESSION_KEY', 'CODEX_THREAD_ID', 'CLAUDE_SESSION_ID', 'CLAUDE_CODE_SESSION_ID',
'CLAUDE_CODE_SSE_PORT',
'OPENCODE_SESSION_ID', 'GEMINI_SESSION_ID', 'CURSOR_SESSION_ID', 'WINDSURF_SESSION_ID',
'TERM_SESSION_ID', 'WT_SESSION', 'TMUX_PANE', 'ZELLIJ_SESSION_NAME',
'TTY', 'SSH_TTY', 'CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS',
'GSD_WORKSTREAM', 'GSD_PROJECT',
'MSD_WORKSTREAM', 'MSD_PROJECT',
];
function saveSessionEnv() {
@@ -1090,48 +1090,48 @@ function clearSessionEnv() {
}
/**
* Save + clear GSD_WORKSTREAM and GSD_PROJECT on process.env, paired with
* Save + clear MSD_WORKSTREAM and MSD_PROJECT on process.env, paired with
* restoreWorkstreamEnv(). planningDir() reads both directly from
* process.env when its params are omitted, so a test asserting
* workstream/project-scoped behavior must isolate them from ambient shell
* state (and from whatever an earlier test in the same process left behind).
*
* Previously duplicated as a local isolateWorkstreamEnv()/restoreWorkstreamEnv()
* pair in tests/phase-locator.test.cjs, and as the GSD_WORKSTREAM/GSD_PROJECT
* pair in tests/phase-locator.test.cjs, and as the MSD_WORKSTREAM/MSD_PROJECT
* slice of tests/model-resolver.test.cjs's broader isolateHome()/restoreHome()
* (which still isolates HOME/USERPROFILE/GSD_HOME/GSD_RUNTIME locally — that
* (which still isolates HOME/USERPROFILE/MSD_HOME/MSD_RUNTIME locally — that
* part is genuinely specific to model-resolver's tests and stays there).
*
* Module-level save slot (not a returned snapshot) to match the exact
* no-arg isolate()/restore() call shape both prior local copies used.
*/
let _origGsdWorkstream;
let _origGsdProject;
let _origMsdWorkstream;
let _origMsdProject;
function isolateWorkstreamEnv() {
_origGsdWorkstream = process.env.GSD_WORKSTREAM;
_origGsdProject = process.env.GSD_PROJECT;
delete process.env.GSD_WORKSTREAM;
delete process.env.GSD_PROJECT;
_origMsdWorkstream = process.env.MSD_WORKSTREAM;
_origMsdProject = process.env.MSD_PROJECT;
delete process.env.MSD_WORKSTREAM;
delete process.env.MSD_PROJECT;
}
function restoreWorkstreamEnv() {
if (_origGsdWorkstream === undefined) delete process.env.GSD_WORKSTREAM;
else process.env.GSD_WORKSTREAM = _origGsdWorkstream;
if (_origGsdProject === undefined) delete process.env.GSD_PROJECT;
else process.env.GSD_PROJECT = _origGsdProject;
if (_origMsdWorkstream === undefined) delete process.env.MSD_WORKSTREAM;
else process.env.MSD_WORKSTREAM = _origMsdWorkstream;
if (_origMsdProject === undefined) delete process.env.MSD_PROJECT;
else process.env.MSD_PROJECT = _origMsdProject;
}
/**
* #3156: env for a RAW installer spawn — one that bypasses runGsdTools and so
* #3156: env for a RAW installer spawn — one that bypasses runMsdTools and so
* never receives TEST_ENV_BASE on its own.
*
* Blanking config-LOCATION vars is necessary but NOT sufficient here.
* bin/install.js writes GSD's own user-owned store through os.homedir()
* DIRECTLY (writeNonClaudeDefaults -> <home>/.gsd/defaults.json, #2834), and
* os.homedir() consults no GSD variable at all — so nothing in
* CONFIG_LOCATION_ENV_KEYS can reach it, and blanking GSD_HOME does not reach
* it either, because a blank GSD_HOME falls back to exactly that homedir().
* bin/install.js writes MSD's own user-owned store through os.homedir()
* DIRECTLY (writeNonClaudeDefaults -> <home>/.msd/defaults.json, #2834), and
* os.homedir() consults no MSD variable at all — so nothing in
* CONFIG_LOCATION_ENV_KEYS can reach it, and blanking MSD_HOME does not reach
* it either, because a blank MSD_HOME falls back to exactly that homedir().
* Only a sandboxed HOME/USERPROFILE contains it.
*
* HOME stays deliberately OUT of TEST_ENV_BASE — blanking it would break far
@@ -1144,7 +1144,7 @@ function restoreWorkstreamEnv() {
*
* SCOPE, stated because it is a real residual rather than an oversight: this is
* one home per test-FILE process, not one per spawn. Two installer spawns in the
* same file therefore share `.gsd` state, so a prior non-Claude install can be
* same file therefore share `.msd` state, so a prior non-Claude install can be
* observed by a later spawn. That is strictly better than the status quo it
* replaces -- which shared the developer's REAL home, and all of its state --
* and it closes the leak this helper exists for; it does not claim isolation
@@ -1153,7 +1153,7 @@ function restoreWorkstreamEnv() {
let installSpawnHomeDir = null;
function installSpawnHome() {
if (installSpawnHomeDir === null) {
installSpawnHomeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-install-home-'));
installSpawnHomeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-install-home-'));
process.on('exit', () => {
try { fs.rmSync(installSpawnHomeDir, { recursive: true, force: true }); } catch { /* best effort */ }
});
@@ -1198,7 +1198,7 @@ function installSpawnEnv(overrides = {}) {
* may declare a global `home` override that resolves from `os.homedir()` rather
* than from the sandboxed configDir — codex's skills kind (`.agents`, ADR-1239 /
* #2088) is the live case. Without this, such a call writes to, and prunes
* `gsd-*` entries from, the developer's REAL ~/.agents/skills.
* `msd-*` entries from, the developer's REAL ~/.agents/skills.
*
* Promoted here from the identical private copies in executed-plan.test.cjs and
* install-runtime-artifacts.test.cjs so new in-process callers have one obvious
@@ -1213,12 +1213,12 @@ function installSpawnEnv(overrides = {}) {
*/
// #3712: the marker NAME is a constant, duplicated here deliberately rather than
// required from the compiled guard. helpers.cjs is imported by ~370 test files and
// documents (see builtLib above) that it must NOT load gsd-core/bin/lib at module
// documents (see builtLib above) that it must NOT load msd-core/bin/lib at module
// scope — an unbuilt tree would then fail on import alone, turning a missing
// `npm run build:lib` into a whole-suite crash. A lazy require inside sandboxHome
// would satisfy that too, but a bare string needs no build at all. The pairing is
// pinned by a test so the two cannot drift.
const TEST_HOME_SANDBOX_MARKER = 'GSD_TEST_HOME_SANDBOX';
const TEST_HOME_SANDBOX_MARKER = 'MSD_TEST_HOME_SANDBOX';
function sandboxHome(t, dir) {
const savedHome = process.env.HOME;
@@ -1245,15 +1245,15 @@ function sandboxHome(t, dir) {
function writePackageSourceMarkerFixture(configDir) {
fs.mkdirSync(configDir, { recursive: true });
fs.writeFileSync(
path.join(configDir, '.gsd-source'),
path.join(__dirname, '..', 'commands', 'gsd') + '\n',
path.join(configDir, '.msd-source'),
path.join(__dirname, '..', 'commands', 'msd') + '\n',
);
return configDir;
}
/** Write one valid third-party gate into a synthetic user capability home. */
function writeAmbientCapabilityGate(home, id, point) {
const capDir = path.join(home, '.gsd', 'capabilities', id);
const capDir = path.join(home, '.msd', 'capabilities', id);
fs.mkdirSync(capDir, { recursive: true });
fs.writeFileSync(path.join(capDir, 'capability.json'), JSON.stringify({
id,
@@ -1262,7 +1262,7 @@ function writeAmbientCapabilityGate(home, id, point) {
role: 'feature',
tier: 'full',
description: 'Capability outside the test fixture that must remain invisible.',
engines: { gsd: '>=1.7.0' },
engines: { msd: '>=1.7.0' },
requires: [],
runtimeCompat: { supported: ['claude'], unsupported: [] },
skills: [],
@@ -1294,7 +1294,7 @@ function withAmbientCapabilityHome(t, prefix, id, point) {
return home;
}
module.exports = { runGsdTools, createTempDir, createTempProject, createTempGitProject, cleanup, tmpRootCandidates, readFileNormalized, readWorkflowCombined, parseFrontmatter, isUsageOutput, captureConsole, toPosixPath, absPlanningPath, runNpm, isolatedNpmEnv, withIsolatedProcessState, delay, waitFor, resetRuntimeWarningCaches, SESSION_ENV_KEYS, saveSessionEnv, restoreSessionEnv, clearSessionEnv, isolateWorkstreamEnv, restoreWorkstreamEnv, TOOLS_PATH, SESSION_IDENTITY_ENV_KEYS, scrubConfigLocationEnv, installSpawnEnv, installSpawnHome, sandboxHome, writePackageSourceMarkerFixture, writeAmbientCapabilityGate, withAmbientCapabilityHome, TEST_HOME_SANDBOX_MARKER, mockPartialWriteThenThrow, captureFdSync, suppressFdAsync };
module.exports = { runMsdTools, createTempDir, createTempProject, createTempGitProject, cleanup, tmpRootCandidates, readFileNormalized, readWorkflowCombined, parseFrontmatter, isUsageOutput, captureConsole, toPosixPath, absPlanningPath, runNpm, isolatedNpmEnv, withIsolatedProcessState, delay, waitFor, resetRuntimeWarningCaches, SESSION_ENV_KEYS, saveSessionEnv, restoreSessionEnv, clearSessionEnv, isolateWorkstreamEnv, restoreWorkstreamEnv, TOOLS_PATH, SESSION_IDENTITY_ENV_KEYS, scrubConfigLocationEnv, installSpawnEnv, installSpawnHome, sandboxHome, writePackageSourceMarkerFixture, writeAmbientCapabilityGate, withAmbientCapabilityHome, TEST_HOME_SANDBOX_MARKER, mockPartialWriteThenThrow, captureFdSync, suppressFdAsync };
// Lazy, for the reason builtLib() is lazy: reading either of these is what
// forces the built-lib require, so a test file that needs neither can still