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

@@ -1,11 +1,11 @@
/**
* gsd2-import — Reverse migration from GSD-2 (.gsd/) to GSD v1 (.planning/)
* gsd2-import — Reverse migration from GSD-2 (.gsd/) to MSD v1 (.planning/)
*
* Reads a GSD-2 project directory structure and produces a complete
* .planning/ artifact tree in GSD v1 format.
* .planning/ artifact tree in MSD v1 format.
*
* GSD-2 hierarchy: Milestone → Slice → Task
* GSD v1 hierarchy: Milestone (in ROADMAP.md) → Phase → Plan
* MSD v1 hierarchy: Milestone (in ROADMAP.md) → Phase → Plan
*
* Mapping rules:
* - Slices are numbered sequentially across all milestones (01, 02, …)
@@ -22,7 +22,7 @@
import fs from 'node:fs';
import path from 'node:path';
import { platformWriteSync } from './shell-command-projection.cjs';
import { formatGsdSlash, resolveRuntime } from './runtime-slash.cjs';
import { formatMsdSlash, resolveRuntime } from './runtime-slash.cjs';
import { realClock } from './clock.cjs';
import { clampPercent, renderProgressBar } from './phase-lifecycle.cjs';
// eslint-disable-next-line @typescript-eslint/no-require-imports -- core-utils.cjs is an export= CommonJS module
@@ -71,7 +71,7 @@ interface Milestone {
slices: Slice[];
}
interface Gsd2Data {
interface Msd2Data {
projectContent: string | null;
requirements: string | null;
milestones: Milestone[];
@@ -108,7 +108,7 @@ function slugify(title: string): string {
* Find the .gsd/ directory starting from a project root.
* Returns the absolute path or null if not found.
*/
function findGsd2Root(startPath: string): string | null {
function findMsd2Root(startPath: string): string | null {
if (path.basename(startPath) === '.gsd' && fs.existsSync(startPath)) {
return startPath;
}
@@ -183,7 +183,7 @@ function parseTaskMustHaves(content: string): string[] {
// `tasks/` directory convention (`T##-PLAN.md`) during a one-time import —
// it is not this project's `.planning/phases/<phase>/` layout at all, has no
// nested-plans/superseded-status concept, and scanPhasePlans's grammar
// (which is scoped to GSD's OWN phase directories) does not apply here.
// (which is scoped to MSD's OWN phase directories) does not apply here.
function readTasksDir(tasksDir: string): TaskInfo[] {
if (!fs.existsSync(tasksDir)) return [];
@@ -209,14 +209,14 @@ function readTasksDir(tasksDir: string): TaskInfo[] {
/**
* Parse a complete GSD-2 .gsd/ directory into a structured representation.
*/
function parseGsd2(gsdDir: string): Gsd2Data {
const data: Gsd2Data = {
projectContent: readOptional(path.join(gsdDir, 'PROJECT.md')),
requirements: readOptional(path.join(gsdDir, 'REQUIREMENTS.md')),
function parseMsd2(msdDir: string): Msd2Data {
const data: Msd2Data = {
projectContent: readOptional(path.join(msdDir, 'PROJECT.md')),
requirements: readOptional(path.join(msdDir, 'REQUIREMENTS.md')),
milestones: [],
};
const milestonesBase = path.join(gsdDir, 'milestones');
const milestonesBase = path.join(msdDir, 'milestones');
if (!fs.existsSync(milestonesBase)) return data;
const milestoneIds = fs.readdirSync(milestonesBase)
@@ -259,7 +259,7 @@ function parseGsd2(gsdDir: string): Gsd2Data {
// ─── Artifact Builders ──────────────────────────────────────────────────────
/**
* Build a GSD v1 PLAN.md from a GSD-2 task.
* Build a MSD v1 PLAN.md from a GSD-2 task.
*/
function buildPlanMd(task: TaskInfo, phasePrefix: string, planPrefix: string, phaseSlug: string, milestoneTitle: string): string {
const lines = [
@@ -295,7 +295,7 @@ function buildPlanMd(task: TaskInfo, phasePrefix: string, planPrefix: string, ph
}
/**
* Build a GSD v1 SUMMARY.md from a GSD-2 task summary.
* Build a MSD v1 SUMMARY.md from a GSD-2 task summary.
* Strips the GSD-2 frontmatter and preserves the body.
*/
function buildSummaryMd(task: TaskInfo, phasePrefix: string, planPrefix: string): string {
@@ -311,7 +311,7 @@ function buildSummaryMd(task: TaskInfo, phasePrefix: string, planPrefix: string)
// `stripFrontmatter` is the same module's canonical body primitive.
//
// `once` is load-bearing. A GSD-2 summary is an arbitrary user-authored
// document, not a GSD artefact with a known frontmatter-doubling failure
// document, not a MSD artefact with a known frontmatter-doubling failure
// mode, so a body opening with a thematic-break-delimited section
// (`---` / heading / `---`) is far likelier than a corrupt second header —
// and the default greedy loop would delete it without a trace.
@@ -329,7 +329,7 @@ function buildSummaryMd(task: TaskInfo, phasePrefix: string, planPrefix: string)
}
/**
* Build a GSD v1 XX-CONTEXT.md from a GSD-2 slice.
* Build a MSD v1 XX-CONTEXT.md from a GSD-2 slice.
*/
function buildContextMd(slice: Slice, phasePrefix: string): string {
const lines = [
@@ -347,7 +347,7 @@ function buildContextMd(slice: Slice, phasePrefix: string): string {
}
/**
* Build the GSD v1 ROADMAP.md with milestone-sectioned format.
* Build the MSD v1 ROADMAP.md with milestone-sectioned format.
*/
function buildRoadmapMd(milestones: Milestone[], phaseMap: PhaseMapEntry[]): string {
const lines = ['# Roadmap', ''];
@@ -368,7 +368,7 @@ function buildRoadmapMd(milestones: Milestone[], phaseMap: PhaseMapEntry[]): str
}
/**
* Build the GSD v1 STATE.md reflecting the current position in the project.
* Build the MSD v1 STATE.md reflecting the current position in the project.
*/
function buildStateMd(phaseMap: PhaseMapEntry[]): string {
const currentEntry = phaseMap.find(p => !p.slice.done);
@@ -427,7 +427,7 @@ function buildStateMd(phaseMap: PhaseMapEntry[]): string {
* Convert parsed GSD-2 data into a map of relative path → file content.
* All paths are relative to the .planning/ root.
*/
function buildPlanningArtifacts(gsd2Data: Gsd2Data): Map<string, string> {
function buildPlanningArtifacts(gsd2Data: Msd2Data): Map<string, string> {
const artifacts = new Map<string, string>();
// Passthrough files
@@ -489,7 +489,7 @@ function buildPlanningArtifacts(gsd2Data: Gsd2Data): Map<string, string> {
/**
* Format a dry-run preview string for display before writing.
*/
function buildPreview(gsd2Data: Gsd2Data, artifacts: Map<string, string>, projectDir: string): string {
function buildPreview(gsd2Data: Msd2Data, artifacts: Map<string, string>, projectDir: string): string {
const lines = ['Preview — files that will be created in .planning/:'];
for (const rel of artifacts.keys()) {
@@ -508,7 +508,7 @@ function buildPreview(gsd2Data: Gsd2Data, artifacts: Map<string, string>, projec
lines.push('');
lines.push('Cannot migrate automatically:');
lines.push(' - GSD-2 cost/token ledger (no v1 equivalent)');
lines.push(` - GSD-2 database state (rebuilt from files on first ${formatGsdSlash('health', resolveRuntime(projectDir)) as string})`);
lines.push(` - GSD-2 database state (rebuilt from files on first ${formatMsdSlash('health', resolveRuntime(projectDir)) as string})`);
lines.push(' - VS Code extension state');
return lines.join('\n');
@@ -529,10 +529,10 @@ function writePlanningDir(artifacts: Map<string, string>, planningRoot: string):
// ─── Command Handler ──────────────────────────────────────────────────────────
/**
* Entry point called from gsd-tools.cjs.
* Entry point called from msd-tools.cjs.
* Supports: --force, --dry-run, --path <dir>
*/
function cmdFromGsd2(args: string[], cwd: string, raw: boolean): void {
function cmdFromMsd2(args: string[], cwd: string, raw: boolean): void {
const force = args.includes('--force');
const dryRun = args.includes('--dry-run');
@@ -541,13 +541,13 @@ function cmdFromGsd2(args: string[], cwd: string, raw: boolean): void {
? path.resolve(cwd, args[pathIdx + 1])
: cwd;
const gsdDir = findGsd2Root(projectDir);
if (!gsdDir) {
const msdDir = findMsd2Root(projectDir);
if (!msdDir) {
output({ success: false, error: `No .gsd/ directory found in ${projectDir}` }, raw, undefined);
return;
}
const planningRoot = path.join(path.dirname(gsdDir), '.planning');
const planningRoot = path.join(path.dirname(msdDir), '.planning');
if (fs.existsSync(planningRoot) && !force) {
output({
success: false,
@@ -556,7 +556,7 @@ function cmdFromGsd2(args: string[], cwd: string, raw: boolean): void {
return;
}
const gsd2Data = parseGsd2(gsdDir);
const gsd2Data = parseMsd2(msdDir);
const artifacts = buildPlanningArtifacts(gsd2Data);
// Use projectDir (resolved from --path) — not the process cwd — so the
// preview command targets the project actually being imported (#3584).
@@ -579,12 +579,12 @@ function cmdFromGsd2(args: string[], cwd: string, raw: boolean): void {
}
export = {
findGsd2Root,
parseGsd2,
findMsd2Root,
parseMsd2,
buildPlanningArtifacts,
buildPreview,
writePlanningDir,
cmdFromGsd2,
cmdFromMsd2,
// Exported for unit tests
parseSlicesFromRoadmap,
parseMilestoneTitle,