@cloverleaf/reference-impl 0.5.0 → 0.5.1
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/.claude-plugin/plugin.json +1 -1
- package/VERSION +1 -1
- package/dist/plan.mjs +1 -0
- package/dist/rfc.mjs +2 -1
- package/dist/spike.mjs +2 -1
- package/dist/task.mjs +2 -1
- package/lib/plan.ts +1 -0
- package/lib/rfc.ts +2 -1
- package/lib/spike.ts +2 -1
- package/lib/task.ts +2 -1
- package/package.json +1 -1
- package/skills/cloverleaf-discover/SKILL.md +11 -10
- package/skills/cloverleaf-new-rfc/SKILL.md +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloverleaf",
|
|
3
3
|
"description": "Cloverleaf reference implementation — Claude Code skills for task scaffolding and the Delivery pipeline (implementer, documenter, reviewer, UI reviewer with multi-viewport visual diff, QA, merge).",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Renato D'Arrigo",
|
|
7
7
|
"email": "renato.darrigo@gmail.com"
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.5.1
|
package/dist/plan.mjs
CHANGED
|
@@ -11,6 +11,7 @@ export function loadPlan(repoRoot, id) {
|
|
|
11
11
|
}
|
|
12
12
|
export function savePlan(repoRoot, plan) {
|
|
13
13
|
validateOrThrow('https://cloverleaf.example/schemas/plan.schema.json', plan);
|
|
14
|
+
mkdirSync(plansDir(repoRoot), { recursive: true });
|
|
14
15
|
const path = join(plansDir(repoRoot), `${plan.id}.json`);
|
|
15
16
|
writeFileSync(path, JSON.stringify(plan, null, 2) + '\n');
|
|
16
17
|
}
|
package/dist/rfc.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { rfcsDir } from './paths.mjs';
|
|
4
4
|
import { validateOrThrow } from './validate.mjs';
|
|
@@ -11,6 +11,7 @@ export function loadRfc(repoRoot, id) {
|
|
|
11
11
|
}
|
|
12
12
|
export function saveRfc(repoRoot, rfc) {
|
|
13
13
|
validateOrThrow('https://cloverleaf.example/schemas/rfc.schema.json', rfc);
|
|
14
|
+
mkdirSync(rfcsDir(repoRoot), { recursive: true });
|
|
14
15
|
const path = join(rfcsDir(repoRoot), `${rfc.id}.json`);
|
|
15
16
|
writeFileSync(path, JSON.stringify(rfc, null, 2) + '\n');
|
|
16
17
|
}
|
package/dist/spike.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { spikesDir } from './paths.mjs';
|
|
4
4
|
import { validateOrThrow } from './validate.mjs';
|
|
@@ -11,6 +11,7 @@ export function loadSpike(repoRoot, id) {
|
|
|
11
11
|
}
|
|
12
12
|
export function saveSpike(repoRoot, spike) {
|
|
13
13
|
validateOrThrow('https://cloverleaf.example/schemas/spike.schema.json', spike);
|
|
14
|
+
mkdirSync(spikesDir(repoRoot), { recursive: true });
|
|
14
15
|
const path = join(spikesDir(repoRoot), `${spike.id}.json`);
|
|
15
16
|
writeFileSync(path, JSON.stringify(spike, null, 2) + '\n');
|
|
16
17
|
}
|
package/dist/task.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { tasksDir, projectsDir } from './paths.mjs';
|
|
4
4
|
import { validateOrThrow } from './validate.mjs';
|
|
@@ -11,6 +11,7 @@ export function loadTask(repoRoot, taskId) {
|
|
|
11
11
|
}
|
|
12
12
|
export function saveTask(repoRoot, task) {
|
|
13
13
|
validateOrThrow('https://cloverleaf.example/schemas/task.schema.json', task);
|
|
14
|
+
mkdirSync(tasksDir(repoRoot), { recursive: true });
|
|
14
15
|
const path = join(tasksDir(repoRoot), `${task.id}.json`);
|
|
15
16
|
writeFileSync(path, JSON.stringify(task, null, 2) + '\n');
|
|
16
17
|
}
|
package/lib/plan.ts
CHANGED
|
@@ -35,6 +35,7 @@ export function loadPlan(repoRoot: string, id: string): PlanDoc {
|
|
|
35
35
|
|
|
36
36
|
export function savePlan(repoRoot: string, plan: PlanDoc): void {
|
|
37
37
|
validateOrThrow('https://cloverleaf.example/schemas/plan.schema.json', plan);
|
|
38
|
+
mkdirSync(plansDir(repoRoot), { recursive: true });
|
|
38
39
|
const path = join(plansDir(repoRoot), `${plan.id}.json`);
|
|
39
40
|
writeFileSync(path, JSON.stringify(plan, null, 2) + '\n');
|
|
40
41
|
}
|
package/lib/rfc.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { rfcsDir } from './paths.js';
|
|
4
4
|
import { validateOrThrow } from './validate.js';
|
|
@@ -27,6 +27,7 @@ export function loadRfc(repoRoot: string, id: string): RfcDoc {
|
|
|
27
27
|
|
|
28
28
|
export function saveRfc(repoRoot: string, rfc: RfcDoc): void {
|
|
29
29
|
validateOrThrow('https://cloverleaf.example/schemas/rfc.schema.json', rfc);
|
|
30
|
+
mkdirSync(rfcsDir(repoRoot), { recursive: true });
|
|
30
31
|
const path = join(rfcsDir(repoRoot), `${rfc.id}.json`);
|
|
31
32
|
writeFileSync(path, JSON.stringify(rfc, null, 2) + '\n');
|
|
32
33
|
}
|
package/lib/spike.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { spikesDir } from './paths.js';
|
|
4
4
|
import { validateOrThrow } from './validate.js';
|
|
@@ -27,6 +27,7 @@ export function loadSpike(repoRoot: string, id: string): SpikeDoc {
|
|
|
27
27
|
|
|
28
28
|
export function saveSpike(repoRoot: string, spike: SpikeDoc): void {
|
|
29
29
|
validateOrThrow('https://cloverleaf.example/schemas/spike.schema.json', spike);
|
|
30
|
+
mkdirSync(spikesDir(repoRoot), { recursive: true });
|
|
30
31
|
const path = join(spikesDir(repoRoot), `${spike.id}.json`);
|
|
31
32
|
writeFileSync(path, JSON.stringify(spike, null, 2) + '\n');
|
|
32
33
|
}
|
package/lib/task.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { tasksDir, projectsDir } from './paths.js';
|
|
4
4
|
import type { Task as SMTask } from '@cloverleaf/standard/validators/index.js';
|
|
@@ -33,6 +33,7 @@ export function loadTask(repoRoot: string, taskId: string): TaskDoc {
|
|
|
33
33
|
|
|
34
34
|
export function saveTask(repoRoot: string, task: TaskDoc): void {
|
|
35
35
|
validateOrThrow('https://cloverleaf.example/schemas/task.schema.json', task);
|
|
36
|
+
mkdirSync(tasksDir(repoRoot), { recursive: true });
|
|
36
37
|
const path = join(tasksDir(repoRoot), `${task.id}.json`);
|
|
37
38
|
writeFileSync(path, JSON.stringify(task, null, 2) + '\n');
|
|
38
39
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloverleaf/reference-impl",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
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",
|
|
@@ -32,14 +32,14 @@ Each sub-skill runs from `main`. Between steps, confirm branch is `main` before
|
|
|
32
32
|
[ "$(git rev-parse --abbrev-ref HEAD)" = "main" ] || { echo "Run /cloverleaf-discover from main" >&2; exit 1; }
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
3. **Create the RFC** —
|
|
35
|
+
3. **Create the RFC** — invoke `/cloverleaf-new-rfc $BRIEF_FILE`. Capture the printed RFC ID as `$RFC_ID`.
|
|
36
36
|
|
|
37
37
|
Initialise revise loop counter:
|
|
38
38
|
```
|
|
39
39
|
revise_loops = 0
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
4. **Draft RFC (Researcher draftRfc)** —
|
|
42
|
+
4. **Draft RFC (Researcher draftRfc)** — invoke `/cloverleaf-draft-rfc $RFC_ID`. Researcher runs with a per-invocation bounce budget (3 bounces inside draft-rfc).
|
|
43
43
|
- On bounce exhaustion (draft-rfc exits non-zero): dump state to `.cloverleaf/runs/$RFC_ID/discover-crash.json` and halt.
|
|
44
44
|
- Reload the RFC. Its new status is either `spike-in-flight` (unknowns non-empty) or `planning` (unknowns empty).
|
|
45
45
|
|
|
@@ -48,7 +48,7 @@ Each sub-skill runs from `main`. Between steps, confirm branch is `main` before
|
|
|
48
48
|
- If `spike-in-flight`: run every pending spike linked to this RFC:
|
|
49
49
|
```bash
|
|
50
50
|
for each spike in .cloverleaf/spikes/*.json where parent_rfc.id === $RFC_ID AND status === "pending":
|
|
51
|
-
|
|
51
|
+
invoke /cloverleaf-spike <SPIKE_ID>.
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
If `/cloverleaf-spike` exits non-zero or leaves the spike in a non-`completed` status after returning, treat that as bounce exhaustion: dump state to `.cloverleaf/runs/$RFC_ID/discover-crash.json` (including the spike ID) and halt. The orchestrator does NOT resume partially-run spike trees — if any spike is unresolved, the whole Discovery halts.
|
|
@@ -74,11 +74,11 @@ Each sub-skill runs from `main`. Between steps, confirm branch is `main` before
|
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
Parse input. On:
|
|
77
|
-
- `approve` →
|
|
78
|
-
- `reject [reason]` →
|
|
79
|
-
- `revise [reason]` →
|
|
77
|
+
- `approve` → invoke `/cloverleaf-gate $RFC_ID approve`. Continue to step 7.
|
|
78
|
+
- `reject [reason]` → invoke `/cloverleaf-gate $RFC_ID reject [reason]`. Exit with summary.
|
|
79
|
+
- `revise [reason]` → invoke `/cloverleaf-gate $RFC_ID revise [reason]`. `revise_loops += 1`. If `revise_loops >= MAX_REVISE_LOOPS`, dump state and halt. Else loop back to step 4.
|
|
80
80
|
|
|
81
|
-
7. **Plan breakdown** —
|
|
81
|
+
7. **Plan breakdown** — invoke `/cloverleaf-breakdown $RFC_ID`. Per-invocation bounce budget (3 bounces inside breakdown). Capture `$PLAN_ID`. On bounce exhaustion: dump state and halt.
|
|
82
82
|
|
|
83
83
|
8. **Gate: task_batch_gate** — Plan is already in `gate-pending` (set by breakdown). Prompt:
|
|
84
84
|
```
|
|
@@ -86,8 +86,8 @@ Each sub-skill runs from `main`. Between steps, confirm branch is `main` before
|
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
On:
|
|
89
|
-
- `approve` →
|
|
90
|
-
- `reject [reason]` →
|
|
89
|
+
- `approve` → invoke `/cloverleaf-gate $PLAN_ID approve`. Continue.
|
|
90
|
+
- `reject [reason]` → invoke `/cloverleaf-gate $PLAN_ID reject [reason]`. Exit with summary.
|
|
91
91
|
|
|
92
92
|
9. **Materialise tasks**:
|
|
93
93
|
```bash
|
|
@@ -123,7 +123,7 @@ Each sub-skill runs from `main`. Between steps, confirm branch is `main` before
|
|
|
123
123
|
Run first root via /cloverleaf-run now? [y/N] > _
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
On `y` or `yes` (case-insensitive):
|
|
126
|
+
On `y` or `yes` (case-insensitive): invoke `/cloverleaf-run <FIRST_ROOT>`. On anything else: exit with summary.
|
|
127
127
|
|
|
128
128
|
12. **Exit summary**:
|
|
129
129
|
- RFC ID + final status
|
|
@@ -137,3 +137,4 @@ Each sub-skill runs from `main`. Between steps, confirm branch is `main` before
|
|
|
137
137
|
- v0.5 prompts only for the FIRST DAG root. Multi-root concurrent Delivery is v0.6 scope.
|
|
138
138
|
- All bounces and revise loops halt cleanly with a state dump at `.cloverleaf/runs/<RFC_ID>/discover-crash.json`. No partial work is left in an inconsistent state — the gate_decision events and work-item status are always coherent.
|
|
139
139
|
- The readline prompts are compatible with the user's session-bridging tool for automated dogfooding.
|
|
140
|
+
- "Invoke `/cloverleaf-X`" means spawn the sub-skill via the Skill tool — do NOT re-enter `/cloverleaf-discover` recursively. Sub-skills run in-process (same session) and return control to this orchestrator.
|
|
@@ -47,8 +47,8 @@ cat > "$TMPFILE" <<EOF
|
|
|
47
47
|
"id": "$RFC_ID",
|
|
48
48
|
"status": "drafting",
|
|
49
49
|
"owner": { "kind": "agent", "id": "researcher" },
|
|
50
|
-
"title": $(
|
|
51
|
-
"problem": $(
|
|
50
|
+
"title": $(printf '%s' "$FIRST_LINE" | jq -Rs .),
|
|
51
|
+
"problem": $(printf '%s' "$BRIEF_CONTENT" | jq -Rs .),
|
|
52
52
|
"solution": "TBD — to be populated by /cloverleaf-draft-rfc.",
|
|
53
53
|
"unknowns": [],
|
|
54
54
|
"acceptance_criteria": ["RFC body populated by researcher agent"],
|