@c-d-cc/reap 0.15.10 → 0.15.11

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/RELEASE_NOTICE.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Release Notices
2
2
 
3
+ ## v0.15.11
4
+ ### en
5
+ Fixed `reap pull` incorrectly recommending merge for ahead-only branches. Now uses `git rev-list` for accurate ahead/behind/diverged detection.
6
+ ### ko
7
+ `reap pull`이 ahead 상태에서 불필요한 merge를 추천하던 문제 수정. `git rev-list` 기반 정확한 ahead/behind/diverged 감지.
8
+
3
9
  ## v0.15.10
4
10
  ### en
5
11
  Fixed release notice language matching (e.g. "korean" → "ko").
package/dist/cli.js CHANGED
@@ -10230,7 +10230,7 @@ function checkLatestVersion() {
10230
10230
  }
10231
10231
  }
10232
10232
  function getCurrentVersion() {
10233
- return "0.15.10";
10233
+ return "0.15.11";
10234
10234
  }
10235
10235
  function formatVersionLine(current, skipCheck) {
10236
10236
  if (skipCheck) {
@@ -10361,12 +10361,25 @@ async function createBacklog(backlogDir, opts) {
10361
10361
  status: "pending",
10362
10362
  priority
10363
10363
  };
10364
- const bodyContent = opts.body ? `
10364
+ const summary = opts.body ? `${opts.body}
10365
+ ` : "";
10366
+ const bodyContent = `
10365
10367
  # ${opts.title}
10366
10368
 
10367
- ${opts.body}
10368
- ` : `
10369
- # ${opts.title}
10369
+ ${summary}
10370
+ ## Problem
10371
+ <!-- Describe the current issue and why this work is needed -->
10372
+
10373
+ ## Solution
10374
+ <!-- Describe the approach, implementation direction, and key ideas -->
10375
+
10376
+ ## Files to Change
10377
+ <!-- List specific file/function/module paths that need modification -->
10378
+
10379
+ ## Context
10380
+ <!-- Related generations, issues, or background that led to this task. Remove if not applicable -->
10381
+
10382
+ <!-- Feel free to add more sections if needed (e.g., Constraints, Alternatives, References) -->
10370
10383
  `;
10371
10384
  const content = `---
10372
10385
  ${import_yaml8.default.stringify(frontmatter).trim()}
@@ -10412,13 +10425,14 @@ async function execute(paths, argv) {
10412
10425
  body: flags.body,
10413
10426
  priority: flags.priority
10414
10427
  });
10428
+ const filePath = `${paths.backlog}/${filename}`;
10415
10429
  emitOutput({
10416
10430
  status: "ok",
10417
10431
  command: "make",
10418
10432
  phase: "done",
10419
10433
  completed: ["backlog-create"],
10420
- message: `Backlog created: ${filename}. Edit the file directly to add detailed content (headers, lists, code blocks, etc.).`,
10421
- context: { filename, type: flags.type, title: flags.title, path: `${paths.backlog}/${filename}` }
10434
+ message: `Backlog created: ${filePath}. Fill in the template sections (문제/해결/수정 대상/배경) and remove HTML comments.`,
10435
+ context: { filename, type: flags.type, title: flags.title, path: filePath }
10422
10436
  });
10423
10437
  }
10424
10438
  var init_backlog2 = __esm(() => {
@@ -12429,7 +12443,7 @@ async function execute17(paths) {
12429
12443
  const gm = new GenerationManager(paths);
12430
12444
  const state = await gm.current();
12431
12445
  const configContent = await readTextFile(paths.config);
12432
- const installedVersion = "0.15.10";
12446
+ const installedVersion = "0.15.11";
12433
12447
  const autoUpdate = configContent?.match(/autoUpdate:\s*(true|false)/)?.[1] === "true";
12434
12448
  const versionDisplay = formatVersionLine(installedVersion, !autoUpdate);
12435
12449
  const rawLang = detectLanguage(configContent);
@@ -12969,31 +12983,6 @@ class MergeGenerationManager {
12969
12983
  await writeTextFile(this.paths.currentYml, import_yaml10.default.stringify(state));
12970
12984
  }
12971
12985
  }
12972
- function canFastForward(localLatestId, remoteLatestId, allMetas) {
12973
- if (localLatestId === remoteLatestId) {
12974
- return { fastForward: true, reason: "Already up to date — same generation on both branches." };
12975
- }
12976
- const metaMap = new Map;
12977
- for (const m of allMetas)
12978
- metaMap.set(m.id, m);
12979
- const remoteAncestors = new Set;
12980
- const queue = [remoteLatestId];
12981
- while (queue.length > 0) {
12982
- const id = queue.shift();
12983
- if (remoteAncestors.has(id))
12984
- continue;
12985
- remoteAncestors.add(id);
12986
- const meta = metaMap.get(id);
12987
- if (meta?.parents) {
12988
- for (const p of meta.parents)
12989
- queue.push(p);
12990
- }
12991
- }
12992
- if (remoteAncestors.has(localLatestId)) {
12993
- return { fastForward: true, reason: `Local generation ${localLatestId} is already included in remote ${remoteLatestId}. Fast-forward possible.` };
12994
- }
12995
- return { fastForward: false, reason: "Branches have diverged — full merge required." };
12996
- }
12997
12986
  function findCommonAncestor(idA, idB, metas) {
12998
12987
  const metaMap = new Map;
12999
12988
  for (const m of metas)
@@ -14050,6 +14039,19 @@ var exports_pull = {};
14050
14039
  __export(exports_pull, {
14051
14040
  execute: () => execute29
14052
14041
  });
14042
+ import { execSync as execSync8 } from "child_process";
14043
+ function getAheadBehind(target, cwd) {
14044
+ try {
14045
+ const result = execSync8(`git rev-list --left-right --count HEAD...${target}`, {
14046
+ cwd,
14047
+ encoding: "utf-8"
14048
+ }).trim();
14049
+ const [aheadStr, behindStr] = result.split(/\s+/);
14050
+ return { ahead: parseInt(aheadStr, 10) || 0, behind: parseInt(behindStr, 10) || 0 };
14051
+ } catch {
14052
+ throw new Error(`Failed to compare HEAD with ${target}. Ensure both refs exist.`);
14053
+ }
14054
+ }
14053
14055
  async function execute29(paths, phase, argv = []) {
14054
14056
  const positionals = argv.filter((a) => !a.startsWith("--"));
14055
14057
  const targetBranchArg = positionals[0];
@@ -14087,85 +14089,76 @@ async function execute29(paths, phase, argv = []) {
14087
14089
  emitError("pull", `Branch "${targetBranch}" does not exist. Run \`git fetch\` first.`);
14088
14090
  }
14089
14091
  const currentBranch = gitCurrentBranch(paths.projectRoot);
14090
- const mgm = new MergeGenerationManager(paths);
14091
- const localMetas = await listMeta(paths);
14092
- let remoteMetasRaw = [];
14093
- try {
14094
- const remoteMetas = mgm.listMetaFromRef(targetBranch, paths.projectRoot);
14095
- remoteMetasRaw = remoteMetas;
14096
- } catch {}
14097
- if (localMetas.length === 0 && remoteMetasRaw.length === 0) {
14092
+ const { ahead, behind } = getAheadBehind(targetBranch, paths.projectRoot);
14093
+ if (ahead === 0 && behind === 0) {
14098
14094
  emitOutput({
14099
14095
  status: "ok",
14100
14096
  command: "pull",
14101
14097
  phase: "up-to-date",
14102
14098
  completed: ["gate", "fetch", "detect"],
14103
- context: { targetBranch, currentBranch },
14104
- message: "No lineage on either branch. Already up to date."
14099
+ context: { targetBranch, currentBranch, ahead, behind },
14100
+ message: "Already up to date."
14105
14101
  });
14106
- return;
14107
- }
14108
- if (localMetas.length > 0 && remoteMetasRaw.length > 0) {
14109
- const localLatest = localMetas.sort((a, b) => new Date(b.completedAt).getTime() - new Date(a.completedAt).getTime())[0];
14110
- const remoteLatest = remoteMetasRaw.sort((a, b) => new Date(b.completedAt).getTime() - new Date(a.completedAt).getTime())[0];
14111
- const allMetas = [...localMetas];
14112
- for (const rm6 of remoteMetasRaw) {
14113
- if (!allMetas.find((m) => m.id === rm6.id))
14114
- allMetas.push(rm6);
14115
- }
14116
- const ffResult = canFastForward(localLatest.id, remoteLatest.id, allMetas);
14117
- if (ffResult.fastForward) {
14118
- emitOutput({
14119
- status: "prompt",
14120
- command: "pull",
14121
- phase: "fast-forward",
14122
- completed: ["gate", "fetch", "detect", "ff-check"],
14123
- context: {
14124
- targetBranch,
14125
- currentBranch,
14126
- localLatestId: localLatest.id,
14127
- remoteLatestId: remoteLatest.id,
14128
- reason: ffResult.reason
14129
- },
14130
- prompt: [
14131
- `Fast-forward possible: ${ffResult.reason}`,
14132
- "",
14133
- `Run \`git merge --ff ${targetBranch}\`, then \`git submodule update --init\`.`,
14134
- "No merge generation needed."
14135
- ].join(`
14102
+ } else if (ahead > 0 && behind === 0) {
14103
+ emitOutput({
14104
+ status: "ok",
14105
+ command: "pull",
14106
+ phase: "ahead",
14107
+ completed: ["gate", "fetch", "detect"],
14108
+ context: { targetBranch, currentBranch, ahead, behind },
14109
+ message: `Local is ${ahead} commit(s) ahead of ${targetBranch}. Consider pushing.`,
14110
+ prompt: [
14111
+ `## Local is ahead by ${ahead} commit(s)`,
14112
+ "",
14113
+ `Your branch is ahead of \`${targetBranch}\`. No pull needed.`,
14114
+ "Run `git push` to update the remote."
14115
+ ].join(`
14136
14116
  `)
14137
- });
14138
- return;
14139
- }
14140
- }
14141
- emitOutput({
14142
- status: "prompt",
14143
- command: "pull",
14144
- phase: "start-merge",
14145
- completed: ["gate", "fetch", "detect", "ff-check"],
14146
- context: {
14147
- targetBranch,
14148
- currentBranch,
14149
- diverged: true
14150
- },
14151
- prompt: [
14152
- "## Branches have diverged -- full merge required",
14153
- "",
14154
- "Execute the following sequence:",
14155
- `1. Run /reap.merge.start ${targetBranch} (creates merge generation + detect report)`,
14156
- "2. Run /reap.merge.evolve (runs detect -> mate -> merge -> sync -> validation -> completion)",
14157
- "3. Run `git submodule update --init` after merge completes"
14158
- ].join(`
14117
+ });
14118
+ } else if (ahead === 0 && behind > 0) {
14119
+ emitOutput({
14120
+ status: "prompt",
14121
+ command: "pull",
14122
+ phase: "fast-forward",
14123
+ completed: ["gate", "fetch", "detect"],
14124
+ context: { targetBranch, currentBranch, ahead, behind },
14125
+ prompt: [
14126
+ `## Behind by ${behind} commit(s) -- fast-forward possible`,
14127
+ "",
14128
+ `Run \`git merge --ff ${targetBranch}\`, then \`git submodule update --init\`.`,
14129
+ "No merge generation needed."
14130
+ ].join(`
14159
14131
  `)
14160
- });
14132
+ });
14133
+ } else {
14134
+ emitOutput({
14135
+ status: "prompt",
14136
+ command: "pull",
14137
+ phase: "start-merge",
14138
+ completed: ["gate", "fetch", "detect"],
14139
+ context: {
14140
+ targetBranch,
14141
+ currentBranch,
14142
+ ahead,
14143
+ behind,
14144
+ diverged: true
14145
+ },
14146
+ prompt: [
14147
+ `## Branches have diverged (ahead: ${ahead}, behind: ${behind}) -- full merge required`,
14148
+ "",
14149
+ "Execute the following sequence:",
14150
+ `1. Run /reap.merge.start ${targetBranch} (creates merge generation + detect report)`,
14151
+ "2. Run /reap.merge.evolve (runs detect -> mate -> merge -> sync -> validation -> completion)",
14152
+ "3. Run `git submodule update --init` after merge completes"
14153
+ ].join(`
14154
+ `)
14155
+ });
14156
+ }
14161
14157
  }
14162
14158
  }
14163
14159
  var init_pull = __esm(() => {
14164
14160
  init_generation();
14165
- init_merge_generation();
14166
14161
  init_git();
14167
- init_lineage();
14168
- init_merge_generation();
14169
14162
  });
14170
14163
 
14171
14164
  // src/cli/commands/run/config.ts
@@ -14178,7 +14171,7 @@ async function execute30(paths) {
14178
14171
  const lines = [
14179
14172
  `REAP Configuration (${paths.config})`,
14180
14173
  "",
14181
- ` version: ${"0.15.10"} (package)`,
14174
+ ` version: ${"0.15.11"} (package)`,
14182
14175
  ` project: ${config.project}`,
14183
14176
  ` entryMode: ${config.entryMode}`,
14184
14177
  ` strict: ${config.strict ?? false}`,
@@ -14466,7 +14459,7 @@ var exports_run = {};
14466
14459
  __export(exports_run, {
14467
14460
  runCommand: () => runCommand
14468
14461
  });
14469
- import { execSync as execSync8 } from "child_process";
14462
+ import { execSync as execSync9 } from "child_process";
14470
14463
  async function runCommand(command, phase, argv = []) {
14471
14464
  const cwd = process.cwd();
14472
14465
  const paths = new ReapPaths(cwd);
@@ -14484,7 +14477,7 @@ async function runCommand(command, phase, argv = []) {
14484
14477
  try {
14485
14478
  const config = await ConfigManager.read(paths);
14486
14479
  if (config.autoIssueReport) {
14487
- const version = "0.15.10";
14480
+ const version = "0.15.11";
14488
14481
  const errMsg = err instanceof Error ? err.message : String(err);
14489
14482
  const title = `[auto] reap run ${command}: ${errMsg.slice(0, 80)}`;
14490
14483
  const body = [
@@ -14494,7 +14487,7 @@ async function runCommand(command, phase, argv = []) {
14494
14487
  `**OS**: ${process.platform} ${process.arch}`,
14495
14488
  `**Node**: ${process.version}`
14496
14489
  ].join("\\n");
14497
- execSync8(`gh issue create --repo c-d-cc/reap --title "${title}" --label "auto-reported,bug" --body "${body}"`, { stdio: "ignore", timeout: 1e4 });
14490
+ execSync9(`gh issue create --repo c-d-cc/reap --title "${title}" --label "auto-reported,bug" --body "${body}"`, { stdio: "ignore", timeout: 1e4 });
14498
14491
  }
14499
14492
  } catch {}
14500
14493
  emitError(command, err instanceof Error ? err.message : String(err));
@@ -16434,7 +16427,7 @@ async function getStatus(projectRoot) {
16434
16427
  const totalCompleted = await mgr.countAllCompleted();
16435
16428
  const integrityResult = await checkIntegrity(paths);
16436
16429
  return {
16437
- version: "0.15.10",
16430
+ version: "0.15.11",
16438
16431
  project: config.project,
16439
16432
  entryMode: config.entryMode,
16440
16433
  lastSyncedGeneration: config.lastSyncedGeneration,
@@ -16757,7 +16750,7 @@ init_fs();
16757
16750
  init_version();
16758
16751
  init_config();
16759
16752
  import { join as join34 } from "path";
16760
- program.name("reap").description("REAP — Recursive Evolutionary Autonomous Pipeline").version("0.15.10");
16753
+ program.name("reap").description("REAP — Recursive Evolutionary Autonomous Pipeline").version("0.15.11");
16761
16754
  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) => {
16762
16755
  try {
16763
16756
  const cwd = process.cwd();
@@ -16814,7 +16807,7 @@ program.command("status").description("Show current project and Generation statu
16814
16807
  const paths = new ReapPaths(cwd);
16815
16808
  const config = await ConfigManager.read(paths);
16816
16809
  const skipCheck = config.autoUpdate === false;
16817
- const installedVersion = "0.15.10";
16810
+ const installedVersion = "0.15.11";
16818
16811
  const versionLine = formatVersionLine(installedVersion, skipCheck);
16819
16812
  console.log(`${versionLine} | Project: ${status.project} (${status.entryMode})`);
16820
16813
  console.log(`Completed Generations: ${status.totalGenerations}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c-d-cc/reap",
3
- "version": "0.15.10",
3
+ "version": "0.15.11",
4
4
  "description": "Recursive Evolutionary Autonomous Pipeline — AI and humans evolve software across generations",
5
5
  "type": "module",
6
6
  "license": "MIT",