@wemuda/launchrail 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/assets/ralph.workflow.js +476 -0
  3. package/dist/commands/add.d.ts +13 -0
  4. package/dist/commands/add.js +189 -0
  5. package/dist/commands/add.js.map +1 -0
  6. package/dist/commands/diff.d.ts +13 -0
  7. package/dist/commands/diff.js +44 -0
  8. package/dist/commands/diff.js.map +1 -0
  9. package/dist/commands/doctor.d.ts +12 -0
  10. package/dist/commands/doctor.js +171 -0
  11. package/dist/commands/doctor.js.map +1 -0
  12. package/dist/commands/eject.d.ts +17 -0
  13. package/dist/commands/eject.js +80 -0
  14. package/dist/commands/eject.js.map +1 -0
  15. package/dist/commands/init.d.ts +13 -0
  16. package/dist/commands/init.js +173 -0
  17. package/dist/commands/init.js.map +1 -0
  18. package/dist/commands/smoke.d.ts +21 -0
  19. package/dist/commands/smoke.js +171 -0
  20. package/dist/commands/smoke.js.map +1 -0
  21. package/dist/commands/status.d.ts +15 -0
  22. package/dist/commands/status.js +91 -0
  23. package/dist/commands/status.js.map +1 -0
  24. package/dist/commands/sync.d.ts +19 -0
  25. package/dist/commands/sync.js +83 -0
  26. package/dist/commands/sync.js.map +1 -0
  27. package/dist/commands/verify.d.ts +18 -0
  28. package/dist/commands/verify.js +50 -0
  29. package/dist/commands/verify.js.map +1 -0
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.js +127 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/lib/browser-testing.d.ts +16 -0
  34. package/dist/lib/browser-testing.js +170 -0
  35. package/dist/lib/browser-testing.js.map +1 -0
  36. package/dist/lib/checksum.d.ts +1 -0
  37. package/dist/lib/checksum.js +5 -0
  38. package/dist/lib/checksum.js.map +1 -0
  39. package/dist/lib/claudeSettings.d.ts +21 -0
  40. package/dist/lib/claudeSettings.js +71 -0
  41. package/dist/lib/claudeSettings.js.map +1 -0
  42. package/dist/lib/detect.d.ts +23 -0
  43. package/dist/lib/detect.js +88 -0
  44. package/dist/lib/detect.js.map +1 -0
  45. package/dist/lib/diff.d.ts +12 -0
  46. package/dist/lib/diff.js +99 -0
  47. package/dist/lib/diff.js.map +1 -0
  48. package/dist/lib/lockfile.d.ts +25 -0
  49. package/dist/lib/lockfile.js +44 -0
  50. package/dist/lib/lockfile.js.map +1 -0
  51. package/dist/lib/manifest.d.ts +41 -0
  52. package/dist/lib/manifest.js +127 -0
  53. package/dist/lib/manifest.js.map +1 -0
  54. package/dist/lib/migrations.d.ts +47 -0
  55. package/dist/lib/migrations.js +76 -0
  56. package/dist/lib/migrations.js.map +1 -0
  57. package/dist/lib/project.d.ts +22 -0
  58. package/dist/lib/project.js +53 -0
  59. package/dist/lib/project.js.map +1 -0
  60. package/dist/lib/ralph.d.ts +12 -0
  61. package/dist/lib/ralph.js +21 -0
  62. package/dist/lib/ralph.js.map +1 -0
  63. package/dist/lib/seeds.d.ts +11 -0
  64. package/dist/lib/seeds.js +120 -0
  65. package/dist/lib/seeds.js.map +1 -0
  66. package/dist/lib/upstream.d.ts +21 -0
  67. package/dist/lib/upstream.js +37 -0
  68. package/dist/lib/upstream.js.map +1 -0
  69. package/dist/lib/writer.d.ts +26 -0
  70. package/dist/lib/writer.js +62 -0
  71. package/dist/lib/writer.js.map +1 -0
  72. package/dist/version.d.ts +1 -0
  73. package/dist/version.js +4 -0
  74. package/dist/version.js.map +1 -0
  75. package/package.json +36 -0
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Minimal line-based diff — enough for `launchrail diff` to preview upstream
3
+ * changes without a dependency. LCS is quadratic, which is fine at the size
4
+ * of the files Launchrail manages.
5
+ */
6
+ function splitLines(text) {
7
+ const lines = text.split("\n");
8
+ if (lines[lines.length - 1] === "")
9
+ lines.pop();
10
+ return lines;
11
+ }
12
+ export function diffOps(oldText, newText) {
13
+ const a = splitLines(oldText);
14
+ const b = splitLines(newText);
15
+ const n = a.length;
16
+ const m = b.length;
17
+ const table = Array.from({ length: n + 1 }, () => new Uint32Array(m + 1));
18
+ for (let i = n - 1; i >= 0; i--) {
19
+ const row = table[i];
20
+ const next = table[i + 1];
21
+ for (let j = m - 1; j >= 0; j--) {
22
+ row[j] = a[i] === b[j] ? next[j + 1] + 1 : Math.max(next[j], row[j + 1]);
23
+ }
24
+ }
25
+ const ops = [];
26
+ let i = 0;
27
+ let j = 0;
28
+ while (i < n && j < m) {
29
+ if (a[i] === b[j]) {
30
+ ops.push({ kind: "context", text: a[i] });
31
+ i += 1;
32
+ j += 1;
33
+ }
34
+ else if (table[i + 1][j] >= table[i][j + 1]) {
35
+ ops.push({ kind: "del", text: a[i] });
36
+ i += 1;
37
+ }
38
+ else {
39
+ ops.push({ kind: "add", text: b[j] });
40
+ j += 1;
41
+ }
42
+ }
43
+ while (i < n)
44
+ ops.push({ kind: "del", text: a[i++] });
45
+ while (j < m)
46
+ ops.push({ kind: "add", text: b[j++] });
47
+ return ops;
48
+ }
49
+ const PREFIX = { context: " ", del: "-", add: "+" };
50
+ /** Unified-diff hunks (no ---/+++ header). Empty string when the texts match. */
51
+ export function formatUnifiedDiff(oldText, newText, context = 3) {
52
+ if (oldText === newText)
53
+ return "";
54
+ const ops = diffOps(oldText, newText);
55
+ const changed = ops.reduce((acc, op, index) => {
56
+ if (op.kind !== "context")
57
+ acc.push(index);
58
+ return acc;
59
+ }, []);
60
+ if (changed.length === 0)
61
+ return "(only a trailing-newline difference)\n";
62
+ const ranges = [];
63
+ for (const index of changed) {
64
+ const start = Math.max(0, index - context);
65
+ const end = Math.min(ops.length - 1, index + context);
66
+ const last = ranges[ranges.length - 1];
67
+ if (last && start <= last[1] + 1)
68
+ last[1] = end;
69
+ else
70
+ ranges.push([start, end]);
71
+ }
72
+ const lines = [];
73
+ let oldPos = 1;
74
+ let newPos = 1;
75
+ let cursor = 0;
76
+ for (const [start, end] of ranges) {
77
+ for (; cursor < start; cursor++) {
78
+ const op = ops[cursor];
79
+ if (op.kind !== "add")
80
+ oldPos += 1;
81
+ if (op.kind !== "del")
82
+ newPos += 1;
83
+ }
84
+ const hunk = ops.slice(start, end + 1);
85
+ const oldCount = hunk.filter((op) => op.kind !== "add").length;
86
+ const newCount = hunk.filter((op) => op.kind !== "del").length;
87
+ lines.push(`@@ -${oldCount === 0 ? oldPos - 1 : oldPos},${oldCount} +${newCount === 0 ? newPos - 1 : newPos},${newCount} @@`);
88
+ for (; cursor <= end; cursor++) {
89
+ const op = ops[cursor];
90
+ lines.push(PREFIX[op.kind] + op.text);
91
+ if (op.kind !== "add")
92
+ oldPos += 1;
93
+ if (op.kind !== "del")
94
+ newPos += 1;
95
+ }
96
+ }
97
+ return lines.join("\n") + "\n";
98
+ }
99
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/lib/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export declare const LOCKFILE_FILENAME = ".launchrail-lock.json";
2
+ export type OwnershipClass = "managed" | "seeded";
3
+ /**
4
+ * Tracked files carry an ownership class, or "ejected": a path Launchrail once
5
+ * wrote and — after `launchrail eject` — will never write again.
6
+ */
7
+ export type LockedClass = OwnershipClass | "ejected";
8
+ export interface LockedFile {
9
+ class: LockedClass;
10
+ checksum: string;
11
+ }
12
+ export interface Lockfile {
13
+ schemaVersion: 1;
14
+ launchrailVersion: string;
15
+ files: Record<string, LockedFile>;
16
+ migrations: string[];
17
+ decisions: Record<string, string | number | boolean | null>;
18
+ }
19
+ export declare function emptyLockfile(launchrailVersion: string): Lockfile;
20
+ export interface LockfileReadResult {
21
+ lockfile: Lockfile | null;
22
+ error: string | null;
23
+ }
24
+ export declare function readLockfile(root: string): LockfileReadResult;
25
+ export declare function writeLockfile(root: string, lockfile: Lockfile): void;
@@ -0,0 +1,44 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ export const LOCKFILE_FILENAME = ".launchrail-lock.json";
4
+ export function emptyLockfile(launchrailVersion) {
5
+ return {
6
+ schemaVersion: 1,
7
+ launchrailVersion,
8
+ files: {},
9
+ migrations: [],
10
+ decisions: {},
11
+ };
12
+ }
13
+ export function readLockfile(root) {
14
+ const path = join(root, LOCKFILE_FILENAME);
15
+ if (!existsSync(path))
16
+ return { lockfile: null, error: null };
17
+ let data;
18
+ try {
19
+ data = JSON.parse(readFileSync(path, "utf8"));
20
+ }
21
+ catch (err) {
22
+ return { lockfile: null, error: `unreadable JSON: ${err instanceof Error ? err.message : String(err)}` };
23
+ }
24
+ if (typeof data !== "object" || data === null || data.schemaVersion !== 1) {
25
+ return { lockfile: null, error: "unsupported lockfile schema" };
26
+ }
27
+ const raw = data;
28
+ return {
29
+ lockfile: {
30
+ schemaVersion: 1,
31
+ launchrailVersion: typeof raw.launchrailVersion === "string" ? raw.launchrailVersion : "0.0.0",
32
+ files: raw.files ?? {},
33
+ migrations: raw.migrations ?? [],
34
+ decisions: raw.decisions ?? {},
35
+ },
36
+ error: null,
37
+ };
38
+ }
39
+ export function writeLockfile(root, lockfile) {
40
+ const sortedFiles = Object.fromEntries(Object.entries(lockfile.files).sort(([a], [b]) => a.localeCompare(b)));
41
+ const content = JSON.stringify({ ...lockfile, files: sortedFiles }, null, 2) + "\n";
42
+ writeFileSync(join(root, LOCKFILE_FILENAME), content, "utf8");
43
+ }
44
+ //# sourceMappingURL=lockfile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lockfile.js","sourceRoot":"","sources":["../../src/lib/lockfile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,MAAM,iBAAiB,GAAG,uBAAuB,CAAC;AAuBzD,MAAM,UAAU,aAAa,CAAC,iBAAyB;IACrD,OAAO;QACL,aAAa,EAAE,CAAC;QAChB,iBAAiB;QACjB,KAAK,EAAE,EAAE;QACT,UAAU,EAAE,EAAE;QACd,SAAS,EAAE,EAAE;KACd,CAAC;AACJ,CAAC;AAOD,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;IAC3C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IAC9D,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;IAC3G,CAAC;IACD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAK,IAAoC,CAAC,aAAa,KAAK,CAAC,EAAE,CAAC;QAC3G,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,6BAA6B,EAAE,CAAC;IAClE,CAAC;IACD,MAAM,GAAG,GAAG,IAAyB,CAAC;IACtC,OAAO;QACL,QAAQ,EAAE;YACR,aAAa,EAAE,CAAC;YAChB,iBAAiB,EAAE,OAAO,GAAG,CAAC,iBAAiB,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO;YAC9F,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;YACtB,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,EAAE;YAChC,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,EAAE;SAC/B;QACD,KAAK,EAAE,IAAI;KACZ,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,QAAkB;IAC5D,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CACpC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CACtE,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACpF,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,41 @@
1
+ export declare const MANIFEST_FILENAME = ".launchrail.yml";
2
+ export declare const MODES: readonly ["spike", "standard-mvp", "high-rigor"];
3
+ export type Mode = (typeof MODES)[number];
4
+ export declare const ISSUE_TRACKERS: readonly ["github", "linear", "none"];
5
+ export type IssueTracker = (typeof ISSUE_TRACKERS)[number];
6
+ export declare const TESTING_KEYS: readonly ["unitCommand", "devCommand", "e2eCommand", "smokeCommand", "appUrl"];
7
+ export type TestingKey = (typeof TESTING_KEYS)[number];
8
+ export interface Manifest {
9
+ schemaVersion: 1;
10
+ mode: Mode;
11
+ issueTracker: IssueTracker;
12
+ conventions: {
13
+ conventionalCommits: boolean;
14
+ };
15
+ testing: Record<TestingKey, string | null>;
16
+ modules: Record<string, boolean>;
17
+ }
18
+ export interface ManifestParseResult {
19
+ manifest: Manifest | null;
20
+ errors: string[];
21
+ }
22
+ /**
23
+ * Strict on the fields that change behavior (schemaVersion, mode), lenient
24
+ * with defaults on the rest so hand-edited manifests stay valid.
25
+ */
26
+ export declare function validateManifest(data: unknown): ManifestParseResult;
27
+ export declare function parseManifest(source: string): ManifestParseResult;
28
+ export declare function serializeManifest(manifest: Manifest): string;
29
+ export interface ModuleEnableResult {
30
+ source: string;
31
+ changed: boolean;
32
+ }
33
+ /**
34
+ * Enable a module and record testing commands in an existing manifest source.
35
+ *
36
+ * The manifest is seeded (project-owned once created), so this is the one
37
+ * deliberate exception to "never rewrite seeded files": a user-requested
38
+ * configuration change, applied via a YAML document round-trip that preserves
39
+ * the user's comments and formatting.
40
+ */
41
+ export declare function setModuleEnabled(source: string, module: string, testing: Partial<Record<TestingKey, string | null>>): ModuleEnableResult;
@@ -0,0 +1,127 @@
1
+ import { parse, parseDocument, stringify } from "yaml";
2
+ export const MANIFEST_FILENAME = ".launchrail.yml";
3
+ export const MODES = ["spike", "standard-mvp", "high-rigor"];
4
+ export const ISSUE_TRACKERS = ["github", "linear", "none"];
5
+ export const TESTING_KEYS = ["unitCommand", "devCommand", "e2eCommand", "smokeCommand", "appUrl"];
6
+ function isRecord(value) {
7
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8
+ }
9
+ /**
10
+ * Strict on the fields that change behavior (schemaVersion, mode), lenient
11
+ * with defaults on the rest so hand-edited manifests stay valid.
12
+ */
13
+ export function validateManifest(data) {
14
+ if (!isRecord(data)) {
15
+ return { manifest: null, errors: ["manifest must be a YAML mapping"] };
16
+ }
17
+ const errors = [];
18
+ if (data.schemaVersion !== 1) {
19
+ errors.push(`schemaVersion must be 1 (found ${JSON.stringify(data.schemaVersion ?? null)})`);
20
+ }
21
+ if (typeof data.mode !== "string" || !MODES.includes(data.mode)) {
22
+ errors.push(`mode must be one of: ${MODES.join(", ")}`);
23
+ }
24
+ let issueTracker = "none";
25
+ if (data.issueTracker !== undefined) {
26
+ if (typeof data.issueTracker === "string" && ISSUE_TRACKERS.includes(data.issueTracker)) {
27
+ issueTracker = data.issueTracker;
28
+ }
29
+ else {
30
+ errors.push(`issueTracker must be one of: ${ISSUE_TRACKERS.join(", ")}`);
31
+ }
32
+ }
33
+ let conventionalCommits = true;
34
+ if (data.conventions !== undefined) {
35
+ if (isRecord(data.conventions) && typeof data.conventions.conventionalCommits === "boolean") {
36
+ conventionalCommits = data.conventions.conventionalCommits;
37
+ }
38
+ else {
39
+ errors.push("conventions.conventionalCommits must be a boolean");
40
+ }
41
+ }
42
+ const testing = {
43
+ unitCommand: null,
44
+ devCommand: null,
45
+ e2eCommand: null,
46
+ smokeCommand: null,
47
+ appUrl: null,
48
+ };
49
+ if (data.testing !== undefined) {
50
+ if (isRecord(data.testing)) {
51
+ for (const key of TESTING_KEYS) {
52
+ const value = data.testing[key];
53
+ if (value === undefined || value === null)
54
+ continue;
55
+ if (typeof value === "string")
56
+ testing[key] = value;
57
+ else
58
+ errors.push(`testing.${key} must be a string or null`);
59
+ }
60
+ }
61
+ else {
62
+ errors.push("testing must be a mapping");
63
+ }
64
+ }
65
+ let modules = { core: true };
66
+ if (data.modules !== undefined) {
67
+ if (isRecord(data.modules) && Object.values(data.modules).every((v) => typeof v === "boolean")) {
68
+ modules = data.modules;
69
+ }
70
+ else {
71
+ errors.push("modules must map module names to booleans");
72
+ }
73
+ }
74
+ if (errors.length > 0)
75
+ return { manifest: null, errors };
76
+ return {
77
+ manifest: {
78
+ schemaVersion: 1,
79
+ mode: data.mode,
80
+ issueTracker,
81
+ conventions: { conventionalCommits },
82
+ testing,
83
+ modules,
84
+ },
85
+ errors: [],
86
+ };
87
+ }
88
+ export function parseManifest(source) {
89
+ let data;
90
+ try {
91
+ data = parse(source);
92
+ }
93
+ catch (err) {
94
+ return { manifest: null, errors: [`invalid YAML: ${err instanceof Error ? err.message : String(err)}`] };
95
+ }
96
+ return validateManifest(data);
97
+ }
98
+ export function serializeManifest(manifest) {
99
+ const header = "# Launchrail project manifest — https://github.com/wemuda/launchrail\n" +
100
+ "# This file is yours to edit; Launchrail reads it and never force-overwrites it.\n";
101
+ return header + stringify(manifest);
102
+ }
103
+ /**
104
+ * Enable a module and record testing commands in an existing manifest source.
105
+ *
106
+ * The manifest is seeded (project-owned once created), so this is the one
107
+ * deliberate exception to "never rewrite seeded files": a user-requested
108
+ * configuration change, applied via a YAML document round-trip that preserves
109
+ * the user's comments and formatting.
110
+ */
111
+ export function setModuleEnabled(source, module, testing) {
112
+ const doc = parseDocument(source);
113
+ let changed = false;
114
+ if (doc.getIn(["modules", module]) !== true) {
115
+ doc.setIn(["modules", module], true);
116
+ changed = true;
117
+ }
118
+ for (const [key, value] of Object.entries(testing)) {
119
+ const current = doc.getIn(["testing", key]);
120
+ if ((current ?? null) !== (value ?? null)) {
121
+ doc.setIn(["testing", key], value);
122
+ changed = true;
123
+ }
124
+ }
125
+ return { source: doc.toString(), changed };
126
+ }
127
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/lib/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import type { Lockfile } from "./lockfile.js";
2
+ export interface MigrationContext {
3
+ cwd: string;
4
+ lockfile: Lockfile;
5
+ }
6
+ export interface MigrationPlan {
7
+ /** Human-readable changes this run would make; empty when already satisfied. */
8
+ changes: string[];
9
+ apply: () => void;
10
+ }
11
+ export interface Migration {
12
+ /** Date-prefixed (`2026-08-plugin-declaration`), so lexicographic order is chronological order. */
13
+ id: string;
14
+ description: string;
15
+ plan(ctx: MigrationContext): MigrationPlan;
16
+ }
17
+ /**
18
+ * Every structural change ever shipped, in order. Entries must be idempotent —
19
+ * planning against an already-migrated repository yields no changes — and every
20
+ * migration's end state must also be produced by the current `init`, which is
21
+ * why a fresh init stamps the whole registry as applied.
22
+ */
23
+ export declare const MIGRATIONS: Migration[];
24
+ export declare function migrationIds(registry?: Migration[]): string[];
25
+ /** Migrations not yet recorded in the lockfile, in the order they must run. */
26
+ export declare function pendingMigrations(lockfile: Lockfile, registry?: Migration[]): Migration[];
27
+ export interface PlannedMigration {
28
+ id: string;
29
+ description: string;
30
+ changes: string[];
31
+ }
32
+ /** Dry-run view of what `applyPendingMigrations` would do. Touches nothing. */
33
+ export declare function planPendingMigrations(ctx: MigrationContext, registry?: Migration[]): PlannedMigration[];
34
+ export type MigrationStatus = "applied" | "already-satisfied" | "failed";
35
+ export interface MigrationResult {
36
+ id: string;
37
+ description: string;
38
+ status: MigrationStatus;
39
+ changes: string[];
40
+ error: string | null;
41
+ }
42
+ /**
43
+ * Apply pending migrations in order, recording each in `lockfile.migrations`.
44
+ * A failure stops the run and is reported; earlier migrations stay recorded so
45
+ * a re-run resumes exactly where this one stopped.
46
+ */
47
+ export declare function applyPendingMigrations(ctx: MigrationContext, registry?: Migration[]): MigrationResult[];
@@ -0,0 +1,76 @@
1
+ import { applyPluginDeclaration, CLAUDE_SETTINGS_PATH, planPluginDeclaration } from "./claudeSettings.js";
2
+ /**
3
+ * Every structural change ever shipped, in order. Entries must be idempotent —
4
+ * planning against an already-migrated repository yields no changes — and every
5
+ * migration's end state must also be produced by the current `init`, which is
6
+ * why a fresh init stamps the whole registry as applied.
7
+ */
8
+ export const MIGRATIONS = [
9
+ {
10
+ id: "2026-08-plugin-declaration",
11
+ description: `declare the Launchrail Claude plugin in ${CLAUDE_SETTINGS_PATH} (ADR-0003)`,
12
+ plan(ctx) {
13
+ const settings = planPluginDeclaration(ctx.cwd);
14
+ if (settings.content === null)
15
+ return { changes: [], apply: () => { } };
16
+ return {
17
+ changes: [`${CLAUDE_SETTINGS_PATH} — ${settings.detail}`],
18
+ apply: () => {
19
+ applyPluginDeclaration(ctx.cwd, settings);
20
+ },
21
+ };
22
+ },
23
+ },
24
+ ];
25
+ export function migrationIds(registry = MIGRATIONS) {
26
+ return registry.map((migration) => migration.id);
27
+ }
28
+ /** Migrations not yet recorded in the lockfile, in the order they must run. */
29
+ export function pendingMigrations(lockfile, registry = MIGRATIONS) {
30
+ const applied = new Set(lockfile.migrations);
31
+ return [...registry].sort((a, b) => a.id.localeCompare(b.id)).filter((m) => !applied.has(m.id));
32
+ }
33
+ /** Dry-run view of what `applyPendingMigrations` would do. Touches nothing. */
34
+ export function planPendingMigrations(ctx, registry = MIGRATIONS) {
35
+ return pendingMigrations(ctx.lockfile, registry).map((migration) => ({
36
+ id: migration.id,
37
+ description: migration.description,
38
+ changes: migration.plan(ctx).changes,
39
+ }));
40
+ }
41
+ /**
42
+ * Apply pending migrations in order, recording each in `lockfile.migrations`.
43
+ * A failure stops the run and is reported; earlier migrations stay recorded so
44
+ * a re-run resumes exactly where this one stopped.
45
+ */
46
+ export function applyPendingMigrations(ctx, registry = MIGRATIONS) {
47
+ const results = [];
48
+ for (const migration of pendingMigrations(ctx.lockfile, registry)) {
49
+ let changes = [];
50
+ try {
51
+ const plan = migration.plan(ctx);
52
+ changes = plan.changes;
53
+ plan.apply();
54
+ }
55
+ catch (err) {
56
+ results.push({
57
+ id: migration.id,
58
+ description: migration.description,
59
+ status: "failed",
60
+ changes,
61
+ error: err instanceof Error ? err.message : String(err),
62
+ });
63
+ return results;
64
+ }
65
+ ctx.lockfile.migrations.push(migration.id);
66
+ results.push({
67
+ id: migration.id,
68
+ description: migration.description,
69
+ status: changes.length > 0 ? "applied" : "already-satisfied",
70
+ changes,
71
+ error: null,
72
+ });
73
+ }
74
+ return results;
75
+ }
76
+ //# sourceMappingURL=migrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.js","sourceRoot":"","sources":["../../src/lib/migrations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { type RepoDetection } from "./detect.js";
2
+ import { type Lockfile } from "./lockfile.js";
3
+ import { type Manifest } from "./manifest.js";
4
+ import type { FileSpec } from "./writer.js";
5
+ export interface ProjectState {
6
+ manifest: Manifest;
7
+ lockfile: Lockfile;
8
+ detection: RepoDetection;
9
+ }
10
+ export interface ProjectLoadResult {
11
+ state: ProjectState | null;
12
+ errors: string[];
13
+ }
14
+ /** Load manifest and lockfile — the precondition for status, diff, sync, and eject. */
15
+ export declare function loadProject(cwd: string): ProjectLoadResult;
16
+ /** The files each enabled module contributes at the current version, rendered for this project. */
17
+ export declare function moduleSpecs(state: ProjectState): Record<string, FileSpec[]>;
18
+ /**
19
+ * The full file surface Launchrail maintains for this configuration —
20
+ * everything init/add would write today — minus paths the project has ejected.
21
+ */
22
+ export declare function desiredSpecs(state: ProjectState): FileSpec[];
@@ -0,0 +1,53 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { BROWSER_TESTING_MODULE, browserTestingFiles } from "./browser-testing.js";
4
+ import { detectRepo } from "./detect.js";
5
+ import { LOCKFILE_FILENAME, readLockfile } from "./lockfile.js";
6
+ import { MANIFEST_FILENAME, parseManifest } from "./manifest.js";
7
+ import { RALPH_MODULE, ralphFiles } from "./ralph.js";
8
+ import { seedFiles } from "./seeds.js";
9
+ import { VERSION } from "../version.js";
10
+ /** Load manifest and lockfile — the precondition for status, diff, sync, and eject. */
11
+ export function loadProject(cwd) {
12
+ const detection = detectRepo(cwd);
13
+ if (!detection.hasManifest) {
14
+ return { state: null, errors: [`${MANIFEST_FILENAME} not found — run \`launchrail init\` first.`] };
15
+ }
16
+ const parsed = parseManifest(readFileSync(join(cwd, MANIFEST_FILENAME), "utf8"));
17
+ if (!parsed.manifest) {
18
+ return { state: null, errors: [`${MANIFEST_FILENAME} is invalid: ${parsed.errors.join("; ")}`] };
19
+ }
20
+ const { lockfile, error } = readLockfile(cwd);
21
+ if (error)
22
+ return { state: null, errors: [`${LOCKFILE_FILENAME}: ${error}`] };
23
+ if (!lockfile) {
24
+ return { state: null, errors: [`${LOCKFILE_FILENAME} not found — run \`launchrail init\` first.`] };
25
+ }
26
+ return { state: { manifest: parsed.manifest, lockfile, detection }, errors: [] };
27
+ }
28
+ /** The files each enabled module contributes at the current version, rendered for this project. */
29
+ export function moduleSpecs(state) {
30
+ const ctx = {
31
+ projectName: state.detection.projectName,
32
+ manifest: state.manifest,
33
+ launchrailVersion: VERSION,
34
+ };
35
+ const modules = { core: seedFiles(ctx) };
36
+ if (state.manifest.modules[BROWSER_TESTING_MODULE]) {
37
+ modules[BROWSER_TESTING_MODULE] = browserTestingFiles({ manifest: state.manifest, detection: state.detection });
38
+ }
39
+ if (state.manifest.modules[RALPH_MODULE]) {
40
+ modules[RALPH_MODULE] = ralphFiles();
41
+ }
42
+ return modules;
43
+ }
44
+ /**
45
+ * The full file surface Launchrail maintains for this configuration —
46
+ * everything init/add would write today — minus paths the project has ejected.
47
+ */
48
+ export function desiredSpecs(state) {
49
+ return Object.values(moduleSpecs(state))
50
+ .flat()
51
+ .filter((spec) => state.lockfile.files[spec.relPath]?.class !== "ejected");
52
+ }
53
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/lib/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { FileSpec } from "./writer.js";
2
+ export declare const RALPH_MODULE = "ralph";
3
+ export declare const RALPH_WORKFLOW_PATH = ".claude/workflows/ralph.js";
4
+ export declare function ralphWorkflowContent(): string;
5
+ /**
6
+ * Everything `launchrail add ralph` writes. The workflow script is managed-class —
7
+ * it contains toolchain logic only (policy is overridden per run via workflow args,
8
+ * never by editing the file), so Launchrail may replace it as the loop improves.
9
+ * The skills half of Ralph (`ralph`, `ralph-implement`, `resolving-merge-conflicts`)
10
+ * ships through the plugin and writes no files here.
11
+ */
12
+ export declare function ralphFiles(): FileSpec[];
@@ -0,0 +1,21 @@
1
+ import { readFileSync } from "node:fs";
2
+ export const RALPH_MODULE = "ralph";
3
+ export const RALPH_WORKFLOW_PATH = ".claude/workflows/ralph.js";
4
+ // The workflow script ships as a real .js asset (readable, reviewable, syntax-checkable)
5
+ // rather than an inline template string. It resolves from both src/ (tests) and dist/
6
+ // (published CLI) because the two are sibling directories of assets/.
7
+ const WORKFLOW_ASSET_URL = new URL("../../assets/ralph.workflow.js", import.meta.url);
8
+ export function ralphWorkflowContent() {
9
+ return readFileSync(WORKFLOW_ASSET_URL, "utf8");
10
+ }
11
+ /**
12
+ * Everything `launchrail add ralph` writes. The workflow script is managed-class —
13
+ * it contains toolchain logic only (policy is overridden per run via workflow args,
14
+ * never by editing the file), so Launchrail may replace it as the loop improves.
15
+ * The skills half of Ralph (`ralph`, `ralph-implement`, `resolving-merge-conflicts`)
16
+ * ships through the plugin and writes no files here.
17
+ */
18
+ export function ralphFiles() {
19
+ return [{ relPath: RALPH_WORKFLOW_PATH, content: ralphWorkflowContent(), ownership: "managed" }];
20
+ }
21
+ //# sourceMappingURL=ralph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ralph.js","sourceRoot":"","sources":["../../src/lib/ralph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AACpC,MAAM,CAAC,MAAM,mBAAmB,GAAG,4BAA4B,CAAC;AAEhE,yFAAyF;AACzF,sFAAsF;AACtF,sEAAsE;AACtE,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,gCAAgC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEtF,MAAM,UAAU,oBAAoB;IAClC,OAAO,YAAY,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,CAAC,EAAE,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,oBAAoB,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;AACnG,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Manifest } from "./manifest.js";
2
+ import type { FileSpec } from "./writer.js";
3
+ export interface SeedContext {
4
+ projectName: string;
5
+ manifest: Manifest;
6
+ launchrailVersion: string;
7
+ }
8
+ /** The managed Claude instructions file — regenerated whenever module configuration changes. */
9
+ export declare function claudeGeneratedFile(ctx: SeedContext): FileSpec;
10
+ /** Everything init seeds beyond the manifest and lockfile. */
11
+ export declare function seedFiles(ctx: SeedContext): FileSpec[];