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