mirror of
https://github.com/glittercowboy/get-shit-done
synced 2026-04-25 17:25:23 +02:00
Commands are now installed as commands/gsd/<name>.md and invoked as /gsd:<name> in Claude Code. The old hyphen form /gsd-<name> was still hardcoded in hundreds of places across workflows, references, templates, lib modules, and command files — causing "Unknown command" errors whenever GSD suggested a command to the user. Replace all /gsd-<cmd> occurrences where <cmd> is a known command name (derived at runtime from commands/gsd/*.md) using a targeted Node.js script. Agent names, tool names (gsd-sdk, gsd-tools), directory names, and path fragments are not touched. Adds regression test tests/bug-2543-gsd-slash-namespace.test.cjs that enforces zero legacy occurrences going forward. Removes inverted tests/stale-colon-refs.test.cjs (bug #1748) which enforced the now-obsolete hyphen form; the new bug-2543 test supersedes it. Updates 5 assertion tests that hardcoded the old hyphen form to accept the new colon form. Closes #2543 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
88
tests/bug-2543-gsd-slash-namespace.test.cjs
Normal file
88
tests/bug-2543-gsd-slash-namespace.test.cjs
Normal file
@@ -0,0 +1,88 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Bug #2543: GSD emits legacy '/gsd-<cmd>' syntax in 102 places.
|
||||
*
|
||||
* Installed commands are under commands/gsd/<name>.md and invoked as
|
||||
* /gsd:<name>. All internal references must use the colon form.
|
||||
*/
|
||||
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { execFileSync } = require('node:child_process');
|
||||
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
const COMMANDS_DIR = path.join(ROOT, 'commands', 'gsd');
|
||||
|
||||
const SEARCH_DIRS = [
|
||||
path.join(ROOT, 'get-shit-done', 'bin', 'lib'),
|
||||
path.join(ROOT, 'get-shit-done', 'workflows'),
|
||||
path.join(ROOT, 'get-shit-done', 'references'),
|
||||
path.join(ROOT, 'get-shit-done', 'templates'),
|
||||
path.join(ROOT, 'get-shit-done', 'contexts'),
|
||||
COMMANDS_DIR,
|
||||
];
|
||||
|
||||
const EXTENSIONS = new Set(['.md', '.cjs', '.js']);
|
||||
|
||||
function collectFiles(dir, results = []) {
|
||||
let entries;
|
||||
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return results; }
|
||||
for (const e of entries) {
|
||||
const full = path.join(dir, e.name);
|
||||
if (e.isDirectory()) collectFiles(full, results);
|
||||
else if (EXTENSIONS.has(path.extname(e.name))) results.push(full);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
const cmdNames = fs.readdirSync(COMMANDS_DIR)
|
||||
.filter(f => f.endsWith('.md'))
|
||||
.map(f => f.replace(/\.md$/, ''))
|
||||
.sort((a, b) => b.length - a.length);
|
||||
|
||||
const legacyPattern = new RegExp(`/gsd-(${cmdNames.join('|')})(?=[^a-zA-Z0-9_-]|$)`);
|
||||
|
||||
const allFiles = SEARCH_DIRS.flatMap(d => collectFiles(d));
|
||||
|
||||
describe('slash-command namespace fix (#2543)', () => {
|
||||
test('commands/gsd/ directory contains known command files', () => {
|
||||
assert.ok(cmdNames.length > 0, 'commands/gsd/ must contain .md files');
|
||||
assert.ok(cmdNames.includes('plan-phase'), 'plan-phase must be a known command');
|
||||
assert.ok(cmdNames.includes('execute-phase'), 'execute-phase must be a known command');
|
||||
});
|
||||
|
||||
test('no /gsd-<cmd> legacy syntax remains in source files', () => {
|
||||
const violations = [];
|
||||
for (const file of allFiles) {
|
||||
const src = fs.readFileSync(file, 'utf-8');
|
||||
const lines = src.split('\n');
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (legacyPattern.test(lines[i])) {
|
||||
violations.push(`${path.relative(ROOT, file)}:${i + 1}: ${lines[i].trim().slice(0, 80)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.strictEqual(
|
||||
violations.length,
|
||||
0,
|
||||
`Found ${violations.length} legacy /gsd-<cmd> reference(s):\n${violations.slice(0, 10).join('\n')}`,
|
||||
);
|
||||
});
|
||||
|
||||
test('gsd-sdk and gsd-tools identifiers are not rewritten', () => {
|
||||
for (const file of allFiles) {
|
||||
const src = fs.readFileSync(file, 'utf-8');
|
||||
assert.ok(
|
||||
!src.includes('/gsd:sdk'),
|
||||
`${path.relative(ROOT, file)} must not contain /gsd:sdk (gsd-sdk was incorrectly renamed)`,
|
||||
);
|
||||
assert.ok(
|
||||
!src.includes('/gsd:tools'),
|
||||
`${path.relative(ROOT, file)} must not contain /gsd:tools (gsd-tools was incorrectly renamed)`,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user