@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.
@@ -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
- Genome (Genetic Information) → Evolution (Cross-generational Evolution) → Civilization (Source Code)
11
- Design and knowledge Life cycle, mutation, adaptation Accumulated artifacts
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
- - **Genome** — Design and knowledge for building the Application. Stored in `.reap/genome/`.
15
- - **Evolution** — The process by which Genome evolves and Civilization grows through repeated Generations.
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 → `/reap.next`
167
- 3. `/reap.planning` — Task decomposition + plan → `/reap.next`
168
- 4. `/reap.implementation` — Code implementation → `/reap.next`
169
- 5. `/reap.validation` — Verification → `/reap.next`
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
- `/reap.next` is a **transition command**, not a lifecycle stage. It advances `current.yml` to the next stage.
173
- `/reap.completion` auto-archives after the genome phase — no separate `/reap.next` needed at the end.
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 command files (copy, not symlink — Claude Code doesn't follow symlinks)
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 projectClaudeCommands = path.join(projectRoot, '.claude', 'commands');
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 dest = path.join(projectClaudeCommands, file);
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 stat = fs.lstatSync(dest);
47
- if (stat.isSymbolicLink()) {
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
- fs.copyFileSync(src, dest);
69
+
70
+ fs.mkdirSync(skillDir, { recursive: true });
71
+ fs.writeFileSync(skillFile, skillContent, 'utf-8');
58
72
  installed++;
59
73
  }
60
- // Ensure .gitignore excludes these files
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 gitignoreEntry = '.claude/commands/reap.*';
91
+ const newEntry = '.claude/skills/reap.*';
92
+ const legacyEntry = '.claude/commands/reap.*';
63
93
  try {
64
- const gitignore = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf-8') : '';
65
- if (!gitignore.includes(gitignoreEntry)) {
66
- fs.appendFileSync(gitignorePath, `\n# REAP command files (managed by session-start hook)\n${gitignoreEntry}\n`);
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
- process.stderr.write(`[REAP] Installed ${installed} commands to .claude/commands/ (${cmdFiles.length - installed} unchanged)\n`);
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 commands: ${err.message}\n`);
113
+ process.stderr.write(`[REAP] Warning: failed to install skills: ${err.message}\n`);
72
114
  }
73
115
  }
74
116
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c-d-cc/reap",
3
- "version": "0.13.4",
3
+ "version": "0.15.0",
4
4
  "description": "Recursive Evolutionary Autonomous Pipeline — AI and humans evolve software across generations",
5
5
  "type": "module",
6
6
  "license": "MIT",