@cloverleaf/reference-impl 0.6.1 → 0.6.3
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 +7 -1
- package/dist/prep-worktree.mjs +1 -0
- package/dist/state.mjs +97 -0
- package/install.sh +24 -3
- package/lib/prep-worktree.ts +1 -0
- package/package.json +2 -2
- package/prompts/documenter.md +8 -0
- package/prompts/implementer.md +8 -0
- package/prompts/qa.md +8 -0
- package/prompts/reviewer.md +8 -0
- package/prompts/ui-reviewer.md +8 -0
- package/skills/cloverleaf-run/SKILL.md +3 -1
- package/skills/cloverleaf-run-plan/SKILL.md +24 -1
package/README.md
CHANGED
|
@@ -96,10 +96,16 @@ The installer (`install.sh`) automatically runs the Playwright browser install s
|
|
|
96
96
|
|
|
97
97
|
npx playwright install chromium webkit firefox
|
|
98
98
|
|
|
99
|
-
On **Linux**, webkit additionally requires system-level dependencies:
|
|
99
|
+
On **Linux**, webkit additionally requires system-level dependencies. By default `install.sh` installs webkit deps only:
|
|
100
100
|
|
|
101
101
|
npx playwright install-deps webkit
|
|
102
102
|
|
|
103
|
+
To also install firefox system deps (enabling the full chromium + webkit + firefox browser matrix for the UI Reviewer), pass `--with-cross-browser` to the installer:
|
|
104
|
+
|
|
105
|
+
./install.sh --with-cross-browser
|
|
106
|
+
|
|
107
|
+
This runs `npx playwright install-deps webkit firefox` instead. When the flag is omitted, `install.sh` prints a note reminding you how to enable cross-browser support later.
|
|
108
|
+
|
|
103
109
|
**Disk footprint:** approximately 600–650 MB total across all three browsers in the default `PLAYWRIGHT_BROWSERS_PATH` location (`~/.cache/ms-playwright/`).
|
|
104
110
|
|
|
105
111
|
| Browser | Approx. size |
|
package/dist/prep-worktree.mjs
CHANGED
|
@@ -115,6 +115,7 @@ export function prepWorktree(mainRoot, worktreePath) {
|
|
|
115
115
|
// EEXIST on vite/node_modules/.bin on second invocation).
|
|
116
116
|
primeCopy(mainStandardNm, wtStandardNm);
|
|
117
117
|
primeCopy(mainRefImplNm, wtRefImplNm);
|
|
118
|
+
primeCopy(join(resolvedMain, 'reference-impl', 'dist'), join(worktreePath, 'reference-impl', 'dist'));
|
|
118
119
|
execSync('npm run build', {
|
|
119
120
|
cwd: join(worktreePath, 'standard'),
|
|
120
121
|
stdio: 'pipe',
|
package/dist/state.mjs
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { tasksDir, projectsDir } from './paths.mjs';
|
|
6
|
+
import { emitStatusTransition, formatReason } from './events.mjs';
|
|
7
|
+
// Import validator from @cloverleaf/standard.
|
|
8
|
+
// The standard package ships TypeScript source only with no exports map.
|
|
9
|
+
// Vitest (via vite-node) resolves .js → .ts for workspace symlinked packages,
|
|
10
|
+
// so the .js convention works here. If it ever fails with "module not found",
|
|
11
|
+
// switch the specifier to '@cloverleaf/standard/validators/index.ts'.
|
|
12
|
+
import { validateStatusTransitionLegality } from '@cloverleaf/standard/validators/index.js';
|
|
13
|
+
import { validateOrThrow } from './validate.mjs';
|
|
14
|
+
const req = createRequire(import.meta.url);
|
|
15
|
+
export function loadTask(repoRoot, taskId) {
|
|
16
|
+
const path = join(tasksDir(repoRoot), `${taskId}.json`);
|
|
17
|
+
if (!existsSync(path))
|
|
18
|
+
throw new Error(`Task ${taskId} not found at ${path}`);
|
|
19
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
20
|
+
}
|
|
21
|
+
export function saveTask(repoRoot, task) {
|
|
22
|
+
validateOrThrow('https://cloverleaf.example/schemas/task.schema.json', task);
|
|
23
|
+
const path = join(tasksDir(repoRoot), `${task.id}.json`);
|
|
24
|
+
writeFileSync(path, JSON.stringify(task, null, 2) + '\n');
|
|
25
|
+
}
|
|
26
|
+
export function loadProject(repoRoot, projectId) {
|
|
27
|
+
const path = join(projectsDir(repoRoot), `${projectId}.json`);
|
|
28
|
+
if (!existsSync(path))
|
|
29
|
+
throw new Error(`Project ${projectId} not found at ${path}`);
|
|
30
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
31
|
+
}
|
|
32
|
+
function loadTaskStateMachine() {
|
|
33
|
+
// state-machines/task.json is a static JSON asset. Navigate from standard's
|
|
34
|
+
// package.json — no exports map support needed.
|
|
35
|
+
const pkgPath = req.resolve('@cloverleaf/standard/package.json');
|
|
36
|
+
const pkgDir = pkgPath.replace(/\/package\.json$/, '');
|
|
37
|
+
return JSON.parse(readFileSync(`${pkgDir}/state-machines/task.json`, 'utf-8'));
|
|
38
|
+
}
|
|
39
|
+
export function advanceStatus(repoRoot, taskId, toStatus, actor, options = {}) {
|
|
40
|
+
const task = loadTask(repoRoot, taskId);
|
|
41
|
+
const from = task.status;
|
|
42
|
+
const sm = loadTaskStateMachine();
|
|
43
|
+
// Read risk_class directly from the task (defaulting to 'low' if absent).
|
|
44
|
+
// The validator derives itemPath from workItem.risk_class: low → fast_lane, else full_pipeline.
|
|
45
|
+
// If caller passed options.path, translate it back to risk_class for the validator.
|
|
46
|
+
const riskClass = options.path === 'fast_lane' ? 'low'
|
|
47
|
+
: options.path === 'full_pipeline' ? 'high'
|
|
48
|
+
: (task.risk_class ?? 'low');
|
|
49
|
+
// Build a minimal Task-shaped object so the validator can resolve path-tagged transitions.
|
|
50
|
+
const workItemForValidator = {
|
|
51
|
+
type: 'task',
|
|
52
|
+
id: task.id,
|
|
53
|
+
project: task.project,
|
|
54
|
+
status: task.status,
|
|
55
|
+
risk_class: riskClass,
|
|
56
|
+
context: { rfc: { project: task.project, id: task.id } },
|
|
57
|
+
definition_of_done: task.definition_of_done,
|
|
58
|
+
acceptance_criteria: task.acceptance_criteria,
|
|
59
|
+
};
|
|
60
|
+
const reason = formatReason({ gate: options.gate, path: options.path });
|
|
61
|
+
const event = {
|
|
62
|
+
event_id: randomUUID(),
|
|
63
|
+
event_type: 'status_transition',
|
|
64
|
+
occurred_at: new Date().toISOString(),
|
|
65
|
+
work_item_id: { project: task.project, id: task.id },
|
|
66
|
+
work_item_type: 'task',
|
|
67
|
+
from_status: from,
|
|
68
|
+
to_status: toStatus,
|
|
69
|
+
actor: { kind: actor, id: actor },
|
|
70
|
+
...(reason ? { reason } : {}),
|
|
71
|
+
};
|
|
72
|
+
const result = validateStatusTransitionLegality(event, sm, workItemForValidator);
|
|
73
|
+
if (!result.ok) {
|
|
74
|
+
const msgs = result.violations.map((v) => v.message).join('; ');
|
|
75
|
+
throw new Error(`Illegal transition ${from} → ${toStatus}: ${msgs}`);
|
|
76
|
+
}
|
|
77
|
+
// NEW: emit first, save second. validateStatusTransitionLegality stays above.
|
|
78
|
+
const emittedPath = emitStatusTransition(repoRoot, {
|
|
79
|
+
project: task.project,
|
|
80
|
+
workItemType: 'task',
|
|
81
|
+
workItemId: task.id,
|
|
82
|
+
from,
|
|
83
|
+
to: toStatus,
|
|
84
|
+
actor,
|
|
85
|
+
gate: options.gate,
|
|
86
|
+
path: options.path,
|
|
87
|
+
});
|
|
88
|
+
const proposed = { ...task, status: toStatus };
|
|
89
|
+
try {
|
|
90
|
+
saveTask(repoRoot, proposed);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
const inner = err instanceof Error ? err.message : String(err);
|
|
94
|
+
throw new Error(`orphan event written to ${emittedPath} but task save failed: ${inner}`);
|
|
95
|
+
}
|
|
96
|
+
return proposed;
|
|
97
|
+
}
|
package/install.sh
CHANGED
|
@@ -7,10 +7,23 @@ set -euo pipefail
|
|
|
7
7
|
# `claude plugin` CLI. Point Claude Code at the cloverleaf repo root (where
|
|
8
8
|
# .claude-plugin/marketplace.json lives), then install the plugin from the
|
|
9
9
|
# resulting marketplace.
|
|
10
|
+
#
|
|
11
|
+
# Flags:
|
|
12
|
+
# --with-cross-browser On Linux, also install firefox system deps (in
|
|
13
|
+
# addition to webkit) so the UI Reviewer can run the
|
|
14
|
+
# full chromium + webkit + firefox browser matrix.
|
|
10
15
|
|
|
11
16
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
17
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
13
18
|
|
|
19
|
+
# Parse flags from "$@".
|
|
20
|
+
WITH_CROSS_BROWSER=0
|
|
21
|
+
for arg in "$@"; do
|
|
22
|
+
case "$arg" in
|
|
23
|
+
--with-cross-browser) WITH_CROSS_BROWSER=1 ;;
|
|
24
|
+
esac
|
|
25
|
+
done
|
|
26
|
+
|
|
14
27
|
if ! command -v claude >/dev/null 2>&1; then
|
|
15
28
|
echo "error: 'claude' CLI not found on PATH."
|
|
16
29
|
echo "Install Claude Code first: https://docs.claude.com/claude-code"
|
|
@@ -50,11 +63,19 @@ echo ""
|
|
|
50
63
|
|
|
51
64
|
npx playwright install chromium webkit firefox
|
|
52
65
|
|
|
53
|
-
# On Linux, system deps
|
|
66
|
+
# On Linux, system deps are also required.
|
|
54
67
|
if [ "$(uname -s)" = "Linux" ]; then
|
|
55
68
|
echo ""
|
|
56
|
-
|
|
57
|
-
|
|
69
|
+
if [ "$WITH_CROSS_BROWSER" = "1" ]; then
|
|
70
|
+
echo "Linux detected — installing webkit + firefox system dependencies (--with-cross-browser)..."
|
|
71
|
+
npx playwright install-deps webkit firefox
|
|
72
|
+
else
|
|
73
|
+
echo "Linux detected — installing webkit system dependencies..."
|
|
74
|
+
npx playwright install-deps webkit
|
|
75
|
+
echo ""
|
|
76
|
+
echo "Note: to also install firefox system dependencies for full cross-browser"
|
|
77
|
+
echo " UI review support, re-run with: ./install.sh --with-cross-browser"
|
|
78
|
+
fi
|
|
58
79
|
fi
|
|
59
80
|
|
|
60
81
|
echo ""
|
package/lib/prep-worktree.ts
CHANGED
|
@@ -128,6 +128,7 @@ export function prepWorktree(mainRoot: string, worktreePath: string): void {
|
|
|
128
128
|
// EEXIST on vite/node_modules/.bin on second invocation).
|
|
129
129
|
primeCopy(mainStandardNm, wtStandardNm);
|
|
130
130
|
primeCopy(mainRefImplNm, wtRefImplNm);
|
|
131
|
+
primeCopy(join(resolvedMain, 'reference-impl', 'dist'), join(worktreePath, 'reference-impl', 'dist'));
|
|
131
132
|
|
|
132
133
|
execSync('npm run build', {
|
|
133
134
|
cwd: join(worktreePath, 'standard'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloverleaf/reference-impl",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "Reference implementation of the Cloverleaf methodology as Claude Code skills. Implements the Tight Loop (Implementer + Reviewer).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"typecheck": "tsc --noEmit",
|
|
46
46
|
"build": "tsc -p tsconfig.build.json && node scripts/rename-to-mjs.mjs",
|
|
47
47
|
"acceptance:walker": "bash scripts/acceptance-walker.sh",
|
|
48
|
-
"prepublishOnly": "npm test && npm run build"
|
|
48
|
+
"prepublishOnly": "node scripts/check-standard-prepped.mjs && npm test && npm run build"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@cloverleaf/standard": "^0.4.0",
|
package/prompts/documenter.md
CHANGED
|
@@ -10,6 +10,14 @@ You are the Cloverleaf Documenter. Your job: produce doc-only commits that updat
|
|
|
10
10
|
- **Repo root**: {{repo_root}}
|
|
11
11
|
- **Diff from base**: {{diff}}
|
|
12
12
|
|
|
13
|
+
## Pre-flight — ensure correct working directory
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd "$(git rev-parse --show-toplevel)"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Run this as the first executable step before anything else. Session B sessions may inherit an arbitrary `cwd` from the walker harness; this anchors you at the repo root.
|
|
20
|
+
|
|
13
21
|
## Tool constraints
|
|
14
22
|
|
|
15
23
|
- Use `git worktree add <temp> {{branch}}` to work on an isolated checkout. Do NOT `git checkout` in the main working directory.
|
package/prompts/implementer.md
CHANGED
|
@@ -11,6 +11,14 @@ You are the Cloverleaf Implementer agent. Your job: take a Task and produce work
|
|
|
11
11
|
|
|
12
12
|
## Your process
|
|
13
13
|
|
|
14
|
+
0. **Pre-flight — ensure correct working directory.**
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
cd "$(git rev-parse --show-toplevel)"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Run this as the first executable step before anything else. Session B sessions may inherit an arbitrary `cwd` from the walker harness; this anchors you at the repo root.
|
|
21
|
+
|
|
14
22
|
1. Read the task's `title`, `acceptance_criteria`, `definition_of_done`, and `context`. Read any referenced files.
|
|
15
23
|
2. If `feedback` is present, re-read each finding; plan how to address them.
|
|
16
24
|
3. Create a new branch named `cloverleaf/<task.id>` from `base_branch` using `git checkout -b cloverleaf/<task.id>`.
|
package/prompts/qa.md
CHANGED
|
@@ -17,6 +17,14 @@ The Standard's QA contract requires a `preview_uri`. You were passed the sentine
|
|
|
17
17
|
|
|
18
18
|
## Runtime procedure
|
|
19
19
|
|
|
20
|
+
0. **Pre-flight — ensure correct working directory.**
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cd "$(git rev-parse --show-toplevel)"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Run this as the first executable step before anything else. Session B sessions may inherit an arbitrary `cwd` from the walker harness; this anchors you at the repo root.
|
|
27
|
+
|
|
20
28
|
1. Set up isolated worktree and prepare its node_modules + standard/dist. The `prep-worktree`
|
|
21
29
|
helper copies main's `standard/node_modules` and `reference-impl/node_modules` into the
|
|
22
30
|
worktree and runs the standard build script so the @cloverleaf/standard symlink resolves
|
package/prompts/reviewer.md
CHANGED
|
@@ -11,6 +11,14 @@ You are the Cloverleaf Reviewer agent. Your job: perform a fresh-eyes review of
|
|
|
11
11
|
|
|
12
12
|
## Your process
|
|
13
13
|
|
|
14
|
+
0. **Pre-flight — ensure correct working directory.**
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
cd "$(git rev-parse --show-toplevel)"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Run this as the first executable step before anything else. Session B sessions may inherit an arbitrary `cwd` from the walker harness; this anchors you at the repo root.
|
|
21
|
+
|
|
14
22
|
1. Read the task's `acceptance_criteria` and `definition_of_done`.
|
|
15
23
|
2. Run `git diff <base_branch>..<branch> --stat` and `git diff <base_branch>..<branch>` to see the change.
|
|
16
24
|
3. For each acceptance criterion, determine whether the diff satisfies it. Note any unsatisfied criteria as findings.
|
package/prompts/ui-reviewer.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
You are the Cloverleaf UI Reviewer. Your job: review a task's UI changes at multiple viewports and browser engines for accessibility violations (axe-core) and visual regressions (pixelmatch) using headless Playwright browsers. You are read-only for source code and tests — but you DO write baseline/diff artifacts under `.cloverleaf/` on the feature branch.
|
|
4
4
|
|
|
5
|
+
## Pre-flight — ensure correct working directory
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd "$(git rev-parse --show-toplevel)"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Run this as the first executable step before anything else. Session B sessions may inherit an arbitrary `cwd` from the walker harness; this anchors you at the repo root.
|
|
12
|
+
|
|
5
13
|
## Input
|
|
6
14
|
|
|
7
15
|
- **Task**: {{task}}
|
|
@@ -7,7 +7,9 @@ description: End-to-end orchestrator. Reads task.risk_class to dispatch fast lan
|
|
|
7
7
|
|
|
8
8
|
## Branch discipline
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
`<repo_root>` is `$(git rev-parse --show-toplevel)` of your current working directory. In walker context (Session B inside `$WORKTREE_ROOT`), this is the worktree path, NOT the primary repo. Pass `<repo_root>` explicitly to every `cloverleaf-cli` invocation, and run `git add .cloverleaf/ && git commit` from `<repo_root>` so state-advance commits land on the worktree's current branch (`cloverleaf/<TASK-ID>` in walker mode).
|
|
11
|
+
|
|
12
|
+
Do NOT `git checkout main` from a walker worktree — main is held by the primary repo. To compare against main, use `git diff main..HEAD` or `git show main:<path>`. Sub-skills run from the worktree's current branch and stay on it; the walker (in the primary repo) does the final merge to main itself after all tasks reach final-gate.
|
|
11
13
|
|
|
12
14
|
## Per-agent bounce budget
|
|
13
15
|
|
|
@@ -178,15 +178,38 @@ description: Autonomous DAG walker for Cloverleaf Plans. Given a PLAN-ID in stat
|
|
|
178
178
|
|
|
179
179
|
If every task in the plan's `task_dag.nodes` has `state: "merged"`, print: "✓ Plan `<PLAN-ID>` complete."
|
|
180
180
|
|
|
181
|
+
## Next steps (release publishing)
|
|
182
|
+
|
|
183
|
+
Once all tasks are merged, run the following commands in order to tag and publish the release:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
git tag -a reference-impl-v<VERSION> -m "reference-impl v<VERSION>"
|
|
187
|
+
git push origin main
|
|
188
|
+
git push origin reference-impl-v<VERSION>
|
|
189
|
+
(cd reference-impl && npm publish --access public)
|
|
190
|
+
gh release create reference-impl-v<VERSION> --title "reference-impl v<VERSION>" --notes-from-tag
|
|
191
|
+
```
|
|
192
|
+
|
|
181
193
|
## Session brief template
|
|
182
194
|
|
|
183
195
|
The walker constructs a per-task `scenario_brief` roughly like:
|
|
184
196
|
|
|
185
197
|
```
|
|
186
198
|
You are driving <TASK-ID> Delivery via /cloverleaf-run inside a dedicated
|
|
187
|
-
git worktree at
|
|
199
|
+
git worktree rooted at $WORKTREE_ROOT. The worktree is checked out to branch
|
|
188
200
|
cloverleaf/<TASK-ID> (already created from main). Task risk_class: <class>.
|
|
189
201
|
|
|
202
|
+
**Pre-flight: before any task steps, run `cd "$WORKTREE_ROOT"` to ensure your
|
|
203
|
+
working directory is the worktree root, not whatever directory the session
|
|
204
|
+
inherited.**
|
|
205
|
+
|
|
206
|
+
**DO NOT run `git checkout main` from this worktree.** The `main` branch is
|
|
207
|
+
held by the primary repo — attempting to check it out here will fail because
|
|
208
|
+
the same branch cannot be checked out in two worktrees simultaneously. To
|
|
209
|
+
compare your work against main, use `git diff main..HEAD` (safe diff) or
|
|
210
|
+
`git show main:<path>` (safe file inspection). All state-advance commits must
|
|
211
|
+
stay on the worktree's current branch (`cloverleaf/<TASK-ID>`).
|
|
212
|
+
|
|
190
213
|
Plan: invoke `/cloverleaf-run <TASK-ID>`.
|
|
191
214
|
|
|
192
215
|
**DO NOT invoke `/cloverleaf-merge`**. Fast lane stops after `/cloverleaf-review`
|