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

@@ -7,29 +7,29 @@ const { createTempDir, cleanup } = require('./helpers.cjs');
const { runNode, runHook } = require('./helpers/process-seam.cjs');
const { BUILD_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
const hooksSurface = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
const hooksSurface = require('../msd-core/bin/lib/runtime-hooks-surface.cjs');
const { HOOKS_TO_COPY } = require('../scripts/build-hooks.js');
const REPO_ROOT = path.join(__dirname, '..');
const RESOLVER_HOOK = 'gsd-node-runner.sh';
const RESOLVER_HOOK = 'msd-node-runner.sh';
const FOREIGN_NODE = '/nonexistent-envA/bin/node';
const STUB_HOOK_BODY = ['#!/usr/bin/env node', 'process.stdout.write("hook-ran-ok");', ''].join('\n');
const MANAGED_JS_HOOKS = [
'gsd-check-update.js',
'gsd-config-reload.js',
'gsd-statusline.js',
'gsd-context-monitor.js',
'gsd-prompt-guard.js',
'gsd-read-guard.js',
'gsd-read-injection-scanner.js',
'gsd-update-banner.js',
'gsd-workflow-guard.js',
'msd-check-update.js',
'msd-config-reload.js',
'msd-statusline.js',
'msd-context-monitor.js',
'msd-prompt-guard.js',
'msd-read-guard.js',
'msd-read-injection-scanner.js',
'msd-update-banner.js',
'msd-workflow-guard.js',
];
const GUARD_HOOKS = [
'gsd-write-guard.js',
'gsd-agent-isolation-guard.js',
'gsd-worktree-path-guard.js',
'gsd-secret-read-guard.js',
'msd-write-guard.js',
'msd-agent-isolation-guard.js',
'msd-worktree-path-guard.js',
'msd-secret-read-guard.js',
];
// Every quoted absolute-node token (POSIX-form as emitted, .exe for win32
@@ -50,7 +50,7 @@ function makeHookTree(t, homeDirName) {
for (const hook of [...MANAGED_JS_HOOKS, ...GUARD_HOOKS]) {
fs.writeFileSync(path.join(configDir, 'hooks', hook), STUB_HOOK_BODY);
}
fs.writeFileSync(path.join(configDir, 'hooks', 'gsd-session-state.sh'), '#!/bin/sh\nexit 0\n');
fs.writeFileSync(path.join(configDir, 'hooks', 'msd-session-state.sh'), '#!/bin/sh\nexit 0\n');
const repoResolver = path.join(REPO_ROOT, 'hooks', RESOLVER_HOOK);
if (fs.existsSync(repoResolver)) {
fs.copyFileSync(repoResolver, path.join(configDir, 'hooks', RESOLVER_HOOK));
@@ -73,7 +73,7 @@ function withHome(t, home) {
}
function makeNodeStub(t, label) {
const dir = createTempDir(`gsd3662-stub-${label}`);
const dir = createTempDir(`msd3662-stub-${label}`);
const stubPath = path.join(dir, 'node');
fs.writeFileSync(stubPath, ['#!/bin/sh', `echo "STUB-NODE:$1"`, ''].join('\n'));
fs.chmodSync(stubPath, 0o755);
@@ -86,7 +86,7 @@ function posixRealNodeDir() {
}
function writeCommandFile(t, command, name) {
const file = path.join(createTempDir('gsd3662-cmd'), `${name}.cmd`);
const file = path.join(createTempDir('msd3662-cmd'), `${name}.cmd`);
fs.writeFileSync(file, command + '\n');
t.after(() => cleanup(path.dirname(file)));
return file;
@@ -116,7 +116,7 @@ describe('#3662 runtime-resolving managed hook runners', () => {
if (skipOnWin32(t, 'POSIX sh execution lane; win32 structural coverage below')) return;
const { home, configDir } = makeHookTree(t, 'portable-run');
withHome(t, home);
const emitted = hooksSurface.buildHookCommand(configDir, 'gsd-statusline.js', {
const emitted = hooksSurface.buildHookCommand(configDir, 'msd-statusline.js', {
portableHooks: true,
runtime: 'claude',
});
@@ -137,7 +137,7 @@ describe('#3662 runtime-resolving managed hook runners', () => {
if (skipOnWin32(t, 'POSIX sh execution lane; win32 structural coverage below')) return;
const { home, configDir } = makeHookTree(t, 'plain-run');
withHome(t, home);
const emitted = hooksSurface.buildHookCommand(configDir, 'gsd-statusline.js', {
const emitted = hooksSurface.buildHookCommand(configDir, 'msd-statusline.js', {
runtime: 'claude',
});
assert.ok(typeof emitted === 'string' && emitted.length > 0);
@@ -158,7 +158,7 @@ describe('#3662 runtime-resolving managed hook runners', () => {
const { home, configDir } = makeHookTree(t, 'chain-order');
withHome(t, home);
const stub = makeNodeStub(t, 'chain-order');
const emitted = hooksSurface.buildHookCommand(configDir, 'gsd-statusline.js', {
const emitted = hooksSurface.buildHookCommand(configDir, 'msd-statusline.js', {
runtime: 'claude',
execPath: stub,
});
@@ -171,7 +171,7 @@ describe('#3662 runtime-resolving managed hook runners', () => {
timeoutMs: RUN_TIMEOUT_MS,
});
assert.equal(result.exitCode, 0, `command: ${emitted}\nstderr: ${result.stderr}`);
assert.match(result.stdout, /^STUB-NODE:\S+gsd-statusline\.js/);
assert.match(result.stdout, /^STUB-NODE:\S+msd-statusline\.js/);
});
test('resolver prefers its first argument', (t) => {
@@ -179,7 +179,7 @@ describe('#3662 runtime-resolving managed hook runners', () => {
const { home, configDir } = makeHookTree(t, 'resolver-order');
withHome(t, home);
const stub = makeNodeStub(t, 'resolver-order');
const emitted = hooksSurface.buildHookCommand(configDir, 'gsd-statusline.js', {
const emitted = hooksSurface.buildHookCommand(configDir, 'msd-statusline.js', {
portableHooks: true,
runtime: 'claude',
execPath: stub,
@@ -193,14 +193,14 @@ describe('#3662 runtime-resolving managed hook runners', () => {
timeoutMs: RUN_TIMEOUT_MS,
});
assert.equal(result.exitCode, 0, `command: ${emitted}\nstderr: ${result.stderr}`);
assert.match(result.stdout, /^STUB-NODE:\S+gsd-statusline\.js/);
assert.match(result.stdout, /^STUB-NODE:\S+msd-statusline\.js/);
});
test('chain resolves with the real execPath under a minimal PATH', (t) => {
if (skipOnWin32(t, 'POSIX sh execution lane; win32 structural coverage below')) return;
const { home, configDir } = makeHookTree(t, 'minimal-path');
withHome(t, home);
const emitted = hooksSurface.buildHookCommand(configDir, 'gsd-statusline.js', {
const emitted = hooksSurface.buildHookCommand(configDir, 'msd-statusline.js', {
runtime: 'claude',
});
assert.ok(typeof emitted === 'string');
@@ -270,8 +270,8 @@ describe('#3662 runtime-resolving managed hook runners', () => {
test('update converges the mixed-state foreign runners from the issue', () => {
const settings = settingsWith([
`"${FOREIGN_NODE}" "/home/u/.claude/hooks/gsd-write-guard.js"`,
`"/usr/bin/node" "/home/u/.claude/hooks/gsd-read-guard.js"`,
`"${FOREIGN_NODE}" "/home/u/.claude/hooks/msd-write-guard.js"`,
`"/usr/bin/node" "/home/u/.claude/hooks/msd-read-guard.js"`,
]);
const changed = hooksSurface.rewriteLegacyManagedNodeHookCommands(
settings,
@@ -310,8 +310,8 @@ describe('#3662 runtime-resolving managed hook runners', () => {
});
test('user and args-form entries are never rewritten', () => {
const userCommand = `"/usr/bin/node" "/home/u/own-tools/not-gsd.js"`;
const argsFormCommand = `"${FOREIGN_NODE}" "/home/u/.claude/hooks/gsd-statusline.js"`;
const userCommand = `"/usr/bin/node" "/home/u/own-tools/not-msd.js"`;
const argsFormCommand = `"${FOREIGN_NODE}" "/home/u/.claude/hooks/msd-statusline.js"`;
const settings = {
hooks: {
PreToolUse: [
@@ -346,7 +346,7 @@ describe('#3662 runtime-resolving managed hook runners', () => {
});
test('legacy bare-node entries are still converged', () => {
const settings = settingsWith([`node "/home/u/.claude/hooks/gsd-statusline.js"`]);
const settings = settingsWith([`node "/home/u/.claude/hooks/msd-statusline.js"`]);
const changed = hooksSurface.rewriteLegacyManagedNodeHookCommands(
settings,
hooksSurface.buildNodeRunnerChainToken(),
@@ -360,8 +360,8 @@ describe('#3662 runtime-resolving managed hook runners', () => {
});
test('rewriter is idempotent and leaves resolving shapes alone', () => {
const chainCommand = `"$(for n in "${FOREIGN_NODE}" "$(command -v node)" /usr/local/bin/node /usr/bin/node; do [ -x "$n" ] && { [ "\${n#/}" != "$n" ] || [ "\${n#?:}" != "$n" ]; } && printf '%s' "$n" && break; done)" "/home/u/.claude/hooks/gsd-statusline.js"`;
const resolverCommand = `bash "/home/u/.claude/hooks/${RESOLVER_HOOK}" "${FOREIGN_NODE}" "/home/u/.claude/hooks/gsd-statusline.js"`;
const chainCommand = `"$(for n in "${FOREIGN_NODE}" "$(command -v node)" /usr/local/bin/node /usr/bin/node; do [ -x "$n" ] && { [ "\${n#/}" != "$n" ] || [ "\${n#?:}" != "$n" ]; } && printf '%s' "$n" && break; done)" "/home/u/.claude/hooks/msd-statusline.js"`;
const resolverCommand = `bash "/home/u/.claude/hooks/${RESOLVER_HOOK}" "${FOREIGN_NODE}" "/home/u/.claude/hooks/msd-statusline.js"`;
const settings = settingsWith([chainCommand, resolverCommand]);
const changed = hooksSurface.rewriteLegacyManagedNodeHookCommands(
settings,
@@ -374,7 +374,7 @@ describe('#3662 runtime-resolving managed hook runners', () => {
});
});
describe('resolver script (hooks/gsd-node-runner.sh)', () => {
describe('resolver script (hooks/msd-node-runner.sh)', () => {
test('installer stages the node resolver script', () => {
const repoResolver = path.join(REPO_ROOT, 'hooks', RESOLVER_HOOK);
assert.ok(fs.existsSync(repoResolver), `${RESOLVER_HOOK} missing from hooks/`);
@@ -397,22 +397,22 @@ describe('#3662 runtime-resolving managed hook runners', () => {
const { home, configDir } = makeHookTree(t, 'resolver-none');
const resolver = path.join(configDir, 'hooks', RESOLVER_HOOK);
assert.ok(fs.existsSync(resolver), 'resolver not staged into fixture');
const emptyBin = createTempDir('gsd3662-emptybin');
const emptyBin = createTempDir('msd3662-emptybin');
t.after(() => cleanup(emptyBin));
const result = runHook(resolver, [FOREIGN_NODE, path.join(configDir, 'hooks', 'gsd-statusline.js')], {
const result = runHook(resolver, [FOREIGN_NODE, path.join(configDir, 'hooks', 'msd-statusline.js')], {
interpreter: 'bash',
cwd: home,
env: {
...process.env,
HOME: home,
PATH: `${emptyBin}:/usr/bin:/bin`,
GSD_NODE_RUNNER_NO_FALLBACKS: '1',
MSD_NODE_RUNNER_NO_FALLBACKS: '1',
},
timeoutMs: RUN_TIMEOUT_MS,
});
assert.notEqual(result.exitCode, 0, 'resolver silently succeeded with no node anywhere');
assert.ok(result.stderr.length > 0, 'resolver produced no stderr diagnostic');
assert.ok(!/\bnode\b\s+\S*gsd-statusline/.test(result.stderr + result.stdout), 'resolver leaked a bare-node invocation');
assert.ok(!/\bnode\b\s+\S*msd-statusline/.test(result.stderr + result.stdout), 'resolver leaked a bare-node invocation');
});
});
@@ -420,7 +420,7 @@ describe('#3662 runtime-resolving managed hook runners', () => {
test('sh hook commands are unchanged', (t) => {
const { home, configDir } = makeHookTree(t, 'sh-pins');
withHome(t, home);
const plain = hooksSurface.buildHookCommand(configDir, 'gsd-session-state.sh', {
const plain = hooksSurface.buildHookCommand(configDir, 'msd-session-state.sh', {
runtime: 'claude',
});
// The pre-#3662 .sh shapes, byte for byte: claude-on-win32 omits the
@@ -428,27 +428,27 @@ describe('#3662 runtime-resolving managed hook runners', () => {
// the bare script token; POSIX prefixes resolveBashRunner's answer.
const bareSh = process.platform === 'win32';
const shRunner = hooksSurface.resolveBashRunner({ platform: process.platform }) || 'bash';
const plainScript = JSON.stringify(path.join(configDir, 'hooks', 'gsd-session-state.sh').replace(/\\/g, '/'));
const plainScript = JSON.stringify(path.join(configDir, 'hooks', 'msd-session-state.sh').replace(/\\/g, '/'));
assert.equal(plain, bareSh ? plainScript : `${shRunner} ${plainScript}`);
const portable = hooksSurface.buildHookCommand(configDir, 'gsd-session-state.sh', {
const portable = hooksSurface.buildHookCommand(configDir, 'msd-session-state.sh', {
portableHooks: true,
runtime: 'claude',
});
assert.equal(portable, bareSh ? '"$HOME/.claude/hooks/gsd-session-state.sh"' : `${shRunner} "$HOME/.claude/hooks/gsd-session-state.sh"`);
assert.equal(portable, bareSh ? '"$HOME/.claude/hooks/msd-session-state.sh"' : `${shRunner} "$HOME/.claude/hooks/msd-session-state.sh"`);
});
test('chain embeds shell-hostile baked paths safely', (t) => {
if (skipOnWin32(t, 'POSIX sh execution lane')) return;
const { home, configDir } = makeHookTree(t, 'hostile-path');
withHome(t, home);
const hostileDir = createTempDir('gsd3662-hostile');
const hostileDir = createTempDir('msd3662-hostile');
const weird = path.join(hostileDir, 'sp ace$dollar');
fs.mkdirSync(weird, { recursive: true });
t.after(() => cleanup(hostileDir));
const stubPath = path.join(weird, 'node');
fs.writeFileSync(stubPath, ['#!/bin/sh', 'echo "STUB-NODE:$1"', ''].join('\n'));
fs.chmodSync(stubPath, 0o755);
const emitted = hooksSurface.buildHookCommand(configDir, 'gsd-statusline.js', {
const emitted = hooksSurface.buildHookCommand(configDir, 'msd-statusline.js', {
runtime: 'claude',
execPath: stubPath,
});
@@ -461,21 +461,21 @@ describe('#3662 runtime-resolving managed hook runners', () => {
timeoutMs: RUN_TIMEOUT_MS,
});
assert.equal(result.exitCode, 0, `command: ${emitted}\nstderr: ${result.stderr}`);
assert.match(result.stdout, /^STUB-NODE:\S*gsd-statusline\.js/, 'script path split or expanded by the shell');
assert.match(result.stdout, /^STUB-NODE:\S*msd-statusline\.js/, 'script path split or expanded by the shell');
});
test('chain stays valid sh when no candidate resolves', (t) => {
if (skipOnWin32(t, 'POSIX sh execution lane')) return;
const { home, configDir } = makeHookTree(t, 'sh-valid');
withHome(t, home);
const emitted = hooksSurface.buildHookCommand(configDir, 'gsd-statusline.js', {
const emitted = hooksSurface.buildHookCommand(configDir, 'msd-statusline.js', {
runtime: 'claude',
execPath: FOREIGN_NODE,
});
assert.ok(typeof emitted === 'string');
const foreign = foreignizeNodeTokens(emitted);
const file = writeCommandFile(t, foreign, 'sh-valid');
const dir = createTempDir('gsd3662-shn');
const dir = createTempDir('msd3662-shn');
const checker = path.join(dir, 'syntax-check.sh');
fs.writeFileSync(checker, ['#!/bin/sh', 'exec bash -n "$1"', ''].join('\n'));
fs.chmodSync(checker, 0o755);
@@ -489,7 +489,7 @@ describe('#3662 runtime-resolving managed hook runners', () => {
});
test('win32 projection keeps forward-slash paths in the chain', () => {
const emitted = hooksSurface.buildHookCommand('C:\\Users\\u\\.claude', 'gsd-statusline.js', {
const emitted = hooksSurface.buildHookCommand('C:\\Users\\u\\.claude', 'msd-statusline.js', {
runtime: 'claude',
platform: 'win32',
execPath: 'C:\\Program Files\\nodejs\\node.exe',
@@ -510,7 +510,7 @@ describe('#3662 runtime-resolving managed hook runners', () => {
// on every JS hook; this row fails the moment one gains a basename the
// other lacks.
test('every managed js basename is recognized by both managed sets', () => {
const projection = require('../gsd-core/bin/lib/shell-command-projection.cjs');
const projection = require('../msd-core/bin/lib/shell-command-projection.cjs');
for (const basename of [...MANAGED_JS_HOOKS, ...GUARD_HOOKS]) {
const scriptPath = `/home/u/.claude/hooks/${basename}`;
assert.ok(
@@ -539,8 +539,8 @@ describe('#3662 runtime-resolving managed hook runners', () => {
m[1].replace(/\\"/g, '"').replace(/\\\\/g, '\\'),
);
assert.ok(commands.length > 0, 'no command entries found in kimi block');
const jsCommand = commands.find((c) => c.includes('gsd-prompt-guard.js'));
assert.ok(jsCommand, `no gsd-prompt-guard.js command in block: ${commands.join(' | ')}`);
const jsCommand = commands.find((c) => c.includes('msd-prompt-guard.js'));
assert.ok(jsCommand, `no msd-prompt-guard.js command in block: ${commands.join(' | ')}`);
const foreign = foreignizeNodeTokens(jsCommand);
const file = writeCommandFile(t, foreign, 'kimi');
const result = runHook(file, [], {