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:
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user