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:
@@ -2,7 +2,7 @@
|
||||
* Tests for Cursor hooks.json lifecycle hook registration (issue #777).
|
||||
*
|
||||
* Cursor v2.4+ supports a hooks.json system with events including sessionStart
|
||||
* and postToolUse. GSD registers two managed command hooks so Cursor users get
|
||||
* and postToolUse. MSD registers two managed command hooks so Cursor users get
|
||||
* baseline parity with Claude Code and Gemini.
|
||||
*
|
||||
* Test plan:
|
||||
@@ -14,7 +14,7 @@
|
||||
* T6 reconcileCursorHooksJson — handles nested { version, hooks: {...} } shape
|
||||
* T7 reconcileCursorHooksJson — handles flat (no version) shape
|
||||
* T8 reconcileCursorHooksJson — corrupted JSON throws descriptive error
|
||||
* T9 isManagedCursorHookEntry — returns true for GSD-marked entries
|
||||
* T9 isManagedCursorHookEntry — returns true for MSD-marked entries
|
||||
* T10 isManagedCursorHookEntry — returns false for user entries
|
||||
* T11 buildCursorHookEntry — emits correct shape with marker
|
||||
* T12 removeCursorHooksJson — removes hooks.json when it becomes empty
|
||||
@@ -24,8 +24,8 @@
|
||||
* T16 Hook scripts exist in hooks/
|
||||
* T17 Hook script content — sessionStart script emits JSON with additional_context
|
||||
* T18 Hook script content — postToolUse script emits JSON {} for non-write tools
|
||||
* T19 GSD_CURSOR_HOOK_MARKER constant is exported
|
||||
* T20 GSD_CURSOR_SESSION_HOOK_SCRIPT / GSD_CURSOR_POST_TOOL_HOOK_SCRIPT constants
|
||||
* T19 MSD_CURSOR_HOOK_MARKER constant is exported
|
||||
* T20 MSD_CURSOR_SESSION_HOOK_SCRIPT / MSD_CURSOR_POST_TOOL_HOOK_SCRIPT constants
|
||||
*/
|
||||
|
||||
// allow-test-rule: source-text-is-the-product
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
process.env.GSD_TEST_MODE = '1';
|
||||
process.env.MSD_TEST_MODE = '1';
|
||||
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
@@ -44,9 +44,9 @@ const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
const { QUICK_SPAWN_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
|
||||
const {
|
||||
GSD_CURSOR_HOOK_MARKER,
|
||||
GSD_CURSOR_SESSION_HOOK_SCRIPT,
|
||||
GSD_CURSOR_POST_TOOL_HOOK_SCRIPT,
|
||||
MSD_CURSOR_HOOK_MARKER,
|
||||
MSD_CURSOR_SESSION_HOOK_SCRIPT,
|
||||
MSD_CURSOR_POST_TOOL_HOOK_SCRIPT,
|
||||
} = require('../bin/install.js');
|
||||
|
||||
const {
|
||||
@@ -54,12 +54,12 @@ const {
|
||||
isManagedCursorHookEntry,
|
||||
buildCursorHookEntry,
|
||||
removeCursorHooksJson,
|
||||
} = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
} = require('../msd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
|
||||
const {
|
||||
resolveRuntimeConfigIntent,
|
||||
INSTALL_SURFACES,
|
||||
} = require('../gsd-core/bin/lib/runtime-config-adapter-registry.cjs');
|
||||
} = require('../msd-core/bin/lib/runtime-config-adapter-registry.cjs');
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
@@ -76,7 +76,7 @@ function readHooksJson(dir) {
|
||||
}
|
||||
|
||||
function managedEntry(command) {
|
||||
return { type: 'command', command, [GSD_CURSOR_HOOK_MARKER]: true };
|
||||
return { type: 'command', command, [MSD_CURSOR_HOOK_MARKER]: true };
|
||||
}
|
||||
|
||||
function userEntry(command) {
|
||||
@@ -93,8 +93,8 @@ describe('reconcileCursorHooksJson', () => {
|
||||
|
||||
const hooksJsonPath = path.join(dir, 'hooks.json');
|
||||
const result = reconcileCursorHooksJson(hooksJsonPath, {
|
||||
sessionStart: managedEntry('/node /path/gsd-cursor-session-start.js'),
|
||||
postToolUse: managedEntry('/node /path/gsd-cursor-post-tool.js'),
|
||||
sessionStart: managedEntry('/node /path/msd-cursor-session-start.js'),
|
||||
postToolUse: managedEntry('/node /path/msd-cursor-post-tool.js'),
|
||||
});
|
||||
|
||||
assert.equal(result.wrote, true);
|
||||
@@ -111,8 +111,8 @@ describe('reconcileCursorHooksJson', () => {
|
||||
assert.ok(Array.isArray(hookTable.postToolUse), 'postToolUse must be an array');
|
||||
assert.equal(hookTable.sessionStart.length, 1);
|
||||
assert.equal(hookTable.postToolUse.length, 1);
|
||||
assert.equal(hookTable.sessionStart[0][GSD_CURSOR_HOOK_MARKER], true);
|
||||
assert.equal(hookTable.postToolUse[0][GSD_CURSOR_HOOK_MARKER], true);
|
||||
assert.equal(hookTable.sessionStart[0][MSD_CURSOR_HOOK_MARKER], true);
|
||||
assert.equal(hookTable.postToolUse[0][MSD_CURSOR_HOOK_MARKER], true);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -124,8 +124,8 @@ describe('reconcileCursorHooksJson', () => {
|
||||
|
||||
const hooksJsonPath = path.join(dir, 'hooks.json');
|
||||
const entries = {
|
||||
sessionStart: managedEntry('/node /gsd-cursor-session-start.js'),
|
||||
postToolUse: managedEntry('/node /gsd-cursor-post-tool.js'),
|
||||
sessionStart: managedEntry('/node /msd-cursor-session-start.js'),
|
||||
postToolUse: managedEntry('/node /msd-cursor-post-tool.js'),
|
||||
};
|
||||
|
||||
reconcileCursorHooksJson(hooksJsonPath, entries);
|
||||
@@ -152,19 +152,19 @@ describe('reconcileCursorHooksJson', () => {
|
||||
}));
|
||||
|
||||
reconcileCursorHooksJson(hooksJsonPath, {
|
||||
sessionStart: managedEntry('/node /gsd-cursor-session-start.js'),
|
||||
postToolUse: managedEntry('/node /gsd-cursor-post-tool.js'),
|
||||
sessionStart: managedEntry('/node /msd-cursor-session-start.js'),
|
||||
postToolUse: managedEntry('/node /msd-cursor-post-tool.js'),
|
||||
});
|
||||
|
||||
const parsed = readHooksJson(dir);
|
||||
const hookTable = parsed.hooks && typeof parsed.hooks === 'object' ? parsed.hooks : parsed;
|
||||
assert.ok(Array.isArray(hookTable.sessionStart));
|
||||
// Both user and GSD entries must survive.
|
||||
assert.equal(hookTable.sessionStart.length, 2, 'user + GSD entry must coexist');
|
||||
// Both user and MSD entries must survive.
|
||||
assert.equal(hookTable.sessionStart.length, 2, 'user + MSD entry must coexist');
|
||||
const userStays = hookTable.sessionStart.some((e) => e.command === '/my-team/hook.sh');
|
||||
const gsdAdded = hookTable.sessionStart.some((e) => e[GSD_CURSOR_HOOK_MARKER]);
|
||||
const msdAdded = hookTable.sessionStart.some((e) => e[MSD_CURSOR_HOOK_MARKER]);
|
||||
assert.ok(userStays, 'user-owned entry must be preserved');
|
||||
assert.ok(gsdAdded, 'GSD managed entry must be present');
|
||||
assert.ok(msdAdded, 'MSD managed entry must be present');
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -182,8 +182,8 @@ describe('reconcileCursorHooksJson', () => {
|
||||
}));
|
||||
|
||||
reconcileCursorHooksJson(hooksJsonPath, {
|
||||
sessionStart: managedEntry('/node /gsd-cursor-session-start.js'),
|
||||
postToolUse: managedEntry('/node /gsd-cursor-post-tool.js'),
|
||||
sessionStart: managedEntry('/node /msd-cursor-session-start.js'),
|
||||
postToolUse: managedEntry('/node /msd-cursor-post-tool.js'),
|
||||
});
|
||||
|
||||
const parsed = readHooksJson(dir);
|
||||
@@ -196,7 +196,7 @@ describe('reconcileCursorHooksJson', () => {
|
||||
// ---------------------------------------------------------------------------
|
||||
// T5: Remove-only (managedEntries=null) strips managed, keeps user entries
|
||||
// ---------------------------------------------------------------------------
|
||||
test('T5: remove-only (null managedEntries) strips GSD entries, preserves user', (t) => {
|
||||
test('T5: remove-only (null managedEntries) strips MSD entries, preserves user', (t) => {
|
||||
const dir = createTempDir();
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
@@ -206,10 +206,10 @@ describe('reconcileCursorHooksJson', () => {
|
||||
hooks: {
|
||||
sessionStart: [
|
||||
userEntry('/user/session.sh'),
|
||||
managedEntry('/node /gsd-cursor-session-start.js'),
|
||||
managedEntry('/node /msd-cursor-session-start.js'),
|
||||
],
|
||||
postToolUse: [
|
||||
managedEntry('/node /gsd-cursor-post-tool.js'),
|
||||
managedEntry('/node /msd-cursor-post-tool.js'),
|
||||
],
|
||||
},
|
||||
}));
|
||||
@@ -222,7 +222,7 @@ describe('reconcileCursorHooksJson', () => {
|
||||
assert.ok(Array.isArray(hookTable.sessionStart), 'sessionStart must remain (user entry)');
|
||||
assert.equal(hookTable.sessionStart.length, 1, 'only user entry remains');
|
||||
assert.equal(hookTable.sessionStart[0].command, '/user/session.sh');
|
||||
assert.equal(hookTable.postToolUse, undefined, 'postToolUse key must be removed (was GSD-only)');
|
||||
assert.equal(hookTable.postToolUse, undefined, 'postToolUse key must be removed (was MSD-only)');
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -236,7 +236,7 @@ describe('reconcileCursorHooksJson', () => {
|
||||
fs.writeFileSync(hooksJsonPath, makeHooksJson({ version: 1, hooks: {} }));
|
||||
|
||||
reconcileCursorHooksJson(hooksJsonPath, {
|
||||
sessionStart: managedEntry('/node /gsd-cursor-session-start.js'),
|
||||
sessionStart: managedEntry('/node /msd-cursor-session-start.js'),
|
||||
});
|
||||
|
||||
const parsed = readHooksJson(dir);
|
||||
@@ -256,7 +256,7 @@ describe('reconcileCursorHooksJson', () => {
|
||||
fs.writeFileSync(hooksJsonPath, makeHooksJson({ sessionStart: [] }));
|
||||
|
||||
reconcileCursorHooksJson(hooksJsonPath, {
|
||||
sessionStart: managedEntry('/node /gsd-cursor-session-start.js'),
|
||||
sessionStart: managedEntry('/node /msd-cursor-session-start.js'),
|
||||
});
|
||||
|
||||
const parsed = readHooksJson(dir);
|
||||
@@ -290,8 +290,8 @@ describe('reconcileCursorHooksJson', () => {
|
||||
// T9-T11: Entry helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('isManagedCursorHookEntry / buildCursorHookEntry', () => {
|
||||
test('T9: isManagedCursorHookEntry returns true for GSD-marked entry', () => {
|
||||
const entry = { type: 'command', command: '/x', [GSD_CURSOR_HOOK_MARKER]: true };
|
||||
test('T9: isManagedCursorHookEntry returns true for MSD-marked entry', () => {
|
||||
const entry = { type: 'command', command: '/x', [MSD_CURSOR_HOOK_MARKER]: true };
|
||||
assert.equal(isManagedCursorHookEntry(entry), true);
|
||||
});
|
||||
|
||||
@@ -305,7 +305,7 @@ describe('isManagedCursorHookEntry / buildCursorHookEntry', () => {
|
||||
test('T11: buildCursorHookEntry emits correct shape', () => {
|
||||
const entry = buildCursorHookEntry('/usr/local/bin/node /path/to/hook.js');
|
||||
assert.equal(entry.type, 'command');
|
||||
assert.equal(entry[GSD_CURSOR_HOOK_MARKER], true);
|
||||
assert.equal(entry[MSD_CURSOR_HOOK_MARKER], true);
|
||||
assert.ok(typeof entry.command === 'string');
|
||||
// Forward slashes only.
|
||||
assert.ok(!entry.command.includes('\\'), 'command must use forward slashes');
|
||||
@@ -316,23 +316,23 @@ describe('isManagedCursorHookEntry / buildCursorHookEntry', () => {
|
||||
// T12-T13: removeCursorHooksJson
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('removeCursorHooksJson', () => {
|
||||
test('T12: removes hooks.json when it becomes empty after GSD removal', (t) => {
|
||||
test('T12: removes hooks.json when it becomes empty after MSD removal', (t) => {
|
||||
const dir = createTempDir();
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
const hooksJsonPath = path.join(dir, 'hooks.json');
|
||||
// GSD-only file.
|
||||
// MSD-only file.
|
||||
fs.writeFileSync(hooksJsonPath, makeHooksJson({
|
||||
version: 1,
|
||||
hooks: {
|
||||
sessionStart: [managedEntry('/node /gsd-cursor-session-start.js')],
|
||||
postToolUse: [managedEntry('/node /gsd-cursor-post-tool.js')],
|
||||
sessionStart: [managedEntry('/node /msd-cursor-session-start.js')],
|
||||
postToolUse: [managedEntry('/node /msd-cursor-post-tool.js')],
|
||||
},
|
||||
}));
|
||||
|
||||
const result = removeCursorHooksJson(dir);
|
||||
assert.equal(result.changed, true);
|
||||
// File should be removed (was GSD-only).
|
||||
// File should be removed (was MSD-only).
|
||||
assert.equal(fs.existsSync(hooksJsonPath), false, 'empty hooks.json must be removed');
|
||||
});
|
||||
|
||||
@@ -345,7 +345,7 @@ describe('removeCursorHooksJson', () => {
|
||||
version: 1,
|
||||
hooks: {
|
||||
sessionStart: [
|
||||
managedEntry('/node /gsd-cursor-session-start.js'),
|
||||
managedEntry('/node /msd-cursor-session-start.js'),
|
||||
userEntry('/user/session.sh'),
|
||||
],
|
||||
},
|
||||
@@ -386,18 +386,18 @@ test('T15: INSTALL_SURFACES includes cursor-hooks-json', () => {
|
||||
// ---------------------------------------------------------------------------
|
||||
test('T16: Cursor hook script files exist in hooks/', () => {
|
||||
const hooksDir = path.join(__dirname, '..', 'hooks');
|
||||
const sessionStart = path.join(hooksDir, GSD_CURSOR_SESSION_HOOK_SCRIPT);
|
||||
const postTool = path.join(hooksDir, GSD_CURSOR_POST_TOOL_HOOK_SCRIPT);
|
||||
assert.ok(fs.existsSync(sessionStart), `${GSD_CURSOR_SESSION_HOOK_SCRIPT} must exist in hooks/`);
|
||||
assert.ok(fs.existsSync(postTool), `${GSD_CURSOR_POST_TOOL_HOOK_SCRIPT} must exist in hooks/`);
|
||||
const sessionStart = path.join(hooksDir, MSD_CURSOR_SESSION_HOOK_SCRIPT);
|
||||
const postTool = path.join(hooksDir, MSD_CURSOR_POST_TOOL_HOOK_SCRIPT);
|
||||
assert.ok(fs.existsSync(sessionStart), `${MSD_CURSOR_SESSION_HOOK_SCRIPT} must exist in hooks/`);
|
||||
assert.ok(fs.existsSync(postTool), `${MSD_CURSOR_POST_TOOL_HOOK_SCRIPT} must exist in hooks/`);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// T17: sessionStart script emits JSON with additional_context on stdin close
|
||||
// ---------------------------------------------------------------------------
|
||||
test('T17: gsd-cursor-session-start.js emits JSON with additional_context', (t, done) => {
|
||||
test('T17: msd-cursor-session-start.js emits JSON with additional_context', (t, done) => {
|
||||
const hooksDir = path.join(__dirname, '..', 'hooks');
|
||||
const scriptPath = path.join(hooksDir, GSD_CURSOR_SESSION_HOOK_SCRIPT);
|
||||
const scriptPath = path.join(hooksDir, MSD_CURSOR_SESSION_HOOK_SCRIPT);
|
||||
const { execFile } = require('child_process');
|
||||
|
||||
const input = JSON.stringify({ session_id: 'test-123', composer_mode: 'agent' });
|
||||
@@ -420,9 +420,9 @@ test('T17: gsd-cursor-session-start.js emits JSON with additional_context', (t,
|
||||
// ---------------------------------------------------------------------------
|
||||
// T18: postToolUse script emits {} for non-write tools
|
||||
// ---------------------------------------------------------------------------
|
||||
test('T18: gsd-cursor-post-tool.js emits {} for non-write tool names', (t, done) => {
|
||||
test('T18: msd-cursor-post-tool.js emits {} for non-write tool names', (t, done) => {
|
||||
const hooksDir = path.join(__dirname, '..', 'hooks');
|
||||
const scriptPath = path.join(hooksDir, GSD_CURSOR_POST_TOOL_HOOK_SCRIPT);
|
||||
const scriptPath = path.join(hooksDir, MSD_CURSOR_POST_TOOL_HOOK_SCRIPT);
|
||||
const { execFile } = require('child_process');
|
||||
|
||||
const input = JSON.stringify({
|
||||
@@ -450,17 +450,17 @@ test('T18: gsd-cursor-post-tool.js emits {} for non-write tool names', (t, done)
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// T19: GSD_CURSOR_HOOK_MARKER is exported and is a non-empty string
|
||||
// T19: MSD_CURSOR_HOOK_MARKER is exported and is a non-empty string
|
||||
// ---------------------------------------------------------------------------
|
||||
test('T19: GSD_CURSOR_HOOK_MARKER is exported and is a non-empty string', () => {
|
||||
assert.equal(typeof GSD_CURSOR_HOOK_MARKER, 'string');
|
||||
assert.ok(GSD_CURSOR_HOOK_MARKER.length > 0);
|
||||
test('T19: MSD_CURSOR_HOOK_MARKER is exported and is a non-empty string', () => {
|
||||
assert.equal(typeof MSD_CURSOR_HOOK_MARKER, 'string');
|
||||
assert.ok(MSD_CURSOR_HOOK_MARKER.length > 0);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// T20: Script name constants are exported and correct
|
||||
// ---------------------------------------------------------------------------
|
||||
test('T20: GSD_CURSOR_SESSION_HOOK_SCRIPT and GSD_CURSOR_POST_TOOL_HOOK_SCRIPT are exported', () => {
|
||||
assert.equal(GSD_CURSOR_SESSION_HOOK_SCRIPT, 'gsd-cursor-session-start.js');
|
||||
assert.equal(GSD_CURSOR_POST_TOOL_HOOK_SCRIPT, 'gsd-cursor-post-tool.js');
|
||||
test('T20: MSD_CURSOR_SESSION_HOOK_SCRIPT and MSD_CURSOR_POST_TOOL_HOOK_SCRIPT are exported', () => {
|
||||
assert.equal(MSD_CURSOR_SESSION_HOOK_SCRIPT, 'msd-cursor-session-start.js');
|
||||
assert.equal(MSD_CURSOR_POST_TOOL_HOOK_SCRIPT, 'msd-cursor-post-tool.js');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user