@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.
@@ -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.0",
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.4.1
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.0",
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** — inline `/cloverleaf-new-rfc $BRIEF_FILE` steps. Capture the printed RFC ID as `$RFC_ID`.
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)** — inline `/cloverleaf-draft-rfc $RFC_ID` steps. Researcher runs with a per-invocation bounce budget (3 bounces inside draft-rfc).
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
- inline /cloverleaf-spike <SPIKE_ID> steps.
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` → inline `/cloverleaf-gate $RFC_ID approve` steps. Continue to step 7.
78
- - `reject [reason]` → inline `/cloverleaf-gate $RFC_ID reject [reason]` steps. Exit with summary.
79
- - `revise [reason]` → inline `/cloverleaf-gate $RFC_ID revise [reason]` steps. `revise_loops += 1`. If `revise_loops >= MAX_REVISE_LOOPS`, dump state and halt. Else loop back to step 4.
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** — inline `/cloverleaf-breakdown $RFC_ID` steps. Per-invocation bounce budget (3 bounces inside breakdown). Capture `$PLAN_ID`. On bounce exhaustion: dump state and halt.
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` → inline `/cloverleaf-gate $PLAN_ID approve` steps. Continue.
90
- - `reject [reason]` → inline `/cloverleaf-gate $PLAN_ID reject [reason]` steps. Exit with summary.
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): inline `/cloverleaf-run <FIRST_ROOT>` steps. On anything else: exit with summary.
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": $(echo "$FIRST_LINE" | jq -Rs .),
51
- "problem": $(echo "$BRIEF_CONTENT" | jq -Rs .),
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"],