@c-d-cc/reap 0.13.4 → 0.15.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 +13 -10
- package/README.ko.md +13 -10
- package/README.md +14 -11
- package/README.zh-CN.md +13 -10
- package/dist/cli.js +1137 -286
- package/dist/templates/commands/reap.refreshKnowledge.md +6 -0
- package/dist/templates/help/en.txt +3 -0
- package/dist/templates/help/ko.txt +3 -0
- package/dist/templates/hooks/genome-loader.cjs +40 -0
- package/dist/templates/hooks/reap-guide.md +13 -11
- package/dist/templates/hooks/session-start.cjs +64 -22
- package/package.json +1 -1
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "REAP Refresh Knowledge — Load REAP context (Genome, Environment, Generation state)"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Run `reap run refreshKnowledge` and incorporate the returned REAP context into your working knowledge.
|
|
6
|
+
This is useful when you don't have REAP context loaded (e.g., in a subagent or after context compaction).
|
|
@@ -5,6 +5,8 @@ CLI Commands:
|
|
|
5
5
|
reap status Show current project and Generation status
|
|
6
6
|
reap update Sync slash commands, templates, and hooks
|
|
7
7
|
reap fix Diagnose and repair .reap/ directory
|
|
8
|
+
reap clean Reset REAP project with interactive options
|
|
9
|
+
reap destroy Remove all REAP files from project
|
|
8
10
|
reap help Show this help message
|
|
9
11
|
|
|
10
12
|
Slash Commands (use in Claude Code):
|
|
@@ -20,6 +22,7 @@ Slash Commands (use in Claude Code):
|
|
|
20
22
|
/reap.status Show current state
|
|
21
23
|
/reap.sync Synchronize Genome with source code
|
|
22
24
|
/reap.help Contextual help (AI-powered)
|
|
25
|
+
/reap.refreshKnowledge Load REAP context for subagents
|
|
23
26
|
|
|
24
27
|
Quick Start:
|
|
25
28
|
1. reap init my-project
|
|
@@ -5,6 +5,8 @@ CLI 명령어:
|
|
|
5
5
|
reap status 현재 프로젝트 및 Generation 상태 확인
|
|
6
6
|
reap update 슬래시 커맨드, 템플릿, 훅을 최신 버전으로 동기화
|
|
7
7
|
reap fix .reap/ 디렉토리 구조 진단 및 복구
|
|
8
|
+
reap clean 대화형 옵션으로 REAP 프로젝트 초기화
|
|
9
|
+
reap destroy 프로젝트에서 모든 REAP 파일 제거
|
|
8
10
|
reap help 이 도움말 표시
|
|
9
11
|
|
|
10
12
|
슬래시 커맨드 (Claude Code에서 사용):
|
|
@@ -20,6 +22,7 @@ CLI 명령어:
|
|
|
20
22
|
/reap.status 현재 상태 확인
|
|
21
23
|
/reap.sync Genome을 소스 코드와 동기화
|
|
22
24
|
/reap.help 상황별 도움말 (AI 기반)
|
|
25
|
+
/reap.refreshKnowledge 서브에이전트용 REAP 컨텍스트 로드
|
|
23
26
|
|
|
24
27
|
빠른 시작:
|
|
25
28
|
1. reap init my-project
|
|
@@ -7,6 +7,17 @@ const { execSync } = require('child_process');
|
|
|
7
7
|
const L1_LIMIT = 500;
|
|
8
8
|
const L2_LIMIT = 200;
|
|
9
9
|
const L1_FILES = ['principles.md', 'conventions.md', 'constraints.md', 'source-map.md'];
|
|
10
|
+
const PLACEHOLDER_PATTERNS = [
|
|
11
|
+
/\(Add .+ here\)/,
|
|
12
|
+
/\(Describe .+\)/,
|
|
13
|
+
/\(language and version\)/,
|
|
14
|
+
/\(External .+\)/,
|
|
15
|
+
/\(runtime environment\)/,
|
|
16
|
+
/\(framework\)/,
|
|
17
|
+
/\(database\)/,
|
|
18
|
+
/^\|\s*\|\s*\|\s*\|\s*\|$/m,
|
|
19
|
+
];
|
|
20
|
+
|
|
10
21
|
const STAGE_COMMANDS = {
|
|
11
22
|
objective: '/reap.objective',
|
|
12
23
|
planning: '/reap.planning',
|
|
@@ -188,6 +199,17 @@ function buildStrictSection(strictEdit, strictMerge, genStage) {
|
|
|
188
199
|
return sections;
|
|
189
200
|
}
|
|
190
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Check if a genome file contains placeholder template content.
|
|
204
|
+
* @param {string} filePath - path to a genome file
|
|
205
|
+
* @returns {boolean} true if the file contains placeholder patterns
|
|
206
|
+
*/
|
|
207
|
+
function hasPlaceholders(filePath) {
|
|
208
|
+
const content = readFile(filePath);
|
|
209
|
+
if (!content) return false;
|
|
210
|
+
return PLACEHOLDER_PATTERNS.some(pattern => pattern.test(content));
|
|
211
|
+
}
|
|
212
|
+
|
|
191
213
|
/**
|
|
192
214
|
* Build Genome health status for session init display.
|
|
193
215
|
* @param {object} params
|
|
@@ -202,6 +224,22 @@ function buildGenomeHealth({ l1Lines, genomeDir, configFile, genomeStaleWarning,
|
|
|
202
224
|
if (!check) { issues.push(`missing ${f}`); severity = 'danger'; }
|
|
203
225
|
}
|
|
204
226
|
if (!fileExists(configFile)) { issues.push('no config.yml'); severity = 'danger'; }
|
|
227
|
+
|
|
228
|
+
// Check for placeholder content in L1 files
|
|
229
|
+
const placeholderFiles = L1_FILES.filter(f => {
|
|
230
|
+
const fp = path.join(genomeDir, f);
|
|
231
|
+
return fileExists(fp) && hasPlaceholders(fp);
|
|
232
|
+
});
|
|
233
|
+
if (placeholderFiles.length > 0) {
|
|
234
|
+
if (placeholderFiles.length === L1_FILES.length) {
|
|
235
|
+
issues.push(`needs customization (${placeholderFiles.length}/${L1_FILES.length} files)`);
|
|
236
|
+
severity = 'danger';
|
|
237
|
+
} else {
|
|
238
|
+
issues.push(`needs customization (${placeholderFiles.length}/${L1_FILES.length} files)`);
|
|
239
|
+
if (severity === 'ok') severity = 'warn';
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
205
243
|
if (genomeStaleWarning && commitsSince > 30) {
|
|
206
244
|
issues.push(`severely stale (${commitsSince} commits)`);
|
|
207
245
|
if (severity !== 'danger') severity = 'danger';
|
|
@@ -223,6 +261,7 @@ module.exports = {
|
|
|
223
261
|
L2_LIMIT,
|
|
224
262
|
L1_FILES,
|
|
225
263
|
STAGE_COMMANDS,
|
|
264
|
+
PLACEHOLDER_PATTERNS,
|
|
226
265
|
readFile,
|
|
227
266
|
fileExists,
|
|
228
267
|
dirExists,
|
|
@@ -233,4 +272,5 @@ module.exports = {
|
|
|
233
272
|
detectStaleness,
|
|
234
273
|
buildStrictSection,
|
|
235
274
|
buildGenomeHealth,
|
|
275
|
+
hasPlaceholders,
|
|
236
276
|
};
|
|
@@ -7,12 +7,12 @@ REAP (Recursive Evolutionary Autonomous Pipeline) is a development pipeline wher
|
|
|
7
7
|
## 3-Layer Model
|
|
8
8
|
|
|
9
9
|
```
|
|
10
|
-
|
|
11
|
-
Design
|
|
10
|
+
Knowledge Base (Genome + Environment) → Evolution (Generational Progress) → Civilization (Source Code)
|
|
11
|
+
Design, knowledge & external context Life cycle, mutation, adaptation Accumulated artifacts
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
- **
|
|
15
|
-
- **Evolution** — The process by which
|
|
14
|
+
- **Knowledge Base** — Genome (architecture, conventions, constraints) and Environment (external APIs, infrastructure). Stored in `.reap/genome/` and `.reap/environment/`.
|
|
15
|
+
- **Evolution** — The process by which knowledge evolves and Civilization grows through repeated Generations.
|
|
16
16
|
- **Civilization** — Source Code. The entire project codebase outside `.reap/`.
|
|
17
17
|
|
|
18
18
|
## Genome Structure
|
|
@@ -161,16 +161,18 @@ REAP supports multiple AI agents simultaneously through the AgentAdapter abstrac
|
|
|
161
161
|
objective → planning → implementation → validation → completion
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
-
**Execution sequence
|
|
164
|
+
**Execution sequence** (auto-transition):
|
|
165
165
|
1. `/reap.start` — Create a new Generation
|
|
166
|
-
2. `/reap.objective` — Define goal + requirements →
|
|
167
|
-
3. `/reap.planning` — Task decomposition + plan →
|
|
168
|
-
4. `/reap.implementation` — Code implementation →
|
|
169
|
-
5. `/reap.validation` — Verification →
|
|
166
|
+
2. `/reap.objective` — Define goal + requirements → `--phase complete` auto-advances to planning
|
|
167
|
+
3. `/reap.planning` — Task decomposition + plan → `--phase complete` auto-advances to implementation
|
|
168
|
+
4. `/reap.implementation` — Code implementation → `--phase complete` auto-advances to validation
|
|
169
|
+
5. `/reap.validation` — Verification → `--phase complete` auto-advances to completion
|
|
170
170
|
6. `/reap.completion` — Retrospective + genome updates + archiving (auto)
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
Each `--phase complete` generates a stage chain token (nonce), auto-transitions to the next stage, and creates the next artifact template. The next stage command verifies the token at entry — this ensures stages cannot be skipped.
|
|
173
|
+
|
|
174
|
+
`/reap.next` is maintained as a **fallback command**. If auto-transition already occurred, it confirms the transition. If not, it errors.
|
|
175
|
+
`/reap.completion` auto-archives after the feedKnowledge phase — no separate `/reap.next` needed at the end.
|
|
174
176
|
|
|
175
177
|
## Language
|
|
176
178
|
|
|
@@ -28,47 +28,89 @@ if (!gl.dirExists(reapDir)) {
|
|
|
28
28
|
process.exit(0);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
// Step 0: Install project-level
|
|
31
|
+
// Step 0: Install project-level skill files (.claude/skills/{name}/SKILL.md)
|
|
32
32
|
const fs = require('fs');
|
|
33
33
|
const os = require('os');
|
|
34
34
|
const userReapCommands = path.join(os.homedir(), '.reap', 'commands');
|
|
35
|
-
const
|
|
35
|
+
const projectClaudeSkills = path.join(projectRoot, '.claude', 'skills');
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Parse frontmatter from a command .md file.
|
|
39
|
+
* Returns { description, body } where body is the content after frontmatter.
|
|
40
|
+
*/
|
|
41
|
+
function parseFrontmatter(content) {
|
|
42
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
43
|
+
if (!match) return { description: '', body: content };
|
|
44
|
+
const fm = match[1];
|
|
45
|
+
const body = match[2];
|
|
46
|
+
const descMatch = fm.match(/^description:\s*"?([^"\n]*)"?/m);
|
|
47
|
+
return { description: descMatch ? descMatch[1].trim() : '', body };
|
|
48
|
+
}
|
|
36
49
|
|
|
37
50
|
if (gl.dirExists(userReapCommands)) {
|
|
38
51
|
try {
|
|
39
|
-
fs.mkdirSync(projectClaudeCommands, { recursive: true });
|
|
40
52
|
const cmdFiles = fs.readdirSync(userReapCommands).filter(f => f.startsWith('reap.') && f.endsWith('.md'));
|
|
41
53
|
let installed = 0;
|
|
42
54
|
for (const file of cmdFiles) {
|
|
43
55
|
const src = path.join(userReapCommands, file);
|
|
44
|
-
const
|
|
56
|
+
const name = file.replace(/\.md$/, ''); // e.g. reap.objective
|
|
57
|
+
const skillDir = path.join(projectClaudeSkills, name);
|
|
58
|
+
const skillFile = path.join(skillDir, 'SKILL.md');
|
|
59
|
+
|
|
60
|
+
const srcContent = fs.readFileSync(src, 'utf-8');
|
|
61
|
+
const { description, body } = parseFrontmatter(srcContent);
|
|
62
|
+
const skillContent = `---\nname: ${name}\ndescription: "${description}"\n---\n${body}`;
|
|
63
|
+
|
|
64
|
+
// Skip if content is identical
|
|
45
65
|
try {
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
48
|
-
fs.unlinkSync(dest); // replace legacy symlink with real file
|
|
49
|
-
} else {
|
|
50
|
-
// Skip if content is identical
|
|
51
|
-
const srcContent = fs.readFileSync(src);
|
|
52
|
-
const destContent = fs.readFileSync(dest);
|
|
53
|
-
if (srcContent.equals(destContent)) continue;
|
|
54
|
-
fs.unlinkSync(dest);
|
|
55
|
-
}
|
|
66
|
+
const existing = fs.readFileSync(skillFile, 'utf-8');
|
|
67
|
+
if (existing === skillContent) continue;
|
|
56
68
|
} catch { /* dest doesn't exist */ }
|
|
57
|
-
|
|
69
|
+
|
|
70
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
71
|
+
fs.writeFileSync(skillFile, skillContent, 'utf-8');
|
|
58
72
|
installed++;
|
|
59
73
|
}
|
|
60
|
-
|
|
74
|
+
|
|
75
|
+
// Clean up legacy .claude/commands/reap.* files
|
|
76
|
+
const projectClaudeCommands = path.join(projectRoot, '.claude', 'commands');
|
|
77
|
+
try {
|
|
78
|
+
if (fs.existsSync(projectClaudeCommands)) {
|
|
79
|
+
const legacyFiles = fs.readdirSync(projectClaudeCommands).filter(f => f.startsWith('reap.') && f.endsWith('.md'));
|
|
80
|
+
for (const file of legacyFiles) {
|
|
81
|
+
fs.unlinkSync(path.join(projectClaudeCommands, file));
|
|
82
|
+
}
|
|
83
|
+
if (legacyFiles.length > 0) {
|
|
84
|
+
process.stderr.write(`[REAP] Cleaned up ${legacyFiles.length} legacy .claude/commands/reap.* files\n`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} catch { /* best effort */ }
|
|
88
|
+
|
|
89
|
+
// Ensure .gitignore excludes skill files (and migrate legacy entry)
|
|
61
90
|
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
62
|
-
const
|
|
91
|
+
const newEntry = '.claude/skills/reap.*';
|
|
92
|
+
const legacyEntry = '.claude/commands/reap.*';
|
|
63
93
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
94
|
+
let gitignore = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf-8') : '';
|
|
95
|
+
let changed = false;
|
|
96
|
+
// Replace legacy entry with new entry
|
|
97
|
+
if (gitignore.includes(legacyEntry)) {
|
|
98
|
+
gitignore = gitignore.replace(legacyEntry, newEntry);
|
|
99
|
+
changed = true;
|
|
100
|
+
}
|
|
101
|
+
// Add new entry if not present
|
|
102
|
+
if (!gitignore.includes(newEntry)) {
|
|
103
|
+
gitignore += `\n# REAP skill files (managed by session-start hook)\n${newEntry}\n`;
|
|
104
|
+
changed = true;
|
|
105
|
+
}
|
|
106
|
+
if (changed) {
|
|
107
|
+
fs.writeFileSync(gitignorePath, gitignore, 'utf-8');
|
|
67
108
|
}
|
|
68
109
|
} catch { /* best effort */ }
|
|
69
|
-
|
|
110
|
+
|
|
111
|
+
process.stderr.write(`[REAP] Installed ${installed} skills to .claude/skills/ (${cmdFiles.length - installed} unchanged)\n`);
|
|
70
112
|
} catch (err) {
|
|
71
|
-
process.stderr.write(`[REAP] Warning: failed to install
|
|
113
|
+
process.stderr.write(`[REAP] Warning: failed to install skills: ${err.message}\n`);
|
|
72
114
|
}
|
|
73
115
|
}
|
|
74
116
|
|