@c-d-cc/reap 0.6.1 → 0.7.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/dist/cli.js CHANGED
@@ -8918,6 +8918,9 @@ class ReapPaths {
8918
8918
  static get userReapTemplates() {
8919
8919
  return join(ReapPaths.userReapDir, "templates");
8920
8920
  }
8921
+ static get userReapCommands() {
8922
+ return join(ReapPaths.userReapDir, "commands");
8923
+ }
8921
8924
  static get packageTemplatesDir() {
8922
8925
  const dir = dirname(fileURLToPath(import.meta.url));
8923
8926
  const distPath = join(dir, "templates");
@@ -9045,12 +9048,22 @@ class ClaudeCodeAdapter {
9045
9048
  return this.commandsDir;
9046
9049
  }
9047
9050
  async installCommands(commandNames, sourceDir) {
9048
- await mkdir(this.commandsDir, { recursive: true });
9051
+ await mkdir(ReapPaths.userReapCommands, { recursive: true });
9049
9052
  for (const cmd of commandNames) {
9050
9053
  const src = join2(sourceDir, `${cmd}.md`);
9051
- const dest = join2(this.commandsDir, `${cmd}.md`);
9054
+ const dest = join2(ReapPaths.userReapCommands, `${cmd}.md`);
9052
9055
  await writeTextFile(dest, await readTextFileOrThrow(src));
9053
9056
  }
9057
+ await mkdir(this.commandsDir, { recursive: true });
9058
+ for (const cmd of commandNames) {
9059
+ const dest = join2(this.commandsDir, `${cmd}.md`);
9060
+ const redirectContent = `---
9061
+ description: "REAP — redirected to ~/.reap/commands/"
9062
+ ---
9063
+ Read \`~/.reap/commands/${cmd}.md\` and follow the instructions there.
9064
+ `;
9065
+ await writeTextFile(dest, redirectContent);
9066
+ }
9054
9067
  }
9055
9068
  async removeStaleCommands(validNames) {
9056
9069
  try {
@@ -9265,12 +9278,22 @@ class OpenCodeAdapter {
9265
9278
  return this.commandsDir;
9266
9279
  }
9267
9280
  async installCommands(commandNames, sourceDir) {
9268
- await mkdir2(this.commandsDir, { recursive: true });
9281
+ await mkdir2(ReapPaths.userReapCommands, { recursive: true });
9269
9282
  for (const cmd of commandNames) {
9270
9283
  const src = join3(sourceDir, `${cmd}.md`);
9271
- const dest = join3(this.commandsDir, `${cmd}.md`);
9284
+ const dest = join3(ReapPaths.userReapCommands, `${cmd}.md`);
9272
9285
  await writeTextFile(dest, await readTextFileOrThrow(src));
9273
9286
  }
9287
+ await mkdir2(this.commandsDir, { recursive: true });
9288
+ for (const cmd of commandNames) {
9289
+ const dest = join3(this.commandsDir, `${cmd}.md`);
9290
+ const redirectContent = `---
9291
+ description: "REAP — redirected to ~/.reap/commands/"
9292
+ ---
9293
+ Read \`~/.reap/commands/${cmd}.md\` and follow the instructions there.
9294
+ `;
9295
+ await writeTextFile(dest, redirectContent);
9296
+ }
9274
9297
  }
9275
9298
  async removeStaleCommands(validNames) {
9276
9299
  try {
@@ -10330,21 +10353,41 @@ async function updateProject(projectRoot, dryRun = false) {
10330
10353
  const adapters = await AgentRegistry.getActiveAdapters(config ?? undefined);
10331
10354
  const commandsDir = ReapPaths.packageCommandsDir;
10332
10355
  const commandFiles = await readdir8(commandsDir);
10356
+ await mkdir6(ReapPaths.userReapCommands, { recursive: true });
10357
+ for (const file of commandFiles) {
10358
+ if (!file.endsWith(".md"))
10359
+ continue;
10360
+ const src = await readTextFileOrThrow(join9(commandsDir, file));
10361
+ const dest = join9(ReapPaths.userReapCommands, file);
10362
+ const existing = await readTextFile(dest);
10363
+ if (existing !== null && existing === src) {
10364
+ result.skipped.push(`~/.reap/commands/${file}`);
10365
+ } else {
10366
+ if (!dryRun)
10367
+ await writeTextFile(dest, src);
10368
+ result.updated.push(`~/.reap/commands/${file}`);
10369
+ }
10370
+ }
10333
10371
  for (const adapter of adapters) {
10334
10372
  const agentCmdDir = adapter.getCommandsDir();
10335
10373
  const label = `${adapter.displayName}`;
10336
10374
  for (const file of commandFiles) {
10337
10375
  if (!file.endsWith(".md"))
10338
10376
  continue;
10339
- const src = await readTextFileOrThrow(join9(commandsDir, file));
10377
+ const cmdName = file.replace(/\.md$/, "");
10378
+ const redirectContent = `---
10379
+ description: "REAP — redirected to ~/.reap/commands/"
10380
+ ---
10381
+ Read \`~/.reap/commands/${cmdName}.md\` and follow the instructions there.
10382
+ `;
10340
10383
  const dest = join9(agentCmdDir, file);
10341
10384
  const existingContent = await readTextFile(dest);
10342
- if (existingContent !== null && existingContent === src) {
10385
+ if (existingContent !== null && existingContent === redirectContent) {
10343
10386
  result.skipped.push(`[${label}] commands/${file}`);
10344
10387
  } else {
10345
10388
  if (!dryRun) {
10346
10389
  await mkdir6(agentCmdDir, { recursive: true });
10347
- await writeTextFile(dest, src);
10390
+ await writeTextFile(dest, redirectContent);
10348
10391
  }
10349
10392
  result.updated.push(`[${label}] commands/${file}`);
10350
10393
  }
@@ -10589,7 +10632,7 @@ async function fixProject(projectRoot) {
10589
10632
 
10590
10633
  // src/cli/index.ts
10591
10634
  import { join as join10 } from "path";
10592
- program.name("reap").description("REAP — Recursive Evolutionary Autonomous Pipeline").version("0.6.1");
10635
+ program.name("reap").description("REAP — Recursive Evolutionary Autonomous Pipeline").version("0.7.0");
10593
10636
  program.command("init").description("Initialize a new REAP project (Genesis)").argument("[project-name]", "Project name (defaults to current directory name)").option("-m, --mode <mode>", "Entry mode: greenfield, migration, adoption", "greenfield").option("-p, --preset <preset>", "Bootstrap with a genome preset (e.g., bun-hono-react)").action(async (projectName, options) => {
10594
10637
  try {
10595
10638
  const cwd = process.cwd();
@@ -56,8 +56,14 @@ Genome(`.reap/genome/`)에 프로젝트의 설계 원칙과 규칙을 기록하
56
56
  | `/reap.sync` | 현재 소스코드와 Genome 간 차이를 분석하고 동기화 |
57
57
  | `/reap.update` | REAP 최신 버전 확인 및 업그레이드 |
58
58
  | `/reap.help` | 도움말. `/reap.help {topic}`으로 주제별 상세 설명 |
59
+ | | **Collaboration** |
60
+ | `/reap.pull {branch}` | fetch + divergence 감지 + merge generation 자동 실행 |
61
+ | `/reap.push` | REAP 상태 검증 + git push |
62
+ | `/reap.merge {branch}` | merge generation 전체 lifecycle 실행 |
63
+ | `/reap.merge.start` | merge generation 생성 + detect |
64
+ | `/reap.merge.evolve` | merge 6단계 자율 실행 |
59
65
 
60
- 💡 `/reap.help {topic}` — workflow, genome, backlog, strict, agents, hooks, config, evolve, regression, author ...
66
+ 💡 `/reap.help {topic}` — workflow, genome, backlog, strict, agents, hooks, config, evolve, regression, merge, pull, push, author ...
61
67
 
62
68
  **Config**: Strict: {on/off} · Auto-Update: {on/off} · Language: {value}
63
69
 
@@ -82,5 +88,7 @@ For command-name topics: read `reap.{name}.md` from the same directory as this f
82
88
  - **regression** — `/reap.back`: 이전 stage 회귀. timeline + artifact에 Regression 섹션 기록.
83
89
  - **minor-fix** — 5분 이내, 설계 변경 없는 수정. stage 전환 없이 현재 artifact에 기록.
84
90
  - **compression** — 10,000줄 + 5세대 이상 시 lineage 자동 압축. L1(40줄), L2(60줄).
91
+ - **merge** / **collaboration** — 분산 협업: genome-first merge 6단계 (detect→mate→merge→sync→validation→completion). `/reap.pull`로 fetch+merge, `/reap.push`로 검증+push. 상세: `domain/collaboration.md`, `domain/merge-lifecycle.md`.
85
92
  - **author** — HyeonIL Choi. Email: hichoi@c-d.cc / Homepage: https://c-d.cc / LinkedIn: https://www.linkedin.com/in/hichoi-dev / GitHub: https://github.com/casamia918
86
93
  - **start** / **objective** / **planning** / **implementation** / **validation** / **completion** / **next** / **back** / **sync** / **status** / **update** / **help** — Read `reap.{name}.md` from same directory, explain.
94
+ - **pull** / **push** / **merge.start** / **merge.detect** / **merge.mate** / **merge.merge** / **merge.sync** / **merge.validation** / **merge.completion** / **merge.evolve** — Read `reap.{name}.md` from same directory, explain.
@@ -34,6 +34,7 @@ If the target branch already includes all local work (fast-forward), skip the me
34
34
  9. Check if the local latest generation is an ancestor of the remote latest generation:
35
35
  - **If yes (fast-forward possible)**:
36
36
  - Run `git merge --ff {branch}`
37
+ - Run `git submodule update --init` to sync submodules
37
38
  - Report: "Fast-forwarded to {branch}. Local lineage now includes {remote-latest-id}. No merge generation needed."
38
39
  - **STOP** — no merge lifecycle needed
39
40
  - **If same generation**: "Already up to date." → **STOP**
@@ -44,7 +45,8 @@ If the target branch already includes all local work (fast-forward), skip the me
44
45
  - This creates the merge generation and runs detect (01-detect.md)
45
46
  11. Execute `/reap.merge.evolve` to run the full merge lifecycle:
46
47
  - Detect → Mate → Merge → Sync → Validation → Completion
47
- 12. The merge generation is archived upon completion
48
+ 12. Run `git submodule update --init` to sync submodules after merge
49
+ 13. The merge generation is archived upon completion
48
50
 
49
51
  ## Completion
50
52
  - Fast-forward: "Fast-forwarded to {branch}. No merge generation needed."
@@ -6,7 +6,7 @@ const gl = require('./genome-loader.cjs');
6
6
 
7
7
  const startTime = Date.now();
8
8
  let step = 0;
9
- const totalSteps = 6;
9
+ const totalSteps = 7;
10
10
 
11
11
  function log(msg) {
12
12
  step++;
@@ -28,6 +28,47 @@ if (!gl.dirExists(reapDir)) {
28
28
  process.exit(0);
29
29
  }
30
30
 
31
+ // Step 0: Install project-level command symlinks
32
+ const fs = require('fs');
33
+ const os = require('os');
34
+ const userReapCommands = path.join(os.homedir(), '.reap', 'commands');
35
+ const projectClaudeCommands = path.join(projectRoot, '.claude', 'commands');
36
+
37
+ if (gl.dirExists(userReapCommands)) {
38
+ try {
39
+ fs.mkdirSync(projectClaudeCommands, { recursive: true });
40
+ const cmdFiles = fs.readdirSync(userReapCommands).filter(f => f.startsWith('reap.') && f.endsWith('.md'));
41
+ for (const file of cmdFiles) {
42
+ const src = path.join(userReapCommands, file);
43
+ const dest = path.join(projectClaudeCommands, file);
44
+ try {
45
+ const stat = fs.lstatSync(dest);
46
+ if (stat.isSymbolicLink()) {
47
+ const target = fs.readlinkSync(dest);
48
+ if (target === src) continue; // already correct
49
+ fs.unlinkSync(dest); // stale symlink
50
+ } else {
51
+ // Regular file (old redirect or original) — replace with symlink
52
+ fs.unlinkSync(dest);
53
+ }
54
+ } catch { /* dest doesn't exist */ }
55
+ fs.symlinkSync(src, dest);
56
+ }
57
+ // Ensure .gitignore excludes these symlinks
58
+ const gitignorePath = path.join(projectRoot, '.gitignore');
59
+ const gitignoreEntry = '.claude/commands/reap.*';
60
+ try {
61
+ const gitignore = fs.existsSync(gitignorePath) ? fs.readFileSync(gitignorePath, 'utf-8') : '';
62
+ if (!gitignore.includes(gitignoreEntry)) {
63
+ fs.appendFileSync(gitignorePath, `\n# REAP command symlinks (managed by session-start hook)\n${gitignoreEntry}\n`);
64
+ }
65
+ } catch { /* best effort */ }
66
+ process.stderr.write(`[REAP] Symlinked ${cmdFiles.length} commands to .claude/commands/\n`);
67
+ } catch (err) {
68
+ process.stderr.write(`[REAP] Warning: failed to create command symlinks: ${err.message}\n`);
69
+ }
70
+ }
71
+
31
72
  // Step 1: Version check + Auto-update
32
73
  log('Checking for updates...');
33
74
  let autoUpdateMessage = '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c-d-cc/reap",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Recursive Evolutionary Autonomous Pipeline — AI and humans evolve software across generations",
5
5
  "type": "module",
6
6
  "license": "MIT",