@wemuda/launchrail 1.6.0 → 1.8.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/README.md +38 -23
- package/assets/agents-docs/domain.md +59 -0
- package/assets/agents-docs/issue-tracker-github.md +51 -0
- package/assets/agents-docs/issue-tracker-gitlab.md +52 -0
- package/assets/agents-docs/issue-tracker-linear.md +52 -0
- package/assets/agents-docs/issue-tracker-local.md +45 -0
- package/assets/ralph.permission-guard.py +90 -0
- package/assets/ralph.workflow.js +43 -8
- package/assets/skills/NOTICE.md +41 -0
- package/assets/skills/launchrail/launch/SKILL.md +67 -0
- package/assets/skills/launchrail/launch/workflow.md +77 -0
- package/assets/skills/launchrail/launch-browser-smoke/SKILL.md +49 -0
- package/assets/skills/launchrail/launch-code-review/SKILL.md +89 -0
- package/assets/skills/launchrail/launch-design-validation/SKILL.md +44 -0
- package/assets/skills/launchrail/launch-discovery/SKILL.md +33 -0
- package/assets/skills/launchrail/launch-grill/CONTEXT-FORMAT.md +62 -0
- package/assets/skills/launchrail/launch-grill/SKILL.md +48 -0
- package/assets/skills/launchrail/launch-grill/domain-modeling.md +45 -0
- package/assets/skills/launchrail/launch-implement/SKILL.md +46 -0
- package/assets/skills/launchrail/launch-project-alignment/SKILL.md +48 -0
- package/assets/skills/launchrail/launch-ralph/SKILL.md +100 -0
- package/assets/skills/launchrail/launch-ralph-implement/SKILL.md +17 -0
- package/assets/skills/launchrail/launch-research/SKILL.md +16 -0
- package/assets/skills/launchrail/launch-resolving-merge-conflicts/SKILL.md +15 -0
- package/assets/skills/launchrail/launch-spec/SKILL.md +77 -0
- package/assets/skills/launchrail/launch-tickets/SKILL.md +107 -0
- package/assets/skills/launchrail/launch-vision-creation/SKILL.md +60 -0
- package/assets/skills/launchrail/launch-wayfinder/SKILL.md +130 -0
- package/dist/commands/add.js +21 -4
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/doctor.js +42 -32
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts +3 -7
- package/dist/commands/init.js +62 -85
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/sync.js +11 -0
- package/dist/commands/sync.js.map +1 -1
- package/dist/index.js +0 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/agentsDocs.d.ts +4 -0
- package/dist/lib/agentsDocs.js +32 -0
- package/dist/lib/agentsDocs.js.map +1 -0
- package/dist/lib/claudeSettings.d.ts +74 -11
- package/dist/lib/claudeSettings.js +185 -17
- package/dist/lib/claudeSettings.js.map +1 -1
- package/dist/lib/detect.d.ts +0 -2
- package/dist/lib/detect.js +0 -1
- package/dist/lib/detect.js.map +1 -1
- package/dist/lib/manifest.d.ts +14 -1
- package/dist/lib/manifest.js +18 -1
- package/dist/lib/manifest.js.map +1 -1
- package/dist/lib/migrations.js +202 -1
- package/dist/lib/migrations.js.map +1 -1
- package/dist/lib/project.js +9 -1
- package/dist/lib/project.js.map +1 -1
- package/dist/lib/ralph.d.ts +16 -5
- package/dist/lib/ralph.js +32 -9
- package/dist/lib/ralph.js.map +1 -1
- package/dist/lib/seeds.js +4 -2
- package/dist/lib/seeds.js.map +1 -1
- package/dist/lib/skills.d.ts +12 -0
- package/dist/lib/skills.js +57 -0
- package/dist/lib/skills.js.map +1 -0
- package/dist/lib/upstream.d.ts +6 -6
- package/dist/lib/upstream.js +1 -1
- package/dist/lib/upstream.js.map +1 -1
- package/package.json +1 -1
- package/dist/lib/claudeCli.d.ts +0 -51
- package/dist/lib/claudeCli.js +0 -71
- package/dist/lib/claudeCli.js.map +0 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
**An updatable development system for taking a software idea from product intent to a verified release.**
|
|
10
10
|
|
|
11
11
|
[](https://github.com/wemuda/launchrail/actions/workflows/ci.yml)
|
|
12
|
-
[](https://github.com/wemuda/launchrail/releases)
|
|
13
13
|
[](https://github.com/wemuda/launchrail/blob/master/package.json)
|
|
14
14
|
[](https://github.com/wemuda/launchrail/blob/master/pnpm-workspace.yaml)
|
|
15
15
|
[](https://github.com/wemuda/launchrail/blob/master/docs/adr/0002-conventional-commits.md)
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
[How it works](#how-it-works) ·
|
|
19
19
|
[Getting started](https://github.com/wemuda/launchrail/blob/master/docs/getting-started.md) ·
|
|
20
20
|
[Using it](#using-it-in-your-project) ·
|
|
21
|
+
[Updating](#updating-a-project) ·
|
|
21
22
|
[Ownership model](#the-ownership-model) ·
|
|
22
23
|
[Repository layout](#repository-layout) ·
|
|
23
|
-
[Roadmap](https://github.com/wemuda/launchrail/blob/master/ROADMAP.md) ·
|
|
24
24
|
[Contributing](#contributing) ·
|
|
25
25
|
[Credits](#credits)
|
|
26
26
|
|
|
@@ -28,21 +28,19 @@
|
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
This repository is the Launchrail **toolchain**: the CLI, Claude Code plugin, templates, and migrations that initialize other repositories and keep them current. It is not an application framework and it does not replace Claude Code, Claude Design, [Matt Pocock's skills](https://github.com/mattpocock/skills), GitHub, Playwright, or a project's chosen stack. It is the shared rail that connects them.
|
|
31
|
+
This repository is the Launchrail **toolchain**: the CLI, the workflow skills (shipped into projects as files), templates, and migrations that initialize other repositories and keep them current. It is not an application framework and it does not replace Claude Code, Claude Design, GitHub, Playwright, or a project's chosen stack. It is the shared rail that connects them.
|
|
34
32
|
|
|
35
33
|
## How it works
|
|
36
34
|
|
|
37
|
-
Launchrail structures development as two movements. The **foundation** runs once per project: it turns an idea into hard constraints and recorded decisions. Then the **delivery loop** takes over — once per feature: size the work and plan it only as deeply as it needs (a small change goes straight from a grill to tickets; a larger one adds `wayfinder`, a spec, and design validation first), then hand the tickets to the **Ralph loop** to implement and verify before going around again for the next feature. Every stage leaves a committed artifact behind, and the next stage starts from that artifact, not from chat memory.
|
|
35
|
+
Launchrail structures development as two movements. The **foundation** runs once per project: it turns an idea into hard constraints and recorded decisions. Then the **delivery loop** takes over — once per feature: size the work and plan it only as deeply as it needs (a small change goes straight from a grill to tickets; a larger one adds `launch-wayfinder`, a spec, and design validation first), then hand the tickets to the built-in **Ralph loop** to implement and verify before going around again for the next feature. Every stage leaves a committed artifact behind, and the next stage starts from that artifact, not from chat memory. Two commands cover the rail: **`/launch`** plans — it reads the committed artifacts, detects where the project is, and routes to the stage's owner — and **`/launch-implement`** builds, driving ready tickets to verified merges.
|
|
38
36
|
|
|
39
37
|
<p align="center">
|
|
40
|
-
<img src="https://github.com/wemuda/launchrail/raw/master/assets/how-launchrail-works.png" alt="How Launchrail works — the foundation runs once per project (Vision → Visual exploration → Complexity grill → Technical research → Architecture decisions); the delivery loop then repeats once per slice (Specify features into tickets → Ralph loop → Verification) before looping back for the next slice." width="880" />
|
|
38
|
+
<img src="https://github.com/wemuda/launchrail/raw/master/assets/how-launchrail-works.png" alt="How Launchrail works — the foundation runs once per project (Vision → Visual exploration → Discovery research → Complexity grill → Technical research → Architecture decisions); the delivery loop then repeats once per slice (Specify features into tickets → Ralph loop → Verification) before looping back for the next slice." width="880" />
|
|
41
39
|
</p>
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
The skills are Launchrail's own complete, `launch-*` prefixed set ([ADR-0020](https://github.com/wemuda/launchrail/blob/master/docs/adr/0020-independent-skill-set.md)); the methodology of several stages is inspired by [Matt Pocock's skills](https://github.com/mattpocock/skills) — see [Credits](#credits). The full stage contract — inputs, artifacts, composition rules, and per-mode rigor — lives in [the workflow doc](https://github.com/wemuda/launchrail/blob/master/packages/cli/assets/skills/launchrail/launch/workflow.md).
|
|
44
42
|
|
|
45
|
-
What makes projects **updatable** instead of copy-once-and-rot is the second half of the system: shared capabilities are *
|
|
43
|
+
What makes projects **updatable** instead of copy-once-and-rot is the second half of the system: shared capabilities are *shipped as managed files and kept current on `sync`*, shared standards are *synchronized*, product knowledge stays *locally owned*, and reusable lessons are *deliberately promoted upstream*.
|
|
46
44
|
|
|
47
45
|
## Using it in your project
|
|
48
46
|
|
|
@@ -52,14 +50,16 @@ One command sets the rails:
|
|
|
52
50
|
npx @wemuda/launchrail init
|
|
53
51
|
```
|
|
54
52
|
|
|
55
|
-
`init` interviews you (or takes `--yes`), runs `git init` if the directory isn't a repository yet, seeds `AGENTS.md` and ADR conventions without touching existing content,
|
|
53
|
+
`init` interviews you (or takes `--yes`), runs `git init` if the directory isn't a repository yet, seeds `AGENTS.md` and ADR conventions without touching existing content, and writes the workflow's skills into `.claude/skills/` as managed files — so every collaborator, and every session whether cloud or local, gets the same skills with no plugin to install, on any agent that reads the repo ([ADR-0019](https://github.com/wemuda/launchrail/blob/master/docs/adr/0019-vendor-skills-retire-plugin.md)). Adopting an existing project is a first-class path: your files are kept, and a `CLAUDE.md` you already have is additively wired to the workflow imports rather than replaced ([ADR-0012](https://github.com/wemuda/launchrail/blob/master/docs/adr/0012-init-wires-imports-into-existing-claude-md.md)). The interview asks whether the project is new or existing and records it as the manifest's `origin`; for an existing project, `launch` takes an **alignment on-ramp** — inferring a draft vision from the code, interviewing only the gaps, and inventorying your existing design system — instead of starting from a blank vision ([ADR-0013](https://github.com/wemuda/launchrail/blob/master/docs/adr/0013-existing-project-alignment.md)).
|
|
56
54
|
|
|
57
|
-
From there, the day-to-day driver is not the CLI — it's the **`launch` skill** inside Claude Code: open the project and run `/
|
|
55
|
+
From there, the day-to-day driver is not the CLI — it's the **`launch` skill** inside Claude Code: open the project and run `/launch`. Invoke it (or just ask "what's next?") and it reads your committed artifacts, works out where the project is — no vision yet, mid-grill, spec validated, tickets ready — and runs or routes to the next stage's owner. Give it a stage name (`launch design-validation`) to jump straight there. The skills carry the rest of the workflow too:
|
|
58
56
|
|
|
59
|
-
- **`project-alignment`** — the on-ramp for an existing codebase: infer a vision from the code, interview only the gaps, inventory the design system, then join the loop
|
|
60
|
-
- **`vision-creation
|
|
61
|
-
- **`
|
|
62
|
-
- **`
|
|
57
|
+
- **`launch-project-alignment`** — the on-ramp for an existing codebase: infer a vision from the code, interview only the gaps, inventory the design system, then join the loop
|
|
58
|
+
- **`launch-vision-creation`**, **`launch-discovery`**, **`launch-grill`**, **`launch-research`** — vision, then the divergent landscape scan, the grill (the convergent interview that runs both as the foundation's complexity grill and per-feature before speccing, keeping the glossary and ADRs honest as it goes), and primary-source research
|
|
59
|
+
- **`launch-wayfinder`**, **`launch-spec`**, **`launch-tickets`**, **`launch-design-validation`** — break big work into decision maps, synthesize the spec, validate it visually, and cut tracer-bullet tickets with blocking edges
|
|
60
|
+
- **`launch-browser-smoke`** — drives a real browser journey and leaves a traceable evidence bundle (with the browser-testing module)
|
|
61
|
+
- **`launch-implement`** — the one door to building: `/launch-implement` drives ready tickets to verified merges through the Ralph loop (a ticket number builds just that one; "the next 5 of spec #2" scopes and caps a run)
|
|
62
|
+
- **`launch-ralph`**, **`launch-ralph-implement`**, **`launch-code-review`**, **`launch-resolving-merge-conflicts`** — the verification-gated loop engine behind that door, installed by `init`
|
|
63
63
|
|
|
64
64
|
The CLI is the maintenance surface you return to between sessions:
|
|
65
65
|
|
|
@@ -67,7 +67,7 @@ The CLI is the maintenance surface you return to between sessions:
|
|
|
67
67
|
npx @wemuda/launchrail status # versions, drift, pending migrations
|
|
68
68
|
npx @wemuda/launchrail diff # preview upstream changes
|
|
69
69
|
npx @wemuda/launchrail sync # apply managed updates + run migrations
|
|
70
|
-
npx @wemuda/launchrail add browser-testing # enable a module
|
|
70
|
+
npx @wemuda/launchrail add browser-testing # enable a module
|
|
71
71
|
npx @wemuda/launchrail doctor # repository and environment checks
|
|
72
72
|
npx @wemuda/launchrail verify # deterministic verification gate
|
|
73
73
|
npx @wemuda/launchrail smoke # scaffold a browser-smoke evidence bundle
|
|
@@ -76,6 +76,24 @@ npx @wemuda/launchrail eject <module|file> # opt out of management (vendor mod
|
|
|
76
76
|
|
|
77
77
|
Initialized projects carry two files: `.launchrail.yml` (configuration) and `.launchrail-lock.json` (versions, checksums, applied migrations; committed to the repo). Full walkthrough: [docs/getting-started.md](https://github.com/wemuda/launchrail/blob/master/docs/getting-started.md). Committed, unedited example of what `init` produces: [examples/hello-launchrail](https://github.com/wemuda/launchrail/tree/master/examples/hello-launchrail).
|
|
78
78
|
|
|
79
|
+
## Updating a project
|
|
80
|
+
|
|
81
|
+
New Launchrail release out? Two steps, from the project root:
|
|
82
|
+
|
|
83
|
+
**1. Sync the project files** — applies the release's managed files (the workflow skills included) and migrations; your own files are never touched:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx -y @wemuda/launchrail@latest sync
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**2. Commit the result:**
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git add -A && git commit -m "chore: sync launchrail"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Done — the skills update *with* the files in one `sync` (no separate plugin step, and teammates just `git pull`), and the next `/launch` runs with everything the release added (the [CHANGELOG](https://github.com/wemuda/launchrail/blob/master/CHANGELOG.md) says what that is). Keep the `@latest`: `npx` caches, and a stale cached CLI reports "everything up to date" against old templates. Want to see the changes before applying? `npx -y @wemuda/launchrail@latest diff` — read-only, like `status` and `sync --dry-run`. The why and the edge cases — ordering, conflicts, migrations — live in [docs/getting-started.md](https://github.com/wemuda/launchrail/blob/master/docs/getting-started.md#updating-to-a-new-release).
|
|
96
|
+
|
|
79
97
|
## The ownership model
|
|
80
98
|
|
|
81
99
|
Every file Launchrail touches in a consuming project belongs to exactly one class — and no feature is allowed to blur the lines:
|
|
@@ -95,10 +113,7 @@ launchrail/
|
|
|
95
113
|
├── assets/ # Logo and other repo media
|
|
96
114
|
├── packages/
|
|
97
115
|
│ └── cli/ # @wemuda/launchrail — the npx entry point
|
|
98
|
-
|
|
99
|
-
│ └── launchrail/ # Claude Code plugin (skills, commands, agents, hooks)
|
|
100
|
-
├── .claude-plugin/
|
|
101
|
-
│ └── marketplace.json # Claude Code plugin marketplace manifest
|
|
116
|
+
│ └── assets/skills/ # The workflow skills: launchrail/ (the complete launch-* set) + NOTICE.md (attribution)
|
|
102
117
|
├── templates/ # Files seeded into consuming projects (added as built)
|
|
103
118
|
├── examples/
|
|
104
119
|
│ └── hello-launchrail/ # Committed, unedited output of `launchrail init` on a tiny app
|
|
@@ -120,9 +135,9 @@ pnpm build
|
|
|
120
135
|
pnpm --filter @wemuda/launchrail exec launchrail --help
|
|
121
136
|
```
|
|
122
137
|
|
|
123
|
-
##
|
|
138
|
+
## Status
|
|
124
139
|
|
|
125
|
-
|
|
140
|
+
The toolchain is stable and versioned. The full surface — `init`/`doctor`, the workflow skills, browser testing, the Ralph loop, and the sync engine — is covered by the test suite, including integration tests against real temporary Git repositories. Releases are automated: Conventional Commits drive release-please, and the changelog is generated from the commit history ([ADR-0008](https://github.com/wemuda/launchrail/blob/master/docs/adr/0008-release-automation.md), [docs/releasing.md](https://github.com/wemuda/launchrail/blob/master/docs/releasing.md)). The CLI is published to npm as [`@wemuda/launchrail`](https://www.npmjs.com/package/@wemuda/launchrail). Shipped history lives in [CHANGELOG.md](https://github.com/wemuda/launchrail/blob/master/CHANGELOG.md).
|
|
126
141
|
|
|
127
142
|
## Contributing
|
|
128
143
|
|
|
@@ -135,7 +150,7 @@ See [CONTRIBUTING.md](https://github.com/wemuda/launchrail/blob/master/CONTRIBUT
|
|
|
135
150
|
|
|
136
151
|
## Credits
|
|
137
152
|
|
|
138
|
-
Launchrail
|
|
153
|
+
Launchrail's skill set is its own, but much of its methodology traces to [**Matt Pocock**](https://www.mattpocock.com/). The grill, research, wayfinding, spec, ticket, and code-review stages absorb the shape — and, in places, the MIT-licensed text — of his [`skills`](https://github.com/mattpocock/skills) repository ([ADR-0020](https://github.com/wemuda/launchrail/blob/master/docs/adr/0020-independent-skill-set.md)); the derived skills carry the attribution in `NOTICE.md`, and upstream is monitored so improvements there keep informing the rail.
|
|
139
154
|
|
|
140
155
|
If Launchrail is useful to you, the credit belongs upstream first: star [`mattpocock/skills`](https://github.com/mattpocock/skills), watch [Matt's YouTube channel](https://www.youtube.com/@mattpocockuk), follow [@mattpocockuk on X](https://x.com/mattpocockuk), and check out [AI Hero](https://www.aihero.dev/).
|
|
141
156
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Seeded by `launchrail init`. This file is yours — edit it freely; Launchrail never overwrites it.
|
|
3
|
+
Contains text derived from Matt Pocock's skills (MIT): https://github.com/mattpocock/skills
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Domain Docs
|
|
7
|
+
|
|
8
|
+
How the workflow skills should consume this repo's domain documentation when exploring the codebase.
|
|
9
|
+
|
|
10
|
+
## Before exploring, read these
|
|
11
|
+
|
|
12
|
+
- **`CONTEXT.md`** at the repo root, or
|
|
13
|
+
- **`CONTEXT-MAP.md`** at the repo root if it exists — it points at one `CONTEXT.md` per context. Read each one relevant to the topic.
|
|
14
|
+
- **`docs/adr/`** — read ADRs that touch the area you're about to work in. In multi-context repos, also check `src/<context>/docs/adr/` for context-scoped decisions.
|
|
15
|
+
|
|
16
|
+
If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `launch-grill` skill's domain-modeling discipline creates them lazily when terms or decisions actually get resolved.
|
|
17
|
+
|
|
18
|
+
## File structure
|
|
19
|
+
|
|
20
|
+
Single-context repo (most repos):
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
/
|
|
24
|
+
├── CONTEXT.md
|
|
25
|
+
├── docs/adr/
|
|
26
|
+
│ ├── 0000-template.md
|
|
27
|
+
│ ├── 0001-event-sourced-orders.md
|
|
28
|
+
│ └── 0002-postgres-for-write-model.md
|
|
29
|
+
└── src/
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Multi-context repo (presence of `CONTEXT-MAP.md` at the root):
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
/
|
|
36
|
+
├── CONTEXT-MAP.md
|
|
37
|
+
├── docs/adr/ ← system-wide decisions
|
|
38
|
+
└── src/
|
|
39
|
+
├── ordering/
|
|
40
|
+
│ ├── CONTEXT.md
|
|
41
|
+
│ └── docs/adr/ ← context-specific decisions
|
|
42
|
+
└── billing/
|
|
43
|
+
├── CONTEXT.md
|
|
44
|
+
└── docs/adr/
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
ADRs use the project's own format — copy `docs/adr/0000-template.md` and number sequentially (`NNNN-short-slug.md`).
|
|
48
|
+
|
|
49
|
+
## Use the glossary's vocabulary
|
|
50
|
+
|
|
51
|
+
When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.
|
|
52
|
+
|
|
53
|
+
If the concept you need isn't in the glossary yet, that's a signal — either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for the next grill session).
|
|
54
|
+
|
|
55
|
+
## Flag ADR conflicts
|
|
56
|
+
|
|
57
|
+
If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
|
|
58
|
+
|
|
59
|
+
> _Contradicts ADR-0007 (event-sourced orders) — but worth reopening because…_
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Seeded by `launchrail init` from `.launchrail.yml` (issueTracker: github).
|
|
3
|
+
This file is yours — edit it freely; Launchrail never overwrites it.
|
|
4
|
+
Contains text derived from Matt Pocock's skills (MIT): https://github.com/mattpocock/skills
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Issue tracker: GitHub
|
|
8
|
+
|
|
9
|
+
Issues and specs for this repo live as GitHub issues. Use the `gh` CLI for all operations.
|
|
10
|
+
|
|
11
|
+
## Conventions
|
|
12
|
+
|
|
13
|
+
- **Create an issue**: `gh issue create --title "..." --body "..."`. Use a heredoc for multi-line bodies.
|
|
14
|
+
- **Read an issue**: `gh issue view <number> --comments`, filtering comments by `jq` and also fetching labels.
|
|
15
|
+
- **List issues**: `gh issue list --state open --json number,title,body,labels,comments --jq '[.[] | {number, title, body, labels: [.labels[].name], comments: [.comments[].body]}]'` with appropriate `--label` and `--state` filters.
|
|
16
|
+
- **Comment on an issue**: `gh issue comment <number> --body "..."`
|
|
17
|
+
- **Apply / remove labels**: `gh issue edit <number> --add-label "..."` / `--remove-label "..."`
|
|
18
|
+
- **Close**: `gh issue close <number> --comment "..."`
|
|
19
|
+
|
|
20
|
+
Infer the repo from `git remote -v` — `gh` does this automatically when run inside a clone.
|
|
21
|
+
|
|
22
|
+
GitHub shares one number space across issues and PRs, so a bare `#42` may be either — resolve with `gh pr view 42` and fall back to `gh issue view 42`.
|
|
23
|
+
|
|
24
|
+
## Labels
|
|
25
|
+
|
|
26
|
+
The Launchrail workflow's label vocabulary — the skills quote these exact strings:
|
|
27
|
+
|
|
28
|
+
- **`ready-for-agent`** — an implementable ticket the implementation loop may pick up. Only tickets wear it; the loop's frontier is computed from this label alone and cannot tell prose from work.
|
|
29
|
+
- **`needs-info`** — a parked ticket, carrying its failure history; a human unblocks it.
|
|
30
|
+
- **`spec`** — a spec or research note published to the tracker. Never `ready-for-agent`.
|
|
31
|
+
- **`ralph:building`** — claimed by an implementer; removed when its PR merges.
|
|
32
|
+
- **`wayfinder:map`** / **`wayfinder:<type>`** — a wayfinder map and its decision tickets (see below).
|
|
33
|
+
|
|
34
|
+
## When a skill says "publish to the issue tracker"
|
|
35
|
+
|
|
36
|
+
Create a GitHub issue.
|
|
37
|
+
|
|
38
|
+
## When a skill says "fetch the relevant ticket"
|
|
39
|
+
|
|
40
|
+
Run `gh issue view <number> --comments`.
|
|
41
|
+
|
|
42
|
+
## Wayfinding operations
|
|
43
|
+
|
|
44
|
+
Used by `launch-wayfinder`. The **map** is a single issue with **child** issues as tickets.
|
|
45
|
+
|
|
46
|
+
- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `gh issue create --label wayfinder:map`.
|
|
47
|
+
- **Child ticket**: an issue linked to the map as a GitHub sub-issue (`gh api` on the sub-issues endpoint). Where sub-issues aren't enabled, add the child to a task list in the map body and put `Part of #<map>` at the top of the child body. Labels: `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
|
|
48
|
+
- **Blocking**: GitHub's **native issue dependencies** — the canonical, UI-visible representation. Add an edge with `gh api --method POST repos/<owner>/<repo>/issues/<child>/dependencies/blocked_by -F issue_id=<blocker-db-id>`, where `<blocker-db-id>` is the blocker's numeric **database id** (`gh api repos/<owner>/<repo>/issues/<n> --jq .id`, _not_ the `#number` or `node_id`). GitHub reports `issue_dependencies_summary.blocked_by` (open blockers only — the live gate). Where dependencies aren't available, fall back to a `Blocked by: #<n>, #<n>` line at the top of the child body. A ticket is unblocked when every blocker is closed.
|
|
49
|
+
- **Frontier query**: list the map's open children (`gh issue list --state open`, scoped to the map's sub-issues / task list), drop any with an open blocker (`issue_dependencies_summary.blocked_by > 0`, or an open issue in the `Blocked by` line) or an assignee; first in map order wins.
|
|
50
|
+
- **Claim**: `gh issue edit <n> --add-assignee @me` — the session's first write.
|
|
51
|
+
- **Resolve**: `gh issue comment <n> --body "<answer>"`, then `gh issue close <n>`, then append a context pointer (gist + link) to the map's Decisions-so-far.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Seeded by `launchrail init` from `.launchrail.yml` (issueTracker: gitlab).
|
|
3
|
+
This file is yours — edit it freely; Launchrail never overwrites it.
|
|
4
|
+
Contains text derived from Matt Pocock's skills (MIT): https://github.com/mattpocock/skills
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Issue tracker: GitLab
|
|
8
|
+
|
|
9
|
+
Issues and specs for this repo live as GitLab issues. Use the [`glab`](https://gitlab.com/gitlab-org/cli) CLI for all operations.
|
|
10
|
+
|
|
11
|
+
## Conventions
|
|
12
|
+
|
|
13
|
+
- **Create an issue**: `glab issue create --title "..." --description "..."`. Use a heredoc for multi-line descriptions. Pass `--description -` to open an editor.
|
|
14
|
+
- **Read an issue**: `glab issue view <number> --comments`. Use `-F json` for machine-readable output.
|
|
15
|
+
- **List issues**: `glab issue list -F json` with appropriate `--label` filters.
|
|
16
|
+
- **Comment on an issue**: `glab issue note <number> --message "..."`. GitLab calls comments "notes".
|
|
17
|
+
- **Apply / remove labels**: `glab issue update <number> --label "..."` / `--unlabel "..."`. Multiple labels can be comma-separated or by repeating the flag.
|
|
18
|
+
- **Close**: `glab issue close <number>`. `glab issue close` does not accept a closing comment, so post the explanation first with `glab issue note <number> --message "..."`, then close.
|
|
19
|
+
- **Merge requests**: GitLab calls PRs "merge requests". Use `glab mr create`, `glab mr view`, `glab mr note`, etc. — the same shape as `gh pr ...` with `mr` in place of `pr` and `note`/`--message` in place of `comment`/`--body`.
|
|
20
|
+
|
|
21
|
+
Infer the repo from `git remote -v` — `glab` does this automatically when run inside a clone.
|
|
22
|
+
|
|
23
|
+
Unlike GitHub, GitLab numbers issues and MRs separately, so `#42` is unambiguous once you know which surface is meant.
|
|
24
|
+
|
|
25
|
+
## Labels
|
|
26
|
+
|
|
27
|
+
The Launchrail workflow's label vocabulary — the skills quote these exact strings:
|
|
28
|
+
|
|
29
|
+
- **`ready-for-agent`** — an implementable ticket the implementation loop may pick up. Only tickets wear it; the loop's frontier is computed from this label alone and cannot tell prose from work.
|
|
30
|
+
- **`needs-info`** — a parked ticket, carrying its failure history; a human unblocks it.
|
|
31
|
+
- **`spec`** — a spec or research note published to the tracker. Never `ready-for-agent`.
|
|
32
|
+
- **`ralph:building`** — claimed by an implementer; removed when its MR merges.
|
|
33
|
+
- **`wayfinder:map`** / **`wayfinder:<type>`** — a wayfinder map and its decision tickets (see below).
|
|
34
|
+
|
|
35
|
+
## When a skill says "publish to the issue tracker"
|
|
36
|
+
|
|
37
|
+
Create a GitLab issue.
|
|
38
|
+
|
|
39
|
+
## When a skill says "fetch the relevant ticket"
|
|
40
|
+
|
|
41
|
+
Run `glab issue view <number> --comments`.
|
|
42
|
+
|
|
43
|
+
## Wayfinding operations
|
|
44
|
+
|
|
45
|
+
Used by `launch-wayfinder`. The **map** is a single issue with **child** issues as tickets.
|
|
46
|
+
|
|
47
|
+
- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `glab issue create --label wayfinder:map`. (On GitLab tiers with native epics, an epic may hold the map instead; a labelled issue works everywhere.)
|
|
48
|
+
- **Child ticket**: an issue carrying `Part of #<map>` at the top of its description and labels `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
|
|
49
|
+
- **Blocking**: GitLab's **native blocking link** — the canonical, UI-visible representation. Add it with the `/blocked_by #<n>` quick action, posted as a note (`glab issue note <child> --message "/blocked_by #<blocker>"`). Native blocking links are a Premium/Ultimate feature; on the free tier (or where unavailable) fall back to a `Blocked by: #<n>, #<n>` line at the top of the description. A ticket is unblocked when every blocker is closed.
|
|
50
|
+
- **Frontier query**: `glab issue list -F json` scoped to the map's children, drop any with an open blocker — a native `blocked_by` link to an open issue (`glab api projects/:id/issues/:iid/links`), or an open issue in the `Blocked by` line — or an assignee; first in map order wins.
|
|
51
|
+
- **Claim**: `glab issue update <n> --assignee @me` — the session's first write.
|
|
52
|
+
- **Resolve**: `glab issue note <n> --message "<answer>"`, then `glab issue close <n>`, then append a context pointer (gist + link) to the map's Decisions-so-far.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Seeded by `launchrail init` from `.launchrail.yml` (issueTracker: linear).
|
|
3
|
+
This file is yours — edit it freely; Launchrail never overwrites it.
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
# Issue tracker: Linear
|
|
7
|
+
|
|
8
|
+
Issues and specs for this repo live in Linear. Use the **Linear MCP server's tools** when the session has them (tool names like `mcp__Linear__save_issue`, `mcp__Linear__list_issues`); otherwise say so and hand the operation to the user rather than guessing at an API.
|
|
9
|
+
|
|
10
|
+
Fill in the team/project so agents don't have to ask:
|
|
11
|
+
|
|
12
|
+
- **Team**: _(e.g. `ENG` — the team whose backlog this repo's tickets live in)_
|
|
13
|
+
- **Project**: _(optional — a Linear project to attach workflow-created issues to)_
|
|
14
|
+
|
|
15
|
+
## Conventions
|
|
16
|
+
|
|
17
|
+
- **Create an issue**: `save_issue` with title, markdown description, team, and labels.
|
|
18
|
+
- **Read an issue**: `get_issue` (by identifier like `ENG-123`), plus `list_comments` for the discussion.
|
|
19
|
+
- **List issues**: `list_issues` filtered by label and state.
|
|
20
|
+
- **Comment on an issue**: `save_comment`.
|
|
21
|
+
- **Apply / remove labels**: update the issue's labels via `save_issue`; create missing labels with `create_issue_label` once, not per issue.
|
|
22
|
+
- **Close**: set the issue's state to Done via `save_issue`, with a closing comment first.
|
|
23
|
+
- **Issue ↔ PR linkage**: reference the Linear identifier (e.g. `ENG-123`) in the branch name or PR title so Linear's GitHub integration links and auto-closes it; if the integration isn't set up, close the issue explicitly after the merge.
|
|
24
|
+
|
|
25
|
+
## Labels
|
|
26
|
+
|
|
27
|
+
The Launchrail workflow's label vocabulary — the skills quote these exact strings:
|
|
28
|
+
|
|
29
|
+
- **`ready-for-agent`** — an implementable ticket the implementation loop may pick up. Only tickets wear it; the loop's frontier is computed from this label alone and cannot tell prose from work.
|
|
30
|
+
- **`needs-info`** — a parked ticket, carrying its failure history; a human unblocks it.
|
|
31
|
+
- **`spec`** — a spec or research note published to the tracker. Never `ready-for-agent`.
|
|
32
|
+
- **`ralph:building`** — claimed by an implementer; removed when its PR merges.
|
|
33
|
+
- **`wayfinder:map`** / **`wayfinder:<type>`** — a wayfinder map and its decision tickets (see below).
|
|
34
|
+
|
|
35
|
+
## When a skill says "publish to the issue tracker"
|
|
36
|
+
|
|
37
|
+
Create a Linear issue in the team above.
|
|
38
|
+
|
|
39
|
+
## When a skill says "fetch the relevant ticket"
|
|
40
|
+
|
|
41
|
+
Fetch the issue by its identifier (`ENG-123`) including comments.
|
|
42
|
+
|
|
43
|
+
## Wayfinding operations
|
|
44
|
+
|
|
45
|
+
Used by `launch-wayfinder`. The **map** is a single issue with **child** issues as tickets.
|
|
46
|
+
|
|
47
|
+
- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body.
|
|
48
|
+
- **Child ticket**: a **sub-issue** of the map (Linear's native parent/child), labelled `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
|
|
49
|
+
- **Blocking**: Linear's **native "blocked by" relation** — the canonical, UI-visible representation. A ticket is unblocked when every blocking issue is Done.
|
|
50
|
+
- **Frontier query**: list the map's open sub-issues, drop any with an open blocking relation or an assignee; first in map order wins.
|
|
51
|
+
- **Claim**: assign the issue to yourself — the session's first write.
|
|
52
|
+
- **Resolve**: post the answer as a comment, move the issue to Done, then append a context pointer (gist + link) to the map's Decisions-so-far.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Seeded by `launchrail init` from `.launchrail.yml` (issueTracker: local).
|
|
3
|
+
This file is yours — edit it freely; Launchrail never overwrites it.
|
|
4
|
+
Contains text derived from Matt Pocock's skills (MIT): https://github.com/mattpocock/skills
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Issue tracker: Local Markdown
|
|
8
|
+
|
|
9
|
+
Issues and specs for this repo live as markdown files in `.scratch/`.
|
|
10
|
+
|
|
11
|
+
## Conventions
|
|
12
|
+
|
|
13
|
+
- One feature per directory: `.scratch/<feature-slug>/`
|
|
14
|
+
- The spec is committed under `docs/specs/` (the rail's stage-7 artifact); `.scratch/<feature-slug>/spec.md` may hold a working copy
|
|
15
|
+
- Implementation issues are one file per ticket at `.scratch/<feature-slug>/issues/<NN>-<slug>.md`, numbered from `01` — never a single combined tickets file
|
|
16
|
+
- Ticket state is recorded as a `Status:` line near the top of each issue file, using the label vocabulary below
|
|
17
|
+
- Comments and conversation history append to the bottom of the file under a `## Comments` heading
|
|
18
|
+
|
|
19
|
+
## Labels
|
|
20
|
+
|
|
21
|
+
The Launchrail workflow's label vocabulary, written as `Status:` / `Type:` line values — the skills quote these exact strings:
|
|
22
|
+
|
|
23
|
+
- **`ready-for-agent`** — an implementable ticket the implementation loop may pick up. Only tickets wear it.
|
|
24
|
+
- **`needs-info`** — a parked ticket, carrying its failure history; a human unblocks it.
|
|
25
|
+
- **`spec`** — a spec or research note, never an implementable ticket.
|
|
26
|
+
- **`ralph:building`** — claimed by an implementer; cleared when its work merges.
|
|
27
|
+
|
|
28
|
+
## When a skill says "publish to the issue tracker"
|
|
29
|
+
|
|
30
|
+
Create a new file under `.scratch/<feature-slug>/` (creating the directory if needed).
|
|
31
|
+
|
|
32
|
+
## When a skill says "fetch the relevant ticket"
|
|
33
|
+
|
|
34
|
+
Read the file at the referenced path. The user will normally pass the path or the issue number directly.
|
|
35
|
+
|
|
36
|
+
## Wayfinding operations
|
|
37
|
+
|
|
38
|
+
Used by `launch-wayfinder`. The **map** is a file with one **child** file per ticket.
|
|
39
|
+
|
|
40
|
+
- **Map**: `.scratch/<effort>/map.md` — the Notes / Decisions-so-far / Fog body.
|
|
41
|
+
- **Child ticket**: `.scratch/<effort>/issues/NN-<slug>.md`, numbered from `01`, with the question in the body. A `Type:` line records the ticket type (`research`/`prototype`/`grilling`/`task`); a `Status:` line records `claimed`/`resolved`.
|
|
42
|
+
- **Blocking**: a `Blocked by: NN, NN` line near the top. A ticket is unblocked when every file it lists is `resolved`.
|
|
43
|
+
- **Frontier**: scan `.scratch/<effort>/issues/` for files that are open, unblocked, and unclaimed; first by number wins.
|
|
44
|
+
- **Claim**: set `Status: claimed` and save before any work.
|
|
45
|
+
- **Resolve**: append the answer under an `## Answer` heading, set `Status: resolved`, then append a context pointer (gist + link) to the map's Decisions-so-far in `map.md`.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Ralph unattended-launch guard (PreToolUse hook on the `Workflow` tool).
|
|
4
|
+
|
|
5
|
+
Launchrail's Ralph loop — the `ralph` workflow at `.claude/workflows/ralph.js`,
|
|
6
|
+
reached through `/launch-implement` — is meant to be launched and then left alone
|
|
7
|
+
for hours while it drives a ticket backlog to merged code. If it is launched
|
|
8
|
+
while the session is in an INTERACTIVE permission mode, a single benign
|
|
9
|
+
permission prompt (e.g. an MCP or Bash tool call the session has not
|
|
10
|
+
pre-approved) can stall the whole run. Once the session sits idle waiting on a
|
|
11
|
+
human who has walked away, an ephemeral cloud container is reclaimed and the run
|
|
12
|
+
dies mid-ticket — leaving a half-finished ticket and starving everything blocked
|
|
13
|
+
behind it.
|
|
14
|
+
|
|
15
|
+
This hook WARNS (it does not block) when Ralph is launched in a prompting mode,
|
|
16
|
+
so the reminder lands while the user is still at the keyboard and can switch to a
|
|
17
|
+
non-prompting mode (bypass / autonomous) before walking away.
|
|
18
|
+
|
|
19
|
+
It is a pure guard: it NEVER grants a permission. For anything that is not a
|
|
20
|
+
Ralph `Workflow` launch, and for Ralph launches already in a safe mode, it prints
|
|
21
|
+
nothing and exits 0, leaving normal permission handling completely untouched.
|
|
22
|
+
|
|
23
|
+
Managed by Launchrail (`launchrail sync` may replace this file). Contract:
|
|
24
|
+
https://code.claude.com/docs/en/hooks (PreToolUse: reads `permission_mode` /
|
|
25
|
+
`tool_name` / `tool_input`; a `systemMessage` with no `permissionDecision` warns
|
|
26
|
+
the user without changing the decision.)
|
|
27
|
+
"""
|
|
28
|
+
import json
|
|
29
|
+
import sys
|
|
30
|
+
|
|
31
|
+
# Modes that still raise interactive permission prompts. In any of these, a tool
|
|
32
|
+
# call the session has not pre-approved waits on a human — fatal for an
|
|
33
|
+
# unattended multi-hour run. `acceptEdits` only auto-approves file edits, so
|
|
34
|
+
# MCP/Bash calls still prompt; it belongs here too. The non-prompting family
|
|
35
|
+
# (bypassPermissions / auto / dontAsk) is intentionally absent — those are safe.
|
|
36
|
+
INTERACTIVE_MODES = {"default", "plan", "acceptEdits"}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def is_ralph_workflow(tool_name, tool_input):
|
|
40
|
+
"""True only for a launch of the `ralph` workflow (named, inline, or resumed)."""
|
|
41
|
+
if tool_name != "Workflow" or not isinstance(tool_input, dict):
|
|
42
|
+
return False
|
|
43
|
+
if tool_input.get("name") == "ralph":
|
|
44
|
+
return True
|
|
45
|
+
# An inline `script` or a `scriptPath` (resume) that points at the ralph script.
|
|
46
|
+
blob = " ".join(str(tool_input.get(k, "")) for k in ("script", "scriptPath"))
|
|
47
|
+
return "ralph" in blob.lower()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def main():
|
|
51
|
+
try:
|
|
52
|
+
data = json.load(sys.stdin)
|
|
53
|
+
except Exception:
|
|
54
|
+
sys.exit(0) # unparseable input → never interfere
|
|
55
|
+
|
|
56
|
+
mode = data.get("permission_mode", "")
|
|
57
|
+
tool_name = data.get("tool_name", "")
|
|
58
|
+
tool_input = data.get("tool_input", {})
|
|
59
|
+
|
|
60
|
+
if not is_ralph_workflow(tool_name, tool_input):
|
|
61
|
+
sys.exit(0) # not a Ralph launch → normal handling
|
|
62
|
+
if mode not in INTERACTIVE_MODES:
|
|
63
|
+
sys.exit(0) # already unattended-safe → let it run silently
|
|
64
|
+
|
|
65
|
+
warning = (
|
|
66
|
+
f"⚠ Ralph is launching in '{mode}' permission mode, which still raises "
|
|
67
|
+
"interactive permission prompts. If you walk away, this run can stall on a single "
|
|
68
|
+
"benign prompt (e.g. an un-allowlisted MCP or Bash tool call) and the idle container "
|
|
69
|
+
"may be reclaimed mid-ticket, leaving a half-finished ticket. For an unattended run, "
|
|
70
|
+
"switch to a non-prompting mode (bypass / autonomous) before leaving. Proceeding anyway."
|
|
71
|
+
)
|
|
72
|
+
# Warn-but-allow: surface the message to the user and add context for the
|
|
73
|
+
# orchestrating agent, but emit NO `permissionDecision`, so the launch
|
|
74
|
+
# proceeds through normal permission handling.
|
|
75
|
+
print(json.dumps({
|
|
76
|
+
"systemMessage": warning,
|
|
77
|
+
"hookSpecificOutput": {
|
|
78
|
+
"hookEventName": "PreToolUse",
|
|
79
|
+
"additionalContext": (
|
|
80
|
+
f"Ralph was launched in an interactive permission mode ('{mode}'). Remind the "
|
|
81
|
+
"user to switch to a non-prompting mode (bypass/autonomous) if this run is meant "
|
|
82
|
+
"to be unattended, so it cannot stall on a permission prompt."
|
|
83
|
+
),
|
|
84
|
+
},
|
|
85
|
+
}))
|
|
86
|
+
sys.exit(0)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if __name__ == "__main__":
|
|
90
|
+
main()
|