@c-d-cc/reap 0.16.5 → 0.16.6
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 +28 -0
- package/RELEASE_NOTICE.md +6 -0
- package/dist/cli/index.js +498 -119
- package/dist/templates/reap-guide.md +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -229,6 +229,7 @@ Pre-approve N generations for autonomous execution:
|
|
|
229
229
|
| `/reap.init` | Initialize REAP in a project |
|
|
230
230
|
| `/reap.run` | Execute a lifecycle command directly |
|
|
231
231
|
| `/reap.config` | View/edit project configuration |
|
|
232
|
+
| `/reap.report` | Submit a bug report or feature request |
|
|
232
233
|
|
|
233
234
|
## Agent Integration
|
|
234
235
|
|
|
@@ -250,6 +251,31 @@ Switch clients by editing `.reap/config.yml`, then run `reap install-skills` fol
|
|
|
250
251
|
|
|
251
252
|
`/reap.evolve` can delegate the entire generation to a subagent that runs autonomously through all stages, surfacing only when genuinely blocked.
|
|
252
253
|
|
|
254
|
+
### Evaluator Agent (opt-in)
|
|
255
|
+
|
|
256
|
+
REAP ships a second subagent definition, `reap-evaluate`, that runs as an **independent reviewer** of the builder's work. It is read-only (Read/Glob/Grep/Bash only), produces qualitative assessments (no scores), and acts as an **advisor** — its concerns surface to you, but the builder owns the final lifecycle verdict.
|
|
257
|
+
|
|
258
|
+
Enable it by adding one line to `.reap/config.yml`:
|
|
259
|
+
|
|
260
|
+
```yaml
|
|
261
|
+
evaluator: true # default: false
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
When enabled, the validation stage instructs the builder to launch `reap-evaluate` as a subagent before declaring pass/partial/fail. The evaluator:
|
|
265
|
+
|
|
266
|
+
- runs typecheck, build, and the full test suite independently,
|
|
267
|
+
- cross-checks completion criteria from `02-planning.md` against the implementation,
|
|
268
|
+
- surfaces concerns about genome convention drift, sycophancy red flags, and regression risk,
|
|
269
|
+
- escalates per a confidence × impact matrix.
|
|
270
|
+
|
|
271
|
+
If the subagent invocation fails for any reason, the builder continues normal validation — the evaluator is opt-in advice, not a gate.
|
|
272
|
+
|
|
273
|
+
Agent definitions are installed automatically by `reap install-skills` **and** `reap update`:
|
|
274
|
+
- Claude Code → `~/.claude/agents/reap-*.md`
|
|
275
|
+
- OpenCode → `~/.config/opencode/agent/reap-*.md`
|
|
276
|
+
|
|
277
|
+
**Fitness phase + cruise mode** (gen-067): the evaluator also runs during the fitness phase. After receiving its reply, the builder persists the verdict on the generation state via `reap run validation --phase report-evaluator --severity <high|low|none> --summary "..."`. High-severity concerns recorded during validation **automatically abort cruise mode** when the next fitness phase runs — `cruiseCount` is cleared from `config.yml`, the cruise prompt is replaced with a supervised fallback, and the human reviews the concern before composing fitness feedback. Cruise can be resumed manually with `reap cruise <N>` once the concern is resolved. Low-severity concerns surface in the prompt's "Prior Evaluator Concerns" section without aborting cruise.
|
|
278
|
+
|
|
253
279
|
## Project Structure
|
|
254
280
|
|
|
255
281
|
```
|
|
@@ -290,6 +316,7 @@ strictEdit: false # Restrict code changes to REAP lifecycle
|
|
|
290
316
|
strictMerge: false # Restrict direct git pull/push/merge
|
|
291
317
|
agentClient: claude-code # AI agent client
|
|
292
318
|
# cruiseCount: 1/5 # Present = cruise mode (current/total)
|
|
319
|
+
# evaluator: true # Opt-in: launch reap-evaluate during validation
|
|
293
320
|
```
|
|
294
321
|
|
|
295
322
|
Key settings:
|
|
@@ -298,6 +325,7 @@ Key settings:
|
|
|
298
325
|
- **`strictEdit`**: Restricts code changes to the implementation stage within the planned scope.
|
|
299
326
|
- **`strictMerge`**: Restricts direct git pull/push/merge — use `/reap.pull`, `/reap.push`, `/reap.merge` instead.
|
|
300
327
|
- **`agentClient`**: Determines which adapter is used for skill deployment.
|
|
328
|
+
- **`evaluator`**: Opt-in independent reviewer. When `true`, the validation stage launches the `reap-evaluate` subagent as an advisor (read-only, qualitative-only). Default `false` keeps validation byte-identical to pre-gen-066 behaviour. See [Evaluator Agent](#evaluator-agent-opt-in) above.
|
|
301
329
|
|
|
302
330
|
## Upgrading from v0.15 [↗](https://reap.cc/docs/migration-guide)
|
|
303
331
|
|
package/RELEASE_NOTICE.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Release Notices
|
|
2
2
|
|
|
3
|
+
## v0.16.6
|
|
4
|
+
### en
|
|
5
|
+
Evaluator Agent integration (opt-in `evaluator: true`): independent `reap-evaluate` subagent runs during validation and fitness phases as an advisor. High-severity concerns automatically abort cruise mode for human review.
|
|
6
|
+
### ko
|
|
7
|
+
Evaluator Agent 통합 (opt-in `evaluator: true`): `reap-evaluate` 서브에이전트가 validation/fitness 단계에서 독립 검토자로 실행됨 (advisor 모델). high-severity concern 발생 시 cruise mode 자동 중단.
|
|
8
|
+
|
|
3
9
|
## v0.16.4
|
|
4
10
|
### en
|
|
5
11
|
Restore missing npm metadata (license, author, repository, homepage, keywords). Fix GitHub Releases showing empty release notes.
|
package/dist/cli/index.js
CHANGED
|
@@ -8984,6 +8984,116 @@ function buildBasePrompt(knowledge, paths, state, cruiseCount, clarityResult, co
|
|
|
8984
8984
|
return lines.join(`
|
|
8985
8985
|
`);
|
|
8986
8986
|
}
|
|
8987
|
+
function buildEvaluatorPrompt(knowledge, paths, state, opts) {
|
|
8988
|
+
const lines = [];
|
|
8989
|
+
const isMerge = state?.type === "merge";
|
|
8990
|
+
lines.push(`## Evaluator Invocation — ${opts.stage} stage`);
|
|
8991
|
+
lines.push("");
|
|
8992
|
+
if (opts.stage === "validation") {
|
|
8993
|
+
lines.push("You are being invoked during the **validation** stage of a REAP generation. " + "The builder (reap-evolve) has completed implementation and run validation checks. " + "Your role is independent verification: confirm the build is sound, tests pass, " + "completion criteria from 02-planning.md are met, and the code changes follow " + "genome conventions. Surface any concern the builder may have missed.");
|
|
8994
|
+
} else {
|
|
8995
|
+
lines.push("You are being invoked during the **fitness** phase of completion. " + "Assess the generation against the 6 fitness dimensions in your agent definition " + "(goal achievement, code quality, regression safety, artifact quality, vision alignment, " + "cross-generation coherence). Cross-reference with vision goals and recent lineage.");
|
|
8996
|
+
}
|
|
8997
|
+
lines.push("");
|
|
8998
|
+
lines.push("## Current Generation");
|
|
8999
|
+
if (state) {
|
|
9000
|
+
lines.push(`- ID: ${state.id}`);
|
|
9001
|
+
lines.push(`- Type: ${state.type}`);
|
|
9002
|
+
lines.push(`- Goal: ${state.goal}`);
|
|
9003
|
+
lines.push(`- Stage: ${state.stage}`);
|
|
9004
|
+
if (isMerge) {
|
|
9005
|
+
lines.push(`- Parents: ${state.parents.join(", ")}`);
|
|
9006
|
+
if (state.commonAncestor)
|
|
9007
|
+
lines.push(`- Common Ancestor: ${state.commonAncestor}`);
|
|
9008
|
+
}
|
|
9009
|
+
} else {
|
|
9010
|
+
lines.push("- (no active generation — abort and report to the user)");
|
|
9011
|
+
}
|
|
9012
|
+
lines.push("");
|
|
9013
|
+
lines.push("## Artifacts to Read");
|
|
9014
|
+
lines.push("");
|
|
9015
|
+
if (isMerge) {
|
|
9016
|
+
lines.push(`- ${paths.artifact("01-detect.md")}`);
|
|
9017
|
+
lines.push(`- ${paths.artifact("02-mate.md")}`);
|
|
9018
|
+
lines.push(`- ${paths.artifact("03-merge.md")}`);
|
|
9019
|
+
lines.push(`- ${paths.artifact("04-reconcile.md")}`);
|
|
9020
|
+
lines.push(`- ${paths.artifact("05-validation.md")}`);
|
|
9021
|
+
} else {
|
|
9022
|
+
lines.push(`- ${paths.artifact("01-learning.md")}`);
|
|
9023
|
+
lines.push(`- ${paths.artifact("02-planning.md")}`);
|
|
9024
|
+
lines.push(`- ${paths.artifact("03-implementation.md")}`);
|
|
9025
|
+
lines.push(`- ${paths.artifact("04-validation.md")}`);
|
|
9026
|
+
}
|
|
9027
|
+
lines.push("");
|
|
9028
|
+
if (knowledge.visionGoals) {
|
|
9029
|
+
lines.push("## Vision Goals (current)");
|
|
9030
|
+
lines.push(knowledge.visionGoals);
|
|
9031
|
+
lines.push("");
|
|
9032
|
+
}
|
|
9033
|
+
if (knowledge.memoryShortterm || knowledge.memoryMidterm) {
|
|
9034
|
+
lines.push("## Memory");
|
|
9035
|
+
if (knowledge.memoryShortterm) {
|
|
9036
|
+
lines.push("### Shortterm (1-2 sessions)");
|
|
9037
|
+
lines.push(knowledge.memoryShortterm);
|
|
9038
|
+
lines.push("");
|
|
9039
|
+
}
|
|
9040
|
+
if (knowledge.memoryMidterm) {
|
|
9041
|
+
lines.push("### Midterm (multi-generation)");
|
|
9042
|
+
lines.push(knowledge.memoryMidterm);
|
|
9043
|
+
lines.push("");
|
|
9044
|
+
}
|
|
9045
|
+
}
|
|
9046
|
+
lines.push("## Verification Tasks");
|
|
9047
|
+
lines.push("");
|
|
9048
|
+
if (opts.stage === "validation") {
|
|
9049
|
+
lines.push("1. Run `npm run typecheck` (or the project's typecheck command) and record the result.");
|
|
9050
|
+
lines.push("2. Run `npm run build` (or the project's build command) and record the result.");
|
|
9051
|
+
lines.push("3. Run the full test suite (unit/e2e/scenario as defined by the project) and record each.");
|
|
9052
|
+
lines.push("4. Read 02-planning.md and verify each completion criterion against the implementation + validation artifacts.");
|
|
9053
|
+
lines.push("5. Read `git diff` against the parent commit and check for genome convention compliance.");
|
|
9054
|
+
lines.push('6. Check 04-validation.md for sycophancy red flags ("it will probably pass", "it passed before").');
|
|
9055
|
+
lines.push("");
|
|
9056
|
+
} else {
|
|
9057
|
+
lines.push("1. Read all completed artifacts and validate goal achievement.");
|
|
9058
|
+
lines.push("2. Cross-reference the implementation against genome conventions (application.md, evolution.md).");
|
|
9059
|
+
lines.push("3. Run the test suite and confirm no regression.");
|
|
9060
|
+
lines.push("4. Compare this generation's contribution against vision/goals.md and recent lineage.");
|
|
9061
|
+
lines.push("5. Assess each of the 6 fitness dimensions qualitatively (no scores).");
|
|
9062
|
+
lines.push("");
|
|
9063
|
+
}
|
|
9064
|
+
lines.push("## Output Format");
|
|
9065
|
+
lines.push("");
|
|
9066
|
+
lines.push("Apply the escalation matrix from your agent definition:");
|
|
9067
|
+
lines.push("");
|
|
9068
|
+
lines.push("| Confidence | Impact | Action |");
|
|
9069
|
+
lines.push("| --- | --- | --- |");
|
|
9070
|
+
lines.push("| High | Low | Direct judgment |");
|
|
9071
|
+
lines.push("| High | High | Escalate with judgment |");
|
|
9072
|
+
lines.push("| Low | Any | Escalate without judgment (facts only) |");
|
|
9073
|
+
lines.push("");
|
|
9074
|
+
lines.push("Structure your reply as:");
|
|
9075
|
+
lines.push("- **Summary** (1-2 sentences)");
|
|
9076
|
+
lines.push("- **Verification results** (typecheck / build / tests / criteria)");
|
|
9077
|
+
lines.push("- **Concerns** (if any — call out the severity)");
|
|
9078
|
+
lines.push("- **Recommendation** (per the matrix)");
|
|
9079
|
+
lines.push("");
|
|
9080
|
+
lines.push("## HARD-GATE — Evaluator Constraints");
|
|
9081
|
+
lines.push("");
|
|
9082
|
+
lines.push("- You MUST NOT write, edit, or create any source files. Use Read/Grep/Glob/Bash only.");
|
|
9083
|
+
lines.push("- You MUST NOT run git commands that modify state (`git commit`, `git push`, `git checkout`, `git reset`).");
|
|
9084
|
+
lines.push("- You MUST NOT run `reap run` commands — the lifecycle is the builder's responsibility.");
|
|
9085
|
+
lines.push("- You MUST NOT produce numerical scores, ratings, or percentages (Goodhart's Law).");
|
|
9086
|
+
lines.push("- Your verdict is an **advisor recommendation**, not a binding judgment. The builder owns the lifecycle verdict; the human owns final fitness.");
|
|
9087
|
+
lines.push("");
|
|
9088
|
+
lines.push("## If You Cannot Proceed");
|
|
9089
|
+
lines.push("");
|
|
9090
|
+
lines.push("If you cannot complete verification (missing tools, broken state, ambiguous artifacts), " + "report the obstacle in your reply with enough context for the human to act. Do not block " + "the builder's lifecycle — the builder will continue validation if you cannot.");
|
|
9091
|
+
lines.push("");
|
|
9092
|
+
lines.push("## Project");
|
|
9093
|
+
lines.push(`Path: ${paths.root}`);
|
|
9094
|
+
return lines.join(`
|
|
9095
|
+
`);
|
|
9096
|
+
}
|
|
8987
9097
|
|
|
8988
9098
|
// src/core/dump-state-sync.ts
|
|
8989
9099
|
var DUMP_COMMANDS = new Set([
|
|
@@ -10604,7 +10714,7 @@ async function execute6() {
|
|
|
10604
10714
|
}
|
|
10605
10715
|
|
|
10606
10716
|
// src/cli/commands/run/index.ts
|
|
10607
|
-
var
|
|
10717
|
+
var import_yaml18 = __toESM(require_dist(), 1);
|
|
10608
10718
|
init_fs();
|
|
10609
10719
|
init_integrity();
|
|
10610
10720
|
|
|
@@ -11865,6 +11975,10 @@ async function execute10(paths, phase) {
|
|
|
11865
11975
|
}
|
|
11866
11976
|
}
|
|
11867
11977
|
|
|
11978
|
+
// src/cli/commands/run/validation.ts
|
|
11979
|
+
var import_yaml11 = __toESM(require_dist(), 1);
|
|
11980
|
+
init_fs();
|
|
11981
|
+
|
|
11868
11982
|
// src/core/artifact-check.ts
|
|
11869
11983
|
init_fs();
|
|
11870
11984
|
var PRE_VALIDATION_NORMAL = {
|
|
@@ -11899,7 +12013,7 @@ function isUnfilled(content) {
|
|
|
11899
12013
|
}
|
|
11900
12014
|
|
|
11901
12015
|
// src/cli/commands/run/validation.ts
|
|
11902
|
-
async function execute11(paths, phase) {
|
|
12016
|
+
async function execute11(paths, phase, extra) {
|
|
11903
12017
|
const gm = new GenerationManager(paths);
|
|
11904
12018
|
const state = await gm.current();
|
|
11905
12019
|
if (!state)
|
|
@@ -11908,6 +12022,64 @@ async function execute11(paths, phase) {
|
|
|
11908
12022
|
emitError("validation", `Current stage is '${state.stage}', not 'validation'.`);
|
|
11909
12023
|
const isMerge = state.type === "merge";
|
|
11910
12024
|
const s = state;
|
|
12025
|
+
if (phase === "report-evaluator") {
|
|
12026
|
+
let severity;
|
|
12027
|
+
let summary;
|
|
12028
|
+
if (extra) {
|
|
12029
|
+
try {
|
|
12030
|
+
const parsed = JSON.parse(extra);
|
|
12031
|
+
severity = parsed.severity;
|
|
12032
|
+
summary = parsed.summary;
|
|
12033
|
+
} catch {
|
|
12034
|
+
emitError("validation", "report-evaluator: failed to parse options. Expected --severity and --summary.");
|
|
12035
|
+
}
|
|
12036
|
+
}
|
|
12037
|
+
if (!severity) {
|
|
12038
|
+
emitError("validation", "report-evaluator requires --severity <high|low|none>.");
|
|
12039
|
+
}
|
|
12040
|
+
const sev = severity.toLowerCase();
|
|
12041
|
+
if (sev === "none") {
|
|
12042
|
+
emitOutput({
|
|
12043
|
+
status: "ok",
|
|
12044
|
+
command: "validation",
|
|
12045
|
+
phase: "report-evaluator",
|
|
12046
|
+
completed: ["gate", "noop"],
|
|
12047
|
+
context: { id: s.id, severity: "none" },
|
|
12048
|
+
message: "Evaluator reported no concern — state unchanged."
|
|
12049
|
+
});
|
|
12050
|
+
return;
|
|
12051
|
+
}
|
|
12052
|
+
if (sev !== "high" && sev !== "low") {
|
|
12053
|
+
emitError("validation", `report-evaluator: invalid severity '${severity}'. Use high, low, or none.`);
|
|
12054
|
+
}
|
|
12055
|
+
if (!summary || summary.trim().length === 0) {
|
|
12056
|
+
emitError("validation", 'report-evaluator requires --summary "<one-line description>".');
|
|
12057
|
+
}
|
|
12058
|
+
const concern = {
|
|
12059
|
+
stage: "validation",
|
|
12060
|
+
severity: sev,
|
|
12061
|
+
summary: summary.trim(),
|
|
12062
|
+
recordedAt: new Date().toISOString()
|
|
12063
|
+
};
|
|
12064
|
+
if (!s.evaluatorConcerns)
|
|
12065
|
+
s.evaluatorConcerns = [];
|
|
12066
|
+
s.evaluatorConcerns.push(concern);
|
|
12067
|
+
await gm.save(s);
|
|
12068
|
+
emitOutput({
|
|
12069
|
+
status: "ok",
|
|
12070
|
+
command: "validation",
|
|
12071
|
+
phase: "report-evaluator",
|
|
12072
|
+
completed: ["gate", "concern-recorded"],
|
|
12073
|
+
context: {
|
|
12074
|
+
id: s.id,
|
|
12075
|
+
severity: concern.severity,
|
|
12076
|
+
summary: concern.summary,
|
|
12077
|
+
total: s.evaluatorConcerns.length
|
|
12078
|
+
},
|
|
12079
|
+
message: `Evaluator concern recorded (severity=${concern.severity}). Total: ${s.evaluatorConcerns.length}.`
|
|
12080
|
+
});
|
|
12081
|
+
return;
|
|
12082
|
+
}
|
|
11911
12083
|
if (!phase || phase === "work") {
|
|
11912
12084
|
verifyTransition("validation", s, "validation:entry");
|
|
11913
12085
|
await copyArtifactTemplate("validation", paths.artifact, isMerge);
|
|
@@ -11944,48 +12116,92 @@ async function execute11(paths, phase) {
|
|
|
11944
12116
|
});
|
|
11945
12117
|
return;
|
|
11946
12118
|
}
|
|
12119
|
+
const basePromptLines = [
|
|
12120
|
+
"## Validation Stage",
|
|
12121
|
+
"",
|
|
12122
|
+
"### HARD-GATE:",
|
|
12123
|
+
"- Do NOT declare 'pass' without running the validation commands.",
|
|
12124
|
+
"- Do NOT reuse results from a previous run — execute them FRESH.",
|
|
12125
|
+
"- 'It will probably pass' is NOT validation.",
|
|
12126
|
+
"",
|
|
12127
|
+
"### Steps:",
|
|
12128
|
+
"1. **TypeCheck**: Run `npm run typecheck` (or project's typecheck command). Record result.",
|
|
12129
|
+
"2. **Build**: Run `npm run build` (or project's build command). Record result.",
|
|
12130
|
+
"3. **Tests**: Run ALL test commands the project has (e.g., e2e scripts). Record each result.",
|
|
12131
|
+
"4. **Completion Criteria**: Verify EACH criterion from 02-planning.md one by one.",
|
|
12132
|
+
"5. **Minor Fix** (trivial issues only, under 5 minutes): Fix and re-run the failed command.",
|
|
12133
|
+
"6. **Verdict**: Determine pass / partial / fail.",
|
|
12134
|
+
"",
|
|
12135
|
+
"### Red Flags (sycophancy prevention):",
|
|
12136
|
+
"- 'It will probably pass' → Run it.",
|
|
12137
|
+
"- 'It passed before' → Run it again.",
|
|
12138
|
+
"- 'It\\'s trivial, no need to test' → Test it anyway.",
|
|
12139
|
+
"",
|
|
12140
|
+
"### Verdict Criteria:",
|
|
12141
|
+
"- **pass**: All checks pass, all completion criteria met.",
|
|
12142
|
+
"- **partial**: Minor issues remain but core functionality works. Document what's incomplete.",
|
|
12143
|
+
"- **fail**: Critical failures. Must regress to implementation.",
|
|
12144
|
+
"",
|
|
12145
|
+
`### Artifact: Write \`.reap/life/${isMerge ? "05" : "04"}-validation.md\` progressively (after each command).`,
|
|
12146
|
+
"",
|
|
12147
|
+
"If pass/partial: reap run validation --phase complete",
|
|
12148
|
+
"If fail: reap run back to regress"
|
|
12149
|
+
];
|
|
12150
|
+
const configContent = await readTextFile(paths.config);
|
|
12151
|
+
const config = configContent ? import_yaml11.default.parse(configContent) : null;
|
|
12152
|
+
const evaluatorEnabled = config?.evaluator === true;
|
|
12153
|
+
const context = {
|
|
12154
|
+
id: s.id,
|
|
12155
|
+
goal: s.goal,
|
|
12156
|
+
type: s.type,
|
|
12157
|
+
artifactPath: paths.artifact(isMerge ? "05-validation.md" : "04-validation.md"),
|
|
12158
|
+
evaluator: { enabled: evaluatorEnabled }
|
|
12159
|
+
};
|
|
12160
|
+
const promptLines = [...basePromptLines];
|
|
12161
|
+
if (evaluatorEnabled) {
|
|
12162
|
+
const knowledge = await loadReapKnowledge(paths);
|
|
12163
|
+
const evaluatorPrompt = buildEvaluatorPrompt(knowledge, paths, s, { stage: "validation" });
|
|
12164
|
+
context.evaluator.prompt = evaluatorPrompt;
|
|
12165
|
+
promptLines.push("");
|
|
12166
|
+
promptLines.push("### Evaluator Subagent Invocation (opt-in via `evaluator: true`)");
|
|
12167
|
+
promptLines.push("");
|
|
12168
|
+
promptLines.push("Before declaring your verdict, launch an independent reviewer using the Agent tool:");
|
|
12169
|
+
promptLines.push("");
|
|
12170
|
+
promptLines.push("- subagent_type: `reap-evaluate`");
|
|
12171
|
+
promptLines.push("- description: independent validation review");
|
|
12172
|
+
promptLines.push("- prompt: the `evaluator.prompt` value from the context above");
|
|
12173
|
+
promptLines.push("");
|
|
12174
|
+
promptLines.push("**Advisor model** — the evaluator's assessment is a recommendation, not a verdict:");
|
|
12175
|
+
promptLines.push("- You (the builder) decide the final pass/partial/fail verdict.");
|
|
12176
|
+
promptLines.push("- Surface every evaluator concern to the user in your validation report, even if you disagree.");
|
|
12177
|
+
promptLines.push("- If the evaluator escalates a high-impact concern, lean toward `partial` and document the concern in 04-validation.md.");
|
|
12178
|
+
promptLines.push("");
|
|
12179
|
+
promptLines.push("**Persist the verdict for the fitness phase (gen-067)**:");
|
|
12180
|
+
promptLines.push("");
|
|
12181
|
+
promptLines.push("After receiving the evaluator's reply, record the outcome on the generation state so the");
|
|
12182
|
+
promptLines.push("subsequent fitness phase can act on it (cruise mode auto-abort, evaluator concern surfacing):");
|
|
12183
|
+
promptLines.push("");
|
|
12184
|
+
promptLines.push("- High-impact escalation:");
|
|
12185
|
+
promptLines.push(' `reap run validation --phase report-evaluator --severity high --summary "<one-line description>"`');
|
|
12186
|
+
promptLines.push("- Low-impact concern (informational):");
|
|
12187
|
+
promptLines.push(' `reap run validation --phase report-evaluator --severity low --summary "<one-line description>"`');
|
|
12188
|
+
promptLines.push("- Clean review (no concern):");
|
|
12189
|
+
promptLines.push(' `reap run validation --phase report-evaluator --severity none --summary ""`');
|
|
12190
|
+
promptLines.push("");
|
|
12191
|
+
promptLines.push("This call does NOT advance the lifecycle — it only appends to `state.evaluatorConcerns`.");
|
|
12192
|
+
promptLines.push("");
|
|
12193
|
+
promptLines.push("**Fallback** — if the evaluator subagent fails (tool unavailable, model error, malformed reply):");
|
|
12194
|
+
promptLines.push("- Tell the user the evaluator could not run and why.");
|
|
12195
|
+
promptLines.push("- Continue normal validation. The evaluator is opt-in advice, not a gate.");
|
|
12196
|
+
promptLines.push("- Skip the `report-evaluator` CLI call (no concern was generated).");
|
|
12197
|
+
}
|
|
11947
12198
|
emitOutput({
|
|
11948
12199
|
status: "prompt",
|
|
11949
12200
|
command: "validation",
|
|
11950
12201
|
phase: "work",
|
|
11951
12202
|
completed: ["gate", "artifact-check"],
|
|
11952
|
-
context
|
|
11953
|
-
|
|
11954
|
-
goal: s.goal,
|
|
11955
|
-
type: s.type,
|
|
11956
|
-
artifactPath: paths.artifact(isMerge ? "05-validation.md" : "04-validation.md")
|
|
11957
|
-
},
|
|
11958
|
-
prompt: [
|
|
11959
|
-
"## Validation Stage",
|
|
11960
|
-
"",
|
|
11961
|
-
"### HARD-GATE:",
|
|
11962
|
-
"- Do NOT declare 'pass' without running the validation commands.",
|
|
11963
|
-
"- Do NOT reuse results from a previous run — execute them FRESH.",
|
|
11964
|
-
"- 'It will probably pass' is NOT validation.",
|
|
11965
|
-
"",
|
|
11966
|
-
"### Steps:",
|
|
11967
|
-
"1. **TypeCheck**: Run `npm run typecheck` (or project's typecheck command). Record result.",
|
|
11968
|
-
"2. **Build**: Run `npm run build` (or project's build command). Record result.",
|
|
11969
|
-
"3. **Tests**: Run ALL test commands the project has (e.g., e2e scripts). Record each result.",
|
|
11970
|
-
"4. **Completion Criteria**: Verify EACH criterion from 02-planning.md one by one.",
|
|
11971
|
-
"5. **Minor Fix** (trivial issues only, under 5 minutes): Fix and re-run the failed command.",
|
|
11972
|
-
"6. **Verdict**: Determine pass / partial / fail.",
|
|
11973
|
-
"",
|
|
11974
|
-
"### Red Flags (sycophancy prevention):",
|
|
11975
|
-
"- 'It will probably pass' → Run it.",
|
|
11976
|
-
"- 'It passed before' → Run it again.",
|
|
11977
|
-
"- 'It\\'s trivial, no need to test' → Test it anyway.",
|
|
11978
|
-
"",
|
|
11979
|
-
"### Verdict Criteria:",
|
|
11980
|
-
"- **pass**: All checks pass, all completion criteria met.",
|
|
11981
|
-
"- **partial**: Minor issues remain but core functionality works. Document what's incomplete.",
|
|
11982
|
-
"- **fail**: Critical failures. Must regress to implementation.",
|
|
11983
|
-
"",
|
|
11984
|
-
`### Artifact: Write \`.reap/life/${isMerge ? "05" : "04"}-validation.md\` progressively (after each command).`,
|
|
11985
|
-
"",
|
|
11986
|
-
"If pass/partial: reap run validation --phase complete",
|
|
11987
|
-
"If fail: reap run back to regress"
|
|
11988
|
-
].join(`
|
|
12203
|
+
context,
|
|
12204
|
+
prompt: promptLines.join(`
|
|
11989
12205
|
`),
|
|
11990
12206
|
nextCommand: "reap run validation --phase complete"
|
|
11991
12207
|
});
|
|
@@ -12013,13 +12229,13 @@ init_fs();
|
|
|
12013
12229
|
|
|
12014
12230
|
// src/core/archive.ts
|
|
12015
12231
|
init_fs();
|
|
12016
|
-
var
|
|
12232
|
+
var import_yaml13 = __toESM(require_dist(), 1);
|
|
12017
12233
|
import { join as join16 } from "path";
|
|
12018
12234
|
import { readdir as readdir13, rm as rm4, unlink } from "fs/promises";
|
|
12019
12235
|
|
|
12020
12236
|
// src/core/compression.ts
|
|
12021
12237
|
init_fs();
|
|
12022
|
-
var
|
|
12238
|
+
var import_yaml12 = __toESM(require_dist(), 1);
|
|
12023
12239
|
import { readdir as readdir12, rm as rm3 } from "fs/promises";
|
|
12024
12240
|
import { join as join15 } from "path";
|
|
12025
12241
|
var LEVEL1_THRESHOLD = 20;
|
|
@@ -12052,7 +12268,7 @@ async function compressLineage(lineageDir) {
|
|
|
12052
12268
|
let meta = { id: dirName, type: "unknown", goal: "", parents: [] };
|
|
12053
12269
|
if (metaContent) {
|
|
12054
12270
|
try {
|
|
12055
|
-
meta =
|
|
12271
|
+
meta = import_yaml12.default.parse(metaContent);
|
|
12056
12272
|
} catch {}
|
|
12057
12273
|
}
|
|
12058
12274
|
const completion = await readTextFile(join15(dirPath, "05-completion.md")) ?? "";
|
|
@@ -12107,7 +12323,7 @@ async function compressToEpoch(lineageDir) {
|
|
|
12107
12323
|
let meta = { id: file.replace(".md", ""), type: "unknown", goal: "", parents: [] };
|
|
12108
12324
|
if (fmMatch) {
|
|
12109
12325
|
try {
|
|
12110
|
-
const parsed =
|
|
12326
|
+
const parsed = import_yaml12.default.parse(fmMatch[1]);
|
|
12111
12327
|
meta = { ...meta, ...parsed };
|
|
12112
12328
|
} catch {}
|
|
12113
12329
|
}
|
|
@@ -12124,7 +12340,7 @@ async function compressToEpoch(lineageDir) {
|
|
|
12124
12340
|
if (!metaContent)
|
|
12125
12341
|
continue;
|
|
12126
12342
|
try {
|
|
12127
|
-
const m =
|
|
12343
|
+
const m = import_yaml12.default.parse(metaContent);
|
|
12128
12344
|
if (m.parents)
|
|
12129
12345
|
m.parents.forEach((p) => dirParents.add(p));
|
|
12130
12346
|
} catch {}
|
|
@@ -12137,7 +12353,7 @@ async function compressToEpoch(lineageDir) {
|
|
|
12137
12353
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
12138
12354
|
if (fmMatch) {
|
|
12139
12355
|
try {
|
|
12140
|
-
const parsed =
|
|
12356
|
+
const parsed = import_yaml12.default.parse(fmMatch[1]);
|
|
12141
12357
|
const lastId = parsed.lastGeneration;
|
|
12142
12358
|
if (lastId)
|
|
12143
12359
|
dirParents.add(lastId);
|
|
@@ -12219,7 +12435,7 @@ async function writeArchive(paths, state, archiveDir, extraMeta) {
|
|
|
12219
12435
|
timeline: state.timeline,
|
|
12220
12436
|
...extraMeta
|
|
12221
12437
|
};
|
|
12222
|
-
await writeTextFile(join16(archiveDir, "meta.yml"),
|
|
12438
|
+
await writeTextFile(join16(archiveDir, "meta.yml"), import_yaml13.default.stringify(meta));
|
|
12223
12439
|
for (const entry of lifeEntries) {
|
|
12224
12440
|
if (entry === "backlog")
|
|
12225
12441
|
continue;
|
|
@@ -12544,7 +12760,7 @@ function tokenize(text) {
|
|
|
12544
12760
|
|
|
12545
12761
|
// src/core/cruise.ts
|
|
12546
12762
|
init_fs();
|
|
12547
|
-
var
|
|
12763
|
+
var import_yaml14 = __toESM(require_dist(), 1);
|
|
12548
12764
|
function parseCruiseCount(config) {
|
|
12549
12765
|
if (!config.cruiseCount)
|
|
12550
12766
|
return null;
|
|
@@ -12557,30 +12773,38 @@ async function advanceCruise(configPath) {
|
|
|
12557
12773
|
const content = await readTextFile(configPath);
|
|
12558
12774
|
if (!content)
|
|
12559
12775
|
return false;
|
|
12560
|
-
const config =
|
|
12776
|
+
const config = import_yaml14.default.parse(content);
|
|
12561
12777
|
const cruise = parseCruiseCount(config);
|
|
12562
12778
|
if (!cruise)
|
|
12563
12779
|
return false;
|
|
12564
12780
|
if (cruise.current >= cruise.total) {
|
|
12565
12781
|
delete config.cruiseCount;
|
|
12566
|
-
await writeTextFile(configPath,
|
|
12782
|
+
await writeTextFile(configPath, import_yaml14.default.stringify(config));
|
|
12567
12783
|
return false;
|
|
12568
12784
|
}
|
|
12569
12785
|
config.cruiseCount = `${cruise.current + 1}/${cruise.total}`;
|
|
12570
|
-
await writeTextFile(configPath,
|
|
12786
|
+
await writeTextFile(configPath, import_yaml14.default.stringify(config));
|
|
12571
12787
|
return true;
|
|
12572
12788
|
}
|
|
12789
|
+
async function clearCruise(configPath) {
|
|
12790
|
+
const content = await readTextFile(configPath);
|
|
12791
|
+
if (!content)
|
|
12792
|
+
return;
|
|
12793
|
+
const config = import_yaml14.default.parse(content);
|
|
12794
|
+
delete config.cruiseCount;
|
|
12795
|
+
await writeTextFile(configPath, import_yaml14.default.stringify(config));
|
|
12796
|
+
}
|
|
12573
12797
|
async function setCruise(configPath, total) {
|
|
12574
12798
|
const content = await readTextFile(configPath);
|
|
12575
12799
|
if (!content)
|
|
12576
12800
|
return;
|
|
12577
|
-
const config =
|
|
12801
|
+
const config = import_yaml14.default.parse(content);
|
|
12578
12802
|
config.cruiseCount = `1/${total}`;
|
|
12579
|
-
await writeTextFile(configPath,
|
|
12803
|
+
await writeTextFile(configPath, import_yaml14.default.stringify(config));
|
|
12580
12804
|
}
|
|
12581
12805
|
|
|
12582
12806
|
// src/cli/commands/run/completion.ts
|
|
12583
|
-
var
|
|
12807
|
+
var import_yaml15 = __toESM(require_dist(), 1);
|
|
12584
12808
|
async function execute12(paths, phase, feedback) {
|
|
12585
12809
|
const gm = new GenerationManager(paths);
|
|
12586
12810
|
const state = await gm.current();
|
|
@@ -12669,52 +12893,145 @@ async function execute12(paths, phase, feedback) {
|
|
|
12669
12893
|
} else {
|
|
12670
12894
|
verifyTransition("completion", s, "completion:fitness");
|
|
12671
12895
|
const configContent = await readTextFile(paths.config);
|
|
12672
|
-
const config = configContent ?
|
|
12896
|
+
const config = configContent ? import_yaml15.default.parse(configContent) : null;
|
|
12673
12897
|
const cruise = config ? parseCruiseCount(config) : null;
|
|
12898
|
+
const evaluatorEnabled = config?.evaluator === true;
|
|
12674
12899
|
setTransitionNonces(s, "completion:fitness");
|
|
12675
12900
|
await gm.save(s);
|
|
12901
|
+
const highConcerns = (s.evaluatorConcerns ?? []).filter((c) => c.severity === "high");
|
|
12902
|
+
let fitnessEvaluatorPrompt;
|
|
12903
|
+
if (evaluatorEnabled) {
|
|
12904
|
+
const knowledge = await loadReapKnowledge(paths);
|
|
12905
|
+
fitnessEvaluatorPrompt = buildEvaluatorPrompt(knowledge, paths, s, { stage: "fitness" });
|
|
12906
|
+
}
|
|
12907
|
+
const evaluatorSection = [];
|
|
12908
|
+
if (evaluatorEnabled) {
|
|
12909
|
+
evaluatorSection.push("");
|
|
12910
|
+
evaluatorSection.push("### Evaluator Subagent Invocation (opt-in via `evaluator: true`)");
|
|
12911
|
+
evaluatorSection.push("");
|
|
12912
|
+
evaluatorSection.push("Before composing the final fitness feedback, launch the independent reviewer using the Agent tool:");
|
|
12913
|
+
evaluatorSection.push("");
|
|
12914
|
+
evaluatorSection.push("- subagent_type: `reap-evaluate`");
|
|
12915
|
+
evaluatorSection.push("- description: independent fitness assessment");
|
|
12916
|
+
evaluatorSection.push("- prompt: the `evaluator.prompt` value from the context above");
|
|
12917
|
+
evaluatorSection.push("");
|
|
12918
|
+
evaluatorSection.push("The evaluator scores the generation along the 6 fitness dimensions (qualitative — no numeric scores).");
|
|
12919
|
+
evaluatorSection.push("Surface every concern to the user in your fitness summary; the human owns the final feedback.");
|
|
12920
|
+
evaluatorSection.push("");
|
|
12921
|
+
evaluatorSection.push("**Fallback** — if the evaluator subagent fails, document the failure and continue. Fitness phase is not gated on the evaluator.");
|
|
12922
|
+
}
|
|
12923
|
+
const priorConcernsSection = [];
|
|
12924
|
+
if ((s.evaluatorConcerns ?? []).length > 0) {
|
|
12925
|
+
priorConcernsSection.push("");
|
|
12926
|
+
priorConcernsSection.push("### Prior Evaluator Concerns (carried from earlier stages)");
|
|
12927
|
+
priorConcernsSection.push("");
|
|
12928
|
+
for (const c of s.evaluatorConcerns) {
|
|
12929
|
+
priorConcernsSection.push(`- [${c.severity}] (${c.stage}) ${c.summary}`);
|
|
12930
|
+
}
|
|
12931
|
+
priorConcernsSection.push("");
|
|
12932
|
+
priorConcernsSection.push("Surface each of the above to the user with your fitness summary.");
|
|
12933
|
+
}
|
|
12934
|
+
if (cruise && highConcerns.length > 0) {
|
|
12935
|
+
await clearCruise(paths.config);
|
|
12936
|
+
const fallbackPrompt = [
|
|
12937
|
+
"## Cruise Aborted by Evaluator Concern",
|
|
12938
|
+
"",
|
|
12939
|
+
"Cruise mode has been **disengaged** because the evaluator raised a high-impact concern:",
|
|
12940
|
+
""
|
|
12941
|
+
];
|
|
12942
|
+
for (const c of highConcerns) {
|
|
12943
|
+
fallbackPrompt.push(`- [${c.severity}] (${c.stage}) ${c.summary}`);
|
|
12944
|
+
}
|
|
12945
|
+
fallbackPrompt.push("");
|
|
12946
|
+
fallbackPrompt.push("The remaining cruise generations will NOT auto-start. This fitness phase now follows the supervised flow:");
|
|
12947
|
+
fallbackPrompt.push("");
|
|
12948
|
+
fallbackPrompt.push("1. Present the concern(s) above to the human together with a summary of this generation.");
|
|
12949
|
+
fallbackPrompt.push("2. Wait for the human's decision (continue, override, abort generation, etc.).");
|
|
12950
|
+
fallbackPrompt.push('3. Submit: `reap run completion --phase fitness --feedback "<human feedback>"`');
|
|
12951
|
+
fallbackPrompt.push("");
|
|
12952
|
+
fallbackPrompt.push("Cruise can be resumed manually with `reap cruise <N>` once the concern is resolved.");
|
|
12953
|
+
fallbackPrompt.push(...evaluatorSection);
|
|
12954
|
+
emitOutput({
|
|
12955
|
+
status: "prompt",
|
|
12956
|
+
command: "completion",
|
|
12957
|
+
phase: "fitness",
|
|
12958
|
+
completed: ["gate", "reflect", "cruise-aborted"],
|
|
12959
|
+
context: {
|
|
12960
|
+
id: s.id,
|
|
12961
|
+
goal: s.goal,
|
|
12962
|
+
cruiseMode: false,
|
|
12963
|
+
cruiseAborted: true,
|
|
12964
|
+
previousCruiseCount: config.cruiseCount,
|
|
12965
|
+
evaluatorConcerns: s.evaluatorConcerns,
|
|
12966
|
+
evaluator: evaluatorEnabled ? { enabled: true, prompt: fitnessEvaluatorPrompt } : { enabled: false }
|
|
12967
|
+
},
|
|
12968
|
+
prompt: fallbackPrompt.join(`
|
|
12969
|
+
`),
|
|
12970
|
+
nextCommand: "reap run completion --phase fitness"
|
|
12971
|
+
});
|
|
12972
|
+
return;
|
|
12973
|
+
}
|
|
12676
12974
|
if (cruise) {
|
|
12975
|
+
const cruisePrompt = [
|
|
12976
|
+
"## Completion — Fitness Phase (Cruise Mode)",
|
|
12977
|
+
"",
|
|
12978
|
+
`Cruise: ${config.cruiseCount}`,
|
|
12979
|
+
"",
|
|
12980
|
+
"### Self-Assessment (not self-fitness, but metacognition):",
|
|
12981
|
+
"1. Did this generation proceed as expected?",
|
|
12982
|
+
"2. Are there uncertain areas or risks?",
|
|
12983
|
+
"3. Are there items that need human confirmation?",
|
|
12984
|
+
"",
|
|
12985
|
+
'High confidence → auto-proceed: reap run completion --phase fitness --feedback "self-assessment: OK"',
|
|
12986
|
+
"Uncertain/risky → stop cruise and request human feedback"
|
|
12987
|
+
];
|
|
12988
|
+
cruisePrompt.push(...priorConcernsSection);
|
|
12989
|
+
cruisePrompt.push(...evaluatorSection);
|
|
12677
12990
|
emitOutput({
|
|
12678
12991
|
status: "prompt",
|
|
12679
12992
|
command: "completion",
|
|
12680
12993
|
phase: "fitness",
|
|
12681
12994
|
completed: ["gate", "reflect"],
|
|
12682
|
-
context: {
|
|
12683
|
-
|
|
12684
|
-
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12689
|
-
|
|
12690
|
-
|
|
12691
|
-
"3. Are there items that need human confirmation?",
|
|
12692
|
-
"",
|
|
12693
|
-
'High confidence → auto-proceed: reap run completion --phase fitness --feedback "self-assessment: OK"',
|
|
12694
|
-
"Uncertain/risky → stop cruise and request human feedback"
|
|
12695
|
-
].join(`
|
|
12995
|
+
context: {
|
|
12996
|
+
id: s.id,
|
|
12997
|
+
goal: s.goal,
|
|
12998
|
+
cruiseMode: true,
|
|
12999
|
+
cruiseCount: config.cruiseCount,
|
|
13000
|
+
evaluatorConcerns: s.evaluatorConcerns ?? [],
|
|
13001
|
+
evaluator: evaluatorEnabled ? { enabled: true, prompt: fitnessEvaluatorPrompt } : { enabled: false }
|
|
13002
|
+
},
|
|
13003
|
+
prompt: cruisePrompt.join(`
|
|
12696
13004
|
`),
|
|
12697
13005
|
nextCommand: "reap run completion --phase fitness"
|
|
12698
13006
|
});
|
|
12699
13007
|
} else {
|
|
13008
|
+
const supervisedPrompt = [
|
|
13009
|
+
"## Completion — Fitness Phase",
|
|
13010
|
+
"",
|
|
13011
|
+
"Collect feedback from the human.",
|
|
13012
|
+
"",
|
|
13013
|
+
"Present to the human:",
|
|
13014
|
+
"1. Summary of what was done in this generation",
|
|
13015
|
+
"2. What went well / areas for improvement",
|
|
13016
|
+
"3. Suggested next direction",
|
|
13017
|
+
"",
|
|
13018
|
+
'Submit: reap run completion --phase fitness --feedback "human feedback here"'
|
|
13019
|
+
];
|
|
13020
|
+
supervisedPrompt.push(...priorConcernsSection);
|
|
13021
|
+
supervisedPrompt.push(...evaluatorSection);
|
|
12700
13022
|
emitOutput({
|
|
12701
13023
|
status: "prompt",
|
|
12702
13024
|
command: "completion",
|
|
12703
13025
|
phase: "fitness",
|
|
12704
13026
|
completed: ["gate", "reflect"],
|
|
12705
|
-
context: {
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
"2. What went well / areas for improvement",
|
|
12714
|
-
"3. Suggested next direction",
|
|
12715
|
-
"",
|
|
12716
|
-
'Submit: reap run completion --phase fitness --feedback "human feedback here"'
|
|
12717
|
-
].join(`
|
|
13027
|
+
context: {
|
|
13028
|
+
id: s.id,
|
|
13029
|
+
goal: s.goal,
|
|
13030
|
+
cruiseMode: false,
|
|
13031
|
+
evaluatorConcerns: s.evaluatorConcerns ?? [],
|
|
13032
|
+
evaluator: evaluatorEnabled ? { enabled: true, prompt: fitnessEvaluatorPrompt } : { enabled: false }
|
|
13033
|
+
},
|
|
13034
|
+
prompt: supervisedPrompt.join(`
|
|
12718
13035
|
`),
|
|
12719
13036
|
nextCommand: "reap run completion --phase fitness"
|
|
12720
13037
|
});
|
|
@@ -12726,7 +13043,7 @@ async function execute12(paths, phase, feedback) {
|
|
|
12726
13043
|
const fitnessFeedback = s.fitnessFeedback;
|
|
12727
13044
|
const visionGoals = await readTextFile(paths.visionGoals);
|
|
12728
13045
|
const configContent = await readTextFile(paths.config);
|
|
12729
|
-
const config = configContent ?
|
|
13046
|
+
const config = configContent ? import_yaml15.default.parse(configContent) : null;
|
|
12730
13047
|
const maturity = detectMaturity(s.type, config?.cruiseCount);
|
|
12731
13048
|
const generationCount = await gm.countLineage();
|
|
12732
13049
|
setTransitionNonces(s, "completion:adapt");
|
|
@@ -12859,7 +13176,7 @@ async function execute12(paths, phase, feedback) {
|
|
|
12859
13176
|
}
|
|
12860
13177
|
|
|
12861
13178
|
// src/cli/commands/run/evolve.ts
|
|
12862
|
-
var
|
|
13179
|
+
var import_yaml16 = __toESM(require_dist(), 1);
|
|
12863
13180
|
import { readdir as readdir14 } from "fs/promises";
|
|
12864
13181
|
init_fs();
|
|
12865
13182
|
async function collectClarityInput(paths, generationType) {
|
|
@@ -12896,7 +13213,7 @@ async function execute13(paths, _phase) {
|
|
|
12896
13213
|
const gm = new GenerationManager(paths);
|
|
12897
13214
|
const state = await gm.current();
|
|
12898
13215
|
const configContent = await readTextFile(paths.config);
|
|
12899
|
-
const config = configContent ?
|
|
13216
|
+
const config = configContent ? import_yaml16.default.parse(configContent) : null;
|
|
12900
13217
|
const autoSubagent = config?.autoSubagent ?? true;
|
|
12901
13218
|
const cruise = config ? parseCruiseCount(config) : null;
|
|
12902
13219
|
const generationType = state?.type ?? "normal";
|
|
@@ -13972,19 +14289,19 @@ async function execute24(paths, _phase, extra) {
|
|
|
13972
14289
|
|
|
13973
14290
|
// src/cli/commands/run/report.ts
|
|
13974
14291
|
init_fs();
|
|
13975
|
-
var
|
|
14292
|
+
var import_yaml17 = __toESM(require_dist(), 1);
|
|
13976
14293
|
async function execute25(paths, _phase) {
|
|
13977
14294
|
let state = null;
|
|
13978
14295
|
try {
|
|
13979
14296
|
const raw = await readTextFile(paths.current);
|
|
13980
14297
|
if (raw)
|
|
13981
|
-
state =
|
|
14298
|
+
state = import_yaml17.default.parse(raw);
|
|
13982
14299
|
} catch {}
|
|
13983
14300
|
let projectName = "unknown";
|
|
13984
14301
|
try {
|
|
13985
14302
|
const raw = await readTextFile(paths.config);
|
|
13986
14303
|
if (raw) {
|
|
13987
|
-
const config =
|
|
14304
|
+
const config = import_yaml17.default.parse(raw);
|
|
13988
14305
|
projectName = config.project ?? "unknown";
|
|
13989
14306
|
}
|
|
13990
14307
|
} catch {}
|
|
@@ -14081,13 +14398,16 @@ async function execute26(stage, options) {
|
|
|
14081
14398
|
if (stage === "early-close") {
|
|
14082
14399
|
extra = JSON.stringify({ reason: options.reason, sourceAction: options.sourceAction, deferTasks: options.deferTasks });
|
|
14083
14400
|
}
|
|
14401
|
+
if (stage === "validation" && options.phase === "report-evaluator") {
|
|
14402
|
+
extra = JSON.stringify({ severity: options.severity, summary: options.summary });
|
|
14403
|
+
}
|
|
14084
14404
|
try {
|
|
14085
14405
|
await handler(paths, options.phase, extra);
|
|
14086
14406
|
} catch (err) {
|
|
14087
14407
|
try {
|
|
14088
14408
|
const configContent = await readTextFile(paths.config);
|
|
14089
14409
|
if (configContent) {
|
|
14090
|
-
const config =
|
|
14410
|
+
const config = import_yaml18.default.parse(configContent);
|
|
14091
14411
|
if (config.autoIssueReport !== false) {
|
|
14092
14412
|
const cmd = `reap run ${stage}${options.phase ? ` --phase ${options.phase}` : ""}`;
|
|
14093
14413
|
autoReport(cmd, err);
|
|
@@ -14230,7 +14550,7 @@ async function execute28(count) {
|
|
|
14230
14550
|
}
|
|
14231
14551
|
|
|
14232
14552
|
// src/cli/commands/install-skills.ts
|
|
14233
|
-
var
|
|
14553
|
+
var import_yaml19 = __toESM(require_dist(), 1);
|
|
14234
14554
|
|
|
14235
14555
|
// src/adapters/claude-code/install.ts
|
|
14236
14556
|
init_fs();
|
|
@@ -14280,18 +14600,35 @@ async function installSkills(_projectRoot) {
|
|
|
14280
14600
|
message: `Cleaned ${cleaned.length} stale skills, installed ${installed} skill files to ${targetDir}`
|
|
14281
14601
|
});
|
|
14282
14602
|
}
|
|
14283
|
-
|
|
14284
|
-
|
|
14285
|
-
|
|
14286
|
-
|
|
14603
|
+
var AGENT_PATTERN = /^reap-.+\.md$/;
|
|
14604
|
+
function agentsTemplateDir() {
|
|
14605
|
+
return __dirname2.includes("dist") ? join20(__dirname2, "..", "templates", "agents") : join20(__dirname2, "..", "..", "templates", "agents");
|
|
14606
|
+
}
|
|
14607
|
+
async function installAgents(home = homedir4()) {
|
|
14608
|
+
const targetDir = join20(home, ".claude", "agents");
|
|
14609
|
+
await ensureDir(targetDir);
|
|
14610
|
+
let cleaned = [];
|
|
14287
14611
|
try {
|
|
14288
|
-
const
|
|
14289
|
-
|
|
14290
|
-
|
|
14291
|
-
|
|
14292
|
-
|
|
14612
|
+
const existing = await readdir16(targetDir);
|
|
14613
|
+
cleaned = existing.filter((f) => AGENT_PATTERN.test(f));
|
|
14614
|
+
for (const file of cleaned) {
|
|
14615
|
+
await unlink2(join20(targetDir, file));
|
|
14616
|
+
}
|
|
14617
|
+
} catch {}
|
|
14618
|
+
let installed = 0;
|
|
14619
|
+
const files = [];
|
|
14620
|
+
const templateDir = agentsTemplateDir();
|
|
14621
|
+
try {
|
|
14622
|
+
const sources = await readdir16(templateDir);
|
|
14623
|
+
for (const file of sources) {
|
|
14624
|
+
if (!file.endsWith(".md"))
|
|
14625
|
+
continue;
|
|
14626
|
+
await cp2(join20(templateDir, file), join20(targetDir, file));
|
|
14627
|
+
files.push(file);
|
|
14628
|
+
installed++;
|
|
14293
14629
|
}
|
|
14294
14630
|
} catch {}
|
|
14631
|
+
return { cleaned, installed, files, targetDir };
|
|
14295
14632
|
}
|
|
14296
14633
|
async function installReapGuide() {
|
|
14297
14634
|
const reapHome = join20(homedir4(), ".reap");
|
|
@@ -14354,6 +14691,7 @@ var claudeCodeAdapter = {
|
|
|
14354
14691
|
},
|
|
14355
14692
|
async registerSessionIntegration(_projectRoot) {
|
|
14356
14693
|
await installSlashCommandsOnly();
|
|
14694
|
+
await installAgents();
|
|
14357
14695
|
await registerSessionHooks();
|
|
14358
14696
|
}
|
|
14359
14697
|
};
|
|
@@ -14559,11 +14897,45 @@ async function installSlashCommands(home = homedir5()) {
|
|
|
14559
14897
|
} catch {}
|
|
14560
14898
|
return { cleaned, installed, targetDir };
|
|
14561
14899
|
}
|
|
14900
|
+
var AGENT_PATTERN2 = /^reap-.+\.md$/;
|
|
14901
|
+
function agentsTemplateDir2() {
|
|
14902
|
+
return __dirname3.includes("dist") ? join21(__dirname3, "..", "templates", "agents") : join21(__dirname3, "..", "..", "templates", "agents");
|
|
14903
|
+
}
|
|
14904
|
+
function opencodeAgentsDir(home = homedir5()) {
|
|
14905
|
+
return join21(home, ".config", "opencode", "agent");
|
|
14906
|
+
}
|
|
14907
|
+
async function installAgents2(home = homedir5()) {
|
|
14908
|
+
const targetDir = opencodeAgentsDir(home);
|
|
14909
|
+
await ensureDir(targetDir);
|
|
14910
|
+
let cleaned = 0;
|
|
14911
|
+
try {
|
|
14912
|
+
const existing = await readdir17(targetDir);
|
|
14913
|
+
for (const file of existing) {
|
|
14914
|
+
if (AGENT_PATTERN2.test(file)) {
|
|
14915
|
+
await unlink3(join21(targetDir, file));
|
|
14916
|
+
cleaned++;
|
|
14917
|
+
}
|
|
14918
|
+
}
|
|
14919
|
+
} catch {}
|
|
14920
|
+
let installed = 0;
|
|
14921
|
+
const srcDir = agentsTemplateDir2();
|
|
14922
|
+
try {
|
|
14923
|
+
const sources = await readdir17(srcDir);
|
|
14924
|
+
for (const file of sources) {
|
|
14925
|
+
if (!file.endsWith(".md"))
|
|
14926
|
+
continue;
|
|
14927
|
+
await cp3(join21(srcDir, file), join21(targetDir, file));
|
|
14928
|
+
installed++;
|
|
14929
|
+
}
|
|
14930
|
+
} catch {}
|
|
14931
|
+
return { cleaned, installed, targetDir };
|
|
14932
|
+
}
|
|
14562
14933
|
async function installSkills2(projectRoot) {
|
|
14563
14934
|
await installPluginFile(projectRoot);
|
|
14564
14935
|
const opencodeJsonAction = await ensureOpencodeJson(projectRoot);
|
|
14565
14936
|
await installReapGuide2();
|
|
14566
14937
|
const slashCommands = await installSlashCommands();
|
|
14938
|
+
const agents = await installAgents2();
|
|
14567
14939
|
emitOutput({
|
|
14568
14940
|
status: "ok",
|
|
14569
14941
|
command: "install-skills",
|
|
@@ -14571,7 +14943,8 @@ async function installSkills2(projectRoot) {
|
|
|
14571
14943
|
"install-plugin",
|
|
14572
14944
|
"ensure-opencode-json",
|
|
14573
14945
|
"install-reap-guide",
|
|
14574
|
-
"install-slash-commands"
|
|
14946
|
+
"install-slash-commands",
|
|
14947
|
+
"install-agents"
|
|
14575
14948
|
],
|
|
14576
14949
|
context: {
|
|
14577
14950
|
agentClient: "opencode",
|
|
@@ -14581,15 +14954,21 @@ async function installSkills2(projectRoot) {
|
|
|
14581
14954
|
cleaned: slashCommands.cleaned,
|
|
14582
14955
|
installed: slashCommands.installed,
|
|
14583
14956
|
targetDir: slashCommands.targetDir
|
|
14957
|
+
},
|
|
14958
|
+
agents: {
|
|
14959
|
+
cleaned: agents.cleaned,
|
|
14960
|
+
installed: agents.installed,
|
|
14961
|
+
targetDir: agents.targetDir
|
|
14584
14962
|
}
|
|
14585
14963
|
},
|
|
14586
|
-
message: `OpenCode integration installed (opencode.json: ${opencodeJsonAction}, ` + `slash commands: ${slashCommands.installed} installed, ` + `${slashCommands.cleaned} cleaned).`
|
|
14964
|
+
message: `OpenCode integration installed (opencode.json: ${opencodeJsonAction}, ` + `slash commands: ${slashCommands.installed} installed, ` + `${slashCommands.cleaned} cleaned; ` + `agents: ${agents.installed} installed, ${agents.cleaned} cleaned).`
|
|
14587
14965
|
});
|
|
14588
14966
|
}
|
|
14589
14967
|
async function registerSessionIntegration(projectRoot) {
|
|
14590
14968
|
await installPluginFile(projectRoot);
|
|
14591
14969
|
await ensureOpencodeJson(projectRoot);
|
|
14592
14970
|
await installSlashCommands();
|
|
14971
|
+
await installAgents2();
|
|
14593
14972
|
}
|
|
14594
14973
|
|
|
14595
14974
|
// src/adapters/opencode/index.ts
|
|
@@ -14632,7 +15011,7 @@ async function execute29() {
|
|
|
14632
15011
|
const raw = await readTextFile(paths.config);
|
|
14633
15012
|
if (raw) {
|
|
14634
15013
|
try {
|
|
14635
|
-
const cfg =
|
|
15014
|
+
const cfg = import_yaml19.default.parse(raw);
|
|
14636
15015
|
agentClient = cfg.agentClient;
|
|
14637
15016
|
} catch {}
|
|
14638
15017
|
}
|
|
@@ -14642,7 +15021,7 @@ async function execute29() {
|
|
|
14642
15021
|
}
|
|
14643
15022
|
|
|
14644
15023
|
// src/cli/commands/fix.ts
|
|
14645
|
-
var
|
|
15024
|
+
var import_yaml20 = __toESM(require_dist(), 1);
|
|
14646
15025
|
import { mkdir as mkdir3, stat as stat4 } from "fs/promises";
|
|
14647
15026
|
import { join as join22, dirname as dirname8 } from "path";
|
|
14648
15027
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
@@ -14694,7 +15073,7 @@ async function fixProject(projectRoot) {
|
|
|
14694
15073
|
if (currentContent !== null) {
|
|
14695
15074
|
if (currentContent.trim()) {
|
|
14696
15075
|
try {
|
|
14697
|
-
const state =
|
|
15076
|
+
const state = import_yaml20.default.parse(currentContent);
|
|
14698
15077
|
const allStages = [
|
|
14699
15078
|
...LIFECYCLE_STAGES,
|
|
14700
15079
|
...MERGE_STAGES
|
|
@@ -14761,7 +15140,7 @@ async function fixProject(projectRoot) {
|
|
|
14761
15140
|
{
|
|
14762
15141
|
const { ensureClaudeMd: ensureClaudeMd2 } = await Promise.resolve().then(() => (init_common(), exports_common));
|
|
14763
15142
|
const configContent = await readTextFile(paths.config);
|
|
14764
|
-
const projectName = configContent ?
|
|
15143
|
+
const projectName = configContent ? import_yaml20.default.parse(configContent)?.project ?? "my-project" : "my-project";
|
|
14765
15144
|
const action = await ensureClaudeMd2(paths.root, projectName);
|
|
14766
15145
|
if (action !== "skipped") {
|
|
14767
15146
|
fixed.push(`CLAUDE.md ${action} with REAP section`);
|
|
@@ -14810,7 +15189,7 @@ async function execute30(check) {
|
|
|
14810
15189
|
}
|
|
14811
15190
|
|
|
14812
15191
|
// src/cli/commands/destroy.ts
|
|
14813
|
-
var
|
|
15192
|
+
var import_yaml21 = __toESM(require_dist(), 1);
|
|
14814
15193
|
import { rm as rm6 } from "fs/promises";
|
|
14815
15194
|
import { join as join23 } from "path";
|
|
14816
15195
|
init_fs();
|
|
@@ -14821,7 +15200,7 @@ async function getProjectName(projectRoot) {
|
|
|
14821
15200
|
const content = await readTextFile(paths.config);
|
|
14822
15201
|
if (!content)
|
|
14823
15202
|
return null;
|
|
14824
|
-
const config =
|
|
15203
|
+
const config = import_yaml21.default.parse(content);
|
|
14825
15204
|
return config.project ?? null;
|
|
14826
15205
|
} catch {
|
|
14827
15206
|
return null;
|
|
@@ -15069,7 +15448,7 @@ async function execute32(options) {
|
|
|
15069
15448
|
|
|
15070
15449
|
// src/cli/commands/check-version.ts
|
|
15071
15450
|
init_integrity();
|
|
15072
|
-
var
|
|
15451
|
+
var import_yaml22 = __toESM(require_dist(), 1);
|
|
15073
15452
|
import { execSync as execSync7 } from "child_process";
|
|
15074
15453
|
import { readFileSync as readFileSync3 } from "fs";
|
|
15075
15454
|
import { join as join26 } from "path";
|
|
@@ -15251,7 +15630,7 @@ async function execute33() {
|
|
|
15251
15630
|
try {
|
|
15252
15631
|
const configPath = join26(root, ".reap", "config.yml");
|
|
15253
15632
|
const configContent = readFileSync3(configPath, "utf-8");
|
|
15254
|
-
const config =
|
|
15633
|
+
const config = import_yaml22.default.parse(configContent);
|
|
15255
15634
|
const language = config?.language ?? "english";
|
|
15256
15635
|
const notice = fetchReleaseNotice(result.to, language);
|
|
15257
15636
|
if (notice)
|
|
@@ -15261,7 +15640,7 @@ async function execute33() {
|
|
|
15261
15640
|
}
|
|
15262
15641
|
|
|
15263
15642
|
// src/cli/commands/config.ts
|
|
15264
|
-
var
|
|
15643
|
+
var import_yaml23 = __toESM(require_dist(), 1);
|
|
15265
15644
|
init_fs();
|
|
15266
15645
|
init_integrity();
|
|
15267
15646
|
async function execute34() {
|
|
@@ -15277,7 +15656,7 @@ async function execute34() {
|
|
|
15277
15656
|
if (!configContent) {
|
|
15278
15657
|
emitError("config", "Config file is empty.");
|
|
15279
15658
|
}
|
|
15280
|
-
const config =
|
|
15659
|
+
const config = import_yaml23.default.parse(configContent);
|
|
15281
15660
|
emitOutput({
|
|
15282
15661
|
status: "ok",
|
|
15283
15662
|
command: "config",
|
|
@@ -15296,7 +15675,7 @@ async function execute34() {
|
|
|
15296
15675
|
}
|
|
15297
15676
|
|
|
15298
15677
|
// src/cli/commands/update.ts
|
|
15299
|
-
var
|
|
15678
|
+
var import_yaml24 = __toESM(require_dist(), 1);
|
|
15300
15679
|
import { readFileSync as readFileSync4 } from "fs";
|
|
15301
15680
|
import { join as join27, dirname as dirname10 } from "path";
|
|
15302
15681
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
@@ -15355,7 +15734,7 @@ async function backfillConfig(paths) {
|
|
|
15355
15734
|
return { added: [], removed: [] };
|
|
15356
15735
|
let config;
|
|
15357
15736
|
try {
|
|
15358
|
-
config =
|
|
15737
|
+
config = import_yaml24.default.parse(content) ?? {};
|
|
15359
15738
|
} catch {
|
|
15360
15739
|
return { added: [], removed: [] };
|
|
15361
15740
|
}
|
|
@@ -15381,7 +15760,7 @@ async function backfillConfig(paths) {
|
|
|
15381
15760
|
}
|
|
15382
15761
|
}
|
|
15383
15762
|
if (added.length > 0 || removed.length > 0) {
|
|
15384
|
-
await writeTextFile(paths.config,
|
|
15763
|
+
await writeTextFile(paths.config, import_yaml24.default.stringify(config));
|
|
15385
15764
|
}
|
|
15386
15765
|
return { added, removed };
|
|
15387
15766
|
}
|
|
@@ -15432,7 +15811,7 @@ async function execute35(phase, postUpgrade) {
|
|
|
15432
15811
|
let agentClient;
|
|
15433
15812
|
if (configContent) {
|
|
15434
15813
|
try {
|
|
15435
|
-
const config =
|
|
15814
|
+
const config = import_yaml24.default.parse(configContent);
|
|
15436
15815
|
projectName = config.project ?? "my-project";
|
|
15437
15816
|
agentClient = config.agentClient;
|
|
15438
15817
|
} catch {}
|
|
@@ -15449,7 +15828,7 @@ async function execute35(phase, postUpgrade) {
|
|
|
15449
15828
|
const raw = configContent ?? "";
|
|
15450
15829
|
if (raw) {
|
|
15451
15830
|
try {
|
|
15452
|
-
const cfg =
|
|
15831
|
+
const cfg = import_yaml24.default.parse(raw);
|
|
15453
15832
|
if (cfg?.language)
|
|
15454
15833
|
language = cfg.language;
|
|
15455
15834
|
} catch {}
|
|
@@ -15816,7 +16195,7 @@ ${lines.join(`
|
|
|
15816
16195
|
}
|
|
15817
16196
|
|
|
15818
16197
|
// src/cli/commands/load-context.ts
|
|
15819
|
-
var
|
|
16198
|
+
var import_yaml25 = __toESM(require_dist(), 1);
|
|
15820
16199
|
import { join as join29 } from "path";
|
|
15821
16200
|
init_fs();
|
|
15822
16201
|
async function buildKnowledgeContext(cwd) {
|
|
@@ -15832,13 +16211,13 @@ async function buildKnowledgeContext(cwd) {
|
|
|
15832
16211
|
let config = null;
|
|
15833
16212
|
if (configContent) {
|
|
15834
16213
|
try {
|
|
15835
|
-
config =
|
|
16214
|
+
config = import_yaml25.default.parse(configContent);
|
|
15836
16215
|
} catch {}
|
|
15837
16216
|
}
|
|
15838
16217
|
let state = null;
|
|
15839
16218
|
if (currentContent) {
|
|
15840
16219
|
try {
|
|
15841
|
-
state =
|
|
16220
|
+
state = import_yaml25.default.parse(currentContent);
|
|
15842
16221
|
} catch {}
|
|
15843
16222
|
}
|
|
15844
16223
|
const sections = [];
|
|
@@ -15951,7 +16330,7 @@ program2.command("init [project-name]").description("Initialize a new reap proje
|
|
|
15951
16330
|
program2.command("status").description("Show current project status").action(async () => {
|
|
15952
16331
|
await execute6();
|
|
15953
16332
|
});
|
|
15954
|
-
program2.command("run <stage>").description("Run a lifecycle stage (start, learning, planning, ...)").option("--phase <phase>", "Stage phase (work, complete, reflect, fitness, adapt, commit)").option("--goal <goal>", "Goal for start command").option("--type <type>", "Generation type (embryo, normal, merge)").option("--parents <parents>", "Parent generation IDs for merge (comma-separated)").option("--feedback <feedback>", "Fitness feedback text").option("--reason <reason>", "Reason for back regression or abort").option("--backlog <backlog>", "Backlog filename to consume for this generation (use --no-backlog to explicitly declare no relevant backlog)").option("--no-backlog", "Explicitly declare no backlog item is relevant to this generation (suppresses pending-backlog prompt). Negates --backlog.").option("--source-action <sourceAction>", "Source action for abort/early-close (rollback, stash, hold, none — rollback only for abort)").option("--save-backlog", "Save progress to backlog on abort").option("--defer-tasks <value>", "For early-close: auto-defer unchecked tasks to new backlog (true|false, default true)").action(async (stage, options) => {
|
|
16333
|
+
program2.command("run <stage>").description("Run a lifecycle stage (start, learning, planning, ...)").option("--phase <phase>", "Stage phase (work, complete, reflect, fitness, adapt, commit)").option("--goal <goal>", "Goal for start command").option("--type <type>", "Generation type (embryo, normal, merge)").option("--parents <parents>", "Parent generation IDs for merge (comma-separated)").option("--feedback <feedback>", "Fitness feedback text").option("--reason <reason>", "Reason for back regression or abort").option("--backlog <backlog>", "Backlog filename to consume for this generation (use --no-backlog to explicitly declare no relevant backlog)").option("--no-backlog", "Explicitly declare no backlog item is relevant to this generation (suppresses pending-backlog prompt). Negates --backlog.").option("--source-action <sourceAction>", "Source action for abort/early-close (rollback, stash, hold, none — rollback only for abort)").option("--save-backlog", "Save progress to backlog on abort").option("--defer-tasks <value>", "For early-close: auto-defer unchecked tasks to new backlog (true|false, default true)").option("--severity <severity>", "For validation --phase report-evaluator: evaluator concern severity (high|low|none — none is a no-op)").option("--summary <summary>", "For validation --phase report-evaluator: one-line evaluator concern summary").action(async (stage, options) => {
|
|
15955
16334
|
await execute26(stage, options);
|
|
15956
16335
|
});
|
|
15957
16336
|
program2.command("make <resource>").description("Create a resource from template (backlog, hook)").option("--type <type>", "Resource type (backlog: genome-change/environment-change/task, hook: sh/md)").option("--title <title>", "Resource title").option("--body <body>", "Optional description body").option("--priority <priority>", "Priority (high, medium, low)").option("--event <event>", "Hook event (e.g. onLifeCompleted)").option("--name <name>", "Hook name").option("--condition <condition>", "Hook condition (default: always)").option("--order <order>", "Hook execution order (default: 50)").action(async (resource, options) => {
|
|
@@ -317,6 +317,10 @@ Switching clients is a config edit: change `agentClient` in `.reap/config.yml`,
|
|
|
317
317
|
|
|
318
318
|
For OpenCode users, see `AGENTS.md` (auto-generated by `reap update`) for the project-level entry-point. `~/.reap/reap-guide.md` (installed by `reap install-skills`) is referenced from `AGENTS.md` and remains the canonical REAP usage guide.
|
|
319
319
|
|
|
320
|
+
### Evaluator agent (opt-in)
|
|
321
|
+
|
|
322
|
+
Both adapters install bundled agent definitions (`reap-evolve.md`, `reap-evaluate.md`) to the client's user-level agents directory (`~/.claude/agents/` for Claude Code, `~/.config/opencode/agent/` for OpenCode). The install runs on `reap install-skills` AND `reap update`, so user-level agents stay in sync with the bundled REAP version. Set `evaluator: true` in `.reap/config.yml` to launch `reap-evaluate` as an independent reviewer during the validation stage (advisor role — the builder owns the final verdict).
|
|
323
|
+
|
|
320
324
|
## Role Separation
|
|
321
325
|
|
|
322
326
|
| Component | Role |
|
package/package.json
CHANGED