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

@@ -10,9 +10,9 @@
* or the AGENTS.md prose counts drift from it.
*
* Classification rules (derived from AGENTS.md section placement):
* - ### gsd-<name> headings BEFORE "## Advanced and Specialized Agents" → "primary"
* - ### gsd-<name> headings INSIDE/AFTER that section → "advanced stub"
* - agents/gsd-*.md files with NO heading in AGENTS.md → "inventory only"
* - ### msd-<name> headings BEFORE "## Advanced and Specialized Agents" → "primary"
* - ### msd-<name> headings INSIDE/AFTER that section → "advanced stub"
* - agents/msd-*.md files with NO heading in AGENTS.md → "inventory only"
*/
const { describe, test } = require('node:test');
@@ -32,10 +32,10 @@ const AGENTS_DIR = path.join(ROOT, 'agents');
/**
* Parse AGENTS.md and return two arrays:
* primaryHeadings — agent names (without "gsd-" prefix) before the advanced section
* primaryHeadings — agent names (without "msd-" prefix) before the advanced section
* advancedHeadings — agent names inside "## Advanced and Specialized Agents"
*
* We work with the full "gsd-<name>" slug so the names are unambiguous.
* We work with the full "msd-<name>" slug so the names are unambiguous.
*
* State machine (three-state):
* 'before' — before "## Advanced and Specialized Agents"
@@ -44,7 +44,7 @@ const AGENTS_DIR = path.join(ROOT, 'agents');
*
* A "## " heading (h2, NOT h3) that appears BEFORE the advanced section does NOT
* trigger 'past'. Only a "## " heading encountered WHILE in 'advanced' does.
* ### gsd-* headings are counted: primary if 'before', advanced if 'advanced',
* ### msd-* headings are counted: primary if 'before', advanced if 'advanced',
* ignored if 'past'.
*/
function parseAgentsMd(raw) {
@@ -52,7 +52,7 @@ function parseAgentsMd(raw) {
const ADVANCED_SECTION = /^##\s+Advanced and Specialized Agents\s*$/;
// Matches an h2 heading (exactly two hashes, not three or more)
const H2 = /^##(?!#)\s+\S/;
const GSD_H3 = /^###\s+(gsd-[\w-]+)\s*$/;
const MSD_H3 = /^###\s+(msd-[\w-]+)\s*$/;
const primaryHeadings = [];
const advancedHeadings = [];
@@ -69,7 +69,7 @@ function parseAgentsMd(raw) {
state = 'past';
continue;
}
const m = GSD_H3.exec(line);
const m = MSD_H3.exec(line);
if (m) {
if (state === 'before') {
primaryHeadings.push(m[1]);
@@ -89,7 +89,7 @@ function parseAgentsMd(raw) {
*
* Scoping: only rows that fall between the "## Agents" heading and the NEXT
* "## " heading are considered. This prevents a future non-agent table that
* happens to contain a "| gsd-..." row from polluting the result.
* happens to contain a "| msd-..." row from polluting the result.
*
* Column resolution: the header row "| Agent | ... | Primary doc |" is parsed
* to find the 0-based index of the "Primary doc" column. Rows are split on "|"
@@ -141,8 +141,8 @@ function parseInventoryMd(raw) {
// Skip separator rows (---|---|...)
if (cells.every((c) => /^[-: ]+$/.test(c))) continue;
// Data rows: first cell must be a gsd-* slug
if (!cells[0].startsWith('gsd-')) continue;
// Data rows: first cell must be a msd-* slug
if (!cells[0].startsWith('msd-')) continue;
if (cells.length <= primaryDocColIndex) continue;
const agentSlug = cells[0];
@@ -173,7 +173,7 @@ const rawInventoryMd = fs.readFileSync(INVENTORY_MD, 'utf8');
const { primaryHeadings, advancedHeadings } = parseAgentsMd(rawAgentsMd);
const inventoryMap = parseInventoryMd(rawInventoryMd);
// Canonical source roster (sorted gsd-* basenames without .md) — shared helper.
// Canonical source roster (sorted msd-* basenames without .md) — shared helper.
const agentFiles = listAgentFiles(AGENTS_DIR);
// ---------------------------------------------------------------------------
@@ -182,22 +182,22 @@ const agentFiles = listAgentFiles(AGENTS_DIR);
assert.ok(
primaryHeadings.length > 0,
'AGENTS.md: no ### gsd-* headings found before Advanced section — raw head:\n' + rawAgentsMd.slice(0, 300),
'AGENTS.md: no ### msd-* headings found before Advanced section — raw head:\n' + rawAgentsMd.slice(0, 300),
);
assert.ok(
advancedHeadings.length > 0,
'AGENTS.md: no ### gsd-* headings found inside Advanced section — raw head:\n' + rawAgentsMd.slice(0, 300),
'AGENTS.md: no ### msd-* headings found inside Advanced section — raw head:\n' + rawAgentsMd.slice(0, 300),
);
assert.ok(
inventoryMap.size > 0,
'INVENTORY.md: no | gsd-* | rows parsed — raw head:\n' + rawInventoryMd.slice(0, 300),
'INVENTORY.md: no | msd-* | rows parsed — raw head:\n' + rawInventoryMd.slice(0, 300),
);
assert.ok(
agentFiles.length > 0,
'agents/: no gsd-*.md files found — check AGENTS_DIR path: ' + AGENTS_DIR,
'agents/: no msd-*.md files found — check AGENTS_DIR path: ' + AGENTS_DIR,
);
// ---------------------------------------------------------------------------
@@ -262,7 +262,7 @@ describe('agent-classification-parity: AGENTS.md section structure is the single
* a) "21 primary agents" → count of primary headings
* b) "Twelve additional" → count of advanced headings
* c) The parenthetical slug list → equals the set of advanced heading names
* (slugs without the "gsd-" prefix, as the prose uses)
* (slugs without the "msd-" prefix, as the prose uses)
*/
test('AGENTS.md prose counts and advanced-agent parenthetical list are accurate', () => {
@@ -276,7 +276,7 @@ describe('agent-classification-parity: AGENTS.md section structure is the single
assert.strictEqual(
prosePrimaryCount,
primaryHeadings.length,
`AGENTS.md prose says "${prosePrimaryCount} primary agents" but there are ${primaryHeadings.length} ### gsd-* headings before the Advanced section`,
`AGENTS.md prose says "${prosePrimaryCount} primary agents" but there are ${primaryHeadings.length} ### msd-* headings before the Advanced section`,
);
// --- (b) prose advanced count (cardinal word or digit) ---
@@ -299,11 +299,11 @@ describe('agent-classification-parity: AGENTS.md section structure is the single
assert.strictEqual(
proseAdvancedCount,
advancedHeadings.length,
`AGENTS.md prose says "${advancedCountRaw} additional shipped agents" but there are ${advancedHeadings.length} ### gsd-* headings in the Advanced section`,
`AGENTS.md prose says "${advancedCountRaw} additional shipped agents" but there are ${advancedHeadings.length} ### msd-* headings in the Advanced section`,
);
// --- (c) parenthetical slug list ---
// The prose lists short slugs without "gsd-" prefix, e.g.:
// The prose lists short slugs without "msd-" prefix, e.g.:
// (pattern-mapper, debug-session-manager, ...)
// eslint-disable-next-line local/no-unbounded-quantifier -- parses docs/AGENTS.md, a maintainer-authored repo doc with bounded prose, not adversarial input
const parenMatch = rawAgentsMd.match(/\(([^)]+)\)\s+have concise stubs/);
@@ -314,8 +314,8 @@ describe('agent-classification-parity: AGENTS.md section structure is the single
const proseSlugs = parenMatch[1].split(',').map((s) => s.trim().toLowerCase());
const proseSlugsSet = new Set(proseSlugs);
// Derive expected slugs from AGENTS.md headings (strip "gsd-" prefix)
const expectedSlugs = new Set(advancedHeadings.map((h) => h.replace(/^gsd-/, '')));
// Derive expected slugs from AGENTS.md headings (strip "msd-" prefix)
const expectedSlugs = new Set(advancedHeadings.map((h) => h.replace(/^msd-/, '')));
const missingFromProse = [...expectedSlugs].filter((s) => !proseSlugsSet.has(s));
const extraInProse = [...proseSlugsSet].filter((s) => !expectedSlugs.has(s));
@@ -333,7 +333,7 @@ describe('agent-classification-parity: AGENTS.md section structure is the single
* Test 3 — Roster completeness.
*
* Sub-checks:
* a) Every agents/gsd-*.md appears exactly once in (primary ∪ advanced ∪ inventoryOnly)
* a) Every agents/msd-*.md appears exactly once in (primary ∪ advanced ∪ inventoryOnly)
* — i.e. no agent is double-classified.
* b) INVENTORY.md has a row for every agent file.
* c) INVENTORY.md has no row for a non-existent agent file.
@@ -354,7 +354,7 @@ describe('agent-classification-parity: AGENTS.md section structure is the single
assert.deepStrictEqual(
missingFromInventory,
[],
'agents/gsd-*.md files missing from INVENTORY.md: ' + JSON.stringify(missingFromInventory),
'agents/msd-*.md files missing from INVENTORY.md: ' + JSON.stringify(missingFromInventory),
);
// (c) INVENTORY.md has no row for a non-existent agent file
@@ -362,7 +362,7 @@ describe('agent-classification-parity: AGENTS.md section structure is the single
assert.deepStrictEqual(
phantomRows,
[],
'INVENTORY.md rows reference agents that have no agents/gsd-*.md file: ' + JSON.stringify(phantomRows),
'INVENTORY.md rows reference agents that have no agents/msd-*.md file: ' + JSON.stringify(phantomRows),
);
// (d) counts add up
@@ -383,7 +383,7 @@ describe('agent-classification-parity: AGENTS.md section structure is the single
* that found it was written, 26 of 34 rows had drifted: 22 omitted `Skill`
* and 7 omitted `Edit`; 8 omitted MCP grants entirely (7 of them writing
* "mcp (context7)" for what was up to eight distinct servers, and
* gsd-executor naming none at all); and one still read `Task`, a tool that
* msd-executor naming none at all); and one still read `Task`, a tool that
* no longer exists. Nothing asserted the two agreed, so the drift was free.
*
* The row must equal the frontmatter value verbatim rather than as a set:
@@ -396,9 +396,9 @@ describe('agent-classification-parity: AGENTS.md section structure is the single
* while the remaining rows kept the test green.
*/
test('AGENTS.md **Tools** rows match each agent\'s tools: frontmatter (#2526)', () => {
const { parseFrontmatter } = require('../gsd-core/bin/lib/frontmatter.cjs');
const { parseFrontmatter } = require('../msd-core/bin/lib/frontmatter.cjs');
const TOOLS_ROW = /^\|\s*\*\*Tools\*\*\s*\|\s*(.*?)\s*\|\s*$/;
const H3 = /^###\s+(gsd-[\w-]+)\s*$/;
const H3 = /^###\s+(msd-[\w-]+)\s*$/;
// /\r?\n/, not '\n': Windows autocrlf yields CRLF, and a trailing \r would
// survive into the row's last cell and fail every comparison (local/no-crlf-fragile-split).