ridgeline 0.7.5 → 0.7.12
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 +105 -14
- package/dist/agents/core/designer.md +18 -0
- package/dist/agents/core/planner.md +32 -0
- package/dist/agents/core/retrospective.md +64 -0
- package/dist/catalog/build-catalog.d.ts +21 -0
- package/dist/catalog/build-catalog.js +305 -0
- package/dist/catalog/build-catalog.js.map +1 -0
- package/dist/catalog/classify.d.ts +16 -0
- package/dist/catalog/classify.js +189 -0
- package/dist/catalog/classify.js.map +1 -0
- package/dist/catalog/extract-metadata.d.ts +41 -0
- package/dist/catalog/extract-metadata.js +175 -0
- package/dist/catalog/extract-metadata.js.map +1 -0
- package/dist/catalog/pack-sprites.d.ts +8 -0
- package/dist/catalog/pack-sprites.js +106 -0
- package/dist/catalog/pack-sprites.js.map +1 -0
- package/dist/catalog/parse-conventions.d.ts +25 -0
- package/dist/catalog/parse-conventions.js +86 -0
- package/dist/catalog/parse-conventions.js.map +1 -0
- package/dist/catalog/resolve-asset-dir.d.ts +15 -0
- package/dist/catalog/resolve-asset-dir.js +96 -0
- package/dist/catalog/resolve-asset-dir.js.map +1 -0
- package/dist/catalog/types.d.ts +74 -0
- package/dist/catalog/types.js +3 -0
- package/dist/catalog/types.js.map +1 -0
- package/dist/catalog/vision-describe.d.ts +12 -0
- package/dist/catalog/vision-describe.js +158 -0
- package/dist/catalog/vision-describe.js.map +1 -0
- package/dist/cli.js +54 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/build.d.ts +2 -2
- package/dist/commands/build.js +156 -30
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/catalog.d.ts +6 -0
- package/dist/commands/catalog.js +141 -0
- package/dist/commands/catalog.js.map +1 -0
- package/dist/commands/design.js +73 -0
- package/dist/commands/design.js.map +1 -1
- package/dist/commands/retrospective.d.ts +7 -0
- package/dist/commands/retrospective.js +119 -0
- package/dist/commands/retrospective.js.map +1 -0
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/engine/discovery/agent.registry.d.ts +3 -0
- package/dist/engine/discovery/agent.registry.js +15 -2
- package/dist/engine/discovery/agent.registry.js.map +1 -1
- package/dist/engine/pipeline/build.exec.d.ts +1 -1
- package/dist/engine/pipeline/build.exec.js +24 -27
- package/dist/engine/pipeline/build.exec.js.map +1 -1
- package/dist/engine/pipeline/ensemble.exec.d.ts +12 -0
- package/dist/engine/pipeline/ensemble.exec.js +123 -30
- package/dist/engine/pipeline/ensemble.exec.js.map +1 -1
- package/dist/engine/pipeline/phase.graph.d.ts +31 -0
- package/dist/engine/pipeline/phase.graph.js +102 -0
- package/dist/engine/pipeline/phase.graph.js.map +1 -0
- package/dist/engine/pipeline/phase.sequence.d.ts +3 -1
- package/dist/engine/pipeline/phase.sequence.js +50 -21
- package/dist/engine/pipeline/phase.sequence.js.map +1 -1
- package/dist/engine/pipeline/pipeline.shared.d.ts +12 -5
- package/dist/engine/pipeline/pipeline.shared.js +50 -26
- package/dist/engine/pipeline/pipeline.shared.js.map +1 -1
- package/dist/engine/pipeline/plan.exec.d.ts +4 -1
- package/dist/engine/pipeline/plan.exec.js +15 -12
- package/dist/engine/pipeline/plan.exec.js.map +1 -1
- package/dist/engine/pipeline/prompt.document.d.ts +28 -0
- package/dist/engine/pipeline/prompt.document.js +50 -0
- package/dist/engine/pipeline/prompt.document.js.map +1 -0
- package/dist/engine/pipeline/refine.exec.js +14 -14
- package/dist/engine/pipeline/refine.exec.js.map +1 -1
- package/dist/engine/pipeline/research.exec.d.ts +0 -2
- package/dist/engine/pipeline/research.exec.js +31 -58
- package/dist/engine/pipeline/research.exec.js.map +1 -1
- package/dist/engine/pipeline/review.exec.d.ts +1 -1
- package/dist/engine/pipeline/review.exec.js +23 -31
- package/dist/engine/pipeline/review.exec.js.map +1 -1
- package/dist/engine/pipeline/specify.exec.js +15 -24
- package/dist/engine/pipeline/specify.exec.js.map +1 -1
- package/dist/engine/pipeline/worktree.parallel.d.ts +22 -0
- package/dist/engine/pipeline/worktree.parallel.js +122 -0
- package/dist/engine/pipeline/worktree.parallel.js.map +1 -0
- package/dist/flavours/web-game/core/builder.md +5 -2
- package/dist/flavours/web-game/core/designer.md +157 -0
- package/dist/git.js +11 -4
- package/dist/git.js.map +1 -1
- package/dist/plugin/visual-tools/skills/agent-browser/SKILL.md +1 -1
- package/dist/plugin/visual-tools/skills/canvas-screenshot/SKILL.md +1 -1
- package/dist/stores/budget.js +21 -17
- package/dist/stores/budget.js.map +1 -1
- package/dist/stores/handoff.d.ts +4 -0
- package/dist/stores/handoff.js +28 -1
- package/dist/stores/handoff.js.map +1 -1
- package/dist/stores/phases.d.ts +8 -0
- package/dist/stores/phases.js +23 -2
- package/dist/stores/phases.js.map +1 -1
- package/dist/stores/settings.d.ts +1 -0
- package/dist/stores/settings.js.map +1 -1
- package/dist/stores/state.d.ts +6 -1
- package/dist/stores/state.js +101 -19
- package/dist/stores/state.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/ui/logger.d.ts +11 -0
- package/dist/ui/logger.js +71 -0
- package/dist/ui/logger.js.map +1 -0
- package/dist/ui/output.d.ts +1 -0
- package/dist/ui/output.js +11 -1
- package/dist/ui/output.js.map +1 -1
- package/dist/utils/file-lock.d.ts +5 -0
- package/dist/utils/file-lock.js +95 -0
- package/dist/utils/file-lock.js.map +1 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -5,35 +5,39 @@
|
|
|
5
5
|
Build harness for long-horizon software execution using AI agents.
|
|
6
6
|
|
|
7
7
|
Ridgeline decomposes large software ideas into phased builds using a
|
|
8
|
-
multi-agent pipeline (shaper, specifier, researcher, refiner, planner,
|
|
9
|
-
reviewer) driven by the Claude CLI. It manages state through git checkpoints,
|
|
8
|
+
multi-agent pipeline (shaper, designer, specifier, researcher, refiner, planner,
|
|
9
|
+
builder, reviewer) driven by the Claude CLI. It manages state through git checkpoints,
|
|
10
10
|
tracks costs, and supports resumable execution when things go wrong.
|
|
11
11
|
|
|
12
12
|
## How it works
|
|
13
13
|
|
|
14
14
|
1. **Shape** -- describe what you want built. The shaper agent analyzes your
|
|
15
15
|
codebase and asks clarifying questions to produce a structured shape document.
|
|
16
|
-
2. **
|
|
16
|
+
2. **Design** (optional) -- the designer agent establishes a visual design system
|
|
17
|
+
(`design.md`) through interactive Q&A. Auto-runs the asset catalog if assets
|
|
18
|
+
exist, and injects catalog context (detected style, palette, resolution) into
|
|
19
|
+
the design conversation. Works at build level or project level.
|
|
20
|
+
3. **Specify** -- an ensemble of three specialist agents (completeness, clarity,
|
|
17
21
|
pragmatism) drafts spec proposals, then a synthesizer merges them into
|
|
18
22
|
`spec.md`, `constraints.md`, and optionally `taste.md`.
|
|
19
|
-
|
|
23
|
+
4. **Research** (optional) -- an ensemble of research specialists (academic,
|
|
20
24
|
ecosystem, competitive) investigates the spec using web sources, then a
|
|
21
25
|
synthesizer merges findings into `research.md`. A gap analysis agenda step
|
|
22
26
|
runs before specialist dispatch to focus research on spec gaps. Findings
|
|
23
27
|
accumulate across iterations rather than being overwritten. A quick
|
|
24
28
|
single-agent mode is also available. See [Research and Refine](docs/research.md).
|
|
25
|
-
|
|
29
|
+
5. **Refine** (optional) -- the refiner agent rewrites `spec.md` incorporating
|
|
26
30
|
research findings and writes `spec.changelog.md` documenting what changed.
|
|
27
31
|
Additive by default -- adds insights without removing user-authored content.
|
|
28
|
-
|
|
32
|
+
6. **Plan** -- an ensemble of three specialist planners (simplicity,
|
|
29
33
|
thoroughness, velocity) proposes phase decompositions, then a synthesizer
|
|
30
34
|
merges them into numbered phase files with acceptance criteria.
|
|
31
|
-
|
|
35
|
+
7. **Build** -- for each phase the builder agent implements the spec inside your
|
|
32
36
|
repo, then creates a git checkpoint.
|
|
33
|
-
|
|
37
|
+
8. **Review** -- the reviewer agent (read-only) checks the output against the
|
|
34
38
|
acceptance criteria and returns a structured verdict. On failure, the harness
|
|
35
39
|
generates a feedback file from the verdict for the builder's next attempt.
|
|
36
|
-
|
|
40
|
+
9. **Retry or advance** -- failed phases are retried up to a configurable limit;
|
|
37
41
|
passing phases hand off context to the next one.
|
|
38
42
|
|
|
39
43
|
## Install
|
|
@@ -42,6 +46,8 @@ tracks costs, and supports resumable execution when things go wrong.
|
|
|
42
46
|
npm install -g ridgeline
|
|
43
47
|
```
|
|
44
48
|
|
|
49
|
+
**Platform:** macOS and Linux. Windows is not supported.
|
|
50
|
+
|
|
45
51
|
Ridgeline requires the [Claude CLI](https://docs.anthropic.com/en/docs/claude-code)
|
|
46
52
|
to be installed and authenticated.
|
|
47
53
|
|
|
@@ -63,13 +69,17 @@ ridgeline my-feature "Build a REST API for task management"
|
|
|
63
69
|
|
|
64
70
|
# Or run each stage individually
|
|
65
71
|
ridgeline shape my-feature "Build a REST API for task management"
|
|
72
|
+
ridgeline design my-feature # optional: establish visual design system
|
|
66
73
|
ridgeline spec my-feature
|
|
67
|
-
ridgeline research my-feature --deep
|
|
68
|
-
ridgeline refine my-feature
|
|
74
|
+
ridgeline research my-feature --deep # optional: enrich spec with web research
|
|
75
|
+
ridgeline refine my-feature # optional: merge research into spec
|
|
69
76
|
ridgeline plan my-feature
|
|
70
77
|
ridgeline dry-run my-feature # preview before committing
|
|
71
78
|
ridgeline build my-feature
|
|
72
79
|
|
|
80
|
+
# Catalog media assets (images, audio, video, text)
|
|
81
|
+
ridgeline catalog my-feature --classify --describe
|
|
82
|
+
|
|
73
83
|
# Resume after a failure (re-run build)
|
|
74
84
|
ridgeline build my-feature
|
|
75
85
|
|
|
@@ -85,8 +95,8 @@ ridgeline clean
|
|
|
85
95
|
### `ridgeline [build-name] [input]` (default)
|
|
86
96
|
|
|
87
97
|
Auto-advances the build through the next incomplete pipeline stage
|
|
88
|
-
(shape → spec → plan → build; research and refine are opt-in).
|
|
89
|
-
flags from the individual commands.
|
|
98
|
+
(shape → spec → plan → build; design, research, and refine are opt-in).
|
|
99
|
+
Accepts all flags from the individual commands.
|
|
90
100
|
|
|
91
101
|
### `ridgeline shape [build-name] [input]`
|
|
92
102
|
|
|
@@ -100,6 +110,20 @@ path to an existing document or a natural language description.
|
|
|
100
110
|
| `--timeout <minutes>` | `10` | Max duration per turn |
|
|
101
111
|
| `--flavour <name-or-path>` | none | Agent flavour: built-in name or path to custom agents |
|
|
102
112
|
|
|
113
|
+
### `ridgeline design [build-name]`
|
|
114
|
+
|
|
115
|
+
Establishes or updates a visual design system through interactive Q&A. Produces
|
|
116
|
+
`design.md` in the build directory (or project-level if no build name is given).
|
|
117
|
+
If an asset directory exists but no catalog has been built, the catalog is
|
|
118
|
+
auto-run and its summary (detected style, palette, resolution, category
|
|
119
|
+
breakdown) is injected into the designer's context.
|
|
120
|
+
|
|
121
|
+
| Flag | Default | Description |
|
|
122
|
+
|------|---------|-------------|
|
|
123
|
+
| `--model <name>` | `opus` | Model for designer agent |
|
|
124
|
+
| `--timeout <minutes>` | `10` | Max duration per turn |
|
|
125
|
+
| `--flavour <name-or-path>` | none | Agent flavour: built-in name or path to custom agents |
|
|
126
|
+
|
|
103
127
|
### `ridgeline spec [build-name]`
|
|
104
128
|
|
|
105
129
|
Runs the specifier ensemble: three specialist agents (completeness, clarity,
|
|
@@ -189,7 +213,70 @@ Resets pipeline state to a given stage and deletes downstream artifacts.
|
|
|
189
213
|
|
|
190
214
|
| Flag | Default | Description |
|
|
191
215
|
|------|---------|-------------|
|
|
192
|
-
| `--to <stage>` | (required) | Stage to rewind to: `shape`, `spec`, `research`, `refine`, or `plan` |
|
|
216
|
+
| `--to <stage>` | (required) | Stage to rewind to: `shape`, `design`, `spec`, `research`, `refine`, or `plan` |
|
|
217
|
+
|
|
218
|
+
### `ridgeline catalog [build-name]`
|
|
219
|
+
|
|
220
|
+
Indexes media assets into `asset-catalog.json` — a structured metadata file that
|
|
221
|
+
feeds into the design and build phases. Supports images, audio, video, and text
|
|
222
|
+
files. The catalog pipeline runs in three tiers:
|
|
223
|
+
|
|
224
|
+
1. **Deterministic metadata** (always runs) — scans the asset directory, extracts
|
|
225
|
+
file metadata (size, hash, dimensions for images), detects spritesheets and
|
|
226
|
+
tileable textures, infers category from directory structure and filename
|
|
227
|
+
conventions (e.g., `characters/knight-walk.png` → category "characters",
|
|
228
|
+
subject "knight", state "walk"). Computes project-wide visual identity
|
|
229
|
+
aggregates (detected style, palette, resolution).
|
|
230
|
+
2. **Classification** (with `--classify`) — assigns categories to uncategorized
|
|
231
|
+
files. Filename heuristics run first (e.g., `bg_*` → backgrounds, `sfx_*` →
|
|
232
|
+
sfx). Files that don't match any pattern fall through to AI classification
|
|
233
|
+
using Claude vision for images or text prompts for other media types.
|
|
234
|
+
3. **Vision enrichment** (with `--describe`) — uses Claude vision to add semantic
|
|
235
|
+
descriptions, facing direction, pose, style tags, and animation type for image
|
|
236
|
+
assets. Layout and UI assets are auto-described regardless of the flag.
|
|
237
|
+
4. **Sprite packing** (with `--pack`) — groups image assets by category and packs
|
|
238
|
+
them into 2048×2048 sprite atlases with PixiJS-compatible JSON metadata.
|
|
239
|
+
Backgrounds and layout references are excluded.
|
|
240
|
+
|
|
241
|
+
The catalog is incremental — unchanged files (by content hash) are skipped on
|
|
242
|
+
subsequent runs unless `--force` is set.
|
|
243
|
+
|
|
244
|
+
| Flag | Default | Description |
|
|
245
|
+
|------|---------|-------------|
|
|
246
|
+
| `--asset-dir <path>` | auto | Path to asset directory |
|
|
247
|
+
| `--classify` | off | AI-classify uncategorized files into categories |
|
|
248
|
+
| `--describe` | off | Add vision-based descriptions for all image assets |
|
|
249
|
+
| `--pack` | off | Generate sprite atlases after cataloging |
|
|
250
|
+
| `--batch` | off | Batch multiple images per vision call |
|
|
251
|
+
| `--force` | off | Re-process all assets ignoring content hash |
|
|
252
|
+
| `--model <name>` | `opus` | Model for vision and classification |
|
|
253
|
+
| `--timeout <minutes>` | `5` | Max duration per AI call |
|
|
254
|
+
|
|
255
|
+
Asset directory is resolved in order: `--asset-dir` flag,
|
|
256
|
+
`.ridgeline/builds/<build-name>/assets/`, `.ridgeline/assets/`, or the
|
|
257
|
+
`assetDir` field in `settings.json`.
|
|
258
|
+
|
|
259
|
+
### `ridgeline retrospective [build-name]`
|
|
260
|
+
|
|
261
|
+
Analyzes a completed build and extracts learnings for future builds. Reads the
|
|
262
|
+
trajectory log, budget, state, and any feedback files, then appends structured
|
|
263
|
+
insights to `.ridgeline/learnings.md`. Future builds automatically pick up these
|
|
264
|
+
learnings if the file exists.
|
|
265
|
+
|
|
266
|
+
| Flag | Default | Description |
|
|
267
|
+
|------|---------|-------------|
|
|
268
|
+
| `--model <name>` | `opus` | Model for retrospective agent |
|
|
269
|
+
| `--timeout <minutes>` | `10` | Max duration |
|
|
270
|
+
| `--flavour <name-or-path>` | none | Agent flavour: built-in name or path to custom agents |
|
|
271
|
+
|
|
272
|
+
### `ridgeline check`
|
|
273
|
+
|
|
274
|
+
Checks recommended tools and prerequisites for a flavour. Reports which
|
|
275
|
+
external tools are available and which are missing.
|
|
276
|
+
|
|
277
|
+
| Flag | Default | Description |
|
|
278
|
+
|------|---------|-------------|
|
|
279
|
+
| `--flavour <name-or-path>` | from settings | Agent flavour to check |
|
|
193
280
|
|
|
194
281
|
### `ridgeline clean`
|
|
195
282
|
|
|
@@ -201,15 +288,19 @@ WIP branches. Use this after inspecting a failed build.
|
|
|
201
288
|
```text
|
|
202
289
|
.ridgeline/
|
|
203
290
|
├── settings.json # Optional project-level config (network allowlist, etc.)
|
|
291
|
+
├── design.md # Optional project-level visual design system
|
|
292
|
+
├── learnings.md # Optional accumulated build learnings (from retrospective)
|
|
204
293
|
├── worktrees/ # Git worktrees for active builds
|
|
205
294
|
│ └── <build-name>/ # Isolated working directory per build
|
|
206
295
|
└── builds/<build-name>/
|
|
207
296
|
├── shape.md # Structured project context (from shaper)
|
|
297
|
+
├── design.md # Optional visual design system (from designer)
|
|
208
298
|
├── spec.md # What to build
|
|
209
299
|
├── constraints.md # Technical constraints and check commands
|
|
210
300
|
├── taste.md # Optional coding style preferences
|
|
211
301
|
├── research.md # Optional research findings (from researcher)
|
|
212
302
|
├── spec.changelog.md # Optional changelog of spec refinements
|
|
303
|
+
├── asset-catalog.json # Optional indexed media assets (from catalog)
|
|
213
304
|
├── phases/
|
|
214
305
|
│ ├── 01-scaffold.md
|
|
215
306
|
│ ├── 01-scaffold.feedback.md # Generated by harness on review failure
|
|
@@ -53,12 +53,22 @@ Round 1 — Art Direction:
|
|
|
53
53
|
- Art style: pixel art, vector, 3D, hand-drawn, realistic
|
|
54
54
|
- Color palette: mood, saturation level, palette constraints
|
|
55
55
|
- Asset dimensions: sprite sizes, texture resolutions, canvas size
|
|
56
|
+
- Shape language: proportions (chunky/slim), corners (rounded/sharp), detail level
|
|
57
|
+
- Rendering: pixel scale, scaling mode (nearest/bilinear), canvas size
|
|
56
58
|
|
|
57
59
|
Round 2 — UI & HUD:
|
|
58
60
|
|
|
59
61
|
- HUD/overlay style: transparency, position, font choices
|
|
60
62
|
- Menu design: navigation patterns, transition styles
|
|
61
63
|
- In-game text: dialogue boxes, tooltips, damage numbers
|
|
64
|
+
- Layout regions: where health, score, inventory, and action buttons go
|
|
65
|
+
- Mood: overall atmosphere and tone in a brief phrase
|
|
66
|
+
|
|
67
|
+
Round 3 — Asset Integration (when asset catalog data is in context):
|
|
68
|
+
|
|
69
|
+
- Asset manifest review: confirm discovered assets match creative intent
|
|
70
|
+
- Background treatment: mood, parallax, scroll behavior for each background
|
|
71
|
+
- Asset loading strategy: preload vs lazy, atlas format, base path
|
|
62
72
|
|
|
63
73
|
**For print-layout projects:**
|
|
64
74
|
|
|
@@ -129,3 +139,11 @@ The format is flexible — brand guidelines, informal notes, formal style guides
|
|
|
129
139
|
**Respect existing design.md.** If one exists, read it as starting context. Offer to refine or extend, don't start from scratch unless asked.
|
|
130
140
|
|
|
131
141
|
**Stay in design territory.** Don't ask about code architecture, error handling, or implementation details. Those belong to the shaper and specifier.
|
|
142
|
+
|
|
143
|
+
**When asset catalog data is present in context:**
|
|
144
|
+
|
|
145
|
+
- Propose palette, style, resolution, and scaling defaults derived from the catalog's visual identity analysis. Include these as `suggestedAnswer` values.
|
|
146
|
+
- Present the asset manifest summary for user confirmation.
|
|
147
|
+
- Use layout region data (if available) to propose HUD/menu arrangements.
|
|
148
|
+
- Flag any catalog warnings about palette mismatches for user review.
|
|
149
|
+
- Cover asset loading strategy (preload/lazy, format, base path) in your questions.
|
|
@@ -65,6 +65,38 @@ Every phase file must follow this structure exactly:
|
|
|
65
65
|
<Relevant sections of spec.md for this phase, quoted or summarized.>
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
+
## Phase Dependencies (Parallel Execution)
|
|
69
|
+
|
|
70
|
+
Phases can declare dependencies to enable parallel execution. When a phase depends only on a subset of prior phases (not the immediately preceding one), add YAML frontmatter:
|
|
71
|
+
|
|
72
|
+
```markdown
|
|
73
|
+
---
|
|
74
|
+
depends_on: [01-scaffold]
|
|
75
|
+
---
|
|
76
|
+
# Phase 3: API Endpoints
|
|
77
|
+
...
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Rules for dependencies:**
|
|
81
|
+
|
|
82
|
+
- Phases without frontmatter automatically depend on the immediately preceding phase (sequential execution).
|
|
83
|
+
- A phase can only depend on phases with a lower index number.
|
|
84
|
+
- If a phase reads or modifies files created by another phase, it must depend on that phase.
|
|
85
|
+
- Phase 01 never has dependencies (it is the root).
|
|
86
|
+
- Use dependencies to enable parallelism when phases work on independent parts of the codebase.
|
|
87
|
+
- When in doubt, omit the frontmatter. False parallelism is worse than false sequentiality.
|
|
88
|
+
|
|
89
|
+
**Example: fan-out pattern**
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
01-scaffold (no deps — root)
|
|
93
|
+
02-api depends_on: [01-scaffold]
|
|
94
|
+
03-ui depends_on: [01-scaffold]
|
|
95
|
+
04-integration depends_on: [02-api, 03-ui]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Phases 02 and 03 run in parallel after 01 completes. Phase 04 waits for both.
|
|
99
|
+
|
|
68
100
|
## Rules
|
|
69
101
|
|
|
70
102
|
**No implementation details.** Do not specify creation order, internal structure, sub-agent assignments, implementation patterns, or approach. The builder decides all of this. You describe the destination, not the route.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: retrospective
|
|
3
|
+
description: Analyzes a completed build to extract learnings, patterns, and recommendations for future builds
|
|
4
|
+
model: opus
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a build retrospective analyst. After a build completes, you analyze the trajectory, budget, feedback files, and final state to extract actionable learnings.
|
|
8
|
+
|
|
9
|
+
## Your inputs
|
|
10
|
+
|
|
11
|
+
These are injected into your context:
|
|
12
|
+
|
|
13
|
+
1. **trajectory.jsonl** — chronological event log of the entire build (plan, build, review, retry events with durations and costs)
|
|
14
|
+
2. **budget.json** — per-phase, per-role cost breakdown
|
|
15
|
+
3. **Feedback files** — reviewer verdicts and feedback from any retried phases
|
|
16
|
+
4. **state.json** — final build state with phase statuses, durations, and retry counts
|
|
17
|
+
|
|
18
|
+
## Your process
|
|
19
|
+
|
|
20
|
+
### 1. Analyze the build trajectory
|
|
21
|
+
|
|
22
|
+
- Which phases completed cleanly on the first attempt?
|
|
23
|
+
- Which phases required retries? What were the reviewer's objections?
|
|
24
|
+
- Where was the most time and money spent?
|
|
25
|
+
- Were there any patterns in failures (e.g., the same type of issue recurring)?
|
|
26
|
+
|
|
27
|
+
### 2. Extract learnings
|
|
28
|
+
|
|
29
|
+
Produce a structured retrospective in the following format. Be specific — name files, patterns, and concrete observations. Avoid generic advice.
|
|
30
|
+
|
|
31
|
+
```markdown
|
|
32
|
+
## Build: {build-name} ({date})
|
|
33
|
+
|
|
34
|
+
### What Worked
|
|
35
|
+
- Specific things that went well (clean passes, efficient phases)
|
|
36
|
+
|
|
37
|
+
### What Didn't
|
|
38
|
+
- Specific failures, retries, and their root causes
|
|
39
|
+
|
|
40
|
+
### Patterns to Repeat
|
|
41
|
+
- Concrete patterns worth carrying forward (spec structures, constraint phrasings, phase granularity choices)
|
|
42
|
+
|
|
43
|
+
### Patterns to Avoid
|
|
44
|
+
- Anti-patterns observed (overly broad phases, missing constraints, spec ambiguities)
|
|
45
|
+
|
|
46
|
+
### Cost Analysis
|
|
47
|
+
- Total cost and duration
|
|
48
|
+
- Most expensive phases and why
|
|
49
|
+
- Efficiency observations
|
|
50
|
+
|
|
51
|
+
### Recommendations for Next Build
|
|
52
|
+
- Specific, actionable suggestions for improving spec, constraints, or phase structure
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 3. Write the output
|
|
56
|
+
|
|
57
|
+
Append your retrospective to the learnings file. Do NOT overwrite previous entries — each build's learnings accumulate.
|
|
58
|
+
|
|
59
|
+
## Rules
|
|
60
|
+
|
|
61
|
+
- Be concrete and specific, not generic. "Phase 03 failed because the spec didn't mention auth middleware" is useful. "Consider being more specific in specs" is not.
|
|
62
|
+
- Focus on what the build artifacts reveal, not hypotheticals.
|
|
63
|
+
- Keep each section to 3-5 bullet points. Quality over quantity.
|
|
64
|
+
- If the build completed cleanly with no retries, say so — a clean build is still worth noting.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AssetCatalog, CatalogOptions } from "./types";
|
|
2
|
+
export type CatalogResult = {
|
|
3
|
+
catalog: AssetCatalog;
|
|
4
|
+
stats: {
|
|
5
|
+
total: number;
|
|
6
|
+
added: number;
|
|
7
|
+
updated: number;
|
|
8
|
+
unchanged: number;
|
|
9
|
+
pruned: number;
|
|
10
|
+
classified: number;
|
|
11
|
+
};
|
|
12
|
+
/** Files in auto-describe categories that need vision enrichment. */
|
|
13
|
+
needsVisionDescribe: string[];
|
|
14
|
+
};
|
|
15
|
+
type BuildCatalogOpts = Pick<CatalogOptions, "isForce" | "isClassify" | "model" | "timeout">;
|
|
16
|
+
/**
|
|
17
|
+
* Build or update the asset catalog.
|
|
18
|
+
* Handles all media types. Vision enrichment and sprite packing are handled separately.
|
|
19
|
+
*/
|
|
20
|
+
export declare const buildCatalog: (assetDir: string, buildDir: string, opts: BuildCatalogOpts) => Promise<CatalogResult>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.buildCatalog = void 0;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const parse_conventions_1 = require("./parse-conventions");
|
|
40
|
+
const extract_metadata_1 = require("./extract-metadata");
|
|
41
|
+
const classify_1 = require("./classify");
|
|
42
|
+
/** Map file extensions to media types. */
|
|
43
|
+
const MEDIA_EXTENSIONS = {
|
|
44
|
+
// images
|
|
45
|
+
".png": "image", ".jpg": "image", ".jpeg": "image",
|
|
46
|
+
".gif": "image", ".webp": "image", ".svg": "image", ".avif": "image",
|
|
47
|
+
// audio
|
|
48
|
+
".mp3": "audio", ".wav": "audio", ".ogg": "audio",
|
|
49
|
+
".flac": "audio", ".aac": "audio", ".m4a": "audio",
|
|
50
|
+
// video
|
|
51
|
+
".mp4": "video", ".webm": "video", ".mov": "video", ".avi": "video",
|
|
52
|
+
// text
|
|
53
|
+
".txt": "text", ".json": "text", ".csv": "text",
|
|
54
|
+
".md": "text", ".yaml": "text", ".yml": "text",
|
|
55
|
+
};
|
|
56
|
+
/** Detect media type from file extension. */
|
|
57
|
+
const detectMediaType = (filePath) => MEDIA_EXTENSIONS[path.extname(filePath).toLowerCase()] ?? null;
|
|
58
|
+
/** Recursively walk a directory tree and return all asset file paths (relative to root). */
|
|
59
|
+
const walkAssets = (dir, root) => {
|
|
60
|
+
const base = root ?? dir;
|
|
61
|
+
const results = [];
|
|
62
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
63
|
+
const fullPath = path.join(dir, entry.name);
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
results.push(...walkAssets(fullPath, base));
|
|
66
|
+
}
|
|
67
|
+
else if (detectMediaType(entry.name) !== null) {
|
|
68
|
+
results.push(path.relative(base, fullPath));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return results;
|
|
72
|
+
};
|
|
73
|
+
/** Load existing catalog from disk, if present. */
|
|
74
|
+
const loadExistingCatalog = (catalogPath) => {
|
|
75
|
+
if (!fs.existsSync(catalogPath))
|
|
76
|
+
return null;
|
|
77
|
+
try {
|
|
78
|
+
return JSON.parse(fs.readFileSync(catalogPath, "utf-8"));
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
/** Build a lookup map from an existing catalog for incremental updates. */
|
|
85
|
+
const buildHashIndex = (catalog) => {
|
|
86
|
+
const index = new Map();
|
|
87
|
+
if (!catalog)
|
|
88
|
+
return index;
|
|
89
|
+
for (const entry of catalog.assets) {
|
|
90
|
+
index.set(entry.file, entry);
|
|
91
|
+
}
|
|
92
|
+
return index;
|
|
93
|
+
};
|
|
94
|
+
/** Detect the most common resolution among square or nearly-square image assets. */
|
|
95
|
+
const detectResolution = (assets) => {
|
|
96
|
+
const sizes = new Map();
|
|
97
|
+
for (const a of assets) {
|
|
98
|
+
if (a.mediaType !== "image")
|
|
99
|
+
continue;
|
|
100
|
+
if (a.isSpritesheet && a.frameSize) {
|
|
101
|
+
const key = `${a.frameSize.w}x${a.frameSize.h}`;
|
|
102
|
+
sizes.set(key, (sizes.get(key) ?? 0) + 1);
|
|
103
|
+
}
|
|
104
|
+
else if (a.width && a.height && a.width === a.height && a.width <= 256) {
|
|
105
|
+
const key = `${a.width}x${a.height}`;
|
|
106
|
+
sizes.set(key, (sizes.get(key) ?? 0) + 1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (sizes.size === 0)
|
|
110
|
+
return null;
|
|
111
|
+
return [...sizes.entries()].sort((a, b) => b[1] - a[1])[0][0];
|
|
112
|
+
};
|
|
113
|
+
/** Build aggregate palette from all image assets (top 8 most frequent colours). */
|
|
114
|
+
const detectPalette = (assets) => {
|
|
115
|
+
const freq = new Map();
|
|
116
|
+
for (const a of assets) {
|
|
117
|
+
if (!a.palette)
|
|
118
|
+
continue;
|
|
119
|
+
for (const c of a.palette) {
|
|
120
|
+
freq.set(c, (freq.get(c) ?? 0) + 1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return [...freq.entries()]
|
|
124
|
+
.sort((a, b) => b[1] - a[1])
|
|
125
|
+
.slice(0, 8)
|
|
126
|
+
.map(([colour]) => colour);
|
|
127
|
+
};
|
|
128
|
+
/** Detect visual style from image asset properties. */
|
|
129
|
+
const detectStyle = (assets) => {
|
|
130
|
+
const imageAssets = assets.filter((a) => a.mediaType === "image");
|
|
131
|
+
if (imageAssets.length === 0)
|
|
132
|
+
return null;
|
|
133
|
+
const smallAssets = imageAssets.filter((a) => {
|
|
134
|
+
const size = a.isSpritesheet && a.frameSize
|
|
135
|
+
? Math.max(a.frameSize.w, a.frameSize.h)
|
|
136
|
+
: Math.max(a.width ?? 0, a.height ?? 0);
|
|
137
|
+
return size <= 128;
|
|
138
|
+
});
|
|
139
|
+
if (smallAssets.length > imageAssets.length * 0.6)
|
|
140
|
+
return "pixel-art";
|
|
141
|
+
const svgCount = imageAssets.filter((a) => a.format === "svg").length;
|
|
142
|
+
if (svgCount > imageAssets.length * 0.5)
|
|
143
|
+
return "vector";
|
|
144
|
+
return null;
|
|
145
|
+
};
|
|
146
|
+
/** Derive aggregate visual identity from all cataloged assets. */
|
|
147
|
+
const deriveVisualIdentity = (assets) => {
|
|
148
|
+
const style = detectStyle(assets);
|
|
149
|
+
return {
|
|
150
|
+
detectedStyle: style,
|
|
151
|
+
detectedPalette: detectPalette(assets),
|
|
152
|
+
detectedResolution: detectResolution(assets),
|
|
153
|
+
detectedScaling: style === "pixel-art" ? "nearest" : null,
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
/** Compare detected palette against design.md palette and produce warnings. */
|
|
157
|
+
const checkPaletteMismatches = (assets, buildDir, ridgelineDir) => {
|
|
158
|
+
const warnings = [];
|
|
159
|
+
const designPaths = [
|
|
160
|
+
path.join(buildDir, "design.md"),
|
|
161
|
+
path.join(ridgelineDir, "design.md"),
|
|
162
|
+
];
|
|
163
|
+
let designContent = null;
|
|
164
|
+
for (const p of designPaths) {
|
|
165
|
+
if (fs.existsSync(p)) {
|
|
166
|
+
designContent = fs.readFileSync(p, "utf-8");
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (!designContent)
|
|
171
|
+
return warnings;
|
|
172
|
+
const hexPattern = /#[0-9a-fA-F]{6}/g;
|
|
173
|
+
const designColours = new Set([...designContent.matchAll(hexPattern)].map((m) => m[0].toLowerCase()));
|
|
174
|
+
if (designColours.size === 0)
|
|
175
|
+
return warnings;
|
|
176
|
+
for (const asset of assets) {
|
|
177
|
+
if (!asset.palette)
|
|
178
|
+
continue;
|
|
179
|
+
const offPalette = asset.palette.filter((c) => !designColours.has(c.toLowerCase()));
|
|
180
|
+
if (offPalette.length > 0) {
|
|
181
|
+
warnings.push(`${asset.file} uses colours (${offPalette.join(", ")}) not found in design.md palette. This may be intentional.`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return warnings;
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Build or update the asset catalog.
|
|
188
|
+
* Handles all media types. Vision enrichment and sprite packing are handled separately.
|
|
189
|
+
*/
|
|
190
|
+
const buildCatalog = async (assetDir, buildDir, opts) => {
|
|
191
|
+
const ridgelineDir = path.join(process.cwd(), ".ridgeline");
|
|
192
|
+
const catalogPath = path.join(buildDir, "asset-catalog.json");
|
|
193
|
+
const existing = loadExistingCatalog(catalogPath);
|
|
194
|
+
const hashIndex = buildHashIndex(existing);
|
|
195
|
+
const assetFiles = walkAssets(assetDir);
|
|
196
|
+
const existingFiles = new Set(hashIndex.keys());
|
|
197
|
+
const assets = [];
|
|
198
|
+
const needsVisionDescribe = [];
|
|
199
|
+
let added = 0;
|
|
200
|
+
let updated = 0;
|
|
201
|
+
let unchanged = 0;
|
|
202
|
+
let classified = 0;
|
|
203
|
+
const timeoutMs = opts.timeout * 60 * 1000;
|
|
204
|
+
const totalFiles = assetFiles.length;
|
|
205
|
+
let processedCount = 0;
|
|
206
|
+
for (const relPath of assetFiles) {
|
|
207
|
+
const absPath = path.join(assetDir, relPath);
|
|
208
|
+
const hash = (0, extract_metadata_1.computeContentHash)(absPath);
|
|
209
|
+
const prev = hashIndex.get(relPath);
|
|
210
|
+
existingFiles.delete(relPath);
|
|
211
|
+
// Skip unchanged files (unless --force)
|
|
212
|
+
if (!opts.isForce && prev && prev.hash === hash) {
|
|
213
|
+
assets.push(prev);
|
|
214
|
+
unchanged++;
|
|
215
|
+
// Still track if auto-describe category needs vision
|
|
216
|
+
if (prev.mediaType === "image") {
|
|
217
|
+
const conv = (0, parse_conventions_1.parseConventions)(relPath);
|
|
218
|
+
if (parse_conventions_1.AUTO_DESCRIBE_CATEGORIES.has(conv.category) && !prev.description) {
|
|
219
|
+
needsVisionDescribe.push(relPath);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
processedCount++;
|
|
225
|
+
const mediaType = detectMediaType(relPath);
|
|
226
|
+
const conventions = (0, parse_conventions_1.parseConventions)(relPath);
|
|
227
|
+
const basic = (0, extract_metadata_1.extractBasicMetadata)(absPath);
|
|
228
|
+
// Build the entry — image-specific metadata only for images
|
|
229
|
+
const entry = {
|
|
230
|
+
file: relPath,
|
|
231
|
+
hash,
|
|
232
|
+
mediaType,
|
|
233
|
+
category: conventions.category,
|
|
234
|
+
name: conventions.name,
|
|
235
|
+
subject: conventions.subject,
|
|
236
|
+
state: conventions.state,
|
|
237
|
+
fileSizeBytes: basic.fileSizeBytes,
|
|
238
|
+
extension: basic.extension,
|
|
239
|
+
};
|
|
240
|
+
if (mediaType === "image") {
|
|
241
|
+
const meta = await (0, extract_metadata_1.extractImageMetadata)(absPath);
|
|
242
|
+
const palette = await (0, extract_metadata_1.extractPalette)(absPath);
|
|
243
|
+
const spritesheet = (0, extract_metadata_1.detectSpritesheet)(meta.width, meta.height);
|
|
244
|
+
const isTileable = await (0, extract_metadata_1.detectTileable)(absPath, meta.width, meta.height);
|
|
245
|
+
const defaults = (0, parse_conventions_1.inferDefaults)(conventions.category);
|
|
246
|
+
entry.width = meta.width;
|
|
247
|
+
entry.height = meta.height;
|
|
248
|
+
entry.format = meta.format;
|
|
249
|
+
entry.hasAlpha = meta.hasAlpha;
|
|
250
|
+
entry.channels = meta.channels;
|
|
251
|
+
entry.dominantColour = palette.dominantColour;
|
|
252
|
+
entry.palette = palette.palette;
|
|
253
|
+
entry.isSpritesheet = spritesheet.isSpritesheet;
|
|
254
|
+
entry.frameCount = spritesheet.frameCount;
|
|
255
|
+
entry.frameSize = spritesheet.frameSize;
|
|
256
|
+
entry.frameDirection = spritesheet.frameDirection;
|
|
257
|
+
entry.suggestedAnchor = defaults.anchor;
|
|
258
|
+
entry.suggestedZLayer = defaults.zLayer;
|
|
259
|
+
entry.isTileable = isTileable;
|
|
260
|
+
}
|
|
261
|
+
// Classify uncategorized files when --classify is set
|
|
262
|
+
if (conventions.category === "uncategorized" && opts.isClassify) {
|
|
263
|
+
process.stderr.write(`\x1b[90m Classifying ${relPath} (${processedCount}/${totalFiles})...\x1b[0m\n`);
|
|
264
|
+
const result = (0, classify_1.classifyByHeuristics)(relPath, basic.extension, mediaType)
|
|
265
|
+
?? (0, classify_1.classifyWithAI)(absPath, relPath, basic.extension, mediaType, opts.model, timeoutMs);
|
|
266
|
+
entry.category = result.category;
|
|
267
|
+
entry.isClassified = true;
|
|
268
|
+
entry.classificationConfidence = result.confidence;
|
|
269
|
+
classified++;
|
|
270
|
+
// Update anchor/zLayer defaults based on new category
|
|
271
|
+
if (mediaType === "image") {
|
|
272
|
+
const defaults = (0, parse_conventions_1.inferDefaults)(entry.category);
|
|
273
|
+
entry.suggestedAnchor = defaults.anchor;
|
|
274
|
+
entry.suggestedZLayer = defaults.zLayer;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
assets.push(entry);
|
|
278
|
+
if (mediaType === "image" && parse_conventions_1.AUTO_DESCRIBE_CATEGORIES.has(entry.category)) {
|
|
279
|
+
needsVisionDescribe.push(relPath);
|
|
280
|
+
}
|
|
281
|
+
if (prev) {
|
|
282
|
+
updated++;
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
added++;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Remaining entries in existingFiles are pruned (files removed from disk)
|
|
289
|
+
const pruned = existingFiles.size;
|
|
290
|
+
const catalog = {
|
|
291
|
+
generatedAt: new Date().toISOString(),
|
|
292
|
+
assetDir,
|
|
293
|
+
isDescribed: existing?.isDescribed ?? false,
|
|
294
|
+
visualIdentity: deriveVisualIdentity(assets),
|
|
295
|
+
warnings: checkPaletteMismatches(assets, buildDir, ridgelineDir),
|
|
296
|
+
assets,
|
|
297
|
+
};
|
|
298
|
+
return {
|
|
299
|
+
catalog,
|
|
300
|
+
stats: { total: assets.length, added, updated, unchanged, pruned, classified },
|
|
301
|
+
needsVisionDescribe,
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
exports.buildCatalog = buildCatalog;
|
|
305
|
+
//# sourceMappingURL=build-catalog.js.map
|