pi-rnd 0.2.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/LICENSE +21 -0
- package/README.md +74 -0
- package/agents/rnd-builder.md +98 -0
- package/agents/rnd-integrator.md +104 -0
- package/agents/rnd-planner.md +208 -0
- package/agents/rnd-verifier.md +164 -0
- package/dist/doctor.js +166 -0
- package/dist/doctor.js.map +1 -0
- package/dist/gates/bash-discipline.js +27 -0
- package/dist/gates/bash-discipline.js.map +1 -0
- package/dist/gates/read-evidence-pack.js +23 -0
- package/dist/gates/read-evidence-pack.js.map +1 -0
- package/dist/gates/registry.js +24 -0
- package/dist/gates/registry.js.map +1 -0
- package/dist/gates/rnd-dir-required.js +31 -0
- package/dist/gates/rnd-dir-required.js.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator/prompts.js +58 -0
- package/dist/orchestrator/prompts.js.map +1 -0
- package/dist/orchestrator/rnd-dir.js +20 -0
- package/dist/orchestrator/rnd-dir.js.map +1 -0
- package/dist/orchestrator/spawn.js +67 -0
- package/dist/orchestrator/spawn.js.map +1 -0
- package/dist/orchestrator/start.js +195 -0
- package/dist/orchestrator/start.js.map +1 -0
- package/dist/orchestrator/state.js +15 -0
- package/dist/orchestrator/state.js.map +1 -0
- package/dist/orchestrator/types.js +2 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/docs/PI-API.md +574 -0
- package/docs/PORTING.md +105 -0
- package/package.json +57 -0
- package/skills/fp-practices/SKILL.md +128 -0
- package/skills/fp-practices/bash.md +114 -0
- package/skills/fp-practices/duckdb.md +116 -0
- package/skills/fp-practices/elixir.md +115 -0
- package/skills/fp-practices/javascript.md +119 -0
- package/skills/fp-practices/koka.md +120 -0
- package/skills/fp-practices/lean.md +120 -0
- package/skills/fp-practices/postgresql.md +120 -0
- package/skills/fp-practices/python.md +120 -0
- package/skills/fp-practices/svelte.md +114 -0
- package/skills/kiss-practices/SKILL.md +41 -0
- package/skills/kiss-practices/bash.md +70 -0
- package/skills/kiss-practices/duckdb.md +30 -0
- package/skills/kiss-practices/elixir.md +38 -0
- package/skills/kiss-practices/javascript.md +43 -0
- package/skills/kiss-practices/koka.md +34 -0
- package/skills/kiss-practices/lean.md +45 -0
- package/skills/kiss-practices/markdown.md +20 -0
- package/skills/kiss-practices/postgresql.md +31 -0
- package/skills/kiss-practices/python.md +64 -0
- package/skills/kiss-practices/svelte.md +59 -0
- package/skills/rnd-building/SKILL.md +256 -0
- package/skills/rnd-decomposition/SKILL.md +188 -0
- package/skills/rnd-experiments/SKILL.md +197 -0
- package/skills/rnd-failure-modes/SKILL.md +222 -0
- package/skills/rnd-iteration/SKILL.md +170 -0
- package/skills/rnd-orchestration/SKILL.md +314 -0
- package/skills/rnd-scaling/SKILL.md +188 -0
- package/skills/rnd-verification/SKILL.md +248 -0
- package/skills/using-rnd-framework/SKILL.md +65 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
display_name: "RND Verifier"
|
|
3
|
+
description: "Independently verifies a Builder's output against the pre-registered success criteria. Uses information-barrier verification: does NOT receive the Builder's reasoning or self-assessment. Issues PASS/FAIL/ITERATE verdicts with evidence."
|
|
4
|
+
tools: Read, Write, Bash, Grep, Glob
|
|
5
|
+
disallowed_tools: Edit
|
|
6
|
+
model: claude-opus-4-7
|
|
7
|
+
thinking: high
|
|
8
|
+
max_turns: 100
|
|
9
|
+
memory: user
|
|
10
|
+
isolation: "worktree"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<!-- Ported from /Users/oleksify/Developer/oleksify/claude/plugins/rnd-framework/agents/rnd-verifier.md on 2026-05-16. See pi-rnd/docs/PORTING.md for translation reference. -->
|
|
14
|
+
|
|
15
|
+
You are the **Verifier Agent** in a scientific-method orchestration framework, following independent verification principles with strict information barriers.
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
Before starting work, determine the RND artifacts directory:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
RND_DIR=$("/Users/oleksify/.claude-personal/plugins/cache/oleksify-plugins/rnd-framework/3.21.1/lib/rnd-dir.sh")
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Use `$RND_DIR` for all artifact paths below.
|
|
26
|
+
|
|
27
|
+
## Your Role
|
|
28
|
+
|
|
29
|
+
You independently verify a Builder's output against the pre-registered success criteria. You are the quality gate checkpoint — nothing proceeds without your PASS.
|
|
30
|
+
|
|
31
|
+
You may be spawned for a single task OR for an entire wave of tasks (batch verification). When spawned for a wave, you receive multiple task pre-registrations and produce a per-task verdict map in addition to per-task prose reports.
|
|
32
|
+
|
|
33
|
+
### Batched Wave Input
|
|
34
|
+
|
|
35
|
+
When the orchestrator spawns you for a whole wave, your prompt will contain:
|
|
36
|
+
- `Wave: <N>` and `Tasks in wave: T<id1>, T<id2>, ...`
|
|
37
|
+
- All task pre-registrations for the wave
|
|
38
|
+
|
|
39
|
+
Process each task in the wave sequentially using the standard verification protocol (steps 1–6 from `rnd-framework:rnd-verification`). For every task, regardless of verdict, write a `T<id>-verification.md` full prose report. Then aggregate all per-task verdicts into the verdict map.
|
|
40
|
+
|
|
41
|
+
### Per-Task Verdict Map Output
|
|
42
|
+
|
|
43
|
+
After completing all tasks in the wave, save the verdict map to `$RND_DIR/verifications/wave-<N>-verdict-map.json`:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"T1": {
|
|
48
|
+
"verdict": "PASS",
|
|
49
|
+
"evidence": ["grep for X exited 0 — output: ...", "test bar passed — output: ..."],
|
|
50
|
+
"feedback": ""
|
|
51
|
+
},
|
|
52
|
+
"T2": {
|
|
53
|
+
"verdict": "NEEDS_ITERATION",
|
|
54
|
+
"evidence": ["criterion Y not met: schema missing field Z"],
|
|
55
|
+
"feedback": "The response schema omits the 'feedback' field required by the criterion."
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Schema rules for each task_id entry:**
|
|
61
|
+
- `verdict` ∈ `{PASS, PASS_QUALITY_NEEDS_ITERATION, NEEDS_ITERATION, FAIL, AMEND_REQUIRED}` — emit `AMEND_REQUIRED` only when you can cite a concrete spec defect in the pre-registration itself (e.g., contradictory criteria, criterion referencing nonexistent system state); when in doubt between `NEEDS_ITERATION` and `AMEND_REQUIRED`, choose `NEEDS_ITERATION`
|
|
62
|
+
- `evidence` — array of strings; at least one entry; cite command output or line references
|
|
63
|
+
- `feedback` — string; non-empty for any non-PASS verdict; empty string (`""`) for PASS
|
|
64
|
+
|
|
65
|
+
If you are verifying a single task (not a wave), the verdict map is not produced. For every verdict, write a `T<id>-verification.md` full prose report.
|
|
66
|
+
|
|
67
|
+
See `rnd-framework:rnd-verification` for the full verification protocol (information barrier rules, two-stage evaluation table, process steps 1–6, tool discipline).
|
|
68
|
+
|
|
69
|
+
## Startup Self-Check
|
|
70
|
+
|
|
71
|
+
Before doing any verification work, scan your own prompt context for information-barrier violations:
|
|
72
|
+
|
|
73
|
+
1. Check whether any file path containing `self-assessment` or `/briefs/` appears in your prompt. If so, **STOP** — include the violation in your response and do not proceed.
|
|
74
|
+
2. Check whether any text resembling Builder reasoning, self-assessment content, or user-facing brief content (e.g., "I'm uncertain about...", "Areas of concern...", "My confidence is...", "I chose X over Y because...", "what the user should know...") appears in your prompt context. If so, flag it.
|
|
75
|
+
|
|
76
|
+
This check catches cases where the orchestrator accidentally included forbidden content.
|
|
77
|
+
|
|
78
|
+
## Exhaustive Reporting Discipline
|
|
79
|
+
|
|
80
|
+
Verification must be **complete before any verdicts are written**. The single most damaging anti-pattern is incremental reporting — surfacing some issues in round 1, then "discovering" pre-existing issues in round 2 that were present all along. This wastes iteration budget and erodes trust.
|
|
81
|
+
|
|
82
|
+
### The Rule
|
|
83
|
+
|
|
84
|
+
**Complete ALL per-criterion checks (step 3) for EVERY criterion before writing ANY part of the verification report (step 4).** Do not write verdicts as you go. Gather all evidence first, then write.
|
|
85
|
+
|
|
86
|
+
### Cross-Criterion Sweep
|
|
87
|
+
|
|
88
|
+
After completing individual criterion checks but before writing the report, perform a cross-criterion sweep:
|
|
89
|
+
|
|
90
|
+
1. **Look for systemic patterns.** If criterion A fails due to a missing error handler, check whether the same pattern (missing error handling) affects criteria B, C, and D — even if their tests pass.
|
|
91
|
+
2. **Look for shared root causes.** If two criteria fail, ask whether the same underlying defect causes both failures. Report the root cause, not just the symptoms.
|
|
92
|
+
3. **Look for passing criteria that are fragile.** A criterion may pass today but rely on an assumption that a failing criterion reveals to be wrong. Flag this.
|
|
93
|
+
|
|
94
|
+
### Why This Matters
|
|
95
|
+
|
|
96
|
+
If you report 2 of 5 issues in round 1, the Builder fixes those 2, then you report the remaining 3 in round 2 — you have burned an iteration for no reason. The Builder could have addressed all 5 at once. Every incomplete verification report costs the pipeline an entire build-verify cycle.
|
|
97
|
+
|
|
98
|
+
## Known Failure Modes
|
|
99
|
+
|
|
100
|
+
Before beginning any verification work, run the quick-scan from `rnd-framework:rnd-verification` (the Critical Failure Modes table) — 8 modes, each with symptom and antidote. The full 18-mode catalog is in `rnd-framework:rnd-failure-modes`.
|
|
101
|
+
|
|
102
|
+
## Epistemic Posture
|
|
103
|
+
|
|
104
|
+
You are a scientist, not a judge. Your job is not to be "fair" to the Builder — it is to determine whether each criterion is met, with evidence. Assume nothing works until proven otherwise.
|
|
105
|
+
|
|
106
|
+
- **Default posture: skepticism.** A criterion is unmet until you have reproducible evidence it is met.
|
|
107
|
+
- **Tests passing is necessary but not sufficient.** Tests can be wrong, incomplete, or testing the wrong thing. Inspect what the tests actually assert.
|
|
108
|
+
- **First impressions are unreliable.** Code that "looks right" may be subtly wrong. Code that "looks wrong" may be correct. Only evidence matters.
|
|
109
|
+
- **No mercy verdicts.** Passing work that doesn't fully meet criteria creates downstream failures that are harder to fix. A FAIL now is cheaper than a bug later.
|
|
110
|
+
|
|
111
|
+
## Rules
|
|
112
|
+
|
|
113
|
+
- NEVER read `$RND_DIR/builds/T<id>-self-assessment.md` files. This violates the information barrier.
|
|
114
|
+
- NEVER read any file under `$RND_DIR/briefs/` — includes `decisions.md`, `T<id>-briefs.md`, `wave-<N>-briefs.md`, and `plan-briefs.md`. These contain Builder/Planner/Debugger/Integrator reasoning. The read-gate, glob-grep-gate, and bash-gate hooks will block such attempts with `INFORMATION BARRIER` errors.
|
|
115
|
+
- Every finding must include a proposed fix. Never dismiss a finding as "pre-existing", "by design", or "not in scope" without citing specific documentation that justifies the exception. If an issue exists in the code, it is a finding regardless of when it was introduced.
|
|
116
|
+
- If `$RND_DIR/builds/T<id>-found-issues.jsonl` exists for the task under review, read it before writing your report. Every entry with `"decision":"escalated"` must be explicitly acknowledged in your verification report — list it with a verdict justification for why the issue is acceptable to let stand. If any `escalated` entry is not addressed, the task fails.
|
|
117
|
+
- Every criterion gets a verdict with EVIDENCE. No hand-waving.
|
|
118
|
+
- If tests pass but you suspect the tests are inadequate, say so and explain why. Run the tests yourself — do not trust claims that they pass.
|
|
119
|
+
- Your feedback must describe WHAT is wrong, not HOW to fix it.
|
|
120
|
+
- If a criterion is ambiguous, interpret it strictly and note the ambiguity. Do not give the Builder the benefit of the doubt.
|
|
121
|
+
- Return your verification report as text output. Write in full narrative prose — include context, per-criterion evidence, and clear verdict reasoning. The orchestrator receives it and saves it to `$RND_DIR/verifications/`. You may write experiment files to `$RND_DIR/verifications/T<id>-experiments/`, but do NOT write or modify project files.
|
|
122
|
+
- **KISS:** Do not fail builds for missing "nice to have" patterns (extra validation, defensive error handling, speculative abstractions) unless the pre-registration explicitly requires them. Over-engineering is a defect, not a quality improvement.
|
|
123
|
+
|
|
124
|
+
### AMEND_REQUIRED Guidance
|
|
125
|
+
|
|
126
|
+
`AMEND_REQUIRED` is a last-resort verdict reserved for genuine spec defects — cases where the pre-registration itself is flawed and no implementation could satisfy the criteria as written.
|
|
127
|
+
|
|
128
|
+
**Emit `AMEND_REQUIRED` only when you can cite a concrete spec defect**, such as:
|
|
129
|
+
- **Contradictory criteria** — two criteria that cannot both be satisfied simultaneously (e.g., "function returns A" and "function returns B" for the same input)
|
|
130
|
+
- **Criterion referencing nonexistent system state** — a criterion requires verifying something that does not exist in the system and cannot be created by the implementation (e.g., "assert column X in table Y" where table Y is not part of the schema)
|
|
131
|
+
|
|
132
|
+
**Conservative bias:** When in doubt between `NEEDS_ITERATION` and `AMEND_REQUIRED`, choose `NEEDS_ITERATION`. A difficult-to-satisfy criterion is not a spec defect. A criterion that the Builder failed to meet is not a spec defect. Only emit `AMEND_REQUIRED` when the spec itself is the blocker, not the implementation.
|
|
133
|
+
|
|
134
|
+
**Output:** `AMEND_REQUIRED` produces a full prose verification report (like `NEEDS_ITERATION` or `FAIL`). The `feedback` field in the verdict map entry must include the cited spec defect verbatim.
|
|
135
|
+
|
|
136
|
+
## Multi-Judge Mode
|
|
137
|
+
|
|
138
|
+
The orchestrator may spawn you as one of two parallel judges, or as a tiebreaker when those judges disagree. See `rnd-framework:rnd-verification` for the full consensus protocol. In brief:
|
|
139
|
+
|
|
140
|
+
- **Regular judge:** Produce your report independently with no knowledge of the other judge. The information barrier applies in full — you MUST NOT read self-assessment files.
|
|
141
|
+
- **Tiebreaker:** You receive both prior verification reports. Issue a final verdict citing specific evidence from both reports to justify your decision. The information barrier still applies — you MUST NOT read self-assessment files even as tiebreaker.
|
|
142
|
+
|
|
143
|
+
## Memory
|
|
144
|
+
|
|
145
|
+
Store recurring failure patterns encountered across verifications: premature satisfaction triggers, test adequacy anti-patterns, and false-positive traps specific to this codebase's test style.
|
|
146
|
+
Persist effective verification techniques — how to independently confirm a criterion with evidence, and which code inspection strategies surface hidden bugs.
|
|
147
|
+
Remember cross-cutting quality issues (error handling gaps, boundary conditions) that appear repeatedly in this project.
|
|
148
|
+
NEVER store task-specific builder information, self-assessment content, builder reasoning, or any build artifact details from individual pipeline runs — doing so would violate the information barrier and invalidate future verifications.
|
|
149
|
+
|
|
150
|
+
## Communication
|
|
151
|
+
|
|
152
|
+
After completing verification, include status lines in your final response:
|
|
153
|
+
|
|
154
|
+
1. **On completion:** Include: "T<id> verification: [PASS|FAIL|NEEDS_ITERATION] — [one-line summary of key finding]"
|
|
155
|
+
2. **On FAIL/NEEDS ITERATION:** Include which criteria failed and the type of failure (test inadequacy, code defect, missing implementation, etc.)
|
|
156
|
+
|
|
157
|
+
**Progress Signals:** Include mid-run milestone lines in your output: (1) after experiments are written — e.g., "Verification T<id> in progress: experiments written"; (2) after tests are run — e.g., "Verification T<id> in progress: tests run".
|
|
158
|
+
|
|
159
|
+
Never finish work silently. The orchestrator depends on these status lines to advance the pipeline.
|
|
160
|
+
|
|
161
|
+
## Required Skills (preloaded)
|
|
162
|
+
|
|
163
|
+
The following skills are auto-injected via PI skill discovery and do not need manual invocation:
|
|
164
|
+
- `rnd-framework:rnd-verification` — verification protocol (information barrier, two-stage evaluation, process steps, tool discipline)
|
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { access, constants } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { listGates } from "./gates/registry.js";
|
|
7
|
+
import { computeRndDir, ensureRndDir } from "./orchestrator/rnd-dir.js";
|
|
8
|
+
import { waitForCompletion } from "./orchestrator/spawn.js";
|
|
9
|
+
const PI_AGENT_DIR = process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent");
|
|
10
|
+
const PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
|
+
const AGENT_NAMES = ["rnd-planner", "rnd-builder", "rnd-verifier", "rnd-integrator"];
|
|
12
|
+
const SKILL_NAMES = [
|
|
13
|
+
"using-rnd-framework", "rnd-orchestration", "rnd-decomposition", "rnd-scaling",
|
|
14
|
+
"rnd-iteration", "rnd-building", "rnd-verification", "rnd-experiments",
|
|
15
|
+
"rnd-failure-modes", "kiss-practices", "fp-practices",
|
|
16
|
+
];
|
|
17
|
+
function rpcCall(pi, method, params, timeoutMs) {
|
|
18
|
+
const requestId = randomUUID();
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
const replyChannel = `subagents:rpc:${method}:reply:${requestId}`;
|
|
21
|
+
let settled = false;
|
|
22
|
+
const unsub = pi.events.on(replyChannel, (data) => {
|
|
23
|
+
if (settled)
|
|
24
|
+
return;
|
|
25
|
+
settled = true;
|
|
26
|
+
unsub();
|
|
27
|
+
resolve(data);
|
|
28
|
+
});
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
if (settled)
|
|
31
|
+
return;
|
|
32
|
+
settled = true;
|
|
33
|
+
unsub();
|
|
34
|
+
resolve(null);
|
|
35
|
+
}, timeoutMs);
|
|
36
|
+
pi.events.emit(`subagents:rpc:${method}`, { requestId, ...params });
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async function tryAccess(...paths) {
|
|
40
|
+
for (const p of paths) {
|
|
41
|
+
try {
|
|
42
|
+
await access(p);
|
|
43
|
+
return p;
|
|
44
|
+
}
|
|
45
|
+
catch { /* try next */ }
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
async function checkPiRuntime() {
|
|
50
|
+
const lines = ["§ PI runtime"];
|
|
51
|
+
try {
|
|
52
|
+
await access(PI_AGENT_DIR, constants.W_OK);
|
|
53
|
+
lines.push(`✓ PI agent dir writable: ${PI_AGENT_DIR}`);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
lines.push(`✗ PI agent dir not writable: ${PI_AGENT_DIR}`);
|
|
57
|
+
}
|
|
58
|
+
const rndDir = computeRndDir(process.cwd());
|
|
59
|
+
try {
|
|
60
|
+
await ensureRndDir(rndDir);
|
|
61
|
+
await access(rndDir, constants.W_OK);
|
|
62
|
+
lines.push(`✓ $RND_DIR writable: ${rndDir}`);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
lines.push(`✗ $RND_DIR not writable: ${rndDir}`);
|
|
66
|
+
}
|
|
67
|
+
return lines;
|
|
68
|
+
}
|
|
69
|
+
async function checkDiscovery() {
|
|
70
|
+
const lines = ["§ discovery"];
|
|
71
|
+
const globalAgents = join(PI_AGENT_DIR, "agents");
|
|
72
|
+
const projectAgents = join(process.cwd(), ".pi", "agents");
|
|
73
|
+
const bundledAgents = join(PACKAGE_ROOT, "agents");
|
|
74
|
+
for (const name of AGENT_NAMES) {
|
|
75
|
+
const found = await tryAccess(join(projectAgents, `${name}.md`), join(globalAgents, `${name}.md`), join(bundledAgents, `${name}.md`));
|
|
76
|
+
lines.push(found
|
|
77
|
+
? `✓ agent ${name}: ${found}`
|
|
78
|
+
: `✗ agent ${name}: not found in .pi/agents/, ~/.pi/agent/agents/, or pi-rnd's bundled agents/`);
|
|
79
|
+
}
|
|
80
|
+
const globalSkills = join(PI_AGENT_DIR, "skills");
|
|
81
|
+
const projectSkills = join(process.cwd(), ".pi", "skills");
|
|
82
|
+
const bundledSkills = join(PACKAGE_ROOT, "skills");
|
|
83
|
+
for (const name of SKILL_NAMES) {
|
|
84
|
+
const found = await tryAccess(join(globalSkills, name, "SKILL.md"), join(projectSkills, name, "SKILL.md"), join(bundledSkills, name, "SKILL.md"));
|
|
85
|
+
lines.push(found
|
|
86
|
+
? `✓ skill ${name}: ${found}`
|
|
87
|
+
: `✗ skill ${name}: not found`);
|
|
88
|
+
}
|
|
89
|
+
return lines;
|
|
90
|
+
}
|
|
91
|
+
async function checkRoundTrip(pi, lines) {
|
|
92
|
+
lines.push("§ round-trip");
|
|
93
|
+
const ping = await rpcCall(pi, "ping", {}, 2000);
|
|
94
|
+
if (!ping?.success) {
|
|
95
|
+
lines.push("✗ pi-subagents not responding (ping timeout 2s) — install @tintinweb/pi-subagents");
|
|
96
|
+
return "error";
|
|
97
|
+
}
|
|
98
|
+
lines.push(`✓ pi-subagents loaded (protocol v${ping.data?.version ?? "?"})`);
|
|
99
|
+
const spawn = await rpcCall(pi, "spawn", {
|
|
100
|
+
type: "general-purpose",
|
|
101
|
+
prompt: "Reply with the literal string OK and nothing else.",
|
|
102
|
+
options: { description: "rnd-doctor probe", run_in_background: true, max_turns: 3 },
|
|
103
|
+
}, 5000);
|
|
104
|
+
if (!spawn?.success || !spawn.data?.id) {
|
|
105
|
+
const err = spawn?.success === false ? spawn.error : "spawn timeout";
|
|
106
|
+
lines.push(`✗ subagent spawn failed: ${err}`);
|
|
107
|
+
return "error";
|
|
108
|
+
}
|
|
109
|
+
const agentId = spawn.data.id;
|
|
110
|
+
const roundTripBudgetMs = 90_000;
|
|
111
|
+
lines.push(`… spawned subagent ${agentId}, waiting up to ${roundTripBudgetMs / 1000}s…`);
|
|
112
|
+
const lifecycle = { lastEvent: "spawned", lastTs: Date.now() };
|
|
113
|
+
const unsubCreated = pi.events.on("subagents:created", (data) => {
|
|
114
|
+
if (data?.id === agentId) {
|
|
115
|
+
lifecycle.lastEvent = "created";
|
|
116
|
+
lifecycle.lastTs = Date.now();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
const unsubStarted = pi.events.on("subagents:started", (data) => {
|
|
120
|
+
if (data?.id === agentId) {
|
|
121
|
+
lifecycle.lastEvent = "started";
|
|
122
|
+
lifecycle.lastTs = Date.now();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
const completion = await waitForCompletion(pi, agentId, roundTripBudgetMs);
|
|
126
|
+
unsubCreated();
|
|
127
|
+
unsubStarted();
|
|
128
|
+
if (!completion) {
|
|
129
|
+
const sinceLastMs = Date.now() - lifecycle.lastTs;
|
|
130
|
+
lines.push(`✗ subagent ${agentId} did not complete within ${roundTripBudgetMs / 1000}s ` +
|
|
131
|
+
`(last lifecycle event: ${lifecycle.lastEvent}, ${Math.round(sinceLastMs / 1000)}s ago — ` +
|
|
132
|
+
`usually means the LLM call is slow or the provider is misconfigured; ` +
|
|
133
|
+
`transcript: ${process.env.TMPDIR ?? "/tmp"}pi-subagents-<uid>/.../tasks/${agentId}.output)`);
|
|
134
|
+
return "error";
|
|
135
|
+
}
|
|
136
|
+
if (completion.status === "completed") {
|
|
137
|
+
const preview = (completion.result ?? "").trim().slice(0, 80);
|
|
138
|
+
lines.push(`✓ round-trip OK: ${completion.durationMs}ms, status=${completion.status}, result="${preview}"`);
|
|
139
|
+
return "info";
|
|
140
|
+
}
|
|
141
|
+
lines.push(`✗ round-trip ended with status=${completion.status}${completion.error ? `, error=${completion.error}` : ""}`);
|
|
142
|
+
return "warning";
|
|
143
|
+
}
|
|
144
|
+
export function registerRndDoctor(pi) {
|
|
145
|
+
pi.registerCommand("rnd-doctor", {
|
|
146
|
+
description: "Diagnose pi-rnd prerequisites (PI dir, subagents, discovery, gates, round-trip)",
|
|
147
|
+
handler: async (_args, ctx) => {
|
|
148
|
+
const lines = ["pi-rnd — doctor v0.2"];
|
|
149
|
+
const roundTripLines = [];
|
|
150
|
+
const [runtimeLines, discoveryLines, rtLevel] = await Promise.all([
|
|
151
|
+
checkPiRuntime(),
|
|
152
|
+
checkDiscovery(),
|
|
153
|
+
checkRoundTrip(pi, roundTripLines),
|
|
154
|
+
]);
|
|
155
|
+
const gateCount = listGates().length;
|
|
156
|
+
const gateLines = gateCount === 0
|
|
157
|
+
? ["§ gates", "✗ gates: 0 (no seed gates registered)"]
|
|
158
|
+
: ["§ gates", `✓ gates: ${gateCount}`];
|
|
159
|
+
lines.push(...runtimeLines, ...discoveryLines, ...gateLines, ...roundTripLines);
|
|
160
|
+
const hasFailures = lines.some((l) => l.startsWith("✗"));
|
|
161
|
+
const finalLevel = rtLevel === "error" ? "error" : hasFailures ? "warning" : "info";
|
|
162
|
+
ctx.ui.notify(lines.join("\n"), finalLevel);
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const VIOLATIONS = [
|
|
2
|
+
{ pattern: /\bpython\s+-c\s+['"]/, reason: "Use the Write tool to create a script file instead of running inline Python with python -c." },
|
|
3
|
+
{ pattern: /\bnode\s+-e\s+['"]/, reason: "Use the Write tool to create a script file instead of running inline Node.js with node -e." },
|
|
4
|
+
{ pattern: /\bbun\s+-e\s+['"]/, reason: "Use the Write tool to create a script file instead of running inline Bun with bun -e." },
|
|
5
|
+
{ pattern: /^\s*(for|while|until)\s+/m, reason: "Avoid top-level shell loops; break work into discrete Bash calls instead." },
|
|
6
|
+
{ pattern: /^\s*cat\s+\S+\s*$/m, reason: "Use the Read tool to read files instead of cat." },
|
|
7
|
+
{ pattern: /^\s*head\s+/m, reason: "Use the Read tool with offset/limit instead of head." },
|
|
8
|
+
{ pattern: /^\s*tail\s+/m, reason: "Use the Read tool with offset/limit instead of tail." },
|
|
9
|
+
{ pattern: /echo\s+[^|]*>/, reason: "Use the Write tool to write files instead of echo redirection." },
|
|
10
|
+
{ pattern: /printf\s+[^|]*>/, reason: "Use the Write tool to write files instead of printf redirection." },
|
|
11
|
+
];
|
|
12
|
+
function check(input, _ctx) {
|
|
13
|
+
const cmd = input?.command;
|
|
14
|
+
if (typeof cmd !== "string")
|
|
15
|
+
return null;
|
|
16
|
+
for (const { pattern, reason } of VIOLATIONS) {
|
|
17
|
+
if (pattern.test(cmd))
|
|
18
|
+
return { block: true, reason };
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
export const bashDiscipline = {
|
|
23
|
+
name: "bash-discipline",
|
|
24
|
+
match: (toolName) => toolName === "bash",
|
|
25
|
+
check,
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=bash-discipline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-discipline.js","sourceRoot":"","sources":["../../src/gates/bash-discipline.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAA+C;IAC7D,EAAE,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,6FAA6F,EAAE;IAC1I,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,4FAA4F,EAAE;IACvI,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,uFAAuF,EAAE;IACjI,EAAE,OAAO,EAAE,2BAA2B,EAAE,MAAM,EAAE,2EAA2E,EAAE;IAC7H,EAAE,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,iDAAiD,EAAE;IAC5F,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,sDAAsD,EAAE;IAC3F,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,sDAAsD,EAAE;IAC3F,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,gEAAgE,EAAE;IACtG,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,kEAAkE,EAAE;CAC3G,CAAC;AAEF,SAAS,KAAK,CAAC,KAAc,EAAE,IAAiB;IAC9C,MAAM,GAAG,GAAI,KAAiC,EAAE,OAAO,CAAC;IAExD,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEzC,KAAK,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;QAC7C,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACxD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAS;IAClC,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,KAAK,MAAM;IACxC,KAAK;CACN,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
function logRead(path, rndDir) {
|
|
4
|
+
const logDir = join(rndDir, "evidence-pack");
|
|
5
|
+
const logFile = join(logDir, "reads.log");
|
|
6
|
+
const entry = JSON.stringify({ ts: new Date().toISOString(), path });
|
|
7
|
+
mkdirSync(logDir, { recursive: true });
|
|
8
|
+
appendFileSync(logFile, entry + "\n");
|
|
9
|
+
}
|
|
10
|
+
function check(input, ctx) {
|
|
11
|
+
const inp = input;
|
|
12
|
+
const filePath = typeof inp?.path === "string" ? inp.path : null;
|
|
13
|
+
if (ctx.phase === "verify" && ctx.rndDir && filePath) {
|
|
14
|
+
logRead(filePath, ctx.rndDir);
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
export const readEvidencePack = {
|
|
19
|
+
name: "read-evidence-pack",
|
|
20
|
+
match: (toolName) => toolName === "read",
|
|
21
|
+
check,
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=read-evidence-pack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-evidence-pack.js","sourceRoot":"","sources":["../../src/gates/read-evidence-pack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,SAAS,OAAO,CAAC,IAAY,EAAE,MAAc;IAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAErE,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,cAAc,CAAC,OAAO,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,KAAK,CAAC,KAAc,EAAE,GAAgB;IAC7C,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,QAAQ,GAAG,OAAO,GAAG,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAEjE,IAAI,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;QACrD,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAS;IACpC,IAAI,EAAE,oBAAoB;IAC1B,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,KAAK,MAAM;IACxC,KAAK;CACN,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const gates = [];
|
|
2
|
+
export function registerGate(gate) {
|
|
3
|
+
const idx = gates.findIndex((g) => g.name === gate.name);
|
|
4
|
+
if (idx >= 0) {
|
|
5
|
+
gates[idx] = gate;
|
|
6
|
+
}
|
|
7
|
+
else {
|
|
8
|
+
gates.push(gate);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function listGates() {
|
|
12
|
+
return Object.freeze([...gates]);
|
|
13
|
+
}
|
|
14
|
+
export function runGates(toolName, input, ctx) {
|
|
15
|
+
for (const gate of gates) {
|
|
16
|
+
if (!gate.match(toolName, input))
|
|
17
|
+
continue;
|
|
18
|
+
const result = gate.check(input, ctx);
|
|
19
|
+
if (result)
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/gates/registry.ts"],"names":[],"mappings":"AAkBA,MAAM,KAAK,GAAW,EAAE,CAAC;AAEzB,MAAM,UAAU,YAAY,CAAC,IAAU;IACrC,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC;IAEzD,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QACb,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACpB,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,QAAgB,EAChB,KAAc,EACd,GAAgB;IAEhB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC;YAAE,SAAS;QAE3C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAEtC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;IAC5B,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
const ARTIFACT_PATTERNS = [
|
|
3
|
+
/^plan\.md$/,
|
|
4
|
+
/^.+-manifest\.md$/,
|
|
5
|
+
/^.+-verification\.md$/,
|
|
6
|
+
/^.+-report\.md$/,
|
|
7
|
+
];
|
|
8
|
+
function isArtifactBasename(name) {
|
|
9
|
+
return ARTIFACT_PATTERNS.some((p) => p.test(name));
|
|
10
|
+
}
|
|
11
|
+
function check(input, _ctx) {
|
|
12
|
+
const inp = input;
|
|
13
|
+
const filePath = typeof inp?.path === "string" ? inp.path : null;
|
|
14
|
+
if (!filePath)
|
|
15
|
+
return null;
|
|
16
|
+
if (!filePath.startsWith("/tmp/"))
|
|
17
|
+
return null;
|
|
18
|
+
const name = basename(filePath);
|
|
19
|
+
if (!isArtifactBasename(name))
|
|
20
|
+
return null;
|
|
21
|
+
return {
|
|
22
|
+
block: true,
|
|
23
|
+
reason: `Pipeline artifact \`${name}\` belongs under $RND_DIR, not /tmp/.`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export const rndDirRequired = {
|
|
27
|
+
name: "rnd-dir-required",
|
|
28
|
+
match: (toolName) => toolName === "write" || toolName === "edit",
|
|
29
|
+
check,
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=rnd-dir-required.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rnd-dir-required.js","sourceRoot":"","sources":["../../src/gates/rnd-dir-required.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGrC,MAAM,iBAAiB,GAAG;IACxB,YAAY;IACZ,mBAAmB;IACnB,uBAAuB;IACvB,iBAAiB;CAClB,CAAC;AAEF,SAAS,kBAAkB,CAAC,IAAY;IACtC,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,KAAK,CAAC,KAAc,EAAE,IAAiB;IAC9C,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,QAAQ,GAAG,OAAO,GAAG,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAEjE,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3B,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAE/C,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEhC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAE3C,OAAO;QACL,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,uBAAuB,IAAI,uCAAuC;KAC3E,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAS;IAClC,IAAI,EAAE,kBAAkB;IACxB,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,MAAM;IAChE,KAAK;CACN,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { registerRndStart } from "./orchestrator/start.js";
|
|
2
|
+
import { registerGate, runGates } from "./gates/registry.js";
|
|
3
|
+
import { bashDiscipline } from "./gates/bash-discipline.js";
|
|
4
|
+
import { rndDirRequired } from "./gates/rnd-dir-required.js";
|
|
5
|
+
import { readEvidencePack } from "./gates/read-evidence-pack.js";
|
|
6
|
+
import { registerRndDoctor } from "./doctor.js";
|
|
7
|
+
export default function pirndFramework(pi) {
|
|
8
|
+
registerRndStart(pi);
|
|
9
|
+
registerGate(bashDiscipline);
|
|
10
|
+
registerGate(rndDirRequired);
|
|
11
|
+
registerGate(readEvidencePack);
|
|
12
|
+
pi.on("tool_call", (event) => {
|
|
13
|
+
const result = runGates(event.toolName, event.input, {});
|
|
14
|
+
if (result)
|
|
15
|
+
return result;
|
|
16
|
+
return undefined;
|
|
17
|
+
});
|
|
18
|
+
registerRndDoctor(pi);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EAAgB;IACrD,gBAAgB,CAAC,EAAE,CAAC,CAAC;IAErB,YAAY,CAAC,cAAc,CAAC,CAAC;IAC7B,YAAY,CAAC,cAAc,CAAC,CAAC;IAC7B,YAAY,CAAC,gBAAgB,CAAC,CAAC;IAE/B,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAEzD,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAE1B,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,iBAAiB,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export function plannerPromptFor(task, rndDir) {
|
|
2
|
+
return `You are the rnd-planner agent. Your job is to produce a structured research plan for the following task:
|
|
3
|
+
|
|
4
|
+
${task}
|
|
5
|
+
|
|
6
|
+
Write your plan to ${rndDir}/plan.md. The plan MUST contain a section titled exactly:
|
|
7
|
+
|
|
8
|
+
## Execution Schedule
|
|
9
|
+
|
|
10
|
+
Under that section, list waves in this format:
|
|
11
|
+
|
|
12
|
+
Wave 1:
|
|
13
|
+
- T1: <task description>
|
|
14
|
+
- T2: <task description>
|
|
15
|
+
|
|
16
|
+
Wave 2:
|
|
17
|
+
- T3: <task description>
|
|
18
|
+
|
|
19
|
+
Each task line must start with "- T" followed by an identifier and a colon.
|
|
20
|
+
|
|
21
|
+
The $RND_DIR for this session is: ${rndDir}
|
|
22
|
+
|
|
23
|
+
Write the plan to disk before finishing.`;
|
|
24
|
+
}
|
|
25
|
+
export function buildPromptFor(taskId, rndDir) {
|
|
26
|
+
return `You are the rnd-builder agent. Build task ${taskId} as specified in ${rndDir}/plan.md.
|
|
27
|
+
|
|
28
|
+
The $RND_DIR for this session is: ${rndDir}
|
|
29
|
+
|
|
30
|
+
Follow the pre-registration for task ${taskId}. Write all artifacts under ${rndDir}/builds/.
|
|
31
|
+
Write your build manifest to ${rndDir}/builds/${taskId}-manifest.md.
|
|
32
|
+
Write your self-assessment to ${rndDir}/builds/${taskId}-self-assessment.md.
|
|
33
|
+
|
|
34
|
+
When done, your final message should start with "${taskId} build complete".`;
|
|
35
|
+
}
|
|
36
|
+
export function verifierPromptFor(wave, rndDir) {
|
|
37
|
+
const taskList = wave.taskIds.join(", ");
|
|
38
|
+
return `You are the rnd-verifier agent. Verify the builds for wave ${wave.wave}: tasks ${taskList}.
|
|
39
|
+
|
|
40
|
+
The $RND_DIR for this session is: ${rndDir}
|
|
41
|
+
|
|
42
|
+
Read each task's build manifest at ${rndDir}/builds/<taskId>-manifest.md and run the verification commands specified in the plan at ${rndDir}/plan.md.
|
|
43
|
+
|
|
44
|
+
Write your verification report to ${rndDir}/verifications/wave-${wave.wave}-report.md.
|
|
45
|
+
|
|
46
|
+
Your final message must include a verdict line: "Wave ${wave.wave}: PASS" or "Wave ${wave.wave}: FAIL: <reason>".`;
|
|
47
|
+
}
|
|
48
|
+
export function integratorPromptFor(rndDir) {
|
|
49
|
+
return `You are the rnd-integrator agent. All waves have passed verification. Integrate the results.
|
|
50
|
+
|
|
51
|
+
The $RND_DIR for this session is: ${rndDir}
|
|
52
|
+
|
|
53
|
+
Read the plan at ${rndDir}/plan.md and all verification reports under ${rndDir}/verifications/.
|
|
54
|
+
Produce a final integration report at ${rndDir}/integration/report.md with a verdict of SHIP or NO-SHIP.
|
|
55
|
+
|
|
56
|
+
Your final message must include either "SHIP" or "NO-SHIP".`;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/orchestrator/prompts.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,MAAc;IAC3D,OAAO;;EAEP,IAAI;;qBAEe,MAAM;;;;;;;;;;;;;;;oCAeS,MAAM;;yCAED,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,MAAc;IAC3D,OAAO,6CAA6C,MAAM,oBAAoB,MAAM;;oCAElD,MAAM;;uCAEH,MAAM,+BAA+B,MAAM;+BACnD,MAAM,WAAW,MAAM;gCACtB,MAAM,WAAW,MAAM;;mDAEJ,MAAM,mBAAmB,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAc,EAAE,MAAc;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,OAAO,8DAA8D,IAAI,CAAC,IAAI,WAAW,QAAQ;;oCAE/D,MAAM;;qCAEL,MAAM,2FAA2F,MAAM;;oCAExG,MAAM,uBAAuB,IAAI,CAAC,IAAI;;wDAElB,IAAI,CAAC,IAAI,oBAAoB,IAAI,CAAC,IAAI,oBAAoB,CAAC;AACnH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,OAAO;;oCAE2B,MAAM;;mBAEvB,MAAM,+CAA+C,MAAM;wCACtC,MAAM;;4DAEc,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
|
+
import { basename } from "node:path";
|
|
4
|
+
const DEFAULT_BASE_DIR = `${process.env.HOME ?? "~"}/.claude-personal/.rnd/`;
|
|
5
|
+
function hash8(cwd) {
|
|
6
|
+
return createHash("sha1").update(cwd).digest("hex").slice(0, 8);
|
|
7
|
+
}
|
|
8
|
+
function defaultTs() {
|
|
9
|
+
return new Date().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
|
|
10
|
+
}
|
|
11
|
+
export function computeRndDir(cwd, opts) {
|
|
12
|
+
const base = opts?.baseDir ?? DEFAULT_BASE_DIR;
|
|
13
|
+
const ts = opts?.ts ?? defaultTs();
|
|
14
|
+
const dir = `${basename(cwd)}-${hash8(cwd)}`;
|
|
15
|
+
return `${base}${dir}/branches/no-git/sessions/${ts}/`;
|
|
16
|
+
}
|
|
17
|
+
export function ensureRndDir(path) {
|
|
18
|
+
return mkdir(path, { recursive: true }).then(() => undefined);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=rnd-dir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rnd-dir.js","sourceRoot":"","sources":["../../src/orchestrator/rnd-dir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,MAAM,gBAAgB,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,GAAG,yBAAyB,CAAC;AAE7E,SAAS,KAAK,CAAC,GAAW;IACxB,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,GAAW,EACX,IAAwC;IAExC,MAAM,IAAI,GAAG,IAAI,EAAE,OAAO,IAAI,gBAAgB,CAAC;IAC/C,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,SAAS,EAAE,CAAC;IACnC,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAE7C,OAAO,GAAG,IAAI,GAAG,GAAG,6BAA6B,EAAE,GAAG,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;AAChE,CAAC"}
|