mergestorm 0.1.1 → 0.3.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/README.md +64 -8
- package/dist/api.d.ts +85 -0
- package/dist/api.js +85 -0
- package/dist/cli.js +72 -353
- package/dist/commands/branches.d.ts +10 -0
- package/dist/commands/branches.js +85 -0
- package/dist/commands/credits.d.ts +1 -0
- package/dist/commands/credits.js +28 -0
- package/dist/commands/jobs.d.ts +6 -0
- package/dist/commands/jobs.js +107 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +103 -0
- package/dist/commands/logout.d.ts +1 -0
- package/dist/commands/logout.js +11 -0
- package/dist/commands/review.d.ts +11 -0
- package/dist/commands/review.js +129 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.js +39 -0
- package/dist/commands/whoami.d.ts +1 -0
- package/dist/commands/whoami.js +35 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.js +41 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +18 -0
- package/dist/git.d.ts +9 -0
- package/dist/git.js +66 -0
- package/dist/shell.d.ts +8 -0
- package/dist/shell.js +183 -0
- package/dist/ui/ansi.d.ts +12 -0
- package/dist/ui/ansi.js +27 -0
- package/dist/ui/banner.d.ts +4 -0
- package/dist/ui/banner.js +98 -0
- package/dist/ui/box.d.ts +11 -0
- package/dist/ui/box.js +36 -0
- package/dist/ui/logo.d.ts +5 -0
- package/dist/ui/logo.js +12 -0
- package/dist/ui/prompt.d.ts +18 -0
- package/dist/ui/prompt.js +220 -0
- package/dist/ui/select.d.ts +13 -0
- package/dist/ui/select.js +103 -0
- package/dist/ui/usage.d.ts +24 -0
- package/dist/ui/usage.js +67 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +11 -0
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -29,21 +29,68 @@ node packages/mergestorm-cli/dist/cli.js <command>
|
|
|
29
29
|
npm run dev --workspace=mergestorm -- <command>
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
## Interactive shell
|
|
33
|
+
|
|
34
|
+
On a TTY, bare `mergestorm` (or `mergestorm shell`) opens a branded REPL: a boxed welcome panel (logo, auth/credits status, tips, what's new) and a bordered input box with a live `/` slash-command dropdown. Non-TTY (CI/pipes) prints usage instead — no hanging prompt.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
╭──────────────────── mergestorm v0.3.0 ────────────────────╮
|
|
38
|
+
│ ▟██████████▛ │
|
|
39
|
+
│ ▜████████▛ │
|
|
40
|
+
│ ▝▜████▛▘ │
|
|
41
|
+
│ ▜██▛ │
|
|
42
|
+
│ ██ │
|
|
43
|
+
│ ▝▘ │
|
|
44
|
+
│ │
|
|
45
|
+
│ ● msk_live_a1b2… · maelstrom │
|
|
46
|
+
│ [████████████████████░░░░░░░░░░░░] 63% used │
|
|
47
|
+
│ │
|
|
48
|
+
│ Tips for getting started: │
|
|
49
|
+
│ review review the diff for main...HEAD │
|
|
50
|
+
│ branches pick a recently reviewed branch │
|
|
51
|
+
│ /help list all commands │
|
|
52
|
+
╰─────────────────────────────────────────────────────────────╯
|
|
53
|
+
mergestorm
|
|
54
|
+
╭─────────────────────────╮
|
|
55
|
+
│ › /re │
|
|
56
|
+
╰─────────────────────────╯
|
|
57
|
+
⁄review Review git diff (default main...HEAD)
|
|
58
|
+
|
|
59
|
+
mergestorm> credits
|
|
60
|
+
mergestorm> branches
|
|
61
|
+
mergestorm> chain
|
|
62
|
+
mergestorm> exit
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Bare command names still work exactly as before (`review`, `help`, `exit`, …) — typing `/` at the start of a line just opens the live autocomplete dropdown (filtered as you type, navigate with Up/Down, accept with Tab/Enter/Right). Arrow-key history, Ctrl+A/E, Ctrl+U/K, and Ctrl+C-to-clear all work as in a regular shell. On a dumb terminal, without color, or when piped, the box falls back to plain ASCII borders (or no shell at all for non-TTY input).
|
|
66
|
+
|
|
67
|
+
`credits` / `usage` print a Claude-style meter — long bar + `N% used`, then `Resets … (UTC)` — for standard and premium. `branches` (alias `chains`) lists recently reviewed branches; on a TTY you arrow-select one to open its review-chain timeline. `chain [slug]` shows that timeline directly (defaults to `local/<current-git-branch>`).
|
|
68
|
+
|
|
32
69
|
## Commands
|
|
33
70
|
|
|
71
|
+
One-shot subcommands work the same inside the shell and from argv (scripts/CI):
|
|
72
|
+
|
|
34
73
|
```
|
|
35
|
-
mergestorm
|
|
36
|
-
mergestorm
|
|
37
|
-
mergestorm
|
|
38
|
-
mergestorm
|
|
39
|
-
mergestorm
|
|
74
|
+
mergestorm Interactive shell (TTY only)
|
|
75
|
+
mergestorm shell Explicit shell entry
|
|
76
|
+
mergestorm login Sign in via browser; stores an API key in ~/.mergestorm/config.json
|
|
77
|
+
mergestorm login --key Paste an existing API key instead (headless/CI)
|
|
78
|
+
mergestorm logout Remove the stored API key
|
|
79
|
+
mergestorm review [base] [head] Review git diff base...head (default: main...HEAD)
|
|
80
|
+
mergestorm status <job_id> Poll a review job and print JSON
|
|
81
|
+
mergestorm credits [--json] Standard + premium credit balance (usage bars)
|
|
82
|
+
mergestorm jobs [n] [--json] Recent review jobs (default 10, max 50)
|
|
83
|
+
mergestorm branches [n] Recently reviewed branches (arrow-pick on TTY)
|
|
84
|
+
mergestorm chain [slug] Branch review-chain timeline (default: current branch)
|
|
85
|
+
mergestorm whoami [--json] Key prefix, plan, API base, config path
|
|
86
|
+
mergestorm thread <slug> Jobs in a review thread
|
|
40
87
|
```
|
|
41
88
|
|
|
42
89
|
### `login`
|
|
43
90
|
|
|
44
91
|
Runs a browser device-authorization flow: prints a short code, opens `https://mergestorm.ai/cli/auth`, and — once you approve — mints and stores an API key in `~/.mergestorm/config.json` (mode `600`). No key copy/paste needed.
|
|
45
92
|
|
|
46
|
-
For headless or CI environments, use `mergestorm login --key` to paste an existing `msk_live_…` key (create one on the dashboard [
|
|
93
|
+
For headless or CI environments, use `mergestorm login --key` to paste an existing `msk_live_…` key (create one on the dashboard [API tab](https://mergestorm.ai/dashboard/api)).
|
|
47
94
|
|
|
48
95
|
### `logout`
|
|
49
96
|
|
|
@@ -59,11 +106,19 @@ mergestorm review develop # develop...HEAD
|
|
|
59
106
|
mergestorm review main feat/x # main...feat/x
|
|
60
107
|
```
|
|
61
108
|
|
|
62
|
-
Exit code is non-zero on quota limit (402), submission failure, a `failed`/`quota_exceeded` job, or a poll timeout.
|
|
109
|
+
Exit code is non-zero on quota limit (402), submission failure, a `failed`/`quota_exceeded` job, or a poll timeout. In the interactive shell, Ctrl+C during the wait detaches (job keeps running) — use `status <job_id>` later.
|
|
63
110
|
|
|
64
111
|
### `status <job_id>`
|
|
65
112
|
|
|
66
|
-
Fetches a job by id and prints the raw JSON (status, verdict, summary, findings).
|
|
113
|
+
Fetches a job by id and prints the raw JSON (status, verdict, summary, findings). In the shell, output is formatted for reading.
|
|
114
|
+
|
|
115
|
+
### `credits` / `jobs` / `whoami`
|
|
116
|
+
|
|
117
|
+
Require a logged-in key and API support for `GET /api/v1/me` and `GET /api/v1/reviews`. Older API deployments degrade gracefully (banner shows local key only).
|
|
118
|
+
|
|
119
|
+
### `branches` / `chain`
|
|
120
|
+
|
|
121
|
+
`branches` calls `GET /api/v1/threads` for recently active review threads (branches). On a TTY it opens an arrow-key selector; Enter loads that thread's job timeline. `chain [slug]` is the direct timeline view — omit the slug to use `local/<current-git-branch>` (same slug `review` stamps).
|
|
67
122
|
|
|
68
123
|
## Context pack (`.mergestorm/`)
|
|
69
124
|
|
|
@@ -75,6 +130,7 @@ Any `*.md` / `*.txt` files under `./.mergestorm/` (one level of nesting) are att
|
|
|
75
130
|
|----------|---------|---------|
|
|
76
131
|
| `MERGESTORM_API_KEY` | API key (overrides the stored config) | — |
|
|
77
132
|
| `MERGESTORM_API_URL` | API base URL | `https://api.mergestorm.ai` |
|
|
133
|
+
| `NO_COLOR` | Disable ANSI colors | — |
|
|
78
134
|
|
|
79
135
|
## See also
|
|
80
136
|
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { type Config } from "./config.js";
|
|
2
|
+
export declare function apiFetch(cfg: Config, route: string, init?: RequestInit & {
|
|
3
|
+
json?: unknown;
|
|
4
|
+
}): Promise<{
|
|
5
|
+
status: number;
|
|
6
|
+
body: unknown;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function devicePost(base: string, route: string, json: unknown): Promise<{
|
|
9
|
+
status: number;
|
|
10
|
+
body: any;
|
|
11
|
+
}>;
|
|
12
|
+
export type MeResponse = {
|
|
13
|
+
key: {
|
|
14
|
+
prefix: string;
|
|
15
|
+
name: string;
|
|
16
|
+
created_at: string;
|
|
17
|
+
last_used_at: string | null;
|
|
18
|
+
};
|
|
19
|
+
plan_key: string;
|
|
20
|
+
plan_label_key?: string;
|
|
21
|
+
/** Start of next UTC month (usage meters reset). Older APIs omit this. */
|
|
22
|
+
resets_at?: string;
|
|
23
|
+
usage: {
|
|
24
|
+
standard: {
|
|
25
|
+
used: number;
|
|
26
|
+
limit: number | null;
|
|
27
|
+
remaining: number | null;
|
|
28
|
+
};
|
|
29
|
+
premium: {
|
|
30
|
+
used: number;
|
|
31
|
+
limit: number | null;
|
|
32
|
+
remaining: number | null;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export type JobListItem = {
|
|
37
|
+
job_id: string;
|
|
38
|
+
thread_slug: string | null;
|
|
39
|
+
status: string;
|
|
40
|
+
verdict: string | null;
|
|
41
|
+
summary: string | null;
|
|
42
|
+
base_label: string | null;
|
|
43
|
+
head_label: string | null;
|
|
44
|
+
created_at: string;
|
|
45
|
+
finished_at: string | null;
|
|
46
|
+
credits: number;
|
|
47
|
+
};
|
|
48
|
+
export type ThreadListItem = {
|
|
49
|
+
slug: string;
|
|
50
|
+
branch: string | null;
|
|
51
|
+
owner: string | null;
|
|
52
|
+
repo: string | null;
|
|
53
|
+
pr_number: number | null;
|
|
54
|
+
status: string;
|
|
55
|
+
job_count: number;
|
|
56
|
+
last_verdict: string | null;
|
|
57
|
+
last_activity_at: string;
|
|
58
|
+
};
|
|
59
|
+
export type ThreadDetail = {
|
|
60
|
+
thread_id: string;
|
|
61
|
+
slug: string;
|
|
62
|
+
source?: string;
|
|
63
|
+
branch: string | null;
|
|
64
|
+
owner: string | null;
|
|
65
|
+
repo: string | null;
|
|
66
|
+
pr_number: number | null;
|
|
67
|
+
status: string;
|
|
68
|
+
pr_linked_at: string | null;
|
|
69
|
+
job_count: number;
|
|
70
|
+
created_at: string;
|
|
71
|
+
jobs: {
|
|
72
|
+
job_id: string;
|
|
73
|
+
status: string;
|
|
74
|
+
summary?: string | null;
|
|
75
|
+
verdict?: string | null;
|
|
76
|
+
thread_job_number?: number | null;
|
|
77
|
+
created_at: string;
|
|
78
|
+
finished_at?: string | null;
|
|
79
|
+
}[];
|
|
80
|
+
};
|
|
81
|
+
/** Fetch /api/v1/me; returns null on 404/network so callers can degrade. */
|
|
82
|
+
export declare function getMe(cfg?: Config): Promise<MeResponse | null>;
|
|
83
|
+
export declare function listJobs(limit?: number, cfg?: Config): Promise<JobListItem[]>;
|
|
84
|
+
/** Recently reviewed branches/threads (Bearer GET /api/v1/threads). */
|
|
85
|
+
export declare function listThreads(limit?: number, cfg?: Config): Promise<ThreadListItem[]>;
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { apiBase, loadConfig, resolveApiKey } from "./config.js";
|
|
2
|
+
import { CommandError } from "./errors.js";
|
|
3
|
+
export async function apiFetch(cfg, route, init = {}) {
|
|
4
|
+
const key = resolveApiKey(cfg);
|
|
5
|
+
if (!key) {
|
|
6
|
+
throw new CommandError("No API key. Run `mergestorm login` or set MERGESTORM_API_KEY.");
|
|
7
|
+
}
|
|
8
|
+
const headers = {
|
|
9
|
+
Authorization: `Bearer ${key}`,
|
|
10
|
+
...init.headers,
|
|
11
|
+
};
|
|
12
|
+
let body = init.body;
|
|
13
|
+
if (init.json !== undefined) {
|
|
14
|
+
headers["Content-Type"] = "application/json";
|
|
15
|
+
body = JSON.stringify(init.json);
|
|
16
|
+
}
|
|
17
|
+
const res = await fetch(`${apiBase(cfg)}${route}`, { ...init, headers, body });
|
|
18
|
+
const text = await res.text();
|
|
19
|
+
let parsed = text;
|
|
20
|
+
try {
|
|
21
|
+
parsed = JSON.parse(text);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// keep text
|
|
25
|
+
}
|
|
26
|
+
return { status: res.status, body: parsed };
|
|
27
|
+
}
|
|
28
|
+
export async function devicePost(base, route, json) {
|
|
29
|
+
const res = await fetch(`${base}${route}`, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: { "Content-Type": "application/json" },
|
|
32
|
+
body: JSON.stringify(json),
|
|
33
|
+
});
|
|
34
|
+
const text = await res.text();
|
|
35
|
+
let parsed = text;
|
|
36
|
+
try {
|
|
37
|
+
parsed = JSON.parse(text);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// keep text
|
|
41
|
+
}
|
|
42
|
+
return { status: res.status, body: parsed };
|
|
43
|
+
}
|
|
44
|
+
/** Fetch /api/v1/me; returns null on 404/network so callers can degrade. */
|
|
45
|
+
export async function getMe(cfg) {
|
|
46
|
+
const resolved = cfg ?? (await loadConfig());
|
|
47
|
+
try {
|
|
48
|
+
const { status, body } = await apiFetch(resolved, "/api/v1/me");
|
|
49
|
+
if (status === 404)
|
|
50
|
+
return null;
|
|
51
|
+
if (status !== 200)
|
|
52
|
+
return null;
|
|
53
|
+
return body;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export async function listJobs(limit = 10, cfg) {
|
|
60
|
+
const resolved = cfg ?? (await loadConfig());
|
|
61
|
+
const capped = Math.min(Math.max(limit, 1), 50);
|
|
62
|
+
const { status, body } = await apiFetch(resolved, `/api/v1/reviews?limit=${capped}`);
|
|
63
|
+
if (status === 404) {
|
|
64
|
+
throw new CommandError("Job list is not available on this server yet. Update the API or use the dashboard.");
|
|
65
|
+
}
|
|
66
|
+
if (status !== 200) {
|
|
67
|
+
throw new CommandError(`Failed to list jobs (HTTP ${status}): ${JSON.stringify(body)}`);
|
|
68
|
+
}
|
|
69
|
+
const items = body.items;
|
|
70
|
+
return Array.isArray(items) ? items : [];
|
|
71
|
+
}
|
|
72
|
+
/** Recently reviewed branches/threads (Bearer GET /api/v1/threads). */
|
|
73
|
+
export async function listThreads(limit = 20, cfg) {
|
|
74
|
+
const resolved = cfg ?? (await loadConfig());
|
|
75
|
+
const capped = Math.min(Math.max(limit, 1), 50);
|
|
76
|
+
const { status, body } = await apiFetch(resolved, `/api/v1/threads?limit=${capped}`);
|
|
77
|
+
if (status === 404) {
|
|
78
|
+
throw new CommandError("Branch list is not available on this server yet. Deploy the API update or use the dashboard.");
|
|
79
|
+
}
|
|
80
|
+
if (status !== 200) {
|
|
81
|
+
throw new CommandError(`Failed to list branches (HTTP ${status}): ${JSON.stringify(body)}`);
|
|
82
|
+
}
|
|
83
|
+
const items = body.items;
|
|
84
|
+
return Array.isArray(items) ? items : [];
|
|
85
|
+
}
|