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

@@ -17,8 +17,8 @@ const {
platformWriteSync,
platformReadSync,
platformEnsureDir,
dispatchGsdCommand,
resolveGsdToolsPath,
dispatchMsdCommand,
resolveMsdToolsPath,
projectPathActionProjection,
projectPathExportLine,
posixNormalize,
@@ -31,7 +31,7 @@ const {
escapeSingleQuotedShellLiteral,
retryRenameSync,
contentChangedAfterNormalize,
} = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
} = require(path.join(__dirname, '..', 'msd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
const { createTempGitProject, createTempDir, cleanup } = require('./helpers.cjs');
@@ -44,7 +44,7 @@ const { createTempGitProject, createTempDir, cleanup } = require('./helpers.cjs'
const EXEC_TOOL_OPTION_PASSTHROUGH_TIMEOUT_MS = 1234;
/**
* Deliberately far smaller than any real dispatchGsdCommand invocation
* Deliberately far smaller than any real dispatchMsdCommand invocation
* could complete, forcing a genuine wall-clock timeout so this test can
* assert timedOut:true is reported correctly (mirrors the
* RUN_BASH_SCRIPT_FORCED_TIMEOUT_MS / BOUNDED_SHELL_FORCED_TIMEOUT_MS
@@ -135,7 +135,7 @@ describe('execTool', () => {
// ─── resolveExecutableBinary (#3411) ────────────────────────────────────────
// Platform is always INJECTED via opts.platform/opts.env — every win32 case
// runs on macOS/Linux too. See .gsd/phase/feat-3411-windows-binary-seam/50-test-matrix.md.
// runs on macOS/Linux too. See .msd/phase/feat-3411-windows-binary-seam/50-test-matrix.md.
describe('resolveExecutableBinary (#3411)', () => {
test('R1: is exported as a function', () => {
@@ -434,7 +434,7 @@ describe('resolveExecutableBinary (#3411)', () => {
// ─── resolveExecutableBinary seam options (#3618) ───────────────────────────
// prependPaths / requireExecutable — both opt-in, both default off, so
// Phase 1 callers (R1-R25 above, P1-P16 below) stay byte-identical. See
// .gsd/phase/chore-3618-fallow-binary-resolution/50-test-matrix.md (S1-S12).
// .msd/phase/chore-3618-fallow-binary-resolution/50-test-matrix.md (S1-S12).
describe('resolveExecutableBinary seam options (#3618)', () => {
test('S1: prependPaths with binary only in dirA resolves in dirA', () => {
@@ -658,7 +658,7 @@ describe('resolveExecutableBinary seam options (#3618)', () => {
// ambient environment." Added so resolveFallowBinary can hand the seam a
// search path without also hand-threading PATHEXT (a private PATHEXT read
// is exactly the shape local/no-private-binary-resolution forbids outside
// this seam). See .gsd/phase/chore-3619-no-bare-binary-spawn/50-test-matrix.md (O1-O7).
// this seam). See .msd/phase/chore-3619-no-bare-binary-spawn/50-test-matrix.md (O1-O7).
describe('resolveExecutableBinary pathOverride (#3619)', () => {
test('O1: pathOverride omitted — identical to today, env.PATH is used', () => {
@@ -1088,9 +1088,9 @@ describe('execTool (#3411 windows resolution)', () => {
test('E2: posix ENOENT contract', (t) => {
if (process.platform === 'win32') { t.skip('posix ENOENT shape'); return; }
const result = execTool('gsd-definitely-absent-binary-9f3a', []);
const result = execTool('msd-definitely-absent-binary-9f3a', []);
assert.equal(result.exitCode, 127);
assert.equal(result.stderr, 'gsd-definitely-absent-binary-9f3a: not found');
assert.equal(result.stderr, 'msd-definitely-absent-binary-9f3a: not found');
});
test('E3: execTool mediates a .cmd through cmd.exe on win32', (t) => {
@@ -1144,24 +1144,24 @@ describe('execTool (#3411 windows resolution)', () => {
});
});
// ─── dispatchGsdCommand (#2102 Stage 2 — subprocess-shim dispatch to gsd-tools.cjs) ──
// ─── dispatchMsdCommand (#2102 Stage 2 — subprocess-shim dispatch to msd-tools.cjs) ──
//
// The command-routing hub (`createHub()`) has no fully-populated factory
// anywhere in the tree — every caller builds a single-family hub — so the
// only dispatch path covering the FULL family/subcommand surface is the
// gsd-tools.cjs CLI itself. This is the shared helper pi/gsd.cjs and the
// msd-tools.cjs CLI itself. This is the shared helper pi/msd.cjs and the
// companion MCP server both dispatch through.
describe('dispatchGsdCommand', () => {
describe('dispatchMsdCommand', () => {
let tmpDir;
beforeEach(() => { tmpDir = createTempDir(); });
afterEach(() => { cleanup(tmpDir); mock.restoreAll(); });
test('resolveGsdToolsPath resolves to the real gsd-tools.cjs on disk', () => {
const toolsPath = resolveGsdToolsPath();
assert.ok(fs.existsSync(toolsPath), `expected gsd-tools.cjs to exist at ${toolsPath}`);
assert.equal(path.basename(toolsPath), 'gsd-tools.cjs');
test('resolveMsdToolsPath resolves to the real msd-tools.cjs on disk', () => {
const toolsPath = resolveMsdToolsPath();
assert.ok(fs.existsSync(toolsPath), `expected msd-tools.cjs to exist at ${toolsPath}`);
assert.equal(path.basename(toolsPath), 'msd-tools.cjs');
});
test('a valid read-only family/subcommand dispatches for real and returns ok:true + non-empty stdout', () => {
@@ -1171,7 +1171,7 @@ describe('dispatchGsdCommand', () => {
// is withheld (null), breaking this reachability proxy.
fs.mkdirSync(path.join(tmpDir, '.planning'), { recursive: true });
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), '# Roadmap\n');
const result = dispatchGsdCommand({ family: 'progress', subcommand: 'json', cwd: tmpDir });
const result = dispatchMsdCommand({ family: 'progress', subcommand: 'json', cwd: tmpDir });
assert.equal(result.ok, true, `expected ok:true, got: ${JSON.stringify(result)}`);
assert.equal(typeof result.stdout, 'string');
assert.ok(result.stdout.length > 0, 'stdout must be non-empty');
@@ -1183,7 +1183,7 @@ describe('dispatchGsdCommand', () => {
test('an unknown family returns ok:false without throwing', () => {
assert.doesNotThrow(() => {
const result = dispatchGsdCommand({ family: 'no-such-family-8675309', cwd: tmpDir });
const result = dispatchMsdCommand({ family: 'no-such-family-8675309', cwd: tmpDir });
assert.equal(result.ok, false);
assert.notEqual(result.code, 0);
assert.equal(typeof result.stderr, 'string');
@@ -1194,12 +1194,12 @@ describe('dispatchGsdCommand', () => {
});
});
test('a missing/bogus gsd-tools.cjs path degrades to ok:false without throwing', () => {
test('a missing/bogus msd-tools.cjs path degrades to ok:false without throwing', () => {
assert.doesNotThrow(() => {
const result = dispatchGsdCommand({
const result = dispatchMsdCommand({
family: 'progress',
cwd: tmpDir,
gsdToolsPath: path.join(tmpDir, 'definitely-not-a-real-gsd-tools-8675309.cjs'),
msdToolsPath: path.join(tmpDir, 'definitely-not-a-real-msd-tools-8675309.cjs'),
});
assert.equal(result.ok, false);
assert.equal(result.timedOut, false);
@@ -1207,7 +1207,7 @@ describe('dispatchGsdCommand', () => {
});
test('a missing/empty "family" is rejected locally without spawning a subprocess', () => {
const result = dispatchGsdCommand({ cwd: tmpDir });
const result = dispatchMsdCommand({ cwd: tmpDir });
assert.equal(result.ok, false);
assert.equal(result.code, null);
assert.match(result.stderr, /requires a non-empty string "family"/);
@@ -1215,13 +1215,13 @@ describe('dispatchGsdCommand', () => {
test('a wall-clock timeout is reported via timedOut:true, ok:false — never throws', () => {
assert.doesNotThrow(() => {
const result = dispatchGsdCommand({ family: 'progress', subcommand: 'json', cwd: tmpDir, timeout: DISPATCH_FORCED_TIMEOUT_MS });
const result = dispatchMsdCommand({ family: 'progress', subcommand: 'json', cwd: tmpDir, timeout: DISPATCH_FORCED_TIMEOUT_MS });
assert.equal(result.ok, false);
assert.equal(result.timedOut, true);
});
});
// #3050 item 4: this site (dispatchGsdCommand's `timedOut` derivation) now
// #3050 item 4: this site (dispatchMsdCommand's `timedOut` derivation) now
// routes through the shared isSpawnTimeout predicate, which drops the
// `signal === 'SIGTERM'` requirement — a Windows-shaped timeout (no signal,
// only error.code === 'ETIMEDOUT') must still be detected. The real-timeout
@@ -1236,7 +1236,7 @@ describe('dispatchGsdCommand', () => {
error: Object.assign(new Error('spawnSync ETIMEDOUT'), { code: 'ETIMEDOUT' }),
}));
const result = dispatchGsdCommand({ family: 'progress', subcommand: 'json', cwd: tmpDir });
const result = dispatchMsdCommand({ family: 'progress', subcommand: 'json', cwd: tmpDir });
assert.equal(result.ok, false);
assert.equal(result.timedOut, true);
});
@@ -1484,7 +1484,7 @@ describe('platformEnsureDir', () => {
* hooks resolve correctly regardless of the shell's current working directory.
*
* The original bug: local install hook commands used bare relative paths like
* `node .claude/hooks/gsd-context-monitor.js`. Claude Code persists the bash
* `node .claude/hooks/msd-context-monitor.js`. Claude Code persists the bash
* tool's cwd between calls, so a single `cd subdir && …` early in a session
* permanently broke every hook for the rest of that session.
*
@@ -1498,7 +1498,7 @@ const { describe, test, before } = require('node:test');
const assert = require('node:assert/strict');
const path = require('path');
const projection = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
const projection = require(path.join(__dirname, '..', 'msd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
const { projectLocalHookPrefix, projectShellCommandText } = projection;
describe('bug #1906: local hook commands use $CLAUDE_PROJECT_DIR', () => {
@@ -1516,13 +1516,13 @@ describe('bug #1906: local hook commands use $CLAUDE_PROJECT_DIR', () => {
const prefix = projectLocalHookPrefix({ runtime: 'claude', dirName: '.claude' });
const command = projectShellCommandText({
runnerToken: '"/usr/local/bin/node"',
argTokens: [`${prefix}/hooks/gsd-context-monitor.js`],
argTokens: [`${prefix}/hooks/msd-context-monitor.js`],
runtime: 'claude',
platform: 'linux',
});
assert.equal(
command,
'"/usr/local/bin/node" "$CLAUDE_PROJECT_DIR"/.claude/hooks/gsd-context-monitor.js',
'"/usr/local/bin/node" "$CLAUDE_PROJECT_DIR"/.claude/hooks/msd-context-monitor.js',
);
});
});
@@ -1543,7 +1543,7 @@ describe('bug #1906: local hook commands use $CLAUDE_PROJECT_DIR', () => {
*
* $CLAUDE_PROJECT_DIR is a Claude Code-specific env variable. Gemini CLI did
* not set it. On Windows, Gemini's own variable-substitution + path-join logic
* produced a doubled path like `D:\Projects\GSD\'D:\Projects\GSD'`, causing
* produced a doubled path like `D:\Projects\MSD\'D:\Projects\MSD'`, causing
* every local project hook to fail at SessionStart.
*
* Fix: localPrefix was made runtime-conditional. Gemini/Antigravity used bare
@@ -1551,7 +1551,7 @@ describe('bug #1906: local hook commands use $CLAUDE_PROJECT_DIR', () => {
* dir as cwd. Claude Code and others still use "$CLAUDE_PROJECT_DIR"/ (#1906).
*
* #1928: Google sunset Gemini CLI (2026-06-18) and the `gemini` runtime was
* removed from GSD entirely.
* removed from MSD entirely.
*
* #2096: `projectLocalHookPrefix` no longer special-cases `antigravity` by
* name — it branches on the caller-supplied `hookPathStyle` (sourced from
@@ -1565,7 +1565,7 @@ const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const path = require('node:path');
const projection = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
const projection = require(path.join(__dirname, '..', 'msd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
const { projectLocalHookPrefix, projectShellCommandText } = projection;
describe('bug #2557: Antigravity local hooks use relative paths (not $CLAUDE_PROJECT_DIR); gemini runtime removed (#1928)', () => {
@@ -1591,7 +1591,7 @@ describe('bug #2557: Antigravity local hooks use relative paths (not $CLAUDE_PRO
assert.equal(prefix, '"$CLAUDE_PROJECT_DIR"/.gemini');
const command = projectShellCommandText({
runnerToken: '"/usr/local/bin/node"',
argTokens: [`${prefix}/hooks/gsd-check-update.js`],
argTokens: [`${prefix}/hooks/msd-check-update.js`],
runtime: 'gemini',
platform: 'linux',
});
@@ -1613,14 +1613,14 @@ describe('bug #2557: Antigravity local hooks use relative paths (not $CLAUDE_PRO
__foldDescribe("folded:bug-3413-shell-command-projection (consolidation epic #1969 B3 #1972)", () => {
'use strict';
process.env.GSD_TEST_MODE = '1';
process.env.MSD_TEST_MODE = '1';
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const path = require('node:path');
const projection = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
const hooksSurface = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'runtime-hooks-surface.cjs'));
const projection = require(path.join(__dirname, '..', 'msd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
const hooksSurface = require(path.join(__dirname, '..', 'msd-core', 'bin', 'lib', 'runtime-hooks-surface.cjs'));
const {
hookCommandNeedsPowerShellCallOperator,
@@ -1693,7 +1693,7 @@ describe('bug #2236: hookShell parameter routes PowerShell call operator', () =>
});
test('formatHookCommandForRuntime prepends & when hookShell=powershell', () => {
const cmd = '"C:/Program Files/nodejs/node.exe" "C:/Users/me/.claude/hooks/gsd-context-monitor.js"';
const cmd = '"C:/Program Files/nodejs/node.exe" "C:/Users/me/.claude/hooks/msd-context-monitor.js"';
assert.equal(
formatHookCommandForRuntime(cmd, { platform: 'win32', runtime: 'claude', hookShell: 'powershell' }),
`& ${cmd}`,
@@ -1701,7 +1701,7 @@ describe('bug #2236: hookShell parameter routes PowerShell call operator', () =>
});
test('formatHookCommandForRuntime omits & when hookShell unset (backward compat)', () => {
const cmd = '"C:/Program Files/nodejs/node.exe" "C:/Users/me/.claude/hooks/gsd-context-monitor.js"';
const cmd = '"C:/Program Files/nodejs/node.exe" "C:/Users/me/.claude/hooks/msd-context-monitor.js"';
assert.equal(
formatHookCommandForRuntime(cmd, { platform: 'win32', runtime: 'claude' }),
cmd,
@@ -1709,7 +1709,7 @@ describe('bug #2236: hookShell parameter routes PowerShell call operator', () =>
});
test('buildHookCommand emits & prefix when hookShell=powershell', () => {
const cmd = buildHookCommand('C:/Users/me/.claude', 'gsd-check-update.js', {
const cmd = buildHookCommand('C:/Users/me/.claude', 'msd-check-update.js', {
platform: 'win32',
runtime: 'claude',
hookShell: 'powershell',
@@ -1719,7 +1719,7 @@ describe('bug #2236: hookShell parameter routes PowerShell call operator', () =>
});
test('buildHookCommand omits & prefix when hookShell=bash (regression lock)', () => {
const cmd = buildHookCommand('C:/Users/me/.claude', 'gsd-check-update.js', {
const cmd = buildHookCommand('C:/Users/me/.claude', 'msd-check-update.js', {
platform: 'win32',
runtime: 'claude',
hookShell: 'bash',
@@ -1731,7 +1731,7 @@ describe('bug #2236: hookShell parameter routes PowerShell call operator', () =>
describe('bug #3413: installer hook surfaces consume runtime-aware projection', () => {
test('buildHookCommand emits shell-neutral Claude hook command on Windows', () => {
const cmd = buildHookCommand('C:/Users/me/.claude', 'gsd-check-update.js', {
const cmd = buildHookCommand('C:/Users/me/.claude', 'msd-check-update.js', {
platform: 'win32',
runtime: 'claude',
});
@@ -1742,7 +1742,7 @@ describe('bug #3413: installer hook surfaces consume runtime-aware projection',
const settings = {
hooks: {
SessionStart: [{
hooks: [{ type: 'command', command: '& "/usr/local/bin/node" "C:/Users/me/.claude/hooks/gsd-check-update.js"' }],
hooks: [{ type: 'command', command: '& "/usr/local/bin/node" "C:/Users/me/.claude/hooks/msd-check-update.js"' }],
}],
},
};
@@ -1753,28 +1753,28 @@ describe('bug #3413: installer hook surfaces consume runtime-aware projection',
assert.equal(changed, true);
assert.equal(
settings.hooks.SessionStart[0].hooks[0].command,
'"/usr/local/bin/node" "C:/Users/me/.claude/hooks/gsd-check-update.js"',
'"/usr/local/bin/node" "C:/Users/me/.claude/hooks/msd-check-update.js"',
);
});
});
describe('bug #3439: shell projection module owns managed-hook policy and legacy rewrite projection', () => {
test('isManagedHookBasename is surface-aware', () => {
assert.equal(isManagedHookBasename('/x/hooks/gsd-check-update.js', { surface: 'settings-json' }), true);
assert.equal(isManagedHookBasename('/x/hooks/gsd-statusline.js', { surface: 'settings-json' }), true);
assert.equal(isManagedHookBasename('/x/hooks/gsd-statusline.js', { surface: 'codex-toml' }), false);
assert.equal(isManagedHookBasename('/x/hooks/msd-check-update.js', { surface: 'settings-json' }), true);
assert.equal(isManagedHookBasename('/x/hooks/msd-statusline.js', { surface: 'settings-json' }), true);
assert.equal(isManagedHookBasename('/x/hooks/msd-statusline.js', { surface: 'codex-toml' }), false);
assert.equal(isManagedHookBasename('/x/hooks/custom-hook.js', { surface: 'settings-json' }), false);
});
test('projectLegacySettingsHookCommand preserves non-Windows script token shape', () => {
const command = projectLegacySettingsHookCommand({
runnerToken: '"/usr/local/bin/node"',
scriptPath: '/x/hooks/gsd-statusline.js',
scriptToken: "'/x/hooks/gsd-statusline.js'",
scriptPath: '/x/hooks/msd-statusline.js',
scriptToken: "'/x/hooks/msd-statusline.js'",
platform: 'linux',
runtime: 'claude',
});
assert.equal(command, `"/usr/local/bin/node" '/x/hooks/gsd-statusline.js'`);
assert.equal(command, `"/usr/local/bin/node" '/x/hooks/msd-statusline.js'`);
});
test('projectLegacySettingsHookCommand normalizes Windows managed paths and runtime wrapper policy', () => {
@@ -1783,12 +1783,12 @@ describe('bug #3439: shell projection module owns managed-hook policy and legacy
// Gemini-backend successor 'antigravity'). No `& ` prefix is ever added.
const command = projectLegacySettingsHookCommand({
runnerToken: '"C:/nvm4w/nodejs/node.exe"',
scriptPath: 'C:\\Users\\me\\.gemini\\hooks\\gsd-prompt-guard.js',
scriptToken: "'C:\\Users\\me\\.gemini\\hooks\\gsd-prompt-guard.js'",
scriptPath: 'C:\\Users\\me\\.gemini\\hooks\\msd-prompt-guard.js',
scriptToken: "'C:\\Users\\me\\.gemini\\hooks\\msd-prompt-guard.js'",
platform: 'win32',
runtime: 'antigravity',
});
assert.equal(command, '"C:/nvm4w/nodejs/node.exe" "C:/Users/me/.gemini/hooks/gsd-prompt-guard.js"');
assert.equal(command, '"C:/nvm4w/nodejs/node.exe" "C:/Users/me/.gemini/hooks/msd-prompt-guard.js"');
});
test('projectLocalHookPrefix centralizes runtime-specific project-dir interpolation policy', () => {
@@ -1837,13 +1837,13 @@ describe('bug #3439: shell projection module owns managed-hook policy and legacy
test('isManagedHookCommand classifies managed settings hooks and leaves user commands untouched', () => {
assert.equal(
isManagedHookCommand('"/usr/local/bin/node" "/Users/me/.claude/hooks/gsd-statusline.js"', {
isManagedHookCommand('"/usr/local/bin/node" "/Users/me/.claude/hooks/msd-statusline.js"', {
surface: 'settings-json',
}),
true,
);
assert.equal(
isManagedHookCommand('"C:/Program Files/Git/bin/bash.exe" "C:/Users/me/.claude/hooks/gsd-session-state.sh"', {
isManagedHookCommand('"C:/Program Files/Git/bin/bash.exe" "C:/Users/me/.claude/hooks/msd-session-state.sh"', {
surface: 'settings-json',
}),
true,
@@ -1857,7 +1857,7 @@ describe('bug #3439: shell projection module owns managed-hook policy and legacy
});
test('isManagedHookCommand supports codex surfaces and optional legacy alias matching', () => {
const command = '"/usr/local/bin/node" "/Users/me/.codex/hooks/gsd-check-update.js"';
const command = '"/usr/local/bin/node" "/Users/me/.codex/hooks/msd-check-update.js"';
assert.equal(
isManagedHookCommand(command, {
surface: 'codex-toml',
@@ -1865,13 +1865,13 @@ describe('bug #3439: shell projection module owns managed-hook policy and legacy
true,
);
assert.equal(
isManagedHookCommand('"/usr/local/bin/node" "/Users/me/.codex/hooks/gsd-update-check.js"', {
isManagedHookCommand('"/usr/local/bin/node" "/Users/me/.codex/hooks/msd-update-check.js"', {
surface: 'codex-toml',
}),
false,
);
assert.equal(
isManagedHookCommand('"/usr/local/bin/node" "/Users/me/.codex/hooks/gsd-update-check.js"', {
isManagedHookCommand('"/usr/local/bin/node" "/Users/me/.codex/hooks/msd-update-check.js"', {
surface: 'codex-toml',
includeLegacyAliases: true,
}),
@@ -1890,7 +1890,7 @@ describe('#1693 regression: Windows legacy-node rewrite must not double-quote a
// a literal `"`, treated it as relative, and died with MODULE_NOT_FOUND —
// breaking every node managed hook at once (self-locking deadlock).
test('projectLegacySettingsHookCommand emits the anchored path verbatim, not re-quoted', () => {
const anchored = '"$CLAUDE_PROJECT_DIR"/.claude/hooks/gsd-context-monitor.js';
const anchored = '"$CLAUDE_PROJECT_DIR"/.claude/hooks/msd-context-monitor.js';
const command = projectLegacySettingsHookCommand({
runnerToken: winRunner,
scriptPath: anchored,
@@ -1900,7 +1900,7 @@ describe('#1693 regression: Windows legacy-node rewrite must not double-quote a
});
assert.equal(
command,
'"C:/Program Files/nodejs/node.exe" "$CLAUDE_PROJECT_DIR"/.claude/hooks/gsd-context-monitor.js',
'"C:/Program Files/nodejs/node.exe" "$CLAUDE_PROJECT_DIR"/.claude/hooks/msd-context-monitor.js',
);
assert.ok(!command.includes('\\"'), 'must not contain escaped double-quotes');
});
@@ -1908,7 +1908,7 @@ describe('#1693 regression: Windows legacy-node rewrite must not double-quote a
// WHY: the fix must be surgical — a BARE absolute Windows path (no anchored
// prefix) can contain spaces ("Program Files") and still REQUIRES quoting.
test('projectLegacySettingsHookCommand still quotes a bare absolute Windows path', () => {
const abs = 'C:/Program Files App/.claude/hooks/gsd-context-monitor.js';
const abs = 'C:/Program Files App/.claude/hooks/msd-context-monitor.js';
const command = projectLegacySettingsHookCommand({
runnerToken: winRunner,
scriptPath: abs,
@@ -1918,7 +1918,7 @@ describe('#1693 regression: Windows legacy-node rewrite must not double-quote a
});
assert.equal(
command,
'"C:/Program Files/nodejs/node.exe" "C:/Program Files App/.claude/hooks/gsd-context-monitor.js"',
'"C:/Program Files/nodejs/node.exe" "C:/Program Files App/.claude/hooks/msd-context-monitor.js"',
);
});
@@ -1931,12 +1931,12 @@ describe('#1693 regression: Windows legacy-node rewrite must not double-quote a
test('projectLegacySettingsHookCommand preserves the original scriptToken for anchored paths on POSIX', () => {
const command = projectLegacySettingsHookCommand({
runnerToken: '"/usr/local/bin/node"',
scriptPath: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/gsd-statusline.js',
scriptToken: "'/x/hooks/gsd-statusline.js'",
scriptPath: '"$CLAUDE_PROJECT_DIR"/.claude/hooks/msd-statusline.js',
scriptToken: "'/x/hooks/msd-statusline.js'",
platform: 'linux',
runtime: 'claude',
});
assert.equal(command, `"/usr/local/bin/node" '/x/hooks/gsd-statusline.js'`);
assert.equal(command, `"/usr/local/bin/node" '/x/hooks/msd-statusline.js'`);
});
// WHY: end-to-end through the installer rewrite — the actual #2979 path that
@@ -1949,7 +1949,7 @@ describe('#1693 regression: Windows legacy-node rewrite must not double-quote a
PostToolUse: [
{
hooks: [
{ command: 'node "$CLAUDE_PROJECT_DIR"/.claude/hooks/gsd-context-monitor.js' },
{ command: 'node "$CLAUDE_PROJECT_DIR"/.claude/hooks/msd-context-monitor.js' },
],
},
],
@@ -1963,7 +1963,7 @@ describe('#1693 regression: Windows legacy-node rewrite must not double-quote a
const rewritten = settings.hooks.PostToolUse[0].hooks[0].command;
assert.equal(
rewritten,
'"C:/Program Files/nodejs/node.exe" "$CLAUDE_PROJECT_DIR"/.claude/hooks/gsd-context-monitor.js',
'"C:/Program Files/nodejs/node.exe" "$CLAUDE_PROJECT_DIR"/.claude/hooks/msd-context-monitor.js',
);
assert.ok(!rewritten.includes('\\"'), 'rewritten command must not contain escaped double-quotes');
});
@@ -1980,7 +1980,7 @@ describe('#1693 regression: Windows legacy-node rewrite must not double-quote a
__foldDescribe("folded:bug-3441-path-action-projection (consolidation epic #1969 B3 #1972)", () => {
'use strict';
process.env.GSD_TEST_MODE = '1';
process.env.MSD_TEST_MODE = '1';
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
@@ -1991,7 +1991,7 @@ const os = require('node:os');
const projection = require(path.join(
__dirname,
'..',
'gsd-core',
'msd-core',
'bin',
'lib',
'shell-command-projection.cjs',
@@ -2000,7 +2000,7 @@ const install = require(path.join(__dirname, '..', 'bin', 'install.js'));
const { withIsolatedProcessState, cleanup } = require('./helpers.cjs');
function createTempHome() {
return fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-home-3441-'));
return fs.mkdtempSync(path.join(os.tmpdir(), 'msd-home-3441-'));
}
@@ -2009,7 +2009,7 @@ describe('bug #3441: PATH guidance is projected from typed shell action IR', ()
assert.equal(typeof projection.projectPathActionProjection, 'function');
});
// (formatSdkPathDiagnostic removed with the gsd-sdk shim, #191 — the PATH
// (formatSdkPathDiagnostic removed with the msd-sdk shim, #191 — the PATH
// action projection it wrapped is still covered by the tests below.)
test('persistent PATH export guidance is projected via the same seam', () => {
@@ -2146,7 +2146,7 @@ const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const path = require('node:path');
const projection = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
const projection = require(path.join(__dirname, '..', 'msd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
const { buildLocalShellHookCommand, shellHookOmitsBashRunner, projectLocalHookPrefix } = projection;
describe('bug #580: local .sh hooks on Claude/Windows must NOT wrap with bash.exe', () => {
@@ -2154,12 +2154,12 @@ describe('bug #580: local .sh hooks on Claude/Windows must NOT wrap with bash.ex
const localPrefix = projectLocalHookPrefix({ runtime: 'claude', dirName: '.claude' });
const result = buildLocalShellHookCommand({
localPrefix,
hookFile: 'gsd-session-state.sh',
hookFile: 'msd-session-state.sh',
bashRunner: '"C:/Program Files/Git/bin/bash.exe"',
runtime: 'claude',
platform: 'win32',
});
assert.equal(result, '"$CLAUDE_PROJECT_DIR"/.claude/hooks/gsd-session-state.sh');
assert.equal(result, '"$CLAUDE_PROJECT_DIR"/.claude/hooks/msd-session-state.sh');
assert.ok(!result.includes('bash.exe'), `result must not contain bash.exe, got: ${result}`);
});
@@ -2167,31 +2167,31 @@ describe('bug #580: local .sh hooks on Claude/Windows must NOT wrap with bash.ex
const localPrefix = projectLocalHookPrefix({ runtime: 'claude', dirName: '.claude' });
const result = buildLocalShellHookCommand({
localPrefix,
hookFile: 'gsd-session-state.sh',
hookFile: 'msd-session-state.sh',
bashRunner: null,
runtime: 'claude',
platform: 'win32',
});
assert.equal(result, '"$CLAUDE_PROJECT_DIR"/.claude/hooks/gsd-session-state.sh');
assert.equal(result, '"$CLAUDE_PROJECT_DIR"/.claude/hooks/msd-session-state.sh');
});
test('local .sh hook on POSIX keeps the bash runner', () => {
const localPrefix = projectLocalHookPrefix({ runtime: 'claude', dirName: '.claude' });
const result = buildLocalShellHookCommand({
localPrefix,
hookFile: 'gsd-session-state.sh',
hookFile: 'msd-session-state.sh',
bashRunner: 'bash',
runtime: 'claude',
platform: 'linux',
});
assert.equal(result, 'bash "$CLAUDE_PROJECT_DIR"/.claude/hooks/gsd-session-state.sh');
assert.equal(result, 'bash "$CLAUDE_PROJECT_DIR"/.claude/hooks/msd-session-state.sh');
});
test('local .sh hook on Windows non-Claude runtime keeps the bash runner', () => {
const localPrefix = projectLocalHookPrefix({ runtime: 'codex', dirName: '.claude' });
const result = buildLocalShellHookCommand({
localPrefix,
hookFile: 'gsd-session-state.sh',
hookFile: 'msd-session-state.sh',
bashRunner: '"C:/Program Files/Git/bin/bash.exe"',
runtime: 'codex',
platform: 'win32',
@@ -2203,10 +2203,10 @@ describe('bug #580: local .sh hooks on Claude/Windows must NOT wrap with bash.ex
test('all four managed local .sh hooks drop the wrapper on Claude/Windows', () => {
const localPrefix = projectLocalHookPrefix({ runtime: 'claude', dirName: '.claude' });
const hooks = [
'gsd-session-state.sh',
'gsd-validate-commit.sh',
'gsd-graphify-update.sh',
'gsd-phase-boundary.sh',
'msd-session-state.sh',
'msd-validate-commit.sh',
'msd-graphify-update.sh',
'msd-phase-boundary.sh',
];
for (const f of hooks) {
const result = buildLocalShellHookCommand({
@@ -2256,7 +2256,7 @@ describe('bug #580: local .sh hooks on Claude/Windows must NOT wrap with bash.ex
* Filesystem fault-injection coverage for the canonical atomic-write
* seam (#3595).
*
* `platformWriteSync` in `gsd-core/bin/lib/shell-command-projection.cjs`
* `platformWriteSync` in `msd-core/bin/lib/shell-command-projection.cjs`
* is the shared seam every config/state/generated-artifact writer in
* the CJS layer routes through. Its contract:
*
@@ -2294,7 +2294,7 @@ const path = require('node:path');
const {
platformWriteSync,
platformEnsureDir,
} = require('../gsd-core/bin/lib/shell-command-projection.cjs');
} = require('../msd-core/bin/lib/shell-command-projection.cjs');
/**
* Create a fresh real-fs scratch dir per test so no two faults share
@@ -2865,7 +2865,7 @@ describe('path export projection — escaping (#3118)', () => {
});
test('leaves an ordinary value byte-identical', () => {
const value = 'C:/Users/dev/gsd.js';
const value = 'C:/Users/dev/msd.js';
assert.equal(escapeTomlDoubleQuotedString(value), value);
});
@@ -3109,7 +3109,7 @@ describe('path export projection — escaping (#3118)', () => {
});
test('returns silently on a successful rename', (t) => {
const dir = createTempDir('gsd-3118-rename-');
const dir = createTempDir('msd-3118-rename-');
t.after(() => cleanup(dir));
const from = path.join(dir, 'source.txt');
const to = path.join(dir, 'dest.txt');