mergestorm 0.3.9 → 0.3.15
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.md +67 -24
- package/dist/api.d.ts +106 -40
- package/dist/api.js +245 -9
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +5 -0
- package/dist/commands/branches.d.ts +6 -2
- package/dist/commands/branches.js +29 -14
- package/dist/commands/browse.d.ts +39 -0
- package/dist/commands/browse.js +259 -0
- package/dist/commands/credits.js +30 -12
- package/dist/commands/help.d.ts +5 -0
- package/dist/commands/help.js +93 -0
- package/dist/commands/jobs.d.ts +11 -2
- package/dist/commands/jobs.js +64 -15
- package/dist/commands/login.js +32 -10
- package/dist/commands/logout.js +2 -1
- package/dist/commands/pr.d.ts +22 -0
- package/dist/commands/pr.js +127 -0
- package/dist/commands/queue.d.ts +12 -0
- package/dist/commands/queue.js +160 -0
- package/dist/commands/registry.d.ts +1 -1
- package/dist/commands/registry.js +115 -33
- package/dist/commands/review-client.d.ts +106 -0
- package/dist/commands/review-client.js +399 -0
- package/dist/commands/review.d.ts +36 -6
- package/dist/commands/review.js +395 -145
- package/dist/commands/settings.d.ts +17 -0
- package/dist/commands/settings.js +89 -0
- package/dist/commands/skill.d.ts +20 -0
- package/dist/commands/skill.js +145 -0
- package/dist/commands/stack.d.ts +36 -2
- package/dist/commands/stack.js +341 -113
- package/dist/commands/status.d.ts +18 -2
- package/dist/commands/status.js +236 -21
- package/dist/commands/whoami.d.ts +3 -1
- package/dist/commands/whoami.js +21 -7
- package/dist/errors.d.ts +18 -2
- package/dist/errors.js +22 -1
- package/dist/git-stack.d.ts +2 -0
- package/dist/git-stack.js +4 -0
- package/dist/git.d.ts +12 -1
- package/dist/git.js +52 -7
- package/dist/shell.d.ts +1 -1
- package/dist/shell.js +39 -28
- package/dist/stack-dto.d.ts +266 -0
- package/dist/stack-dto.js +7 -0
- package/dist/submit-pr-base.d.ts +31 -0
- package/dist/submit-pr-base.js +69 -0
- package/dist/ui/ansi.d.ts +2 -0
- package/dist/ui/ansi.js +2 -0
- package/dist/ui/banner.d.ts +34 -2
- package/dist/ui/banner.js +170 -79
- package/dist/ui/box.d.ts +13 -9
- package/dist/ui/box.js +28 -44
- package/dist/ui/findings.d.ts +4 -1
- package/dist/ui/findings.js +14 -7
- package/dist/ui/job-envelope.d.ts +68 -0
- package/dist/ui/job-envelope.js +35 -0
- package/dist/ui/line-tabs.d.ts +27 -0
- package/dist/ui/line-tabs.js +170 -0
- package/dist/ui/logo.d.ts +10 -2
- package/dist/ui/logo.js +13 -9
- package/dist/ui/present.d.ts +10 -0
- package/dist/ui/present.js +13 -0
- package/dist/ui/prompt.d.ts +148 -0
- package/dist/ui/prompt.js +433 -71
- package/dist/ui/select.js +1 -1
- package/dist/ui/tabs.d.ts +125 -0
- package/dist/ui/tabs.js +480 -0
- package/dist/ui/usage.d.ts +29 -1
- package/dist/ui/usage.js +71 -2
- package/dist/ui/width.d.ts +26 -0
- package/dist/ui/width.js +262 -0
- package/package.json +6 -1
- package/skill/SKILL.md +98 -0
- package/skill/mergestorm-pr-loop/SKILL.md +76 -0
package/dist/shell.js
CHANGED
|
@@ -5,18 +5,15 @@ import { dispatchCommand, shellCommandSpecs, } from "./commands/registry.js";
|
|
|
5
5
|
import { ansi } from "./ui/ansi.js";
|
|
6
6
|
import { printBannerHeader, shellPrompt } from "./ui/banner.js";
|
|
7
7
|
import { CTRL_C_EXIT_HINT, CtrlCExitGate } from "./ui/ctrl-c-exit.js";
|
|
8
|
+
import { openHelpBrowser } from "./commands/help.js";
|
|
9
|
+
import { showLinePanel } from "./ui/line-tabs.js";
|
|
8
10
|
import { askLine, PromptClosedError } from "./ui/prompt.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const rows = COMMANDS.map((c) => ` ${ansi.green("/" + c.name.padEnd(10))} ${c.summary}`).join("\n");
|
|
13
|
-
console.log(`
|
|
14
|
-
${ansi.bold("Commands")}
|
|
15
|
-
${rows}
|
|
16
|
-
|
|
17
|
-
Dashboard: https://mergestorm.ai/settings#api
|
|
18
|
-
`);
|
|
11
|
+
async function showShellNotice(title, message) {
|
|
12
|
+
const lines = message.split("\n").flatMap((line) => (line ? [` ${line}`] : [""]));
|
|
13
|
+
await showLinePanel(title, [...lines, "", ` ${ansi.dim("q to return")}`]);
|
|
19
14
|
}
|
|
15
|
+
/** Shell autocomplete + `/help` � derived from the command registry (STRUCT-01). */
|
|
16
|
+
export const COMMANDS = shellCommandSpecs();
|
|
20
17
|
/** Strips an optional leading "/" so both "review" and "/review" work. */
|
|
21
18
|
export function parseLine(line) {
|
|
22
19
|
let trimmed = line.trim();
|
|
@@ -31,10 +28,22 @@ export function parseLine(line) {
|
|
|
31
28
|
return { cmd, args: parts.slice(1) };
|
|
32
29
|
}
|
|
33
30
|
export async function runShell() {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
// Idle is always the home screen: header + docked prompt. Command TUIs
|
|
32
|
+
// (usage / credits / status) and printed output become scrollback when
|
|
33
|
+
// the next askLine homes + ED 0. Do not retire the header after the
|
|
34
|
+
// first command � that is what floated a bare prompt at the top.
|
|
35
|
+
let banner = null;
|
|
36
|
+
const showBanner = async () => {
|
|
37
|
+
try {
|
|
38
|
+
banner = await printBannerHeader({ paint: false });
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Keep the previous chrome on a transient refresh failure rather than
|
|
42
|
+
// blanking the always-on home header.
|
|
43
|
+
}
|
|
44
|
+
return banner;
|
|
45
|
+
};
|
|
46
|
+
banner = await showBanner();
|
|
38
47
|
const history = [];
|
|
39
48
|
let busyAbort = null;
|
|
40
49
|
const idleCtrlC = new CtrlCExitGate();
|
|
@@ -68,6 +77,11 @@ export async function runShell() {
|
|
|
68
77
|
prompt: shellPrompt(loggedIn),
|
|
69
78
|
history,
|
|
70
79
|
commands: COMMANDS,
|
|
80
|
+
...(banner
|
|
81
|
+
? {
|
|
82
|
+
header: (columns, variant) => banner.rows(columns, variant),
|
|
83
|
+
}
|
|
84
|
+
: {}),
|
|
71
85
|
});
|
|
72
86
|
}
|
|
73
87
|
catch (err) {
|
|
@@ -87,15 +101,12 @@ export async function runShell() {
|
|
|
87
101
|
if (cmd === "exit" || cmd === "quit")
|
|
88
102
|
break;
|
|
89
103
|
if (cmd === "help" || cmd === "?") {
|
|
90
|
-
|
|
104
|
+
await openHelpBrowser();
|
|
91
105
|
continue;
|
|
92
106
|
}
|
|
93
107
|
if (cmd === "clear") {
|
|
94
108
|
output.write("[2J[H");
|
|
95
|
-
|
|
96
|
-
await printBannerHeader();
|
|
97
|
-
}
|
|
98
|
-
catch { /* banner is best-effort */ }
|
|
109
|
+
await showBanner();
|
|
99
110
|
continue;
|
|
100
111
|
}
|
|
101
112
|
busyAbort = new AbortController();
|
|
@@ -104,33 +115,33 @@ export async function runShell() {
|
|
|
104
115
|
mode: "shell",
|
|
105
116
|
signal: busyAbort.signal,
|
|
106
117
|
afterAuth: async () => {
|
|
107
|
-
|
|
108
|
-
await printBannerHeader();
|
|
109
|
-
}
|
|
110
|
-
catch { /* banner is best-effort */ }
|
|
118
|
+
await showBanner();
|
|
111
119
|
},
|
|
112
120
|
});
|
|
113
121
|
if (!ok) {
|
|
114
|
-
|
|
122
|
+
await showShellNotice("Error", `unknown command "${cmd}"\ntype help for the list`);
|
|
115
123
|
}
|
|
116
124
|
}
|
|
117
125
|
catch (err) {
|
|
118
126
|
if (err instanceof DetachedError) {
|
|
119
|
-
|
|
127
|
+
await showShellNotice("Detached", err.message);
|
|
120
128
|
}
|
|
121
129
|
else if (err instanceof CommandError) {
|
|
122
|
-
|
|
130
|
+
await showShellNotice("Error", err.message);
|
|
123
131
|
}
|
|
124
132
|
else if (err instanceof Error && err.name === "AbortError") {
|
|
125
|
-
|
|
133
|
+
await showShellNotice("Aborted", "aborted");
|
|
126
134
|
}
|
|
127
135
|
else {
|
|
128
|
-
|
|
136
|
+
await showShellNotice("Error", err instanceof Error ? err.message : String(err));
|
|
129
137
|
}
|
|
130
138
|
}
|
|
131
139
|
finally {
|
|
132
140
|
busyAbort = null;
|
|
133
141
|
}
|
|
142
|
+
// Re-capture the home header so usage/credits shown above the prompt
|
|
143
|
+
// reflect what the command just consumed (e.g. review spending a credit).
|
|
144
|
+
await showBanner();
|
|
134
145
|
}
|
|
135
146
|
}
|
|
136
147
|
finally {
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire types returned by the stack list/enrich HTTP endpoints.
|
|
3
|
+
*
|
|
4
|
+
* `api/src/stacks.ts#loadStackDtos` is the producer. Keep this file
|
|
5
|
+
* dependency-free and byte-for-byte aligned with the canonical wire contract.
|
|
6
|
+
*/
|
|
7
|
+
export type StackBranchState = "clean" | "needs_restack" | "restacking" | "conflict" | "merged" | "closed";
|
|
8
|
+
export type StackCiStatus = "success" | "pending" | "failure" | "neutral" | "none" | "unknown";
|
|
9
|
+
export type StackReviewStatus = "approved" | "changes_requested" | "reviewed" | "failed" | "none" | "unknown";
|
|
10
|
+
/** `failed` is the newest Vortex `pr_reviews` row. Work paints Review failed. */
|
|
11
|
+
export type StackVortexStatus = "reviewing" | "seam_pending" | "all_clear" | "findings" | "throttled" | "failed";
|
|
12
|
+
export type StackCycloneStatus = "patching" | "awaiting_fix";
|
|
13
|
+
export type StackTempestStatus = "reviewing" | "findings" | "clear" | "failed" | "stale";
|
|
14
|
+
export type StackAgentName = "vortex" | "cyclone" | "tempest";
|
|
15
|
+
/**
|
|
16
|
+
* One live-or-resting agent run for a PR (#1119): a dumb projection of one
|
|
17
|
+
* ledger row (webhook job / lease / review-state / pr_reviews). Two live
|
|
18
|
+
* Vortex jobs on two SHAs → two runs → two chips. Carries only safe facts —
|
|
19
|
+
* never job ids, holders, or error strings.
|
|
20
|
+
*/
|
|
21
|
+
export type StackAgentRun = {
|
|
22
|
+
agent: StackAgentName;
|
|
23
|
+
/** Agent-specific status, e.g. "reviewing" / "patching" / "all_clear". */
|
|
24
|
+
status: string;
|
|
25
|
+
/** Full SHA when known. UI shortens to 7. */
|
|
26
|
+
sha: string | null;
|
|
27
|
+
startedAt?: string | null;
|
|
28
|
+
finishedAt?: string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Vortex in-flight sub-state from pr_reviews.phase (#1189): "fanout" while
|
|
31
|
+
* Core + specialists run in parallel, "synthesizing" during consolidation.
|
|
32
|
+
* Only on live `reviewing` runs; unknown values fall back to plain Reviewing.
|
|
33
|
+
*/
|
|
34
|
+
phase?: string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Actionable finding count from the latest `pr_reviews.findings` payload
|
|
37
|
+
* (#1248). Info-only notes (Continue, Decision required) are excluded.
|
|
38
|
+
* Only on resting Vortex `findings` runs.
|
|
39
|
+
*/
|
|
40
|
+
findingCount?: number | null;
|
|
41
|
+
};
|
|
42
|
+
export type StackLayerChecks = {
|
|
43
|
+
total: number;
|
|
44
|
+
success: number;
|
|
45
|
+
pending: number;
|
|
46
|
+
failure: number;
|
|
47
|
+
failingName: string | null;
|
|
48
|
+
/**
|
|
49
|
+
* True when the persisted `ci_checks` carries a `namedRuns` seed (enrich
|
|
50
|
+
* writes it so CI webhook deliveries fold into the snapshot). Pre-seeding
|
|
51
|
+
* rows are false — the webhook cannot fold into them, so the interval poll
|
|
52
|
+
* must keep enriching until the first enrich seeds one (#1602). Status-only
|
|
53
|
+
* layers (empty `namedRuns` over a non-empty rollup) are also false — the
|
|
54
|
+
* webhook refuses to fold into an empty map, so the poll keeps enriching
|
|
55
|
+
* them (#1602).
|
|
56
|
+
*/
|
|
57
|
+
namedRunsSeeded?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* True when the persisted `namedRuns` carries the reserved synthetic
|
|
60
|
+
* "commit status" run at a pending/failure verdict. Webhook deliveries
|
|
61
|
+
* re-key by check/context name and can never advance that seed, so the
|
|
62
|
+
* interval poll must keep enriching until the next enrich re-seeds it green
|
|
63
|
+
* (or drops it). Absent (false) when the seed is green — folds keep that
|
|
64
|
+
* layer fresh (#1602).
|
|
65
|
+
*/
|
|
66
|
+
combinedStatusSeeded?: boolean;
|
|
67
|
+
};
|
|
68
|
+
/** Closing / Development-sidebar issue. Cap 3 on the wire. */
|
|
69
|
+
export type StackLinkedIssue = {
|
|
70
|
+
number: number;
|
|
71
|
+
title: string | null;
|
|
72
|
+
};
|
|
73
|
+
/** Cyclone merge-conflict specialist resolution for this PR (#973). */
|
|
74
|
+
export type StackLayerConflictResolution = {
|
|
75
|
+
resolvedAt: string;
|
|
76
|
+
/** Branch the conflicts were resolved against (stacks parent or GitHub base). */
|
|
77
|
+
baseBranch: string | null;
|
|
78
|
+
/** Conflicted paths the specialist resolved; the writer caps the list. */
|
|
79
|
+
files: string[];
|
|
80
|
+
/** Head commit of the pushed resolution, when known. */
|
|
81
|
+
headSha: string | null;
|
|
82
|
+
/**
|
|
83
|
+
* Chat dispatch that triggered the run (#973). Traces a mark to the job that
|
|
84
|
+
* pushed it; null when the row predates dispatch tracing (or had no job).
|
|
85
|
+
*/
|
|
86
|
+
dispatchId: string | null;
|
|
87
|
+
};
|
|
88
|
+
export type StackLayerDto = {
|
|
89
|
+
branch: string;
|
|
90
|
+
parentBranch: string | null;
|
|
91
|
+
prNumber: number;
|
|
92
|
+
/** Persisted PR timing and diff snapshots from pr_stats. */
|
|
93
|
+
openedAt: string | null;
|
|
94
|
+
mergedAt: string | null;
|
|
95
|
+
closedAt: string | null;
|
|
96
|
+
additions: number | null;
|
|
97
|
+
deletions: number | null;
|
|
98
|
+
openAdditions: number | null;
|
|
99
|
+
openDeletions: number | null;
|
|
100
|
+
position: number;
|
|
101
|
+
state: StackBranchState;
|
|
102
|
+
title: string | null;
|
|
103
|
+
htmlUrl: string | null;
|
|
104
|
+
ciStatus: StackCiStatus;
|
|
105
|
+
reviewStatus: StackReviewStatus;
|
|
106
|
+
checks: StackLayerChecks | null;
|
|
107
|
+
/** null on the fast list or when no Vortex/seam signal exists. */
|
|
108
|
+
vortexStatus: StackVortexStatus | null;
|
|
109
|
+
/** null on the fast list or when no Cyclone signal exists. */
|
|
110
|
+
cycloneStatus: StackCycloneStatus | null;
|
|
111
|
+
/** null when no Tempest signal exists. Fast list and enrich both paint this from pr_reviews (#876). */
|
|
112
|
+
tempestStatus: StackTempestStatus | null;
|
|
113
|
+
/**
|
|
114
|
+
* Ledger-backed agent runs, one per live row (#1119). Fast list and enrich
|
|
115
|
+
* both paint this from our tables only — never GitHub. The per-agent status
|
|
116
|
+
* fields above stay as a rollup of the newest run for older clients.
|
|
117
|
+
* Undefined = producer predates runs (clients fall back to the rollup).
|
|
118
|
+
*/
|
|
119
|
+
agentRuns?: StackAgentRun[];
|
|
120
|
+
conflictDetail: string | null;
|
|
121
|
+
lastRestackedSha: string | null;
|
|
122
|
+
/** GitHub PR mergeability from enrich; null when unknown. */
|
|
123
|
+
mergeable: boolean | null;
|
|
124
|
+
/** GitHub REST mergeable_state; null when unknown. */
|
|
125
|
+
mergeableState: string | null;
|
|
126
|
+
/** Most recently observed PR head SHA. */
|
|
127
|
+
headSha?: string | null;
|
|
128
|
+
/** Head SHA that produced mergeable / mergeableState. */
|
|
129
|
+
mergeableHeadSha?: string | null;
|
|
130
|
+
/** GitHub PR draft flag from enrich; the auto-land watcher never enqueues a draft. */
|
|
131
|
+
draft?: boolean | null;
|
|
132
|
+
/**
|
|
133
|
+
* Vortex-style linked issues (keywords + Development sidebar), cap 3.
|
|
134
|
+
* null until first enrich finds any; kept on merged unit members (unlike CI).
|
|
135
|
+
*/
|
|
136
|
+
linkedIssues?: StackLinkedIssue[] | null;
|
|
137
|
+
/**
|
|
138
|
+
* Specialist lanes Vortex launched on this PR (smart router / Manual).
|
|
139
|
+
* Fast list and enrich both fill from vortex_pr_review_state (#896 / #915).
|
|
140
|
+
* Durable after merge — terminal layers keep the last run so Work can grey
|
|
141
|
+
* the marks on a done-growing unit member instead of dropping them (#1021).
|
|
142
|
+
*/
|
|
143
|
+
specialistsRun?: string[] | null;
|
|
144
|
+
/** Published invocation count per specialist lane. Frontend paint follows separately. */
|
|
145
|
+
specialistRunCounts?: Record<string, number> | null;
|
|
146
|
+
/** Number of published reviews Core participated in for this PR. */
|
|
147
|
+
coreRuns?: number | null;
|
|
148
|
+
/**
|
|
149
|
+
* Cyclone's merge-conflict specialist resolved this PR (#973). Fast list and
|
|
150
|
+
* enrich both paint from cyclone_conflict_resolutions; null on terminal
|
|
151
|
+
* layers and when the specialist never ran.
|
|
152
|
+
*/
|
|
153
|
+
conflictResolution?: StackLayerConflictResolution | null;
|
|
154
|
+
};
|
|
155
|
+
export type StackUnitMemberDto = {
|
|
156
|
+
prNumber: number;
|
|
157
|
+
/** Persisted PR timing and diff snapshots from pr_stats. */
|
|
158
|
+
openedAt: string | null;
|
|
159
|
+
mergedAt: string | null;
|
|
160
|
+
closedAt: string | null;
|
|
161
|
+
additions: number | null;
|
|
162
|
+
deletions: number | null;
|
|
163
|
+
openAdditions: number | null;
|
|
164
|
+
openDeletions: number | null;
|
|
165
|
+
branch: string;
|
|
166
|
+
position: number;
|
|
167
|
+
seamState: string;
|
|
168
|
+
seamReviewedSha: string | null;
|
|
169
|
+
promotedHeadSha: string | null;
|
|
170
|
+
promotedAt: string;
|
|
171
|
+
/**
|
|
172
|
+
* GitHub PR title snapshotted at promote / enrich (#965).
|
|
173
|
+
* Survives stack_branches dropping the open-chain row. null until first write.
|
|
174
|
+
*/
|
|
175
|
+
title?: string | null;
|
|
176
|
+
/**
|
|
177
|
+
* Linked issues snapshotted onto the member at promote / enrich (#936).
|
|
178
|
+
* Survives stack_branches dropping the open-chain row. null until first write.
|
|
179
|
+
*/
|
|
180
|
+
linkedIssues?: StackLinkedIssue[] | null;
|
|
181
|
+
/**
|
|
182
|
+
* Vortex specialist lanes that ran on this PR (#1021). Painted from
|
|
183
|
+
* vortex_pr_review_state on the fast list — the open layer is gone after
|
|
184
|
+
* promote, so the member must carry the marks for the greyed-out row.
|
|
185
|
+
*/
|
|
186
|
+
specialistsRun?: string[] | null;
|
|
187
|
+
/** Published invocation count per specialist lane. */
|
|
188
|
+
specialistRunCounts?: Record<string, number> | null;
|
|
189
|
+
/** Number of published reviews Core participated in for this PR. */
|
|
190
|
+
coreRuns?: number | null;
|
|
191
|
+
};
|
|
192
|
+
export type StackUnitDto = {
|
|
193
|
+
id: string;
|
|
194
|
+
uNumber: number;
|
|
195
|
+
state: string;
|
|
196
|
+
branch: string;
|
|
197
|
+
landTarget: string;
|
|
198
|
+
members: StackUnitMemberDto[];
|
|
199
|
+
landPrNumber: number | null;
|
|
200
|
+
tempestLandStatus: string | null;
|
|
201
|
+
landingBlockReason: string | null;
|
|
202
|
+
/**
|
|
203
|
+
* Land-PR chrome. Fast list hydrates last-known CI/title/mergeable/issues from
|
|
204
|
+
* `review_units` (#878 / #892) and may attach a tempest-only stub (#876). Enrich
|
|
205
|
+
* refreshes GitHub and writes back. Null when the land PR is an open stack
|
|
206
|
+
* layer (chips live on `layers`) or the unit has no land PR.
|
|
207
|
+
*/
|
|
208
|
+
landPr: StackLayerDto | null;
|
|
209
|
+
};
|
|
210
|
+
export type StackDto = {
|
|
211
|
+
id: string;
|
|
212
|
+
owner: string;
|
|
213
|
+
repo: string;
|
|
214
|
+
trunkBranch: string;
|
|
215
|
+
/** Real trunk the review unit eventually lands into. */
|
|
216
|
+
landTarget: string;
|
|
217
|
+
/**
|
|
218
|
+
* Dashboard-only archive (#963): set hides the stack from the active Work
|
|
219
|
+
* list; GitHub PRs stay open. Only PATCH /stacks/:id toggles it.
|
|
220
|
+
*/
|
|
221
|
+
archivedAt: string | null;
|
|
222
|
+
/**
|
|
223
|
+
* Auto land (#1511): enqueue this unit-less single-PR stack when eligible.
|
|
224
|
+
* Missing on older payloads is off.
|
|
225
|
+
*/
|
|
226
|
+
autoEnqueueWhenReady?: boolean;
|
|
227
|
+
layers: StackLayerDto[];
|
|
228
|
+
/** Present when a review-unit row exists for this stack. */
|
|
229
|
+
unit?: StackUnitDto;
|
|
230
|
+
};
|
|
231
|
+
export type MergeQueueEntryState = "queued" | "running" | "waiting" | "landed" | "bounced" | "cancelled";
|
|
232
|
+
export type MergeQueueEnqueuedBy = "human" | "agent";
|
|
233
|
+
export type MergeQueueEnqueuedVia = "dashboard" | "cli" | "chat" | "api" | "mcp";
|
|
234
|
+
export type MergeQueueBounceKind = "ci_failure" | "ci_timeout" | "head_moved" | "tempest_findings" | "seam_findings" | "restack_conflict" | "pr_draft" | "merge_failed" | "gh_error";
|
|
235
|
+
/** Structured bounce handoff — enough for the bar chip and the chat quote. */
|
|
236
|
+
export type MergeQueueBounceDetail = {
|
|
237
|
+
kind: MergeQueueBounceKind;
|
|
238
|
+
prNumber?: number;
|
|
239
|
+
headSha?: string;
|
|
240
|
+
failingCheck?: string;
|
|
241
|
+
conflictBranch?: string;
|
|
242
|
+
conflictDetail?: string;
|
|
243
|
+
message?: string;
|
|
244
|
+
};
|
|
245
|
+
export type MergeQueueEntryDto = {
|
|
246
|
+
id: string;
|
|
247
|
+
stackId: string;
|
|
248
|
+
owner: string;
|
|
249
|
+
repo: string;
|
|
250
|
+
state: MergeQueueEntryState;
|
|
251
|
+
/** 1-based FIFO among this user's live entries; `0` on bounced history rows (MQ-7). */
|
|
252
|
+
position: number;
|
|
253
|
+
waitReason: string | null;
|
|
254
|
+
bounceReason: string | null;
|
|
255
|
+
bounceDetail: MergeQueueBounceDetail | null;
|
|
256
|
+
enqueuedBy: MergeQueueEnqueuedBy;
|
|
257
|
+
enqueuedVia: MergeQueueEnqueuedVia;
|
|
258
|
+
enqueuedAt: string;
|
|
259
|
+
attempts: number;
|
|
260
|
+
landedPrNumbers: number[];
|
|
261
|
+
/** Combined PR head the queue verified. Null until the worker records it. */
|
|
262
|
+
verifyHeadSha: string | null;
|
|
263
|
+
/** Target tip included in the verified PR head. Null until recorded. */
|
|
264
|
+
verifyBaseSha: string | null;
|
|
265
|
+
finishedAt: string | null;
|
|
266
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub PR base for `stack submit` (#1104).
|
|
3
|
+
*
|
|
4
|
+
* Git parent stays the previous layer (local rebase / `--onto`). Position 3+
|
|
5
|
+
* must open onto the review-unit park freeze so adopt does not retarget after
|
|
6
|
+
* `opened` and double-wake Vortex.
|
|
7
|
+
*/
|
|
8
|
+
import type { StackDto } from "./api.js";
|
|
9
|
+
export declare const MG_PARK_PREFIX = "mg-park-";
|
|
10
|
+
export declare function isMgParkBranch(branch: string): boolean;
|
|
11
|
+
/** Live freeze already used as a GitHub base on this stack, if any. */
|
|
12
|
+
export declare function parkFreezeBranchFromStack(stack: StackDto): string | null;
|
|
13
|
+
export type SubmitLayerPlan = {
|
|
14
|
+
branch: string;
|
|
15
|
+
gitParent: string;
|
|
16
|
+
position: number;
|
|
17
|
+
/** GitHub `--base` when known. `null` means mint the freeze first. */
|
|
18
|
+
githubBase: string | null;
|
|
19
|
+
needsPark: boolean;
|
|
20
|
+
stackId: string | null;
|
|
21
|
+
};
|
|
22
|
+
export declare function planSubmitLayerBases(input: {
|
|
23
|
+
layers: readonly {
|
|
24
|
+
branch: string;
|
|
25
|
+
parentBranch: string;
|
|
26
|
+
}[];
|
|
27
|
+
trunk: string;
|
|
28
|
+
registered: readonly StackDto[];
|
|
29
|
+
owner: string;
|
|
30
|
+
repo: string;
|
|
31
|
+
}): SubmitLayerPlan[];
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
function registeredParentHit(stacks, parentBranch, owner, repo) {
|
|
2
|
+
const want = parentBranch.trim();
|
|
3
|
+
if (!want)
|
|
4
|
+
return null;
|
|
5
|
+
for (const stack of stacks) {
|
|
6
|
+
if (stack.owner !== owner || stack.repo !== repo)
|
|
7
|
+
continue;
|
|
8
|
+
for (const layer of stack.layers) {
|
|
9
|
+
if (layer.branch === want) {
|
|
10
|
+
return { stackId: stack.id, position: layer.position };
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
export const MG_PARK_PREFIX = "mg-park-";
|
|
17
|
+
export function isMgParkBranch(branch) {
|
|
18
|
+
return branch.trim().toLowerCase().startsWith(MG_PARK_PREFIX);
|
|
19
|
+
}
|
|
20
|
+
/** Live freeze already used as a GitHub base on this stack, if any. */
|
|
21
|
+
export function parkFreezeBranchFromStack(stack) {
|
|
22
|
+
for (const layer of stack.layers) {
|
|
23
|
+
const parent = layer.parentBranch?.trim() ?? "";
|
|
24
|
+
if (isMgParkBranch(parent))
|
|
25
|
+
return parent;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
export function planSubmitLayerBases(input) {
|
|
30
|
+
const { layers, trunk, registered, owner, repo } = input;
|
|
31
|
+
const localIndex = new Map(layers.map((layer, i) => [layer.branch, i]));
|
|
32
|
+
const plans = [];
|
|
33
|
+
for (let i = 0; i < layers.length; i++) {
|
|
34
|
+
const layer = layers[i];
|
|
35
|
+
const gitParent = layer.parentBranch.trim() || trunk;
|
|
36
|
+
const priorLocal = localIndex.get(gitParent);
|
|
37
|
+
let position;
|
|
38
|
+
let stackId = null;
|
|
39
|
+
if (priorLocal !== undefined && plans[priorLocal]) {
|
|
40
|
+
position = plans[priorLocal].position + 1;
|
|
41
|
+
stackId = plans[priorLocal].stackId;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const hit = registeredParentHit(registered, gitParent, owner, repo);
|
|
45
|
+
if (hit) {
|
|
46
|
+
position = hit.position + 1;
|
|
47
|
+
stackId = hit.stackId;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
position = 1;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const stack = stackId
|
|
54
|
+
? registered.find((row) => row.id === stackId) ?? null
|
|
55
|
+
: null;
|
|
56
|
+
const existingPark = stack ? parkFreezeBranchFromStack(stack) : null;
|
|
57
|
+
const needsPark = position >= 3;
|
|
58
|
+
const githubBase = needsPark ? existingPark : gitParent;
|
|
59
|
+
plans.push({
|
|
60
|
+
branch: layer.branch,
|
|
61
|
+
gitParent,
|
|
62
|
+
position,
|
|
63
|
+
githubBase,
|
|
64
|
+
needsPark,
|
|
65
|
+
stackId,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return plans;
|
|
69
|
+
}
|
package/dist/ui/ansi.d.ts
CHANGED
package/dist/ui/ansi.js
CHANGED
|
@@ -24,4 +24,6 @@ export const ansi = {
|
|
|
24
24
|
brightGreen: (t) => wrap("92", t),
|
|
25
25
|
invert: (t) => wrap("7", t),
|
|
26
26
|
boldGreen: (t) => (computeEnabled() ? `[1;32m${t}[0m` : t),
|
|
27
|
+
/** Prompt / mark ink. Never 32 — that reads as sick dark green. */
|
|
28
|
+
boldBrightGreen: (t) => (computeEnabled() ? `[1;92m${t}[0m` : t),
|
|
27
29
|
};
|
package/dist/ui/banner.d.ts
CHANGED
|
@@ -1,4 +1,36 @@
|
|
|
1
|
+
import { type MeResponse } from "../api.js";
|
|
1
2
|
/** Used by the non-interactive `usage()` path in cli.ts — the boxed banner is shell-only. */
|
|
2
3
|
export declare function printWordmark(): void;
|
|
3
|
-
|
|
4
|
-
export
|
|
4
|
+
/** Everything the welcome panel needs, captured once so redraws stay sync. */
|
|
5
|
+
export type BannerState = {
|
|
6
|
+
version: string;
|
|
7
|
+
me: MeResponse | null;
|
|
8
|
+
key: string | undefined;
|
|
9
|
+
keyInvalid: boolean;
|
|
10
|
+
};
|
|
11
|
+
/** Full welcome, mid pane, or Claude-style short chrome that still shows the mark. */
|
|
12
|
+
export type BannerDensity = "full" | "compact" | "mini" | "nano";
|
|
13
|
+
/** `true` = compact. Strings pick the short-pane densities. */
|
|
14
|
+
export type BannerVariant = boolean | "mini" | "nano";
|
|
15
|
+
/** Handle returned by {@link printBannerHeader}; `rows` reflows for a resize. */
|
|
16
|
+
export type BannerHandle = {
|
|
17
|
+
rows(columns?: number, variant?: BannerVariant): string[];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The whole welcome panel as rows, reflowed to `columns`. Every row fits in
|
|
21
|
+
* `columns - 1` cells so a redraw's cursor math stays honest; below
|
|
22
|
+
* MIN_BOXED_COLUMNS the frame drops away instead of wrapping.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildBannerRows(state: BannerState, columns?: number, opts?: {
|
|
25
|
+
compact?: boolean;
|
|
26
|
+
density?: BannerDensity;
|
|
27
|
+
}): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Print the welcome panel and return a handle whose `rows()` reflows the
|
|
30
|
+
* same data for a new width (used by the prompt's resize redraw).
|
|
31
|
+
*/
|
|
32
|
+
export declare function printBannerHeader(opts?: {
|
|
33
|
+
/** When false, only build the handle; the prompt paints the rows. */
|
|
34
|
+
paint?: boolean;
|
|
35
|
+
}): Promise<BannerHandle>;
|
|
36
|
+
export declare function shellPrompt(_loggedIn: boolean): string;
|