@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.
- package/LICENSE +21 -0
- package/assets/ralph.workflow.js +476 -0
- package/dist/commands/add.d.ts +13 -0
- package/dist/commands/add.js +189 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/diff.d.ts +13 -0
- package/dist/commands/diff.js +44 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/doctor.d.ts +12 -0
- package/dist/commands/doctor.js +171 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/eject.d.ts +17 -0
- package/dist/commands/eject.js +80 -0
- package/dist/commands/eject.js.map +1 -0
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.js +173 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/smoke.d.ts +21 -0
- package/dist/commands/smoke.js +171 -0
- package/dist/commands/smoke.js.map +1 -0
- package/dist/commands/status.d.ts +15 -0
- package/dist/commands/status.js +91 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +19 -0
- package/dist/commands/sync.js +83 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/verify.d.ts +18 -0
- package/dist/commands/verify.js +50 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +127 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/browser-testing.d.ts +16 -0
- package/dist/lib/browser-testing.js +170 -0
- package/dist/lib/browser-testing.js.map +1 -0
- package/dist/lib/checksum.d.ts +1 -0
- package/dist/lib/checksum.js +5 -0
- package/dist/lib/checksum.js.map +1 -0
- package/dist/lib/claudeSettings.d.ts +21 -0
- package/dist/lib/claudeSettings.js +71 -0
- package/dist/lib/claudeSettings.js.map +1 -0
- package/dist/lib/detect.d.ts +23 -0
- package/dist/lib/detect.js +88 -0
- package/dist/lib/detect.js.map +1 -0
- package/dist/lib/diff.d.ts +12 -0
- package/dist/lib/diff.js +99 -0
- package/dist/lib/diff.js.map +1 -0
- package/dist/lib/lockfile.d.ts +25 -0
- package/dist/lib/lockfile.js +44 -0
- package/dist/lib/lockfile.js.map +1 -0
- package/dist/lib/manifest.d.ts +41 -0
- package/dist/lib/manifest.js +127 -0
- package/dist/lib/manifest.js.map +1 -0
- package/dist/lib/migrations.d.ts +47 -0
- package/dist/lib/migrations.js +76 -0
- package/dist/lib/migrations.js.map +1 -0
- package/dist/lib/project.d.ts +22 -0
- package/dist/lib/project.js +53 -0
- package/dist/lib/project.js.map +1 -0
- package/dist/lib/ralph.d.ts +12 -0
- package/dist/lib/ralph.js +21 -0
- package/dist/lib/ralph.js.map +1 -0
- package/dist/lib/seeds.d.ts +11 -0
- package/dist/lib/seeds.js +120 -0
- package/dist/lib/seeds.js.map +1 -0
- package/dist/lib/upstream.d.ts +21 -0
- package/dist/lib/upstream.js +37 -0
- package/dist/lib/upstream.js.map +1 -0
- package/dist/lib/writer.d.ts +26 -0
- package/dist/lib/writer.js +62 -0
- package/dist/lib/writer.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import { BROWSER_TESTING_MODULE, browserTestingFiles } from "../lib/browser-testing.js";
|
|
5
|
+
import { detectRepo } from "../lib/detect.js";
|
|
6
|
+
import { emptyLockfile, readLockfile, writeLockfile } from "../lib/lockfile.js";
|
|
7
|
+
import { MANIFEST_FILENAME, parseManifest, setModuleEnabled } from "../lib/manifest.js";
|
|
8
|
+
import { RALPH_MODULE, RALPH_WORKFLOW_PATH, ralphFiles } from "../lib/ralph.js";
|
|
9
|
+
import { claudeGeneratedFile } from "../lib/seeds.js";
|
|
10
|
+
import { ACTION_LABEL, applyPlan, planWrites } from "../lib/writer.js";
|
|
11
|
+
import { VERSION } from "../version.js";
|
|
12
|
+
export const AVAILABLE_MODULES = [BROWSER_TESTING_MODULE, RALPH_MODULE];
|
|
13
|
+
function defaultAnswers(manifest, detection) {
|
|
14
|
+
const pm = detection.packageManager ?? "npm";
|
|
15
|
+
return {
|
|
16
|
+
appUrl: manifest.testing.appUrl ?? "http://localhost:3000",
|
|
17
|
+
devCommand: manifest.testing.devCommand ?? (detection.devScript ? `${pm} run dev` : null),
|
|
18
|
+
e2eCommand: manifest.testing.e2eCommand ?? "npx playwright test",
|
|
19
|
+
smokeCommand: manifest.testing.smokeCommand ?? "node scripts/smoke.mjs",
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
async function interview(defaults) {
|
|
23
|
+
p.intro("launchrail add browser-testing");
|
|
24
|
+
const answers = await p.group({
|
|
25
|
+
appUrl: () => p.text({
|
|
26
|
+
message: "App URL browser tests run against",
|
|
27
|
+
initialValue: defaults.appUrl,
|
|
28
|
+
}),
|
|
29
|
+
devCommand: () => p.text({
|
|
30
|
+
message: "Command that starts the app (empty to fill in later)",
|
|
31
|
+
initialValue: defaults.devCommand ?? "",
|
|
32
|
+
placeholder: "e.g. pnpm dev",
|
|
33
|
+
}),
|
|
34
|
+
e2eCommand: () => p.text({
|
|
35
|
+
message: "Deterministic browser-test command",
|
|
36
|
+
initialValue: defaults.e2eCommand,
|
|
37
|
+
}),
|
|
38
|
+
smokeCommand: () => p.text({
|
|
39
|
+
message: "Smoke entry command",
|
|
40
|
+
initialValue: defaults.smokeCommand,
|
|
41
|
+
}),
|
|
42
|
+
}, {
|
|
43
|
+
onCancel: () => {
|
|
44
|
+
p.cancel("Cancelled — nothing was written.");
|
|
45
|
+
process.exit(130);
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
return {
|
|
49
|
+
appUrl: answers.appUrl.trim() || defaults.appUrl,
|
|
50
|
+
devCommand: answers.devCommand.trim() === "" ? null : answers.devCommand.trim(),
|
|
51
|
+
e2eCommand: answers.e2eCommand.trim() || defaults.e2eCommand,
|
|
52
|
+
smokeCommand: answers.smokeCommand.trim() || defaults.smokeCommand,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function planBrowserTesting(parsed, detection, answers) {
|
|
56
|
+
const manifest = {
|
|
57
|
+
...parsed,
|
|
58
|
+
testing: { ...parsed.testing, ...answers },
|
|
59
|
+
modules: { ...parsed.modules, [BROWSER_TESTING_MODULE]: true },
|
|
60
|
+
};
|
|
61
|
+
const notes = [];
|
|
62
|
+
if (detection.playwrightConfigFile) {
|
|
63
|
+
notes.push(`Existing ${detection.playwrightConfigFile} detected — keeping it; no config or baseline spec seeded.`);
|
|
64
|
+
}
|
|
65
|
+
const nextSteps = ["Run `node scripts/setup.mjs` — installs @playwright/test and browser binaries."];
|
|
66
|
+
if (!answers.devCommand) {
|
|
67
|
+
nextSteps.push("Set the dev command: edit scripts/dev.mjs (DEV_COMMAND) and testing.devCommand in .launchrail.yml.");
|
|
68
|
+
}
|
|
69
|
+
nextSteps.push("Review the seeded files — they are yours: playwright config, tests/e2e/, docs/testing/smoke-journeys.md.", "Verify: `node scripts/verify.mjs`, then `npx @wemuda/launchrail smoke` with the app running.");
|
|
70
|
+
return {
|
|
71
|
+
manifest,
|
|
72
|
+
testing: { ...answers },
|
|
73
|
+
specs: browserTestingFiles({ manifest, detection }),
|
|
74
|
+
decisions: { ...answers },
|
|
75
|
+
notes,
|
|
76
|
+
nextSteps,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function planRalph(parsed) {
|
|
80
|
+
const manifest = {
|
|
81
|
+
...parsed,
|
|
82
|
+
modules: { ...parsed.modules, [RALPH_MODULE]: true },
|
|
83
|
+
};
|
|
84
|
+
const notes = [];
|
|
85
|
+
if (parsed.issueTracker === "none") {
|
|
86
|
+
notes.push("Warning: .launchrail.yml has issueTracker: none — Ralph runs off tracker tickets and will refuse to start until one is configured.");
|
|
87
|
+
}
|
|
88
|
+
if (!parsed.testing.unitCommand && !parsed.testing.e2eCommand) {
|
|
89
|
+
notes.push("Warning: no testing commands configured — `launchrail verify` fails on an empty contract, and Ralph refuses to start on a red gate.");
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
manifest,
|
|
93
|
+
testing: {},
|
|
94
|
+
specs: ralphFiles(),
|
|
95
|
+
decisions: {},
|
|
96
|
+
notes,
|
|
97
|
+
nextSteps: [
|
|
98
|
+
"Create the tracker labels Ralph uses: ready-for-agent, ralph:building, needs-info.",
|
|
99
|
+
"Produce tickets with explicit `Blocked by: #n` edges and the ready-for-agent label (Matt Pocock's to-tickets, stage 8 of the workflow).",
|
|
100
|
+
"Run a campaign: the launchrail:ralph skill (watchable) or the `ralph` workflow (wide or long runs; scope with args, e.g. { width: 1 }).",
|
|
101
|
+
"Start with width 1 until a few tickets have landed cleanly, then widen.",
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export async function runAdd(opts) {
|
|
106
|
+
if (!AVAILABLE_MODULES.includes(opts.module)) {
|
|
107
|
+
console.error(`launchrail: unknown module "${opts.module}". Available modules: ${AVAILABLE_MODULES.join(", ")}`);
|
|
108
|
+
return { code: 1, actions: [] };
|
|
109
|
+
}
|
|
110
|
+
const detection = detectRepo(opts.cwd);
|
|
111
|
+
if (!detection.hasManifest) {
|
|
112
|
+
console.error(`launchrail: ${MANIFEST_FILENAME} not found — run \`launchrail init\` first.`);
|
|
113
|
+
return { code: 1, actions: [] };
|
|
114
|
+
}
|
|
115
|
+
const manifestSource = readFileSync(join(opts.cwd, MANIFEST_FILENAME), "utf8");
|
|
116
|
+
const parsed = parseManifest(manifestSource);
|
|
117
|
+
if (!parsed.manifest) {
|
|
118
|
+
console.error(`launchrail: ${MANIFEST_FILENAME} is invalid:`);
|
|
119
|
+
for (const error of parsed.errors)
|
|
120
|
+
console.error(` - ${error}`);
|
|
121
|
+
return { code: 1, actions: [] };
|
|
122
|
+
}
|
|
123
|
+
let plan;
|
|
124
|
+
if (opts.module === BROWSER_TESTING_MODULE) {
|
|
125
|
+
const interactive = !opts.yes && process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
126
|
+
const answers = interactive
|
|
127
|
+
? await interview(defaultAnswers(parsed.manifest, detection))
|
|
128
|
+
: defaultAnswers(parsed.manifest, detection);
|
|
129
|
+
plan = planBrowserTesting(parsed.manifest, detection, answers);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
plan = planRalph(parsed.manifest);
|
|
133
|
+
}
|
|
134
|
+
const manifestUpdate = setModuleEnabled(manifestSource, opts.module, plan.testing);
|
|
135
|
+
const existing = readLockfile(opts.cwd);
|
|
136
|
+
if (existing.error) {
|
|
137
|
+
console.error(`launchrail: ${existing.error} — refusing to continue. Fix or remove the lockfile first.`);
|
|
138
|
+
return { code: 1, actions: [] };
|
|
139
|
+
}
|
|
140
|
+
const lockfile = existing.lockfile ?? emptyLockfile(VERSION);
|
|
141
|
+
const lockBefore = JSON.stringify(lockfile);
|
|
142
|
+
const specs = [
|
|
143
|
+
...plan.specs,
|
|
144
|
+
claudeGeneratedFile({ projectName: detection.projectName, manifest: plan.manifest, launchrailVersion: VERSION }),
|
|
145
|
+
];
|
|
146
|
+
const actions = planWrites(opts.cwd, specs, lockfile);
|
|
147
|
+
console.log("");
|
|
148
|
+
console.log(manifestUpdate.changed
|
|
149
|
+
? ` update ${MANIFEST_FILENAME} (enable ${opts.module}${Object.keys(plan.testing).length > 0 ? ", record testing commands" : ""})`
|
|
150
|
+
: ` ok ${MANIFEST_FILENAME} (${opts.module} already enabled)`);
|
|
151
|
+
for (const action of actions) {
|
|
152
|
+
console.log(` ${ACTION_LABEL[action.kind]} ${action.spec.relPath} (${action.detail})`);
|
|
153
|
+
}
|
|
154
|
+
for (const note of plan.notes)
|
|
155
|
+
console.log(`\n${note}`);
|
|
156
|
+
if (opts.dryRun) {
|
|
157
|
+
console.log("\nDry run — nothing was written.");
|
|
158
|
+
return { code: 0, actions };
|
|
159
|
+
}
|
|
160
|
+
if (manifestUpdate.changed) {
|
|
161
|
+
writeFileSync(join(opts.cwd, MANIFEST_FILENAME), manifestUpdate.source, "utf8");
|
|
162
|
+
}
|
|
163
|
+
const written = applyPlan(opts.cwd, actions, lockfile);
|
|
164
|
+
lockfile.launchrailVersion = VERSION;
|
|
165
|
+
lockfile.decisions = {
|
|
166
|
+
...lockfile.decisions,
|
|
167
|
+
[`module:${opts.module}`]: true,
|
|
168
|
+
...plan.decisions,
|
|
169
|
+
};
|
|
170
|
+
if (JSON.stringify(lockfile) !== lockBefore || !existing.lockfile) {
|
|
171
|
+
writeLockfile(opts.cwd, lockfile);
|
|
172
|
+
}
|
|
173
|
+
const conflicts = actions.filter((a) => a.kind === "conflict");
|
|
174
|
+
if (conflicts.length > 0) {
|
|
175
|
+
console.log(`\nNot overwritten (locally modified managed file${conflicts.length > 1 ? "s" : ""}): ${conflicts
|
|
176
|
+
.map((a) => a.spec.relPath)
|
|
177
|
+
.join(", ")} — revert local edits to receive updates, or run \`launchrail eject <file>\` to own them permanently.`);
|
|
178
|
+
}
|
|
179
|
+
console.log(written.length > 0 || manifestUpdate.changed
|
|
180
|
+
? `\nWrote ${written.length + (manifestUpdate.changed ? 1 : 0)} file(s).`
|
|
181
|
+
: "\nEverything already up to date — nothing written.");
|
|
182
|
+
console.log("\nNext steps:");
|
|
183
|
+
plan.nextSteps.forEach((step, i) => console.log(` ${i + 1}. ${step}`));
|
|
184
|
+
if (opts.module === RALPH_MODULE) {
|
|
185
|
+
console.log(`\nThe campaign workflow lives at ${RALPH_WORKFLOW_PATH} (managed — do not hand-edit).`);
|
|
186
|
+
}
|
|
187
|
+
return { code: 0, actions };
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=add.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface DiffEntry {
|
|
2
|
+
relPath: string;
|
|
3
|
+
kind: "create" | "update" | "conflict";
|
|
4
|
+
diff: string;
|
|
5
|
+
}
|
|
6
|
+
export interface DiffOutcome {
|
|
7
|
+
code: number;
|
|
8
|
+
errors: string[];
|
|
9
|
+
entries: DiffEntry[];
|
|
10
|
+
}
|
|
11
|
+
/** Preview what `launchrail sync` would write, as unified diffs against disk. */
|
|
12
|
+
export declare function runDiff(cwd: string): DiffOutcome;
|
|
13
|
+
export declare function printDiff(outcome: DiffOutcome): void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { formatUnifiedDiff } from "../lib/diff.js";
|
|
4
|
+
import { loadProject, desiredSpecs } from "../lib/project.js";
|
|
5
|
+
import { planWrites } from "../lib/writer.js";
|
|
6
|
+
/** Preview what `launchrail sync` would write, as unified diffs against disk. */
|
|
7
|
+
export function runDiff(cwd) {
|
|
8
|
+
const { state, errors } = loadProject(cwd);
|
|
9
|
+
if (!state)
|
|
10
|
+
return { code: 1, errors, entries: [] };
|
|
11
|
+
const entries = [];
|
|
12
|
+
for (const action of planWrites(cwd, desiredSpecs(state), state.lockfile)) {
|
|
13
|
+
if (action.kind !== "create" && action.kind !== "update" && action.kind !== "conflict")
|
|
14
|
+
continue;
|
|
15
|
+
const current = action.kind === "create" ? "" : readFileSync(join(cwd, action.spec.relPath), "utf8");
|
|
16
|
+
entries.push({
|
|
17
|
+
relPath: action.spec.relPath,
|
|
18
|
+
kind: action.kind,
|
|
19
|
+
diff: formatUnifiedDiff(current, action.spec.content),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return { code: 0, errors: [], entries };
|
|
23
|
+
}
|
|
24
|
+
export function printDiff(outcome) {
|
|
25
|
+
if (outcome.errors.length > 0) {
|
|
26
|
+
for (const error of outcome.errors)
|
|
27
|
+
console.error(`launchrail: ${error}`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (outcome.entries.length === 0) {
|
|
31
|
+
console.log("No upstream changes — managed files and seeds are up to date.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
for (const entry of outcome.entries) {
|
|
35
|
+
console.log(`--- ${entry.kind === "create" ? "/dev/null" : `a/${entry.relPath}`}`);
|
|
36
|
+
console.log(`+++ b/${entry.relPath}`);
|
|
37
|
+
if (entry.kind === "conflict") {
|
|
38
|
+
console.log("(locally modified — sync will keep your version; shown for reference)");
|
|
39
|
+
}
|
|
40
|
+
process.stdout.write(entry.diff);
|
|
41
|
+
console.log("");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAc9C,iFAAiF;AACjF,MAAM,UAAU,OAAO,CAAC,GAAW;IACjC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAEpD,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,KAAK,MAAM,MAAM,IAAI,UAAU,CAAC,GAAG,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1E,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU;YAAE,SAAS;QACjG,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;QACrG,OAAO,CAAC,IAAI,CAAC;YACX,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO;YAC5B,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;SACtD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAoB;IAC5C,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM;YAAE,OAAO,CAAC,KAAK,CAAC,eAAe,KAAK,EAAE,CAAC,CAAC;QAC1E,OAAO;IACT,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,+DAA+D,CAAC,CAAC;QAC7E,OAAO;IACT,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACnF,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACtC,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,uEAAuE,CAAC,CAAC;QACvF,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type CheckStatus = "pass" | "warn" | "fail";
|
|
2
|
+
export interface DoctorCheck {
|
|
3
|
+
status: CheckStatus;
|
|
4
|
+
name: string;
|
|
5
|
+
message: string;
|
|
6
|
+
}
|
|
7
|
+
export interface DoctorOutcome {
|
|
8
|
+
code: number;
|
|
9
|
+
checks: DoctorCheck[];
|
|
10
|
+
}
|
|
11
|
+
export declare function runDoctor(cwd: string): DoctorOutcome;
|
|
12
|
+
export declare function printDoctor(outcome: DoctorOutcome): void;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { BROWSER_TESTING_MODULE, SEMANTIC_SCRIPTS, SMOKE_JOURNEYS_PATH } from "../lib/browser-testing.js";
|
|
4
|
+
import { sha256 } from "../lib/checksum.js";
|
|
5
|
+
import { CLAUDE_SETTINGS_PATH, declarationState } from "../lib/claudeSettings.js";
|
|
6
|
+
import { detectRepo } from "../lib/detect.js";
|
|
7
|
+
import { readLockfile } from "../lib/lockfile.js";
|
|
8
|
+
import { pendingMigrations } from "../lib/migrations.js";
|
|
9
|
+
import { MANIFEST_FILENAME, parseManifest } from "../lib/manifest.js";
|
|
10
|
+
import { RALPH_MODULE, RALPH_WORKFLOW_PATH } from "../lib/ralph.js";
|
|
11
|
+
export function runDoctor(cwd) {
|
|
12
|
+
const checks = [];
|
|
13
|
+
const add = (status, name, message = "") => {
|
|
14
|
+
checks.push({ status, name, message });
|
|
15
|
+
};
|
|
16
|
+
const detection = detectRepo(cwd);
|
|
17
|
+
if (detection.isGitRepo)
|
|
18
|
+
add("pass", "git repository");
|
|
19
|
+
else
|
|
20
|
+
add("warn", "git repository", "not a git repo — Launchrail relies on git history for safe writes");
|
|
21
|
+
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
22
|
+
if (nodeMajor >= 22)
|
|
23
|
+
add("pass", "node version", `v${process.versions.node}`);
|
|
24
|
+
else
|
|
25
|
+
add("warn", "node version", `v${process.versions.node} — Launchrail targets Node >= 22`);
|
|
26
|
+
if (detection.packageManager)
|
|
27
|
+
add("pass", "package manager", detection.packageManager);
|
|
28
|
+
else
|
|
29
|
+
add("warn", "package manager", "none detected (no lockfile or packageManager field)");
|
|
30
|
+
let manifest = null;
|
|
31
|
+
if (!existsSync(join(cwd, MANIFEST_FILENAME))) {
|
|
32
|
+
add("fail", "manifest", `${MANIFEST_FILENAME} missing — run \`launchrail init\``);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
const parsed = parseManifest(readFileSync(join(cwd, MANIFEST_FILENAME), "utf8"));
|
|
36
|
+
if (parsed.manifest) {
|
|
37
|
+
manifest = parsed.manifest;
|
|
38
|
+
add("pass", "manifest", `valid (mode: ${parsed.manifest.mode})`);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
add("fail", "manifest", `invalid: ${parsed.errors.join("; ")}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const { lockfile, error: lockError } = readLockfile(cwd);
|
|
45
|
+
if (lockError) {
|
|
46
|
+
add("fail", "lockfile", lockError);
|
|
47
|
+
}
|
|
48
|
+
else if (!lockfile) {
|
|
49
|
+
add("fail", "lockfile", "missing — run `launchrail init`");
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
add("pass", "lockfile", `${Object.keys(lockfile.files).length} tracked file(s)`);
|
|
53
|
+
const managed = Object.entries(lockfile.files).filter(([, f]) => f.class === "managed");
|
|
54
|
+
const drifted = [];
|
|
55
|
+
let missing = 0;
|
|
56
|
+
for (const [relPath, locked] of managed) {
|
|
57
|
+
const abs = join(cwd, relPath);
|
|
58
|
+
if (!existsSync(abs)) {
|
|
59
|
+
add("fail", "managed file", `${relPath} is tracked but missing`);
|
|
60
|
+
missing += 1;
|
|
61
|
+
}
|
|
62
|
+
else if (sha256(readFileSync(abs, "utf8")) !== locked.checksum) {
|
|
63
|
+
drifted.push(relPath);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (drifted.length > 0) {
|
|
67
|
+
add("warn", "managed drift", `${drifted.join(", ")} locally modified — sync will ask before replacing`);
|
|
68
|
+
}
|
|
69
|
+
else if (missing === 0 && managed.length > 0) {
|
|
70
|
+
add("pass", "managed files", "match lockfile checksums");
|
|
71
|
+
}
|
|
72
|
+
const pending = pendingMigrations(lockfile);
|
|
73
|
+
if (pending.length > 0) {
|
|
74
|
+
add("warn", "migrations", `${pending.length} pending (${pending.map((m) => m.id).join(", ")}) — run \`launchrail sync\``);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
add("pass", "migrations", "all applied");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (detection.hasAgentsMd)
|
|
81
|
+
add("pass", "AGENTS.md");
|
|
82
|
+
else
|
|
83
|
+
add("fail", "AGENTS.md", "missing — run `launchrail init`");
|
|
84
|
+
if (!detection.hasClaudeMd) {
|
|
85
|
+
add("fail", "CLAUDE.md", "missing — run `launchrail init`");
|
|
86
|
+
}
|
|
87
|
+
else if (readFileSync(join(cwd, "CLAUDE.md"), "utf8").includes("@AGENTS.md")) {
|
|
88
|
+
add("pass", "CLAUDE.md", "imports @AGENTS.md");
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
add("warn", "CLAUDE.md", "does not import @AGENTS.md — Claude will not see the shared agent contract");
|
|
92
|
+
}
|
|
93
|
+
if (detection.hasMattPocockSetup)
|
|
94
|
+
add("pass", "Matt Pocock setup", "docs/agents/ present");
|
|
95
|
+
else
|
|
96
|
+
add("warn", "Matt Pocock setup", "docs/agents/ not found — install the skills and run /setup-matt-pocock-skills");
|
|
97
|
+
const declaration = declarationState(cwd);
|
|
98
|
+
if (declaration === "declared") {
|
|
99
|
+
add("pass", "plugin declaration", `${CLAUDE_SETTINGS_PATH} declares the Launchrail plugin`);
|
|
100
|
+
}
|
|
101
|
+
else if (declaration === "invalid-json") {
|
|
102
|
+
add("warn", "plugin declaration", `${CLAUDE_SETTINGS_PATH} is not valid JSON`);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
add("warn", "plugin declaration", `Launchrail plugin not declared in ${CLAUDE_SETTINGS_PATH} — run \`launchrail init\``);
|
|
106
|
+
}
|
|
107
|
+
if (manifest?.modules[BROWSER_TESTING_MODULE]) {
|
|
108
|
+
if (detection.hasPlaywrightDep) {
|
|
109
|
+
add("pass", "playwright dependency", "@playwright/test declared");
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
add("fail", "playwright dependency", "@playwright/test not declared — run `node scripts/setup.mjs`");
|
|
113
|
+
}
|
|
114
|
+
if (detection.playwrightConfigFile) {
|
|
115
|
+
add("pass", "playwright config", detection.playwrightConfigFile);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
add("fail", "playwright config", "no playwright.config.* found — re-run `launchrail add browser-testing`");
|
|
119
|
+
}
|
|
120
|
+
if (existsSync(join(cwd, SMOKE_JOURNEYS_PATH))) {
|
|
121
|
+
add("pass", "smoke journeys", SMOKE_JOURNEYS_PATH);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
add("warn", "smoke journeys", `${SMOKE_JOURNEYS_PATH} missing — smoke runs will have no defined journeys`);
|
|
125
|
+
}
|
|
126
|
+
const missingScripts = SEMANTIC_SCRIPTS.filter((name) => !existsSync(join(cwd, "scripts", `${name}.mjs`)));
|
|
127
|
+
if (missingScripts.length === 0) {
|
|
128
|
+
add("pass", "semantic scripts", "scripts/{setup,dev,verify,smoke,doctor}.mjs");
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
add("warn", "semantic scripts", `missing: ${missingScripts.map((name) => `scripts/${name}.mjs`).join(", ")}`);
|
|
132
|
+
}
|
|
133
|
+
if (manifest.testing.e2eCommand && manifest.testing.smokeCommand) {
|
|
134
|
+
add("pass", "testing commands", "e2e and smoke commands configured");
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
add("warn", "testing commands", `set testing.e2eCommand and testing.smokeCommand in ${MANIFEST_FILENAME}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (manifest?.modules[RALPH_MODULE]) {
|
|
141
|
+
if (existsSync(join(cwd, RALPH_WORKFLOW_PATH))) {
|
|
142
|
+
add("pass", "ralph workflow", RALPH_WORKFLOW_PATH);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
add("fail", "ralph workflow", `${RALPH_WORKFLOW_PATH} missing — re-run \`launchrail add ralph\``);
|
|
146
|
+
}
|
|
147
|
+
if (manifest.issueTracker !== "none") {
|
|
148
|
+
add("pass", "ralph tracker", `issueTracker: ${manifest.issueTracker}`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
add("warn", "ralph tracker", `issueTracker is none — Ralph runs off tickets; set it in ${MANIFEST_FILENAME}`);
|
|
152
|
+
}
|
|
153
|
+
if (manifest.testing.unitCommand || manifest.testing.e2eCommand) {
|
|
154
|
+
add("pass", "ralph verification gate", "testing commands configured");
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
add("warn", "ralph verification gate", `no testing commands in ${MANIFEST_FILENAME} — \`verify\` fails on an empty contract and Ralph refuses to start`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return { code: checks.some((c) => c.status === "fail") ? 1 : 0, checks };
|
|
161
|
+
}
|
|
162
|
+
const SYMBOL = { pass: "✓", warn: "!", fail: "✗" };
|
|
163
|
+
export function printDoctor(outcome) {
|
|
164
|
+
for (const check of outcome.checks) {
|
|
165
|
+
console.log(` ${SYMBOL[check.status]} ${check.name}${check.message ? ` — ${check.message}` : ""}`);
|
|
166
|
+
}
|
|
167
|
+
const failed = outcome.checks.filter((c) => c.status === "fail").length;
|
|
168
|
+
const warned = outcome.checks.filter((c) => c.status === "warn").length;
|
|
169
|
+
console.log(`\n${failed === 0 ? "Healthy" : "Unhealthy"}: ${outcome.checks.length} checks, ${failed} failed, ${warned} warning(s).`);
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface EjectOptions {
|
|
2
|
+
cwd: string;
|
|
3
|
+
/** A module name or a tracked file path; null with `all` for vendor mode. */
|
|
4
|
+
target: string | null;
|
|
5
|
+
all: boolean;
|
|
6
|
+
dryRun: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface EjectOutcome {
|
|
9
|
+
code: number;
|
|
10
|
+
ejected: string[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Stop managing paths: mark them "ejected" in the lockfile so no future init,
|
|
14
|
+
* sync, or add ever writes them again — not even to recreate them if deleted.
|
|
15
|
+
* `--all` is vendor mode: eject everything Launchrail has ever written here.
|
|
16
|
+
*/
|
|
17
|
+
export declare function runEject(opts: EjectOptions): EjectOutcome;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { sha256 } from "../lib/checksum.js";
|
|
4
|
+
import { writeLockfile } from "../lib/lockfile.js";
|
|
5
|
+
import { loadProject, moduleSpecs } from "../lib/project.js";
|
|
6
|
+
const MANAGED_HEADER = /^<!-- Managed by Launchrail[\s\S]*?-->\n?/;
|
|
7
|
+
const EJECTED_HEADER = "<!-- Ejected from Launchrail management — this file is yours to edit. -->\n";
|
|
8
|
+
/**
|
|
9
|
+
* Stop managing paths: mark them "ejected" in the lockfile so no future init,
|
|
10
|
+
* sync, or add ever writes them again — not even to recreate them if deleted.
|
|
11
|
+
* `--all` is vendor mode: eject everything Launchrail has ever written here.
|
|
12
|
+
*/
|
|
13
|
+
export function runEject(opts) {
|
|
14
|
+
const { state, errors } = loadProject(opts.cwd);
|
|
15
|
+
if (!state) {
|
|
16
|
+
for (const error of errors)
|
|
17
|
+
console.error(`launchrail: ${error}`);
|
|
18
|
+
return { code: 1, ejected: [] };
|
|
19
|
+
}
|
|
20
|
+
if (!opts.all && opts.target === null) {
|
|
21
|
+
console.error("launchrail: usage: launchrail eject <module|file> [--dry-run], or launchrail eject --all");
|
|
22
|
+
return { code: 1, ejected: [] };
|
|
23
|
+
}
|
|
24
|
+
const { lockfile } = state;
|
|
25
|
+
const tracked = (relPath) => lockfile.files[relPath] !== undefined;
|
|
26
|
+
const notEjected = (relPath) => lockfile.files[relPath]?.class !== "ejected";
|
|
27
|
+
const modules = moduleSpecs(state);
|
|
28
|
+
let targets;
|
|
29
|
+
if (opts.all) {
|
|
30
|
+
targets = Object.keys(lockfile.files).filter(notEjected);
|
|
31
|
+
}
|
|
32
|
+
else if (opts.target !== null && modules[opts.target] !== undefined) {
|
|
33
|
+
targets = modules[opts.target].map((spec) => spec.relPath).filter(tracked).filter(notEjected);
|
|
34
|
+
}
|
|
35
|
+
else if (opts.target !== null && tracked(opts.target)) {
|
|
36
|
+
targets = [opts.target].filter(notEjected);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
console.error(`launchrail: "${opts.target}" is neither an enabled module (${Object.keys(modules).join(", ")}) nor a file tracked in the lockfile.`);
|
|
40
|
+
return { code: 1, ejected: [] };
|
|
41
|
+
}
|
|
42
|
+
if (targets.length === 0) {
|
|
43
|
+
console.log("Nothing to eject — the selected path(s) are already ejected.");
|
|
44
|
+
return { code: 0, ejected: [] };
|
|
45
|
+
}
|
|
46
|
+
// The managed do-not-edit header becomes wrong the moment a file is ejected.
|
|
47
|
+
// Rewrite it only when the file is provably unmodified; otherwise the file is
|
|
48
|
+
// already the user's and is left byte-for-byte alone.
|
|
49
|
+
const headerRewrites = new Map();
|
|
50
|
+
for (const relPath of targets) {
|
|
51
|
+
const locked = lockfile.files[relPath];
|
|
52
|
+
const abs = join(opts.cwd, relPath);
|
|
53
|
+
if (locked.class !== "managed" || !existsSync(abs))
|
|
54
|
+
continue;
|
|
55
|
+
const current = readFileSync(abs, "utf8");
|
|
56
|
+
if (sha256(current) === locked.checksum && MANAGED_HEADER.test(current)) {
|
|
57
|
+
headerRewrites.set(relPath, current.replace(MANAGED_HEADER, EJECTED_HEADER));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
for (const relPath of targets) {
|
|
61
|
+
console.log(` eject ${relPath}${headerRewrites.has(relPath) ? " (managed header rewritten)" : ""}`);
|
|
62
|
+
}
|
|
63
|
+
if (opts.dryRun) {
|
|
64
|
+
console.log("\nDry run — nothing was written.");
|
|
65
|
+
return { code: 0, ejected: [] };
|
|
66
|
+
}
|
|
67
|
+
for (const relPath of targets) {
|
|
68
|
+
const rewritten = headerRewrites.get(relPath);
|
|
69
|
+
if (rewritten !== undefined)
|
|
70
|
+
writeFileSync(join(opts.cwd, relPath), rewritten, "utf8");
|
|
71
|
+
lockfile.files[relPath] = {
|
|
72
|
+
class: "ejected",
|
|
73
|
+
checksum: rewritten !== undefined ? sha256(rewritten) : lockfile.files[relPath].checksum,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
writeLockfile(opts.cwd, lockfile);
|
|
77
|
+
console.log(`\nEjected ${targets.length} file(s) — Launchrail will never write them again.`);
|
|
78
|
+
return { code: 0, ejected: targets };
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=eject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eject.js","sourceRoot":"","sources":["../../src/commands/eject.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type SettingsPlan } from "../lib/claudeSettings.js";
|
|
2
|
+
import { type PlannedAction } from "../lib/writer.js";
|
|
3
|
+
export interface InitOptions {
|
|
4
|
+
cwd: string;
|
|
5
|
+
dryRun: boolean;
|
|
6
|
+
yes: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface InitOutcome {
|
|
9
|
+
code: number;
|
|
10
|
+
actions: PlannedAction[];
|
|
11
|
+
settings: SettingsPlan;
|
|
12
|
+
}
|
|
13
|
+
export declare function runInit(opts: InitOptions): Promise<InitOutcome>;
|