@os-eco/overstory-cli 0.6.1
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/LICENSE +21 -0
- package/README.md +381 -0
- package/agents/builder.md +137 -0
- package/agents/coordinator.md +263 -0
- package/agents/lead.md +301 -0
- package/agents/merger.md +160 -0
- package/agents/monitor.md +214 -0
- package/agents/reviewer.md +140 -0
- package/agents/scout.md +119 -0
- package/agents/supervisor.md +423 -0
- package/package.json +47 -0
- package/src/agents/checkpoint.test.ts +88 -0
- package/src/agents/checkpoint.ts +101 -0
- package/src/agents/hooks-deployer.test.ts +2040 -0
- package/src/agents/hooks-deployer.ts +607 -0
- package/src/agents/identity.test.ts +603 -0
- package/src/agents/identity.ts +384 -0
- package/src/agents/lifecycle.test.ts +196 -0
- package/src/agents/lifecycle.ts +183 -0
- package/src/agents/manifest.test.ts +746 -0
- package/src/agents/manifest.ts +354 -0
- package/src/agents/overlay.test.ts +676 -0
- package/src/agents/overlay.ts +308 -0
- package/src/beads/client.test.ts +217 -0
- package/src/beads/client.ts +202 -0
- package/src/beads/molecules.test.ts +338 -0
- package/src/beads/molecules.ts +198 -0
- package/src/commands/agents.test.ts +322 -0
- package/src/commands/agents.ts +287 -0
- package/src/commands/clean.test.ts +670 -0
- package/src/commands/clean.ts +618 -0
- package/src/commands/completions.test.ts +342 -0
- package/src/commands/completions.ts +887 -0
- package/src/commands/coordinator.test.ts +1530 -0
- package/src/commands/coordinator.ts +733 -0
- package/src/commands/costs.test.ts +1119 -0
- package/src/commands/costs.ts +564 -0
- package/src/commands/dashboard.test.ts +308 -0
- package/src/commands/dashboard.ts +838 -0
- package/src/commands/doctor.test.ts +294 -0
- package/src/commands/doctor.ts +213 -0
- package/src/commands/errors.test.ts +647 -0
- package/src/commands/errors.ts +248 -0
- package/src/commands/feed.test.ts +578 -0
- package/src/commands/feed.ts +361 -0
- package/src/commands/group.test.ts +262 -0
- package/src/commands/group.ts +511 -0
- package/src/commands/hooks.test.ts +458 -0
- package/src/commands/hooks.ts +253 -0
- package/src/commands/init.test.ts +347 -0
- package/src/commands/init.ts +650 -0
- package/src/commands/inspect.test.ts +670 -0
- package/src/commands/inspect.ts +431 -0
- package/src/commands/log.test.ts +1454 -0
- package/src/commands/log.ts +724 -0
- package/src/commands/logs.test.ts +379 -0
- package/src/commands/logs.ts +546 -0
- package/src/commands/mail.test.ts +1270 -0
- package/src/commands/mail.ts +771 -0
- package/src/commands/merge.test.ts +670 -0
- package/src/commands/merge.ts +355 -0
- package/src/commands/metrics.test.ts +444 -0
- package/src/commands/metrics.ts +143 -0
- package/src/commands/monitor.test.ts +191 -0
- package/src/commands/monitor.ts +390 -0
- package/src/commands/nudge.test.ts +230 -0
- package/src/commands/nudge.ts +372 -0
- package/src/commands/prime.test.ts +470 -0
- package/src/commands/prime.ts +381 -0
- package/src/commands/replay.test.ts +741 -0
- package/src/commands/replay.ts +360 -0
- package/src/commands/run.test.ts +431 -0
- package/src/commands/run.ts +351 -0
- package/src/commands/sling.test.ts +657 -0
- package/src/commands/sling.ts +661 -0
- package/src/commands/spec.test.ts +203 -0
- package/src/commands/spec.ts +168 -0
- package/src/commands/status.test.ts +430 -0
- package/src/commands/status.ts +398 -0
- package/src/commands/stop.test.ts +420 -0
- package/src/commands/stop.ts +151 -0
- package/src/commands/supervisor.test.ts +187 -0
- package/src/commands/supervisor.ts +535 -0
- package/src/commands/trace.test.ts +745 -0
- package/src/commands/trace.ts +325 -0
- package/src/commands/watch.test.ts +145 -0
- package/src/commands/watch.ts +247 -0
- package/src/commands/worktree.test.ts +786 -0
- package/src/commands/worktree.ts +311 -0
- package/src/config.test.ts +822 -0
- package/src/config.ts +829 -0
- package/src/doctor/agents.test.ts +454 -0
- package/src/doctor/agents.ts +396 -0
- package/src/doctor/config-check.test.ts +190 -0
- package/src/doctor/config-check.ts +183 -0
- package/src/doctor/consistency.test.ts +651 -0
- package/src/doctor/consistency.ts +294 -0
- package/src/doctor/databases.test.ts +290 -0
- package/src/doctor/databases.ts +218 -0
- package/src/doctor/dependencies.test.ts +184 -0
- package/src/doctor/dependencies.ts +175 -0
- package/src/doctor/logs.test.ts +251 -0
- package/src/doctor/logs.ts +295 -0
- package/src/doctor/merge-queue.test.ts +216 -0
- package/src/doctor/merge-queue.ts +144 -0
- package/src/doctor/structure.test.ts +291 -0
- package/src/doctor/structure.ts +198 -0
- package/src/doctor/types.ts +37 -0
- package/src/doctor/version.test.ts +136 -0
- package/src/doctor/version.ts +129 -0
- package/src/e2e/init-sling-lifecycle.test.ts +277 -0
- package/src/errors.ts +217 -0
- package/src/events/store.test.ts +660 -0
- package/src/events/store.ts +369 -0
- package/src/events/tool-filter.test.ts +330 -0
- package/src/events/tool-filter.ts +126 -0
- package/src/index.ts +316 -0
- package/src/insights/analyzer.test.ts +466 -0
- package/src/insights/analyzer.ts +203 -0
- package/src/logging/color.test.ts +142 -0
- package/src/logging/color.ts +71 -0
- package/src/logging/logger.test.ts +813 -0
- package/src/logging/logger.ts +266 -0
- package/src/logging/reporter.test.ts +259 -0
- package/src/logging/reporter.ts +109 -0
- package/src/logging/sanitizer.test.ts +190 -0
- package/src/logging/sanitizer.ts +57 -0
- package/src/mail/broadcast.test.ts +203 -0
- package/src/mail/broadcast.ts +92 -0
- package/src/mail/client.test.ts +773 -0
- package/src/mail/client.ts +223 -0
- package/src/mail/store.test.ts +705 -0
- package/src/mail/store.ts +387 -0
- package/src/merge/queue.test.ts +359 -0
- package/src/merge/queue.ts +231 -0
- package/src/merge/resolver.test.ts +1345 -0
- package/src/merge/resolver.ts +645 -0
- package/src/metrics/store.test.ts +667 -0
- package/src/metrics/store.ts +445 -0
- package/src/metrics/summary.test.ts +398 -0
- package/src/metrics/summary.ts +178 -0
- package/src/metrics/transcript.test.ts +356 -0
- package/src/metrics/transcript.ts +175 -0
- package/src/mulch/client.test.ts +671 -0
- package/src/mulch/client.ts +332 -0
- package/src/sessions/compat.test.ts +280 -0
- package/src/sessions/compat.ts +104 -0
- package/src/sessions/store.test.ts +873 -0
- package/src/sessions/store.ts +494 -0
- package/src/test-helpers.test.ts +124 -0
- package/src/test-helpers.ts +126 -0
- package/src/tracker/beads.ts +56 -0
- package/src/tracker/factory.test.ts +80 -0
- package/src/tracker/factory.ts +64 -0
- package/src/tracker/seeds.ts +182 -0
- package/src/tracker/types.ts +52 -0
- package/src/types.ts +724 -0
- package/src/watchdog/daemon.test.ts +1975 -0
- package/src/watchdog/daemon.ts +671 -0
- package/src/watchdog/health.test.ts +431 -0
- package/src/watchdog/health.ts +264 -0
- package/src/watchdog/triage.test.ts +164 -0
- package/src/watchdog/triage.ts +179 -0
- package/src/worktree/manager.test.ts +439 -0
- package/src/worktree/manager.ts +198 -0
- package/src/worktree/tmux.test.ts +1009 -0
- package/src/worktree/tmux.ts +509 -0
- package/templates/CLAUDE.md.tmpl +89 -0
- package/templates/hooks.json.tmpl +105 -0
- package/templates/overlay.md.tmpl +81 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: overstory merge
|
|
3
|
+
*
|
|
4
|
+
* Merges agent branches back to the canonical branch using
|
|
5
|
+
* the merge queue and tiered conflict resolver.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* overstory merge --branch <name> Merge a specific branch
|
|
9
|
+
* overstory merge --all Merge all pending branches
|
|
10
|
+
* overstory merge --dry-run Check for conflicts without merging
|
|
11
|
+
* overstory merge --json Output results as JSON
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { loadConfig } from "../config.ts";
|
|
16
|
+
import { MergeError, ValidationError } from "../errors.ts";
|
|
17
|
+
import { createMergeQueue } from "../merge/queue.ts";
|
|
18
|
+
import { createMergeResolver } from "../merge/resolver.ts";
|
|
19
|
+
import { createMulchClient } from "../mulch/client.ts";
|
|
20
|
+
import type { MergeEntry, MergeResult } from "../types.ts";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parse a named flag value from an args array.
|
|
24
|
+
* Returns the value after the flag, or undefined if not present.
|
|
25
|
+
*/
|
|
26
|
+
function getFlag(args: string[], flag: string): string | undefined {
|
|
27
|
+
const idx = args.indexOf(flag);
|
|
28
|
+
if (idx === -1 || idx + 1 >= args.length) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return args[idx + 1];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Check if a boolean flag is present in the args. */
|
|
35
|
+
function hasFlag(args: string[], flag: string): boolean {
|
|
36
|
+
return args.includes(flag);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Extract agent name from a branch following the overstory naming convention.
|
|
41
|
+
* Pattern: overstory/{agentName}/{beadId}
|
|
42
|
+
* Falls back to "unknown" if the pattern does not match.
|
|
43
|
+
*/
|
|
44
|
+
function parseAgentName(branchName: string): string {
|
|
45
|
+
const parts = branchName.split("/");
|
|
46
|
+
if (parts[0] === "overstory" && parts[1] !== undefined) {
|
|
47
|
+
return parts[1];
|
|
48
|
+
}
|
|
49
|
+
return "unknown";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Extract bead ID from a branch following the overstory naming convention.
|
|
54
|
+
* Pattern: overstory/{agentName}/{beadId}
|
|
55
|
+
* Falls back to "unknown" if the pattern does not match.
|
|
56
|
+
*/
|
|
57
|
+
function parseBeadId(branchName: string): string {
|
|
58
|
+
const parts = branchName.split("/");
|
|
59
|
+
if (parts[0] === "overstory" && parts[2] !== undefined) {
|
|
60
|
+
return parts[2];
|
|
61
|
+
}
|
|
62
|
+
return "unknown";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Detect modified files between a branch and the canonical branch using git diff.
|
|
67
|
+
* Returns an array of file paths that differ.
|
|
68
|
+
*/
|
|
69
|
+
async function detectModifiedFiles(
|
|
70
|
+
repoRoot: string,
|
|
71
|
+
canonicalBranch: string,
|
|
72
|
+
branchName: string,
|
|
73
|
+
): Promise<string[]> {
|
|
74
|
+
const proc = Bun.spawn(["git", "diff", "--name-only", `${canonicalBranch}...${branchName}`], {
|
|
75
|
+
cwd: repoRoot,
|
|
76
|
+
stdout: "pipe",
|
|
77
|
+
stderr: "pipe",
|
|
78
|
+
});
|
|
79
|
+
const exitCode = await proc.exited;
|
|
80
|
+
|
|
81
|
+
if (exitCode !== 0) {
|
|
82
|
+
const stderr = await new Response(proc.stderr).text();
|
|
83
|
+
throw new MergeError(
|
|
84
|
+
`Failed to detect modified files for branch "${branchName}": ${stderr.trim()}`,
|
|
85
|
+
{ branchName },
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const stdout = await new Response(proc.stdout).text();
|
|
90
|
+
return stdout
|
|
91
|
+
.trim()
|
|
92
|
+
.split("\n")
|
|
93
|
+
.filter((line) => line.length > 0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Format a single merge result for human-readable output. */
|
|
97
|
+
function formatResult(result: MergeResult): string {
|
|
98
|
+
const statusIcon = result.success ? "Merged" : "Failed";
|
|
99
|
+
const lines: string[] = [
|
|
100
|
+
`Merging branch: ${result.entry.branchName}`,
|
|
101
|
+
` Agent: ${result.entry.agentName} | Task: ${result.entry.beadId}`,
|
|
102
|
+
` Files: ${result.entry.filesModified.length} modified`,
|
|
103
|
+
` Result: ${statusIcon} (tier: ${result.tier})`,
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
if (result.conflictFiles.length > 0) {
|
|
107
|
+
lines.push(` Conflicts: ${result.conflictFiles.join(", ")}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (result.errorMessage) {
|
|
111
|
+
lines.push(` Error: ${result.errorMessage}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return lines.join("\n");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Format a dry-run report for a merge entry. */
|
|
118
|
+
function formatDryRun(entry: MergeEntry): string {
|
|
119
|
+
const lines: string[] = [
|
|
120
|
+
`[dry-run] Branch: ${entry.branchName}`,
|
|
121
|
+
` Agent: ${entry.agentName} | Task: ${entry.beadId}`,
|
|
122
|
+
` Status: ${entry.status}`,
|
|
123
|
+
` Files: ${entry.filesModified.length} modified`,
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
if (entry.filesModified.length > 0) {
|
|
127
|
+
for (const f of entry.filesModified) {
|
|
128
|
+
lines.push(` - ${f}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return lines.join("\n");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Entry point for `overstory merge [flags]`.
|
|
137
|
+
*
|
|
138
|
+
* Flags:
|
|
139
|
+
* --branch <name> Merge a specific branch
|
|
140
|
+
* --all Merge all pending branches in the queue
|
|
141
|
+
* --dry-run Check for conflicts without actually merging
|
|
142
|
+
* --json Output results as JSON
|
|
143
|
+
*/
|
|
144
|
+
const MERGE_HELP = `overstory merge — Merge agent branches into canonical
|
|
145
|
+
|
|
146
|
+
Usage: overstory merge --branch <name> | --all [--into <branch>] [--dry-run] [--json]
|
|
147
|
+
|
|
148
|
+
Options:
|
|
149
|
+
--branch <name> Merge a specific branch
|
|
150
|
+
--all Merge all pending branches in the queue
|
|
151
|
+
--into <branch> Target branch to merge into (default: config canonicalBranch)
|
|
152
|
+
--dry-run Check for conflicts without actually merging
|
|
153
|
+
--json Output results as JSON
|
|
154
|
+
--help, -h Show this help`;
|
|
155
|
+
|
|
156
|
+
export async function mergeCommand(args: string[]): Promise<void> {
|
|
157
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
158
|
+
process.stdout.write(`${MERGE_HELP}\n`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const branchName = getFlag(args, "--branch");
|
|
163
|
+
const all = hasFlag(args, "--all");
|
|
164
|
+
const into = getFlag(args, "--into");
|
|
165
|
+
const dryRun = hasFlag(args, "--dry-run");
|
|
166
|
+
const json = hasFlag(args, "--json");
|
|
167
|
+
|
|
168
|
+
if (!branchName && !all) {
|
|
169
|
+
throw new ValidationError("Either --branch <name> or --all is required for overstory merge", {
|
|
170
|
+
field: "branch|all",
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const cwd = process.cwd();
|
|
175
|
+
const config = await loadConfig(cwd);
|
|
176
|
+
|
|
177
|
+
// Resolution chain: --into flag > session-start branch > config canonicalBranch
|
|
178
|
+
let sessionBranch: string | null = null;
|
|
179
|
+
if (into === undefined) {
|
|
180
|
+
const sessionBranchPath = join(config.project.root, ".overstory", "session-branch.txt");
|
|
181
|
+
const sessionBranchFile = Bun.file(sessionBranchPath);
|
|
182
|
+
if (await sessionBranchFile.exists()) {
|
|
183
|
+
const content = (await sessionBranchFile.text()).trim();
|
|
184
|
+
if (content) {
|
|
185
|
+
sessionBranch = content;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const targetBranch = into ?? sessionBranch ?? config.project.canonicalBranch;
|
|
190
|
+
const queuePath = join(config.project.root, ".overstory", "merge-queue.db");
|
|
191
|
+
const queue = createMergeQueue(queuePath);
|
|
192
|
+
const mulchClient = createMulchClient(config.project.root);
|
|
193
|
+
const resolver = createMergeResolver({
|
|
194
|
+
aiResolveEnabled: config.merge.aiResolveEnabled,
|
|
195
|
+
reimagineEnabled: config.merge.reimagineEnabled,
|
|
196
|
+
mulchClient,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (branchName) {
|
|
200
|
+
await handleBranch(branchName, queue, resolver, config, targetBranch, dryRun, json);
|
|
201
|
+
} else {
|
|
202
|
+
await handleAll(queue, resolver, config, targetBranch, dryRun, json);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Handle merging a specific branch.
|
|
208
|
+
* If the branch is not in the queue, creates a new entry by detecting
|
|
209
|
+
* agent name, bead ID, and modified files from git.
|
|
210
|
+
*/
|
|
211
|
+
async function handleBranch(
|
|
212
|
+
branchName: string,
|
|
213
|
+
queue: ReturnType<typeof createMergeQueue>,
|
|
214
|
+
resolver: ReturnType<typeof createMergeResolver>,
|
|
215
|
+
config: Awaited<ReturnType<typeof loadConfig>>,
|
|
216
|
+
targetBranch: string,
|
|
217
|
+
dryRun: boolean,
|
|
218
|
+
json: boolean,
|
|
219
|
+
): Promise<void> {
|
|
220
|
+
const canonicalBranch = targetBranch;
|
|
221
|
+
const repoRoot = config.project.root;
|
|
222
|
+
|
|
223
|
+
// Look for existing entry in the queue
|
|
224
|
+
const allEntries = queue.list();
|
|
225
|
+
let entry = allEntries.find((e) => e.branchName === branchName) ?? null;
|
|
226
|
+
|
|
227
|
+
// If not in queue, create one by detecting info from the branch
|
|
228
|
+
if (entry === null) {
|
|
229
|
+
// Validate that the branch exists before attempting any git operations
|
|
230
|
+
const verifyProc = Bun.spawn(["git", "rev-parse", "--verify", `refs/heads/${branchName}`], {
|
|
231
|
+
cwd: repoRoot,
|
|
232
|
+
stdout: "pipe",
|
|
233
|
+
stderr: "pipe",
|
|
234
|
+
});
|
|
235
|
+
const verifyExit = await verifyProc.exited;
|
|
236
|
+
if (verifyExit !== 0) {
|
|
237
|
+
throw new ValidationError(`Branch "${branchName}" not found`, {
|
|
238
|
+
field: "branch",
|
|
239
|
+
value: branchName,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const agentName = parseAgentName(branchName);
|
|
244
|
+
const beadId = parseBeadId(branchName);
|
|
245
|
+
const filesModified = await detectModifiedFiles(repoRoot, canonicalBranch, branchName);
|
|
246
|
+
|
|
247
|
+
entry = queue.enqueue({
|
|
248
|
+
branchName,
|
|
249
|
+
beadId,
|
|
250
|
+
agentName,
|
|
251
|
+
filesModified,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (dryRun) {
|
|
256
|
+
if (json) {
|
|
257
|
+
process.stdout.write(`${JSON.stringify(entry)}\n`);
|
|
258
|
+
} else {
|
|
259
|
+
process.stdout.write(`${formatDryRun(entry)}\n`);
|
|
260
|
+
}
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Perform the actual merge
|
|
265
|
+
const result = await resolver.resolve(entry, canonicalBranch, repoRoot);
|
|
266
|
+
|
|
267
|
+
// Update queue status based on result
|
|
268
|
+
queue.updateStatus(branchName, result.success ? "merged" : "conflict", result.tier);
|
|
269
|
+
|
|
270
|
+
if (json) {
|
|
271
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
272
|
+
} else {
|
|
273
|
+
process.stdout.write(`${formatResult(result)}\n`);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (!result.success) {
|
|
277
|
+
throw new MergeError(result.errorMessage ?? `Merge failed for branch "${branchName}"`, {
|
|
278
|
+
branchName,
|
|
279
|
+
conflictFiles: result.conflictFiles,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Handle merging all pending branches in the queue.
|
|
286
|
+
* Processes entries sequentially in FIFO order.
|
|
287
|
+
*/
|
|
288
|
+
async function handleAll(
|
|
289
|
+
queue: ReturnType<typeof createMergeQueue>,
|
|
290
|
+
resolver: ReturnType<typeof createMergeResolver>,
|
|
291
|
+
config: Awaited<ReturnType<typeof loadConfig>>,
|
|
292
|
+
targetBranch: string,
|
|
293
|
+
dryRun: boolean,
|
|
294
|
+
json: boolean,
|
|
295
|
+
): Promise<void> {
|
|
296
|
+
const canonicalBranch = targetBranch;
|
|
297
|
+
const repoRoot = config.project.root;
|
|
298
|
+
|
|
299
|
+
const pendingEntries = queue.list("pending");
|
|
300
|
+
|
|
301
|
+
if (pendingEntries.length === 0) {
|
|
302
|
+
if (json) {
|
|
303
|
+
process.stdout.write(`${JSON.stringify({ results: [], count: 0 })}\n`);
|
|
304
|
+
} else {
|
|
305
|
+
process.stdout.write("No pending branches to merge.\n");
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (dryRun) {
|
|
311
|
+
if (json) {
|
|
312
|
+
process.stdout.write(`${JSON.stringify(pendingEntries)}\n`);
|
|
313
|
+
} else {
|
|
314
|
+
process.stdout.write(
|
|
315
|
+
`${pendingEntries.length} pending branch${pendingEntries.length === 1 ? "" : "es"}:\n\n`,
|
|
316
|
+
);
|
|
317
|
+
for (const entry of pendingEntries) {
|
|
318
|
+
process.stdout.write(`${formatDryRun(entry)}\n\n`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const results: MergeResult[] = [];
|
|
325
|
+
let successCount = 0;
|
|
326
|
+
let failCount = 0;
|
|
327
|
+
|
|
328
|
+
for (const entry of pendingEntries) {
|
|
329
|
+
const result = await resolver.resolve(entry, canonicalBranch, repoRoot);
|
|
330
|
+
|
|
331
|
+
queue.updateStatus(entry.branchName, result.success ? "merged" : "conflict", result.tier);
|
|
332
|
+
|
|
333
|
+
results.push(result);
|
|
334
|
+
|
|
335
|
+
if (result.success) {
|
|
336
|
+
successCount++;
|
|
337
|
+
} else {
|
|
338
|
+
failCount++;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (!json) {
|
|
342
|
+
process.stdout.write(`${formatResult(result)}\n\n`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (json) {
|
|
347
|
+
process.stdout.write(
|
|
348
|
+
`${JSON.stringify({ results, count: results.length, successCount, failCount })}\n`,
|
|
349
|
+
);
|
|
350
|
+
} else {
|
|
351
|
+
process.stdout.write(
|
|
352
|
+
`Done: ${successCount} merged, ${failCount} failed out of ${results.length} total.\n`,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|