@c-d-cc/reap 0.2.1 → 0.3.0
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.ja.md +36 -20
- package/README.ko.md +36 -20
- package/README.md +36 -20
- package/README.zh-CN.md +36 -20
- package/dist/cli.js +35 -21
- package/dist/templates/commands/reap.back.md +12 -4
- package/dist/templates/commands/reap.completion.md +33 -0
- package/dist/templates/commands/reap.help.md +1 -1
- package/dist/templates/commands/reap.next.md +23 -7
- package/dist/templates/commands/reap.start.md +12 -4
- package/dist/templates/conditions/always.sh +3 -0
- package/dist/templates/conditions/has-code-changes.sh +13 -0
- package/dist/templates/conditions/version-bumped.sh +17 -0
- package/dist/templates/genome/constraints.md +1 -1
- package/dist/templates/genome/conventions.md +1 -1
- package/dist/templates/genome/principles.md +3 -7
- package/dist/templates/genome/source-map.md +22 -0
- package/dist/templates/hooks/reap-guide.md +31 -19
- package/dist/templates/hooks/session-start.cjs +245 -0
- package/dist/templates/presets/bun-hono-react/source-map.md +22 -0
- package/package.json +1 -1
- package/dist/templates/hooks/session-start.sh +0 -200
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// REAP SessionStart hook — injects REAP guide + Genome + current generation context
|
|
3
|
+
// Single Node.js process (replaces session-start.sh for better performance)
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const startTime = Date.now();
|
|
9
|
+
let step = 0;
|
|
10
|
+
const totalSteps = 6;
|
|
11
|
+
|
|
12
|
+
function log(msg) {
|
|
13
|
+
step++;
|
|
14
|
+
process.stderr.write(`[REAP ${step}/${totalSteps} +${Date.now() - startTime}ms] ${msg}\n`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function readFile(p) {
|
|
18
|
+
try { return fs.readFileSync(p, 'utf-8'); } catch { return null; }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function fileExists(p) {
|
|
22
|
+
try { return fs.statSync(p).isFile(); } catch { return false; }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function dirExists(p) {
|
|
26
|
+
try { return fs.statSync(p).isDirectory(); } catch { return false; }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function exec(cmd) {
|
|
30
|
+
try { return execSync(cmd, { encoding: 'utf-8', timeout: 10000 }).trim(); } catch { return ''; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Paths
|
|
34
|
+
const scriptDir = __dirname;
|
|
35
|
+
const projectRoot = process.cwd();
|
|
36
|
+
const reapDir = path.join(projectRoot, '.reap');
|
|
37
|
+
const genomeDir = path.join(reapDir, 'genome');
|
|
38
|
+
const configFile = path.join(reapDir, 'config.yml');
|
|
39
|
+
const currentYml = path.join(reapDir, 'life', 'current.yml');
|
|
40
|
+
const guideFile = path.join(scriptDir, 'reap-guide.md');
|
|
41
|
+
|
|
42
|
+
// Check REAP project
|
|
43
|
+
if (!dirExists(reapDir)) {
|
|
44
|
+
process.stderr.write('[REAP] Not a REAP project, skipping\n');
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Step 1: Auto-update
|
|
49
|
+
log('Checking for updates...');
|
|
50
|
+
let autoUpdateMessage = '';
|
|
51
|
+
const configContent = readFile(configFile);
|
|
52
|
+
if (configContent) {
|
|
53
|
+
const autoUpdate = /^autoUpdate:\s*true/m.test(configContent);
|
|
54
|
+
if (autoUpdate) {
|
|
55
|
+
const installed = exec('reap --version');
|
|
56
|
+
const latest = exec('npm view @c-d-cc/reap version');
|
|
57
|
+
if (installed && latest && installed !== latest) {
|
|
58
|
+
const updated = exec('npm update -g @c-d-cc/reap');
|
|
59
|
+
if (updated !== null) {
|
|
60
|
+
exec('reap update');
|
|
61
|
+
autoUpdateMessage = `REAP auto-updated: v${installed} → v${latest}`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Step 2: Load REAP guide
|
|
68
|
+
log('Loading REAP guide...');
|
|
69
|
+
const reapGuide = readFile(guideFile) || '';
|
|
70
|
+
|
|
71
|
+
// Step 3: Load Genome (tiered)
|
|
72
|
+
log('Loading Genome...');
|
|
73
|
+
const L1_LIMIT = 500;
|
|
74
|
+
const L2_LIMIT = 200;
|
|
75
|
+
let genomeContent = '';
|
|
76
|
+
let l1Lines = 0;
|
|
77
|
+
|
|
78
|
+
const l1Files = ['principles.md', 'conventions.md', 'constraints.md', 'source-map.md'];
|
|
79
|
+
for (const file of l1Files) {
|
|
80
|
+
const content = readFile(path.join(genomeDir, file));
|
|
81
|
+
if (!content) continue;
|
|
82
|
+
const lines = content.split('\n').length;
|
|
83
|
+
l1Lines += lines;
|
|
84
|
+
if (l1Lines <= L1_LIMIT) {
|
|
85
|
+
genomeContent += `\n### ${file}\n${content}\n`;
|
|
86
|
+
} else {
|
|
87
|
+
genomeContent += `\n### ${file} [TRUNCATED — L1 budget exceeded, read full file directly]\n${content.split('\n').slice(0, 20).join('\n')}\n...\n`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// L2: domain/ files
|
|
92
|
+
const domainDir = path.join(genomeDir, 'domain');
|
|
93
|
+
if (dirExists(domainDir)) {
|
|
94
|
+
let l2Lines = 0;
|
|
95
|
+
let l2Overflow = false;
|
|
96
|
+
const domainFiles = fs.readdirSync(domainDir).filter(f => f.endsWith('.md')).sort();
|
|
97
|
+
for (const file of domainFiles) {
|
|
98
|
+
const content = readFile(path.join(domainDir, file));
|
|
99
|
+
if (!content) continue;
|
|
100
|
+
const lines = content.split('\n').length;
|
|
101
|
+
l2Lines += lines;
|
|
102
|
+
if (!l2Overflow && l2Lines <= L2_LIMIT) {
|
|
103
|
+
genomeContent += `\n### domain/${file}\n${content}\n`;
|
|
104
|
+
} else {
|
|
105
|
+
l2Overflow = true;
|
|
106
|
+
const firstLine = content.split('\n').find(l => l.startsWith('>')) || content.split('\n')[0];
|
|
107
|
+
genomeContent += `\n### domain/${file} [summary — read full file for details]\n${firstLine}\n`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Step 4: Check Genome & source-map sync
|
|
113
|
+
log('Checking sync...');
|
|
114
|
+
let genomeStaleWarning = '';
|
|
115
|
+
let commitsSince = 0;
|
|
116
|
+
if (dirExists(path.join(projectRoot, '.git'))) {
|
|
117
|
+
const lastGenomeCommit = exec(`git -C "${projectRoot}" log -1 --format="%H" -- ".reap/genome/"`);
|
|
118
|
+
if (lastGenomeCommit) {
|
|
119
|
+
commitsSince = parseInt(exec(`git -C "${projectRoot}" rev-list --count "${lastGenomeCommit}..HEAD" -- src/ tests/ package.json tsconfig.json scripts/`) || '0', 10);
|
|
120
|
+
if (commitsSince > 10) {
|
|
121
|
+
genomeStaleWarning = `WARNING: Genome may be stale — ${commitsSince} commits since last Genome update. Consider running /reap.sync to synchronize.`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let sourcemapDriftWarning = '';
|
|
127
|
+
let documented = 0, actual = 0;
|
|
128
|
+
const sourcemapFile = path.join(genomeDir, 'source-map.md');
|
|
129
|
+
const srcCoreDir = path.join(projectRoot, 'src', 'core');
|
|
130
|
+
if (fileExists(sourcemapFile) && dirExists(srcCoreDir)) {
|
|
131
|
+
const smContent = readFile(sourcemapFile) || '';
|
|
132
|
+
documented = (smContent.match(/Component\(/g) || []).length;
|
|
133
|
+
const coreEntries = fs.readdirSync(srcCoreDir);
|
|
134
|
+
actual = coreEntries.filter(e => e.endsWith('.ts')).length
|
|
135
|
+
+ coreEntries.filter(e => { try { return fs.statSync(path.join(srcCoreDir, e)).isDirectory(); } catch { return false; } }).length;
|
|
136
|
+
if (documented > 0 && actual > 0 && documented !== actual) {
|
|
137
|
+
sourcemapDriftWarning = `WARNING: source-map.md drift — ${documented} components documented, ${actual} core files found. Consider running /reap.sync.`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Step 5: Read generation state
|
|
142
|
+
log('Reading generation state...');
|
|
143
|
+
const strictMode = configContent ? /^strict:\s*true/m.test(configContent) : false;
|
|
144
|
+
|
|
145
|
+
let genStage = 'none', genId = '', genGoal = '', generationContext = '';
|
|
146
|
+
const currentContent = readFile(currentYml);
|
|
147
|
+
if (currentContent && currentContent.trim()) {
|
|
148
|
+
genId = (currentContent.match(/^id:\s*(.+)/m) || [])[1] || '';
|
|
149
|
+
genGoal = (currentContent.match(/^goal:\s*(.+)/m) || [])[1] || '';
|
|
150
|
+
genStage = (currentContent.match(/^stage:\s*(.+)/m) || [])[1] || 'none';
|
|
151
|
+
if (genId && genStage !== 'none') {
|
|
152
|
+
generationContext = `Active Generation: ${genId} | Goal: ${genGoal} | Stage: ${genStage}`;
|
|
153
|
+
} else {
|
|
154
|
+
genStage = 'none';
|
|
155
|
+
generationContext = 'No active Generation. Run `/reap.start` to start one.';
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
generationContext = 'No active Generation. Run `/reap.start` to start one.';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const stageCommands = { objective: '/reap.objective', planning: '/reap.planning', implementation: '/reap.implementation', validation: '/reap.validation', completion: '/reap.completion' };
|
|
162
|
+
const nextCmd = stageCommands[genStage] || '/reap.start';
|
|
163
|
+
|
|
164
|
+
// Build strict mode section
|
|
165
|
+
let strictSection = '';
|
|
166
|
+
if (strictMode) {
|
|
167
|
+
if (genStage === 'implementation') {
|
|
168
|
+
strictSection = '\n\n## Strict Mode (ACTIVE — SCOPED MODIFICATION ALLOWED)\n<HARD-GATE>\nStrict mode is enabled. Code modification is ALLOWED only within the scope of the current Generation\'s plan.\n- You MUST read `.reap/life/02-planning.md` before writing any code.\n- You may ONLY modify files and modules listed in the plan\'s task list.\n- Changes outside the plan\'s scope are BLOCKED. If you discover out-of-scope work is needed, add it to the backlog instead of implementing it.\n- If the user explicitly requests to bypass strict mode (e.g., "override", "bypass strict"), you may proceed — but inform them that strict mode is being bypassed.\n</HARD-GATE>';
|
|
169
|
+
} else if (genStage === 'none') {
|
|
170
|
+
strictSection = '\n\n## Strict Mode (ACTIVE — CODE MODIFICATION BLOCKED)\n<HARD-GATE>\nStrict mode is enabled and there is NO active Generation.\nYou MUST NOT write, edit, or create any source code files.\nAllowed actions: reading files, analyzing code, answering questions, running commands.\nTo start coding, the user must first run `/reap.start` and advance to the implementation stage.\nIf the user explicitly requests to bypass strict mode (e.g., "override", "bypass strict", "just do it"), you may proceed — but inform them that strict mode is being bypassed.\n</HARD-GATE>';
|
|
171
|
+
} else {
|
|
172
|
+
strictSection = `\n\n## Strict Mode (ACTIVE — CODE MODIFICATION BLOCKED)\n<HARD-GATE>\nStrict mode is enabled. Current stage is '${genStage}', which is NOT the implementation stage.\nYou MUST NOT write, edit, or create any source code files.\nAllowed actions: reading files, analyzing code, answering questions, running commands, writing REAP artifacts.\nAdvance to the implementation stage via the REAP lifecycle to unlock code modification.\nIf the user explicitly requests to bypass strict mode (e.g., "override", "bypass strict", "just do it"), you may proceed — but inform them that strict mode is being bypassed.\n</HARD-GATE>`;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Build staleness section
|
|
177
|
+
let staleSection = '';
|
|
178
|
+
if (genomeStaleWarning) {
|
|
179
|
+
staleSection = `\n\n## Genome Staleness\n${genomeStaleWarning}\nIf the user wants to proceed without syncing, ask: "The Genome may be stale. Would you like to run /reap.sync now, or do it later?" and respect their choice.`;
|
|
180
|
+
}
|
|
181
|
+
if (sourcemapDriftWarning) {
|
|
182
|
+
staleSection += `\n${sourcemapDriftWarning}`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Build auto-update section
|
|
186
|
+
let updateSection = '';
|
|
187
|
+
if (autoUpdateMessage) {
|
|
188
|
+
updateSection = `\n\n## Auto-Update\n${autoUpdateMessage}. Tell the user: "${autoUpdateMessage}"`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Write session init log
|
|
192
|
+
const initLines = [];
|
|
193
|
+
if (autoUpdateMessage) initLines.push(`🟢 ${autoUpdateMessage}`);
|
|
194
|
+
|
|
195
|
+
// Genome status (single line)
|
|
196
|
+
const issues = [];
|
|
197
|
+
let severity = 'ok';
|
|
198
|
+
if (l1Lines === 0) { issues.push('empty'); severity = 'danger'; }
|
|
199
|
+
for (const f of [...l1Files, 'domain/']) {
|
|
200
|
+
const check = f.endsWith('/') ? dirExists(path.join(genomeDir, f.slice(0, -1))) : fileExists(path.join(genomeDir, f));
|
|
201
|
+
if (!check) { issues.push(`missing ${f}`); severity = 'danger'; }
|
|
202
|
+
}
|
|
203
|
+
if (!fileExists(configFile)) { issues.push('no config.yml'); severity = 'danger'; }
|
|
204
|
+
if (sourcemapDriftWarning) {
|
|
205
|
+
const diff = Math.abs(documented - actual);
|
|
206
|
+
issues.push(`source-map drift (${documented}→${actual})`);
|
|
207
|
+
if (diff > 3) { if (severity === 'ok') severity = 'danger'; }
|
|
208
|
+
else { if (severity === 'ok') severity = 'warn'; }
|
|
209
|
+
}
|
|
210
|
+
if (genomeStaleWarning && commitsSince > 30) {
|
|
211
|
+
issues.push(`severely stale (${commitsSince} commits)`);
|
|
212
|
+
if (severity !== 'danger') severity = 'danger';
|
|
213
|
+
} else if (genomeStaleWarning) {
|
|
214
|
+
issues.push(`stale (${commitsSince} commits)`);
|
|
215
|
+
if (severity === 'ok') severity = 'warn';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (severity === 'ok') initLines.push(`🟢 Genome — loaded (${l1Lines} lines), synced`);
|
|
219
|
+
else if (severity === 'warn') initLines.push(`🟡 Genome — ${issues.join(', ')}. /reap.sync`);
|
|
220
|
+
else initLines.push(`🔴 Genome — ${issues.join(', ')}. \`reap fix\` or /reap.sync`);
|
|
221
|
+
|
|
222
|
+
// Generation status
|
|
223
|
+
if (currentContent && currentContent.trim()) {
|
|
224
|
+
if (!genId || genStage === 'none') {
|
|
225
|
+
initLines.push('🔴 Generation — current.yml corrupted. `reap fix`');
|
|
226
|
+
} else {
|
|
227
|
+
initLines.push(`🟢 Generation ${genId} — ${genStage}`);
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
initLines.push('⚪ No active Generation');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const initSummary = `**REAP Session Initialized**\n${initLines.join('\n')}`;
|
|
234
|
+
|
|
235
|
+
// Step 6: Output JSON
|
|
236
|
+
log('Done. Injecting context.');
|
|
237
|
+
|
|
238
|
+
const reapContext = `<REAP_WORKFLOW>\n${reapGuide}\n\n---\n\n## Genome (Project Knowledge — treat as authoritative source of truth)\n${genomeContent}\n\n---\n\n## Current State\n${generationContext}${staleSection}${strictSection}${updateSection}\n\n## Session Init\n${initSummary}\nWhen the user sends their first message, start your response with this init summary exactly as-is. Do not add greetings, explanations, or command suggestions — just the summary, then respond to the user's message.\n\n## Rules\n1. ALL development work MUST follow the REAP lifecycle. Do NOT bypass it.\n2. Before writing any code, check if a Generation is active and what stage it is in.\n3. If a Generation is active, use \`${nextCmd}\` to proceed with the current stage.\n4. If no Generation is active, use \`/reap.start\` to start a new one.\n5. Do NOT implement features, fix bugs, or make changes outside of the REAP lifecycle unless the user explicitly asks to bypass it.\n6. When the user says "reap evolve", "next stage", "proceed", or similar — invoke the appropriate REAP skill.\n7. **Genome is the authoritative knowledge source.** When making decisions about architecture, conventions, or constraints, ALWAYS reference the Genome first. If code contradicts Genome, flag it as a potential genome-change backlog item.\n8. If you notice the Genome is outdated or missing information relevant to your current task, inform the user and suggest running \`/reap.sync\`.\n</REAP_WORKFLOW>`;
|
|
239
|
+
|
|
240
|
+
process.stdout.write(JSON.stringify({
|
|
241
|
+
hookSpecificOutput: {
|
|
242
|
+
hookEventName: 'SessionStart',
|
|
243
|
+
additionalContext: reapContext
|
|
244
|
+
}
|
|
245
|
+
}) + '\n');
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Source Map
|
|
2
|
+
|
|
3
|
+
> C4 Container + Component level project structure map.
|
|
4
|
+
> Agents reference this before exploring code to quickly identify entry points.
|
|
5
|
+
> Line limit: ~150 lines (adaptive to codebase size).
|
|
6
|
+
> Modified only during the Completion stage.
|
|
7
|
+
|
|
8
|
+
## C4 Context
|
|
9
|
+
|
|
10
|
+
(Describe the system context: users, external systems, and relationships)
|
|
11
|
+
|
|
12
|
+
## C4 Container
|
|
13
|
+
|
|
14
|
+
(Describe containers: applications, data stores, and their interactions)
|
|
15
|
+
|
|
16
|
+
## Core Components
|
|
17
|
+
|
|
18
|
+
(Describe key components within each container)
|
|
19
|
+
|
|
20
|
+
## Data Flow
|
|
21
|
+
|
|
22
|
+
(Describe how data flows through the system)
|
package/package.json
CHANGED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# REAP SessionStart hook — injects REAP guide + Genome + current generation context into every Claude session
|
|
3
|
-
# This script runs from the package directory but uses cwd to find the project's .reap/
|
|
4
|
-
set -euo pipefail
|
|
5
|
-
|
|
6
|
-
# Timing (node is guaranteed since REAP is an npm package)
|
|
7
|
-
_reap_ms() { node -e 'process.stdout.write(String(Date.now()))'; }
|
|
8
|
-
_reap_start_time=$(_reap_ms)
|
|
9
|
-
_reap_log() {
|
|
10
|
-
local now=$(_reap_ms)
|
|
11
|
-
local elapsed=$(( now - _reap_start_time ))
|
|
12
|
-
echo "[REAP hook +${elapsed}ms] $1" >&2
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
# Script directory (package-internal) for guide file
|
|
16
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
17
|
-
|
|
18
|
-
# Project directory is cwd (where Claude Code session started)
|
|
19
|
-
PROJECT_ROOT="$(pwd)"
|
|
20
|
-
REAP_DIR="${PROJECT_ROOT}/.reap"
|
|
21
|
-
CURRENT_YML="${REAP_DIR}/life/current.yml"
|
|
22
|
-
GUIDE_FILE="${SCRIPT_DIR}/reap-guide.md"
|
|
23
|
-
GENOME_DIR="${REAP_DIR}/genome"
|
|
24
|
-
|
|
25
|
-
_reap_log "Starting..."
|
|
26
|
-
|
|
27
|
-
# Check if this is a REAP project
|
|
28
|
-
if [ ! -d "$REAP_DIR" ]; then
|
|
29
|
-
_reap_log "Not a REAP project, exiting"
|
|
30
|
-
exit 0
|
|
31
|
-
fi
|
|
32
|
-
|
|
33
|
-
_reap_log "REAP project detected"
|
|
34
|
-
|
|
35
|
-
_reap_log "Checking auto-update..."
|
|
36
|
-
CONFIG_FILE="${REAP_DIR}/config.yml"
|
|
37
|
-
auto_update_message=""
|
|
38
|
-
if [ -f "$CONFIG_FILE" ]; then
|
|
39
|
-
auto_update=$(grep "^autoUpdate:" "$CONFIG_FILE" 2>/dev/null | sed 's/^autoUpdate: *//' | tr -d ' ' || true)
|
|
40
|
-
if [ "$auto_update" = "true" ]; then
|
|
41
|
-
installed=$(reap --version 2>/dev/null || echo "")
|
|
42
|
-
latest=$(npm view @c-d-cc/reap version 2>/dev/null || echo "")
|
|
43
|
-
if [ -n "$installed" ] && [ -n "$latest" ] && [ "$installed" != "$latest" ]; then
|
|
44
|
-
if npm update -g @c-d-cc/reap >/dev/null 2>&1; then
|
|
45
|
-
reap update >/dev/null 2>&1 || true
|
|
46
|
-
auto_update_message="REAP auto-updated: v${installed} → v${latest}"
|
|
47
|
-
fi
|
|
48
|
-
fi
|
|
49
|
-
fi
|
|
50
|
-
fi
|
|
51
|
-
|
|
52
|
-
_reap_log "Auto-update check done"
|
|
53
|
-
|
|
54
|
-
# Read REAP guide
|
|
55
|
-
_reap_log "Loading REAP guide..."
|
|
56
|
-
reap_guide=""
|
|
57
|
-
if [ -f "$GUIDE_FILE" ]; then
|
|
58
|
-
reap_guide=$(cat "$GUIDE_FILE")
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
_reap_log "Loading Genome..."
|
|
62
|
-
# Read Genome files with tiered loading
|
|
63
|
-
# L1 (~500 lines max): principles.md, conventions.md, constraints.md — always full load
|
|
64
|
-
# L2 (~200 lines max): domain/*.md — full load if within budget, otherwise title+summary only
|
|
65
|
-
L1_LIMIT=500
|
|
66
|
-
L2_LIMIT=200
|
|
67
|
-
|
|
68
|
-
genome_content=""
|
|
69
|
-
l1_lines=0
|
|
70
|
-
if [ -d "$GENOME_DIR" ]; then
|
|
71
|
-
for f in "$GENOME_DIR"/principles.md "$GENOME_DIR"/conventions.md "$GENOME_DIR"/constraints.md; do
|
|
72
|
-
if [ -f "$f" ]; then
|
|
73
|
-
file_content=$(cat "$f")
|
|
74
|
-
file_lines=$(echo "$file_content" | wc -l | tr -d ' ')
|
|
75
|
-
l1_lines=$((l1_lines + file_lines))
|
|
76
|
-
if [ "$l1_lines" -le "$L1_LIMIT" ]; then
|
|
77
|
-
genome_content="${genome_content}\n### $(basename "$f")\n${file_content}\n"
|
|
78
|
-
else
|
|
79
|
-
# L1 budget exceeded — include truncated with warning
|
|
80
|
-
genome_content="${genome_content}\n### $(basename "$f") [TRUNCATED — L1 budget exceeded, read full file directly]\n$(echo "$file_content" | head -20)\n...\n"
|
|
81
|
-
fi
|
|
82
|
-
fi
|
|
83
|
-
done
|
|
84
|
-
|
|
85
|
-
# L2: domain/ files
|
|
86
|
-
if [ -d "$GENOME_DIR/domain" ]; then
|
|
87
|
-
l2_lines=0
|
|
88
|
-
l2_overflow=false
|
|
89
|
-
for f in "$GENOME_DIR"/domain/*.md; do
|
|
90
|
-
if [ -f "$f" ]; then
|
|
91
|
-
file_content=$(cat "$f")
|
|
92
|
-
file_lines=$(echo "$file_content" | wc -l | tr -d ' ')
|
|
93
|
-
l2_lines=$((l2_lines + file_lines))
|
|
94
|
-
if [ "$l2_overflow" = false ] && [ "$l2_lines" -le "$L2_LIMIT" ]; then
|
|
95
|
-
genome_content="${genome_content}\n### domain/$(basename "$f")\n${file_content}\n"
|
|
96
|
-
else
|
|
97
|
-
# L2 budget exceeded — title + first line only
|
|
98
|
-
l2_overflow=true
|
|
99
|
-
first_line=$(echo "$file_content" | grep -m1 "^>" || echo "$file_content" | head -1)
|
|
100
|
-
genome_content="${genome_content}\n### domain/$(basename "$f") [summary — read full file for details]\n${first_line}\n"
|
|
101
|
-
fi
|
|
102
|
-
fi
|
|
103
|
-
done
|
|
104
|
-
fi
|
|
105
|
-
fi
|
|
106
|
-
|
|
107
|
-
# Detect Genome staleness — count commits since last Genome modification
|
|
108
|
-
genome_stale_warning=""
|
|
109
|
-
if command -v git &>/dev/null && [ -d "$PROJECT_ROOT/.git" ]; then
|
|
110
|
-
last_genome_commit=$(git -C "$PROJECT_ROOT" log -1 --format="%H" -- ".reap/genome/" 2>/dev/null || echo "")
|
|
111
|
-
if [ -n "$last_genome_commit" ]; then
|
|
112
|
-
commits_since=$(git -C "$PROJECT_ROOT" rev-list --count "${last_genome_commit}..HEAD" 2>/dev/null || echo "0")
|
|
113
|
-
if [ "$commits_since" -gt 10 ]; then
|
|
114
|
-
genome_stale_warning="WARNING: Genome may be stale — ${commits_since} commits since last Genome update. Consider running /reap.sync to synchronize."
|
|
115
|
-
fi
|
|
116
|
-
fi
|
|
117
|
-
fi
|
|
118
|
-
|
|
119
|
-
# Read strict mode from config.yml
|
|
120
|
-
strict_mode=false
|
|
121
|
-
CONFIG_FILE="${REAP_DIR}/config.yml"
|
|
122
|
-
if [ -f "$CONFIG_FILE" ]; then
|
|
123
|
-
strict_val=$(grep "^strict:" "$CONFIG_FILE" 2>/dev/null | sed 's/^strict: *//' | tr -d ' ' || true)
|
|
124
|
-
if [ "$strict_val" = "true" ]; then
|
|
125
|
-
strict_mode=true
|
|
126
|
-
fi
|
|
127
|
-
fi
|
|
128
|
-
|
|
129
|
-
# Read current.yml
|
|
130
|
-
gen_stage="none"
|
|
131
|
-
if [ ! -f "$CURRENT_YML" ]; then
|
|
132
|
-
generation_context="No active Generation. Run \`/reap.start\` to start one."
|
|
133
|
-
else
|
|
134
|
-
content=$(cat "$CURRENT_YML")
|
|
135
|
-
if [ -z "$content" ]; then
|
|
136
|
-
generation_context="No active Generation. Run \`/reap.start\` to start one."
|
|
137
|
-
else
|
|
138
|
-
# Parse YAML fields (simple grep-based, no external deps)
|
|
139
|
-
gen_id=$(echo "$content" | grep "^id:" | sed 's/^id: *//')
|
|
140
|
-
gen_goal=$(echo "$content" | grep "^goal:" | sed 's/^goal: *//')
|
|
141
|
-
gen_stage=$(echo "$content" | grep "^stage:" | sed 's/^stage: *//')
|
|
142
|
-
generation_context="Active Generation: ${gen_id} | Goal: ${gen_goal} | Stage: ${gen_stage}"
|
|
143
|
-
fi
|
|
144
|
-
fi
|
|
145
|
-
|
|
146
|
-
# Map stage to command
|
|
147
|
-
case "${gen_stage}" in
|
|
148
|
-
objective) next_cmd="/reap.objective" ;;
|
|
149
|
-
planning) next_cmd="/reap.planning" ;;
|
|
150
|
-
implementation) next_cmd="/reap.implementation" ;;
|
|
151
|
-
validation) next_cmd="/reap.validation" ;;
|
|
152
|
-
completion) next_cmd="/reap.completion" ;;
|
|
153
|
-
*) next_cmd="/reap.start" ;;
|
|
154
|
-
esac
|
|
155
|
-
|
|
156
|
-
# Escape for JSON
|
|
157
|
-
escape_for_json() {
|
|
158
|
-
printf '%s' "$1" | node -e 'let d="";process.stdin.on("data",c=>d+=c);process.stdin.on("end",()=>process.stdout.write(JSON.stringify(d).slice(1,-1)))'
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
# Build strict mode section
|
|
162
|
-
strict_section=""
|
|
163
|
-
if [ "$strict_mode" = true ]; then
|
|
164
|
-
if [ "$gen_stage" = "implementation" ]; then
|
|
165
|
-
strict_section="\n\n## Strict Mode (ACTIVE — SCOPED MODIFICATION ALLOWED)\n<HARD-GATE>\nStrict mode is enabled. Code modification is ALLOWED only within the scope of the current Generation's plan.\n- You MUST read \`.reap/life/02-planning.md\` before writing any code.\n- You may ONLY modify files and modules listed in the plan's task list.\n- Changes outside the plan's scope are BLOCKED. If you discover out-of-scope work is needed, add it to the backlog instead of implementing it.\n- If the user explicitly requests to bypass strict mode (e.g., \"override\", \"bypass strict\"), you may proceed — but inform them that strict mode is being bypassed.\n</HARD-GATE>"
|
|
166
|
-
elif [ "$gen_stage" = "none" ]; then
|
|
167
|
-
strict_section="\n\n## Strict Mode (ACTIVE — CODE MODIFICATION BLOCKED)\n<HARD-GATE>\nStrict mode is enabled and there is NO active Generation.\nYou MUST NOT write, edit, or create any source code files.\nAllowed actions: reading files, analyzing code, answering questions, running commands.\nTo start coding, the user must first run \`/reap.start\` and advance to the implementation stage.\nIf the user explicitly requests to bypass strict mode (e.g., \"override\", \"bypass strict\", \"just do it\"), you may proceed — but inform them that strict mode is being bypassed.\n</HARD-GATE>"
|
|
168
|
-
else
|
|
169
|
-
strict_section="\n\n## Strict Mode (ACTIVE — CODE MODIFICATION BLOCKED)\n<HARD-GATE>\nStrict mode is enabled. Current stage is '${gen_stage}', which is NOT the implementation stage.\nYou MUST NOT write, edit, or create any source code files.\nAllowed actions: reading files, analyzing code, answering questions, running commands, writing REAP artifacts.\nAdvance to the implementation stage via the REAP lifecycle to unlock code modification.\nIf the user explicitly requests to bypass strict mode (e.g., \"override\", \"bypass strict\", \"just do it\"), you may proceed — but inform them that strict mode is being bypassed.\n</HARD-GATE>"
|
|
170
|
-
fi
|
|
171
|
-
fi
|
|
172
|
-
|
|
173
|
-
# Build staleness section
|
|
174
|
-
stale_section=""
|
|
175
|
-
if [ -n "$genome_stale_warning" ]; then
|
|
176
|
-
stale_section="\n\n## Genome Staleness\n${genome_stale_warning}\nIf the user wants to proceed without syncing, ask: \"The Genome may be stale. Would you like to run /reap.sync now, or do it later?\" and respect their choice."
|
|
177
|
-
fi
|
|
178
|
-
|
|
179
|
-
# Build auto-update section
|
|
180
|
-
update_section=""
|
|
181
|
-
if [ -n "$auto_update_message" ]; then
|
|
182
|
-
update_section="\n\n## Auto-Update\n${auto_update_message}. Tell the user: \"${auto_update_message}\""
|
|
183
|
-
fi
|
|
184
|
-
|
|
185
|
-
reap_context="<REAP_WORKFLOW>\n${reap_guide}\n\n---\n\n## Genome (Project Knowledge — treat as authoritative source of truth)\n${genome_content}\n\n---\n\n## Current State\n${generation_context}${stale_section}${strict_section}${update_section}\n\n## Rules\n1. ALL development work MUST follow the REAP lifecycle. Do NOT bypass it.\n2. Before writing any code, check if a Generation is active and what stage it is in.\n3. If a Generation is active, use \`${next_cmd}\` to proceed with the current stage.\n4. If no Generation is active, use \`/reap.start\` to start a new one.\n5. Do NOT implement features, fix bugs, or make changes outside of the REAP lifecycle unless the user explicitly asks to bypass it.\n6. When the user says \"reap evolve\", \"next stage\", \"proceed\", or similar — invoke the appropriate REAP skill.\n7. **Genome is the authoritative knowledge source.** When making decisions about architecture, conventions, or constraints, ALWAYS reference the Genome first. If code contradicts Genome, flag it as a potential genome-change backlog item.\n8. If you notice the Genome is outdated or missing information relevant to your current task, inform the user and suggest running \`/reap.sync\`.\n</REAP_WORKFLOW>"
|
|
186
|
-
|
|
187
|
-
_reap_log "Building context..."
|
|
188
|
-
escaped_context=$(escape_for_json "$reap_context")
|
|
189
|
-
_reap_log "Done. Outputting JSON."
|
|
190
|
-
|
|
191
|
-
cat <<EOF
|
|
192
|
-
{
|
|
193
|
-
"hookSpecificOutput": {
|
|
194
|
-
"hookEventName": "SessionStart",
|
|
195
|
-
"additionalContext": "${escaped_context}"
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
EOF
|
|
199
|
-
|
|
200
|
-
exit 0
|