@sanurb/ringi 0.2.0 → 0.2.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/dist/cli.mjs +1741 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/mcp.mjs +1061 -0
- package/dist/mcp.mjs.map +1 -0
- package/dist/runtime.mjs +2975 -0
- package/dist/runtime.mjs.map +1 -0
- package/package.json +9 -9
- package/dist/chunk-KMYSGMD3.js +0 -3526
- package/dist/chunk-KMYSGMD3.js.map +0 -1
- package/dist/cli.js +0 -1839
- package/dist/cli.js.map +0 -1
- package/dist/mcp.js +0 -1228
- package/dist/mcp.js.map +0 -1
package/dist/cli.js
DELETED
|
@@ -1,1839 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { ReviewService, TodoService, CommentService, ExportService, GitService, parseDiff, getDiffSummary, ReviewNotFound, TodoNotFound, CoreLive } from './chunk-KMYSGMD3.js';
|
|
3
|
-
import { fork, exec, execFileSync } from 'node:child_process';
|
|
4
|
-
import { existsSync } from 'node:fs';
|
|
5
|
-
import { resolve } from 'node:path';
|
|
6
|
-
import * as Either from 'effect/Either';
|
|
7
|
-
import { writeFile } from 'node:fs/promises';
|
|
8
|
-
import * as Effect from 'effect/Effect';
|
|
9
|
-
import * as Context from 'effect/Context';
|
|
10
|
-
import * as Layer2 from 'effect/Layer';
|
|
11
|
-
import * as Schema from 'effect/Schema';
|
|
12
|
-
import * as Option from 'effect/Option';
|
|
13
|
-
import * as ConfigProvider from 'effect/ConfigProvider';
|
|
14
|
-
import * as ManagedRuntime from 'effect/ManagedRuntime';
|
|
15
|
-
|
|
16
|
-
var CliConfig = class extends Context.Tag("@ringi/CliConfig")() {
|
|
17
|
-
};
|
|
18
|
-
var CliConfigLive = (config) => Layer2.succeed(CliConfig, config);
|
|
19
|
-
var ExitCode = {
|
|
20
|
-
AuthFailure: 5,
|
|
21
|
-
ResourceNotFound: 3,
|
|
22
|
-
RuntimeFailure: 1,
|
|
23
|
-
StateUnavailable: 4,
|
|
24
|
-
Success: 0,
|
|
25
|
-
UsageError: 2
|
|
26
|
-
};
|
|
27
|
-
var success = (command, result, nextActions = []) => ({
|
|
28
|
-
command,
|
|
29
|
-
next_actions: nextActions,
|
|
30
|
-
ok: true,
|
|
31
|
-
result
|
|
32
|
-
});
|
|
33
|
-
var failure = (command, error, fix, nextActions = []) => ({
|
|
34
|
-
command,
|
|
35
|
-
error,
|
|
36
|
-
fix,
|
|
37
|
-
next_actions: nextActions,
|
|
38
|
-
ok: false
|
|
39
|
-
});
|
|
40
|
-
var CliFailure = class extends Schema.TaggedError()("CliFailure", {
|
|
41
|
-
details: Schema.optional(Schema.String),
|
|
42
|
-
exitCode: Schema.Number,
|
|
43
|
-
message: Schema.String
|
|
44
|
-
}) {
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
// src/cli/commands.ts
|
|
48
|
-
var formatTable = (headers, rows) => {
|
|
49
|
-
const widths = headers.map((header, index) => {
|
|
50
|
-
const cellWidths = rows.map((row) => row[index]?.length ?? 0);
|
|
51
|
-
return Math.max(header.length, ...cellWidths);
|
|
52
|
-
});
|
|
53
|
-
const renderRow = (row) => row.map((cell, index) => cell.padEnd(widths.at(index) ?? 0)).join(" ").trimEnd();
|
|
54
|
-
return [
|
|
55
|
-
renderRow(headers),
|
|
56
|
-
renderRow(widths.map((width) => "-".repeat(width))),
|
|
57
|
-
...rows.map(renderRow)
|
|
58
|
-
].join("\n");
|
|
59
|
-
};
|
|
60
|
-
var renderReviewList = (reviews) => {
|
|
61
|
-
if (reviews.length === 0) {
|
|
62
|
-
return "No reviews found.";
|
|
63
|
-
}
|
|
64
|
-
return formatTable(
|
|
65
|
-
["ID", "STATUS", "SOURCE", "FILES", "CREATED"],
|
|
66
|
-
reviews.map((review) => [
|
|
67
|
-
review.id,
|
|
68
|
-
review.status,
|
|
69
|
-
review.sourceType,
|
|
70
|
-
String(review.fileCount),
|
|
71
|
-
review.createdAt
|
|
72
|
-
])
|
|
73
|
-
);
|
|
74
|
-
};
|
|
75
|
-
var renderReviewShow = (input) => {
|
|
76
|
-
const { comments, review, todos } = input;
|
|
77
|
-
const lines = [
|
|
78
|
-
`Review ${review.id}`,
|
|
79
|
-
`Status: ${review.status}`,
|
|
80
|
-
`Source: ${review.sourceType}${review.sourceRef ? ` (${review.sourceRef})` : ""}`,
|
|
81
|
-
`Created: ${review.createdAt}`,
|
|
82
|
-
`Files: ${review.summary.totalFiles}`,
|
|
83
|
-
`Diff: +${review.summary.totalAdditions} / -${review.summary.totalDeletions}`
|
|
84
|
-
];
|
|
85
|
-
if (review.files.length > 0) {
|
|
86
|
-
lines.push("", "Files:");
|
|
87
|
-
for (const file of review.files) {
|
|
88
|
-
lines.push(
|
|
89
|
-
`- ${file.status.toUpperCase()} ${file.filePath} (+${file.additions} -${file.deletions})`
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (comments && comments.length > 0) {
|
|
94
|
-
lines.push("", "Comments:");
|
|
95
|
-
for (const comment of comments) {
|
|
96
|
-
const location = `${comment.filePath}:${comment.lineNumber ?? "-"}`;
|
|
97
|
-
const state = comment.resolved ? "resolved" : "open";
|
|
98
|
-
lines.push(`- [${state}] ${location} ${comment.content}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if (todos && todos.length > 0) {
|
|
102
|
-
lines.push("", "Todos:");
|
|
103
|
-
for (const todo of todos) {
|
|
104
|
-
const marker = todo.completed ? "x" : " ";
|
|
105
|
-
lines.push(`- [${marker}] (${todo.position + 1}) ${todo.content}`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return lines.join("\n");
|
|
109
|
-
};
|
|
110
|
-
var renderTodoList = (todos) => {
|
|
111
|
-
if (todos.length === 0) {
|
|
112
|
-
return "No todos found.";
|
|
113
|
-
}
|
|
114
|
-
return todos.map(
|
|
115
|
-
(todo) => `- [${todo.completed ? "x" : " "}] (${todo.position + 1}) ${todo.content}`
|
|
116
|
-
).join("\n");
|
|
117
|
-
};
|
|
118
|
-
var renderSourceList = (input) => {
|
|
119
|
-
const lines = [
|
|
120
|
-
`Repository: ${input.repo.name}`,
|
|
121
|
-
`Path: ${input.repo.path}`,
|
|
122
|
-
`Current branch: ${input.repo.branch}`,
|
|
123
|
-
`Staged files: ${input.stagedFiles.length}`
|
|
124
|
-
];
|
|
125
|
-
if (input.stagedFiles.length > 0) {
|
|
126
|
-
lines.push("", "Staged:");
|
|
127
|
-
for (const file of input.stagedFiles) {
|
|
128
|
-
lines.push(`- ${file.status} ${file.path}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
if (input.branches.length > 0) {
|
|
132
|
-
lines.push("", "Branches:");
|
|
133
|
-
for (const branch of input.branches.slice(0, 10)) {
|
|
134
|
-
lines.push(`- ${branch.current ? "*" : " "} ${branch.name}`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (input.commits.length > 0) {
|
|
138
|
-
lines.push("", "Recent commits:");
|
|
139
|
-
for (const commit of input.commits.slice(0, 5)) {
|
|
140
|
-
lines.push(
|
|
141
|
-
`- ${commit.hash.slice(0, 8)} ${commit.message} (${commit.author})`
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return lines.join("\n");
|
|
146
|
-
};
|
|
147
|
-
var resolveReviewSelector = Effect.fn("CLI.resolveReviewSelector")(
|
|
148
|
-
function* resolveReviewSelector2(selector) {
|
|
149
|
-
if (selector !== "last") {
|
|
150
|
-
return selector;
|
|
151
|
-
}
|
|
152
|
-
const cliConfig = yield* CliConfig;
|
|
153
|
-
const reviewService = yield* ReviewService;
|
|
154
|
-
const result = yield* reviewService.list({
|
|
155
|
-
page: 1,
|
|
156
|
-
pageSize: 1,
|
|
157
|
-
repositoryPath: cliConfig.repoRoot
|
|
158
|
-
});
|
|
159
|
-
const [review] = result.reviews;
|
|
160
|
-
if (!review) {
|
|
161
|
-
return yield* new CliFailure({
|
|
162
|
-
exitCode: ExitCode.ResourceNotFound,
|
|
163
|
-
message: "No review sessions exist for this repository yet."
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
return review.id;
|
|
167
|
-
}
|
|
168
|
-
);
|
|
169
|
-
var requireServerMode = (label) => Effect.fail(
|
|
170
|
-
new CliFailure({
|
|
171
|
-
details: "Start 'ringi serve' and retry the command.",
|
|
172
|
-
exitCode: ExitCode.StateUnavailable,
|
|
173
|
-
message: `${label} requires a running local Ringi server. Standalone local writes are intentionally unsupported.`
|
|
174
|
-
})
|
|
175
|
-
);
|
|
176
|
-
var diffSourceStrategies = {
|
|
177
|
-
branch: (git, command) => git.getBranchDiff(command.branch ?? ""),
|
|
178
|
-
commits: (git, command) => git.getCommitDiff(
|
|
179
|
-
(command.commits ?? "").split(",").map((item) => item.trim()).filter(Boolean)
|
|
180
|
-
),
|
|
181
|
-
staged: (git) => git.getStagedDiff
|
|
182
|
-
};
|
|
183
|
-
var runReviewList = Effect.fn("CLI.reviewList")(function* runReviewList2(command) {
|
|
184
|
-
const reviewService = yield* ReviewService;
|
|
185
|
-
const cliConfig = yield* CliConfig;
|
|
186
|
-
const result = yield* reviewService.list({
|
|
187
|
-
page: command.page,
|
|
188
|
-
pageSize: command.limit,
|
|
189
|
-
repositoryPath: cliConfig.repoRoot,
|
|
190
|
-
sourceType: command.source,
|
|
191
|
-
status: command.status
|
|
192
|
-
});
|
|
193
|
-
const nextActions = [];
|
|
194
|
-
for (const review of result.reviews.slice(0, 3)) {
|
|
195
|
-
nextActions.push({
|
|
196
|
-
command: `ringi review show ${review.id} --comments --todos`,
|
|
197
|
-
description: `Inspect review ${review.id} (${review.status})`
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
if (result.reviews.length > 0) {
|
|
201
|
-
nextActions.push({
|
|
202
|
-
command: "ringi review show <id> [--comments] [--todos]",
|
|
203
|
-
description: "Show full review details",
|
|
204
|
-
params: {
|
|
205
|
-
id: { description: "Review ID or 'last'", required: true }
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
nextActions.push({
|
|
210
|
-
command: "ringi review create [--source <source>]",
|
|
211
|
-
description: "Create a new review session",
|
|
212
|
-
params: {
|
|
213
|
-
source: { default: "staged", enum: ["staged", "branch", "commits"] }
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
return {
|
|
217
|
-
data: result,
|
|
218
|
-
human: renderReviewList(result.reviews),
|
|
219
|
-
nextActions
|
|
220
|
-
};
|
|
221
|
-
});
|
|
222
|
-
var runReviewShow = Effect.fn("CLI.reviewShow")(function* runReviewShow2(command) {
|
|
223
|
-
const reviewService = yield* ReviewService;
|
|
224
|
-
const todoService = yield* TodoService;
|
|
225
|
-
const commentService = yield* CommentService;
|
|
226
|
-
const reviewId = yield* resolveReviewSelector(command.id);
|
|
227
|
-
const review = yield* reviewService.getById(reviewId);
|
|
228
|
-
const comments = command.comments ? yield* commentService.getByReview(reviewId) : void 0;
|
|
229
|
-
const todos = command.todos ? (yield* todoService.list({ reviewId })).data : void 0;
|
|
230
|
-
const data = { comments, review, todos };
|
|
231
|
-
const nextActions = [
|
|
232
|
-
{
|
|
233
|
-
command: `ringi review export ${reviewId}`,
|
|
234
|
-
description: "Export this review as markdown"
|
|
235
|
-
},
|
|
236
|
-
{
|
|
237
|
-
command: `ringi review show ${reviewId} --comments --todos`,
|
|
238
|
-
description: "Show with comments and todos"
|
|
239
|
-
},
|
|
240
|
-
{
|
|
241
|
-
command: "ringi todo list [--review <review-id>] [--status <status>]",
|
|
242
|
-
description: "List todos for this review",
|
|
243
|
-
params: {
|
|
244
|
-
"review-id": { value: reviewId },
|
|
245
|
-
status: { default: "pending", enum: ["pending", "done", "all"] }
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
command: "ringi review list",
|
|
250
|
-
description: "Back to review list"
|
|
251
|
-
}
|
|
252
|
-
];
|
|
253
|
-
return {
|
|
254
|
-
data,
|
|
255
|
-
human: renderReviewShow(data),
|
|
256
|
-
nextActions
|
|
257
|
-
};
|
|
258
|
-
});
|
|
259
|
-
var runReviewExport = Effect.fn("CLI.reviewExport")(function* runReviewExport2(command) {
|
|
260
|
-
if (command.noResolved || command.noSnippets) {
|
|
261
|
-
yield* new CliFailure({
|
|
262
|
-
exitCode: ExitCode.UsageError,
|
|
263
|
-
message: "--no-resolved and --no-snippets are documented, but the shared export service does not support adapter-level filtering yet."
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
const exportService = yield* ExportService;
|
|
267
|
-
const cliConfig = yield* CliConfig;
|
|
268
|
-
const reviewId = yield* resolveReviewSelector(command.id);
|
|
269
|
-
const markdown = yield* exportService.exportReview(reviewId);
|
|
270
|
-
const outputPath = command.outputPath ? resolve(cliConfig.cwd, command.outputPath) : void 0;
|
|
271
|
-
if (outputPath) {
|
|
272
|
-
yield* Effect.tryPromise({
|
|
273
|
-
catch: (error) => new CliFailure({
|
|
274
|
-
exitCode: ExitCode.RuntimeFailure,
|
|
275
|
-
message: `Failed to write export to ${outputPath}: ${String(error)}`
|
|
276
|
-
}),
|
|
277
|
-
try: () => writeFile(outputPath, markdown, "utf8")
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
const shouldPrintMarkdown = command.stdout || !outputPath;
|
|
281
|
-
const data = { markdown, outputPath: outputPath ?? null, reviewId };
|
|
282
|
-
const nextActions = [
|
|
283
|
-
{
|
|
284
|
-
command: `ringi review show ${reviewId}`,
|
|
285
|
-
description: "View the exported review"
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
command: "ringi review list",
|
|
289
|
-
description: "Back to review list"
|
|
290
|
-
}
|
|
291
|
-
];
|
|
292
|
-
return {
|
|
293
|
-
data,
|
|
294
|
-
human: shouldPrintMarkdown ? markdown : `Exported review ${reviewId} to ${outputPath}.`,
|
|
295
|
-
nextActions
|
|
296
|
-
};
|
|
297
|
-
});
|
|
298
|
-
var runSourceList = Effect.fn("CLI.sourceList")(function* runSourceList2() {
|
|
299
|
-
const gitService = yield* GitService;
|
|
300
|
-
const repo = yield* gitService.getRepositoryInfo;
|
|
301
|
-
const stagedFiles = yield* gitService.getStagedFiles;
|
|
302
|
-
const branches = yield* gitService.getBranches;
|
|
303
|
-
const commitsResult = yield* gitService.getCommits({
|
|
304
|
-
limit: 10,
|
|
305
|
-
offset: 0
|
|
306
|
-
});
|
|
307
|
-
const data = {
|
|
308
|
-
branches,
|
|
309
|
-
commits: commitsResult.commits,
|
|
310
|
-
repo,
|
|
311
|
-
stagedFiles
|
|
312
|
-
};
|
|
313
|
-
const nextActions = [
|
|
314
|
-
{
|
|
315
|
-
command: "ringi source diff <source> [--stat]",
|
|
316
|
-
description: "View diff for a source",
|
|
317
|
-
params: {
|
|
318
|
-
source: { enum: ["staged", "branch", "commits"] }
|
|
319
|
-
}
|
|
320
|
-
},
|
|
321
|
-
{
|
|
322
|
-
command: "ringi review create [--source <source>]",
|
|
323
|
-
description: "Create a review from a source",
|
|
324
|
-
params: {
|
|
325
|
-
source: { default: "staged", enum: ["staged", "branch", "commits"] }
|
|
326
|
-
}
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
command: "ringi review list",
|
|
330
|
-
description: "List existing reviews"
|
|
331
|
-
}
|
|
332
|
-
];
|
|
333
|
-
return {
|
|
334
|
-
data,
|
|
335
|
-
human: renderSourceList(data),
|
|
336
|
-
nextActions
|
|
337
|
-
};
|
|
338
|
-
});
|
|
339
|
-
var runSourceDiff = Effect.fn("CLI.sourceDiff")(function* runSourceDiff2(command) {
|
|
340
|
-
const gitService = yield* GitService;
|
|
341
|
-
const strategy = diffSourceStrategies[command.source];
|
|
342
|
-
if (!strategy) {
|
|
343
|
-
return yield* new CliFailure({
|
|
344
|
-
exitCode: ExitCode.UsageError,
|
|
345
|
-
message: "Unsupported review source."
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
const diffText = yield* strategy(gitService, command);
|
|
349
|
-
if (!diffText.trim()) {
|
|
350
|
-
yield* new CliFailure({
|
|
351
|
-
exitCode: ExitCode.RuntimeFailure,
|
|
352
|
-
message: "No diff available for the requested source."
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
const files = parseDiff(diffText);
|
|
356
|
-
const data = {
|
|
357
|
-
diff: diffText,
|
|
358
|
-
source: command.source,
|
|
359
|
-
summary: getDiffSummary(files)
|
|
360
|
-
};
|
|
361
|
-
const nextActions = [
|
|
362
|
-
{
|
|
363
|
-
command: `ringi review create --source ${command.source}`,
|
|
364
|
-
description: `Create a review from this ${command.source} diff`
|
|
365
|
-
},
|
|
366
|
-
{
|
|
367
|
-
command: "ringi source list",
|
|
368
|
-
description: "List repository sources"
|
|
369
|
-
}
|
|
370
|
-
];
|
|
371
|
-
return {
|
|
372
|
-
data,
|
|
373
|
-
human: command.stat ? [
|
|
374
|
-
`Source: ${command.source}`,
|
|
375
|
-
`Files: ${data.summary.totalFiles}`,
|
|
376
|
-
`Additions: ${data.summary.totalAdditions}`,
|
|
377
|
-
`Deletions: ${data.summary.totalDeletions}`
|
|
378
|
-
].join("\n") : diffText,
|
|
379
|
-
nextActions
|
|
380
|
-
};
|
|
381
|
-
});
|
|
382
|
-
var runReviewStatus = Effect.fn("CLI.reviewStatus")(function* runReviewStatus2(command) {
|
|
383
|
-
const reviewService = yield* ReviewService;
|
|
384
|
-
const todoService = yield* TodoService;
|
|
385
|
-
const commentService = yield* CommentService;
|
|
386
|
-
const gitService = yield* GitService;
|
|
387
|
-
const cliConfig = yield* CliConfig;
|
|
388
|
-
const repo = yield* gitService.getRepositoryInfo;
|
|
389
|
-
const stagedFiles = yield* gitService.getStagedFiles;
|
|
390
|
-
let reviewId;
|
|
391
|
-
if (command.reviewId) {
|
|
392
|
-
reviewId = yield* resolveReviewSelector(command.reviewId);
|
|
393
|
-
}
|
|
394
|
-
const reviews = yield* reviewService.list({
|
|
395
|
-
page: 1,
|
|
396
|
-
pageSize: 1,
|
|
397
|
-
repositoryPath: cliConfig.repoRoot,
|
|
398
|
-
sourceType: command.source
|
|
399
|
-
});
|
|
400
|
-
const latestReview = reviewId ? yield* reviewService.getById(reviewId) : reviews.reviews[0];
|
|
401
|
-
let commentStats;
|
|
402
|
-
let todoStats;
|
|
403
|
-
if (latestReview) {
|
|
404
|
-
commentStats = yield* commentService.getStats(latestReview.id);
|
|
405
|
-
todoStats = yield* todoService.getStats();
|
|
406
|
-
}
|
|
407
|
-
const data = {
|
|
408
|
-
commentStats: commentStats ?? null,
|
|
409
|
-
repository: {
|
|
410
|
-
branch: repo.branch,
|
|
411
|
-
name: repo.name,
|
|
412
|
-
path: repo.path,
|
|
413
|
-
stagedFileCount: stagedFiles.length
|
|
414
|
-
},
|
|
415
|
-
review: latestReview ? {
|
|
416
|
-
createdAt: latestReview.createdAt,
|
|
417
|
-
id: latestReview.id,
|
|
418
|
-
sourceType: latestReview.sourceType,
|
|
419
|
-
status: latestReview.status
|
|
420
|
-
} : null,
|
|
421
|
-
todoStats: todoStats ?? null
|
|
422
|
-
};
|
|
423
|
-
const lines = [
|
|
424
|
-
`Repository: ${repo.name}`,
|
|
425
|
-
`Branch: ${repo.branch}`,
|
|
426
|
-
`Staged files: ${stagedFiles.length}`
|
|
427
|
-
];
|
|
428
|
-
if (latestReview) {
|
|
429
|
-
lines.push(
|
|
430
|
-
"",
|
|
431
|
-
`Review: ${latestReview.id}`,
|
|
432
|
-
`Status: ${latestReview.status}`,
|
|
433
|
-
`Source: ${latestReview.sourceType}`
|
|
434
|
-
);
|
|
435
|
-
if (commentStats) {
|
|
436
|
-
lines.push(
|
|
437
|
-
`Comments: ${commentStats.unresolved ?? 0} unresolved / ${commentStats.total} total`
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
if (todoStats) {
|
|
441
|
-
lines.push(
|
|
442
|
-
`Todos: ${todoStats.pending} pending / ${todoStats.total} total`
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
} else {
|
|
446
|
-
lines.push("", "No review sessions found.");
|
|
447
|
-
}
|
|
448
|
-
const nextActions = [];
|
|
449
|
-
if (latestReview) {
|
|
450
|
-
nextActions.push(
|
|
451
|
-
{
|
|
452
|
-
command: `ringi review show ${latestReview.id} --comments --todos`,
|
|
453
|
-
description: "Inspect the latest review"
|
|
454
|
-
},
|
|
455
|
-
{
|
|
456
|
-
command: `ringi review export ${latestReview.id}`,
|
|
457
|
-
description: "Export the latest review"
|
|
458
|
-
}
|
|
459
|
-
);
|
|
460
|
-
}
|
|
461
|
-
nextActions.push({
|
|
462
|
-
command: "ringi review create [--source <source>]",
|
|
463
|
-
description: "Create a new review session",
|
|
464
|
-
params: {
|
|
465
|
-
source: { default: "staged", enum: ["staged", "branch", "commits"] }
|
|
466
|
-
}
|
|
467
|
-
});
|
|
468
|
-
return {
|
|
469
|
-
data,
|
|
470
|
-
human: lines.join("\n"),
|
|
471
|
-
nextActions
|
|
472
|
-
};
|
|
473
|
-
});
|
|
474
|
-
var runTodoList = Effect.fn("CLI.todoList")(function* runTodoList2(command) {
|
|
475
|
-
const todoService = yield* TodoService;
|
|
476
|
-
const result = yield* todoService.list({
|
|
477
|
-
completed: command.status === "all" ? void 0 : command.status === "done",
|
|
478
|
-
limit: command.limit,
|
|
479
|
-
offset: command.offset,
|
|
480
|
-
reviewId: command.reviewId
|
|
481
|
-
});
|
|
482
|
-
const nextActions = [];
|
|
483
|
-
if (command.reviewId) {
|
|
484
|
-
nextActions.push({
|
|
485
|
-
command: `ringi review show ${command.reviewId}`,
|
|
486
|
-
description: "View the associated review"
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
nextActions.push(
|
|
490
|
-
{
|
|
491
|
-
command: "ringi todo add --text <text> [--review <review-id>]",
|
|
492
|
-
description: "Add a new todo",
|
|
493
|
-
params: {
|
|
494
|
-
text: { description: "Todo text", required: true }
|
|
495
|
-
}
|
|
496
|
-
},
|
|
497
|
-
{
|
|
498
|
-
command: "ringi review list",
|
|
499
|
-
description: "List reviews"
|
|
500
|
-
}
|
|
501
|
-
);
|
|
502
|
-
return {
|
|
503
|
-
data: result,
|
|
504
|
-
human: renderTodoList(result.data),
|
|
505
|
-
nextActions
|
|
506
|
-
};
|
|
507
|
-
});
|
|
508
|
-
var COMMAND_HANDLERS = {
|
|
509
|
-
"data-migrate": () => requireServerMode("ringi data migrate"),
|
|
510
|
-
"data-reset": () => requireServerMode("ringi data reset"),
|
|
511
|
-
doctor: () => Effect.succeed({
|
|
512
|
-
data: { checks: [], ok: true },
|
|
513
|
-
human: "ringi doctor: not yet implemented.",
|
|
514
|
-
nextActions: []
|
|
515
|
-
}),
|
|
516
|
-
events: () => requireServerMode("ringi events"),
|
|
517
|
-
mcp: () => Effect.fail(
|
|
518
|
-
new CliFailure({
|
|
519
|
-
exitCode: ExitCode.UsageError,
|
|
520
|
-
message: "ringi mcp is a runtime command. Use it directly, not through the command dispatcher."
|
|
521
|
-
})
|
|
522
|
-
),
|
|
523
|
-
"review-create": () => requireServerMode("ringi review create"),
|
|
524
|
-
"review-export": (c) => runReviewExport(c),
|
|
525
|
-
"review-list": (c) => runReviewList(c),
|
|
526
|
-
"review-resolve": () => requireServerMode("ringi review resolve"),
|
|
527
|
-
"review-show": (c) => runReviewShow(c),
|
|
528
|
-
"review-status": (c) => runReviewStatus(c),
|
|
529
|
-
serve: () => Effect.fail(
|
|
530
|
-
new CliFailure({
|
|
531
|
-
exitCode: ExitCode.UsageError,
|
|
532
|
-
message: "ringi serve is a runtime command. Use it directly, not through the command dispatcher."
|
|
533
|
-
})
|
|
534
|
-
),
|
|
535
|
-
"source-diff": (c) => runSourceDiff(c),
|
|
536
|
-
"source-list": () => runSourceList(),
|
|
537
|
-
"todo-add": () => requireServerMode("ringi todo add"),
|
|
538
|
-
"todo-clear": () => requireServerMode("ringi todo clear"),
|
|
539
|
-
"todo-done": () => requireServerMode("ringi todo done"),
|
|
540
|
-
"todo-list": (c) => runTodoList(c),
|
|
541
|
-
"todo-move": () => requireServerMode("ringi todo move"),
|
|
542
|
-
"todo-remove": () => requireServerMode("ringi todo remove"),
|
|
543
|
-
"todo-undone": () => requireServerMode("ringi todo undone")
|
|
544
|
-
};
|
|
545
|
-
var COMMAND_LABELS = {
|
|
546
|
-
"data-migrate": "ringi data migrate",
|
|
547
|
-
"data-reset": "ringi data reset",
|
|
548
|
-
doctor: "ringi doctor",
|
|
549
|
-
events: "ringi events",
|
|
550
|
-
mcp: "ringi mcp",
|
|
551
|
-
"review-create": "ringi review create",
|
|
552
|
-
"review-export": "ringi review export",
|
|
553
|
-
"review-list": "ringi review list",
|
|
554
|
-
"review-resolve": "ringi review resolve",
|
|
555
|
-
"review-show": "ringi review show",
|
|
556
|
-
"review-status": "ringi review status",
|
|
557
|
-
serve: "ringi serve",
|
|
558
|
-
"source-diff": "ringi source diff",
|
|
559
|
-
"source-list": "ringi source list",
|
|
560
|
-
"todo-add": "ringi todo add",
|
|
561
|
-
"todo-clear": "ringi todo clear",
|
|
562
|
-
"todo-done": "ringi todo done",
|
|
563
|
-
"todo-list": "ringi todo list",
|
|
564
|
-
"todo-move": "ringi todo move",
|
|
565
|
-
"todo-remove": "ringi todo remove",
|
|
566
|
-
"todo-undone": "ringi todo undone"
|
|
567
|
-
};
|
|
568
|
-
var commandLabel = (command) => COMMAND_LABELS[command.kind] ?? `ringi ${command.kind}`;
|
|
569
|
-
var runCommand = (command) => {
|
|
570
|
-
const handler = COMMAND_HANDLERS[command.kind];
|
|
571
|
-
if (!handler) {
|
|
572
|
-
return Effect.fail(
|
|
573
|
-
new CliFailure({
|
|
574
|
-
exitCode: ExitCode.UsageError,
|
|
575
|
-
message: `No executable handler exists for ${command.kind}.`
|
|
576
|
-
})
|
|
577
|
-
);
|
|
578
|
-
}
|
|
579
|
-
return handler(command);
|
|
580
|
-
};
|
|
581
|
-
var REVIEW_SOURCES = /* @__PURE__ */ new Set([
|
|
582
|
-
"branch",
|
|
583
|
-
"commits",
|
|
584
|
-
"staged"
|
|
585
|
-
]);
|
|
586
|
-
var REVIEW_STATUSES = /* @__PURE__ */ new Set([
|
|
587
|
-
"approved",
|
|
588
|
-
"changes_requested",
|
|
589
|
-
"in_progress"
|
|
590
|
-
]);
|
|
591
|
-
var TODO_STATUSES = /* @__PURE__ */ new Set(["all", "done", "pending"]);
|
|
592
|
-
var usageError = (message) => new CliFailure({ exitCode: ExitCode.UsageError, message });
|
|
593
|
-
var requireValue = (state, flag) => {
|
|
594
|
-
const value = state.tokens[state.index + 1];
|
|
595
|
-
if (!value || value.startsWith("-")) {
|
|
596
|
-
return Option.some(usageError(`Missing value for ${flag}.`));
|
|
597
|
-
}
|
|
598
|
-
state.index += 2;
|
|
599
|
-
return Option.none();
|
|
600
|
-
};
|
|
601
|
-
var peekValue = (state) => state.tokens[state.index + 1] ?? "";
|
|
602
|
-
var decodePositiveInt = (raw, flag) => {
|
|
603
|
-
const value = Number.parseInt(raw, 10);
|
|
604
|
-
if (!Number.isInteger(value) || value < 0) {
|
|
605
|
-
return Either.left(usageError(`${flag} must be a non-negative integer.`));
|
|
606
|
-
}
|
|
607
|
-
return Either.right(value);
|
|
608
|
-
};
|
|
609
|
-
var decodeEnum = (raw, valid, label) => {
|
|
610
|
-
if (!valid.has(raw)) {
|
|
611
|
-
return Either.left(usageError(`Invalid ${label}: ${raw}.`));
|
|
612
|
-
}
|
|
613
|
-
return Either.right(raw);
|
|
614
|
-
};
|
|
615
|
-
var boolFlag = (key) => (state, acc) => {
|
|
616
|
-
acc[key] = true;
|
|
617
|
-
state.index += 1;
|
|
618
|
-
return Option.none();
|
|
619
|
-
};
|
|
620
|
-
var stringFlag = (key) => (state, acc) => {
|
|
621
|
-
const flag = state.tokens[state.index] ?? "";
|
|
622
|
-
const value = peekValue(state);
|
|
623
|
-
const error = requireValue(state, flag);
|
|
624
|
-
if (Option.isSome(error)) {
|
|
625
|
-
return error;
|
|
626
|
-
}
|
|
627
|
-
acc[key] = value;
|
|
628
|
-
return Option.none();
|
|
629
|
-
};
|
|
630
|
-
var positiveIntFlag = (key, opts) => (state, acc) => {
|
|
631
|
-
const flag = state.tokens[state.index] ?? "";
|
|
632
|
-
const raw = peekValue(state);
|
|
633
|
-
const error = requireValue(state, flag);
|
|
634
|
-
if (Option.isSome(error)) {
|
|
635
|
-
return error;
|
|
636
|
-
}
|
|
637
|
-
const decoded = decodePositiveInt(raw, flag);
|
|
638
|
-
if (Either.isLeft(decoded)) {
|
|
639
|
-
return Option.some(decoded.left);
|
|
640
|
-
}
|
|
641
|
-
if (opts?.min !== void 0 && decoded.right <= opts.min) {
|
|
642
|
-
return Option.some(
|
|
643
|
-
usageError(`${flag} must be greater than ${opts.min}.`)
|
|
644
|
-
);
|
|
645
|
-
}
|
|
646
|
-
acc[key] = decoded.right;
|
|
647
|
-
return Option.none();
|
|
648
|
-
};
|
|
649
|
-
var enumFlag = (key, valid, label) => (state, acc) => {
|
|
650
|
-
const flag = state.tokens[state.index] ?? "";
|
|
651
|
-
const raw = peekValue(state);
|
|
652
|
-
const error = requireValue(state, flag);
|
|
653
|
-
if (Option.isSome(error)) {
|
|
654
|
-
return error;
|
|
655
|
-
}
|
|
656
|
-
const decoded = decodeEnum(raw, valid, label);
|
|
657
|
-
if (Either.isLeft(decoded)) {
|
|
658
|
-
return Option.some(decoded.left);
|
|
659
|
-
}
|
|
660
|
-
acc[key] = decoded.right;
|
|
661
|
-
return Option.none();
|
|
662
|
-
};
|
|
663
|
-
var createDefaultOptions = () => ({
|
|
664
|
-
color: true,
|
|
665
|
-
dbPath: void 0,
|
|
666
|
-
help: false,
|
|
667
|
-
json: false,
|
|
668
|
-
quiet: false,
|
|
669
|
-
repo: void 0,
|
|
670
|
-
verbose: false,
|
|
671
|
-
version: false
|
|
672
|
-
});
|
|
673
|
-
var GLOBAL_FLAG_HANDLERS = {
|
|
674
|
-
"--db-path": stringFlag("dbPath"),
|
|
675
|
-
"--help": boolFlag("help"),
|
|
676
|
-
"--json": boolFlag("json"),
|
|
677
|
-
"--no-color": (state, acc) => {
|
|
678
|
-
acc.color = false;
|
|
679
|
-
state.index += 1;
|
|
680
|
-
return Option.none();
|
|
681
|
-
},
|
|
682
|
-
"--quiet": boolFlag("quiet"),
|
|
683
|
-
"--repo": stringFlag("repo"),
|
|
684
|
-
"--verbose": boolFlag("verbose"),
|
|
685
|
-
"--version": boolFlag("version")
|
|
686
|
-
};
|
|
687
|
-
var maybeParseGlobalFlag = (state) => {
|
|
688
|
-
const token = state.tokens[state.index];
|
|
689
|
-
if (!token) {
|
|
690
|
-
return false;
|
|
691
|
-
}
|
|
692
|
-
const handler = GLOBAL_FLAG_HANDLERS[token];
|
|
693
|
-
if (!handler) {
|
|
694
|
-
return false;
|
|
695
|
-
}
|
|
696
|
-
handler(state, state.options);
|
|
697
|
-
return true;
|
|
698
|
-
};
|
|
699
|
-
var runFlagLoop = (state, acc, handlers, commandLabel2) => {
|
|
700
|
-
while (state.index < state.tokens.length) {
|
|
701
|
-
if (maybeParseGlobalFlag(state)) {
|
|
702
|
-
continue;
|
|
703
|
-
}
|
|
704
|
-
const token = state.tokens[state.index] ?? "";
|
|
705
|
-
const handler = handlers[token];
|
|
706
|
-
if (!handler) {
|
|
707
|
-
return Option.some(
|
|
708
|
-
usageError(`Unknown flag for ${commandLabel2}: ${token}.`)
|
|
709
|
-
);
|
|
710
|
-
}
|
|
711
|
-
const error = handler(state, acc);
|
|
712
|
-
if (Option.isSome(error)) {
|
|
713
|
-
return error;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
return Option.none();
|
|
717
|
-
};
|
|
718
|
-
var REVIEW_LIST_FLAGS = {
|
|
719
|
-
"--limit": positiveIntFlag("limit", { min: 0 }),
|
|
720
|
-
"--page": positiveIntFlag("page", { min: 0 }),
|
|
721
|
-
"--source": enumFlag("source", REVIEW_SOURCES, "review source"),
|
|
722
|
-
"--status": enumFlag("status", REVIEW_STATUSES, "review status")
|
|
723
|
-
};
|
|
724
|
-
var parseReviewList = (state) => {
|
|
725
|
-
const acc = {
|
|
726
|
-
limit: 20,
|
|
727
|
-
page: 1,
|
|
728
|
-
source: void 0,
|
|
729
|
-
status: void 0
|
|
730
|
-
};
|
|
731
|
-
const error = runFlagLoop(state, acc, REVIEW_LIST_FLAGS, "review list");
|
|
732
|
-
if (Option.isSome(error)) {
|
|
733
|
-
return Either.left(error.value);
|
|
734
|
-
}
|
|
735
|
-
return Either.right({ kind: "review-list", ...acc });
|
|
736
|
-
};
|
|
737
|
-
var REVIEW_SHOW_FLAGS = {
|
|
738
|
-
"--comments": boolFlag("comments"),
|
|
739
|
-
"--todos": boolFlag("todos")
|
|
740
|
-
};
|
|
741
|
-
var parseReviewShow = (state) => {
|
|
742
|
-
const id = state.tokens[state.index];
|
|
743
|
-
if (!id) {
|
|
744
|
-
return Either.left(usageError("review show requires <id|last>."));
|
|
745
|
-
}
|
|
746
|
-
state.index += 1;
|
|
747
|
-
const acc = { comments: false, todos: false };
|
|
748
|
-
const error = runFlagLoop(state, acc, REVIEW_SHOW_FLAGS, "review show");
|
|
749
|
-
if (Option.isSome(error)) {
|
|
750
|
-
return Either.left(error.value);
|
|
751
|
-
}
|
|
752
|
-
return Either.right({ id, kind: "review-show", ...acc });
|
|
753
|
-
};
|
|
754
|
-
var REVIEW_EXPORT_FLAGS = {
|
|
755
|
-
"--no-resolved": boolFlag("noResolved"),
|
|
756
|
-
"--no-snippets": boolFlag("noSnippets"),
|
|
757
|
-
"--output": stringFlag("outputPath"),
|
|
758
|
-
"--stdout": boolFlag("stdout")
|
|
759
|
-
};
|
|
760
|
-
var parseReviewExport = (state) => {
|
|
761
|
-
const id = state.tokens[state.index];
|
|
762
|
-
if (!id) {
|
|
763
|
-
return Either.left(usageError("review export requires <id|last>."));
|
|
764
|
-
}
|
|
765
|
-
state.index += 1;
|
|
766
|
-
const acc = {
|
|
767
|
-
noResolved: false,
|
|
768
|
-
noSnippets: false,
|
|
769
|
-
outputPath: void 0,
|
|
770
|
-
stdout: false
|
|
771
|
-
};
|
|
772
|
-
const error = runFlagLoop(state, acc, REVIEW_EXPORT_FLAGS, "review export");
|
|
773
|
-
if (Option.isSome(error)) {
|
|
774
|
-
return Either.left(error.value);
|
|
775
|
-
}
|
|
776
|
-
return Either.right({ id, kind: "review-export", ...acc });
|
|
777
|
-
};
|
|
778
|
-
var REVIEW_CREATE_FLAGS = {
|
|
779
|
-
"--branch": stringFlag("branch"),
|
|
780
|
-
"--commits": stringFlag("commits"),
|
|
781
|
-
"--source": enumFlag("source", REVIEW_SOURCES, "review source"),
|
|
782
|
-
"--title": stringFlag("title")
|
|
783
|
-
};
|
|
784
|
-
var validateReviewCreate = (acc) => {
|
|
785
|
-
if (acc.source === "branch" && !acc.branch) {
|
|
786
|
-
return Option.some(
|
|
787
|
-
usageError("review create --source branch requires --branch.")
|
|
788
|
-
);
|
|
789
|
-
}
|
|
790
|
-
if (acc.source === "commits" && !acc.commits) {
|
|
791
|
-
return Option.some(
|
|
792
|
-
usageError("review create --source commits requires --commits.")
|
|
793
|
-
);
|
|
794
|
-
}
|
|
795
|
-
if (acc.source === "staged" && (acc.branch || acc.commits)) {
|
|
796
|
-
return Option.some(
|
|
797
|
-
usageError(
|
|
798
|
-
"review create --source staged does not accept --branch or --commits."
|
|
799
|
-
)
|
|
800
|
-
);
|
|
801
|
-
}
|
|
802
|
-
return Option.none();
|
|
803
|
-
};
|
|
804
|
-
var parseReviewCreate = (state) => {
|
|
805
|
-
const acc = {
|
|
806
|
-
branch: void 0,
|
|
807
|
-
commits: void 0,
|
|
808
|
-
source: "staged",
|
|
809
|
-
title: void 0
|
|
810
|
-
};
|
|
811
|
-
const error = runFlagLoop(state, acc, REVIEW_CREATE_FLAGS, "review create");
|
|
812
|
-
if (Option.isSome(error)) {
|
|
813
|
-
return Either.left(error.value);
|
|
814
|
-
}
|
|
815
|
-
const validationError = validateReviewCreate(acc);
|
|
816
|
-
if (Option.isSome(validationError)) {
|
|
817
|
-
return Either.left(validationError.value);
|
|
818
|
-
}
|
|
819
|
-
return Either.right({ kind: "review-create", ...acc });
|
|
820
|
-
};
|
|
821
|
-
var SOURCE_DIFF_FLAGS = {
|
|
822
|
-
"--branch": stringFlag("branch"),
|
|
823
|
-
"--commits": stringFlag("commits"),
|
|
824
|
-
"--stat": boolFlag("stat")
|
|
825
|
-
};
|
|
826
|
-
var validateSourceDiff = (source, acc) => {
|
|
827
|
-
if (source === "branch" && !acc.branch) {
|
|
828
|
-
return Option.some(usageError("source diff branch requires --branch."));
|
|
829
|
-
}
|
|
830
|
-
if (source === "commits" && !acc.commits) {
|
|
831
|
-
return Option.some(usageError("source diff commits requires --commits."));
|
|
832
|
-
}
|
|
833
|
-
return Option.none();
|
|
834
|
-
};
|
|
835
|
-
var parseSourceDiff = (state) => {
|
|
836
|
-
const source = state.tokens[state.index];
|
|
837
|
-
if (!source || !REVIEW_SOURCES.has(source)) {
|
|
838
|
-
return Either.left(
|
|
839
|
-
usageError("source diff requires <staged|branch|commits>.")
|
|
840
|
-
);
|
|
841
|
-
}
|
|
842
|
-
state.index += 1;
|
|
843
|
-
const acc = {
|
|
844
|
-
branch: void 0,
|
|
845
|
-
commits: void 0,
|
|
846
|
-
stat: false
|
|
847
|
-
};
|
|
848
|
-
const error = runFlagLoop(state, acc, SOURCE_DIFF_FLAGS, "source diff");
|
|
849
|
-
if (Option.isSome(error)) {
|
|
850
|
-
return Either.left(error.value);
|
|
851
|
-
}
|
|
852
|
-
const validationError = validateSourceDiff(source, acc);
|
|
853
|
-
if (Option.isSome(validationError)) {
|
|
854
|
-
return Either.left(validationError.value);
|
|
855
|
-
}
|
|
856
|
-
return Either.right({ kind: "source-diff", source, ...acc });
|
|
857
|
-
};
|
|
858
|
-
var TODO_LIST_FLAGS = {
|
|
859
|
-
"--limit": positiveIntFlag("limit"),
|
|
860
|
-
"--offset": positiveIntFlag("offset"),
|
|
861
|
-
"--review": stringFlag("reviewId"),
|
|
862
|
-
"--status": enumFlag("status", TODO_STATUSES, "todo status")
|
|
863
|
-
};
|
|
864
|
-
var parseTodoList = (state) => {
|
|
865
|
-
const acc = {
|
|
866
|
-
limit: void 0,
|
|
867
|
-
offset: 0,
|
|
868
|
-
reviewId: void 0,
|
|
869
|
-
status: "pending"
|
|
870
|
-
};
|
|
871
|
-
const error = runFlagLoop(state, acc, TODO_LIST_FLAGS, "todo list");
|
|
872
|
-
if (Option.isSome(error)) {
|
|
873
|
-
return Either.left(error.value);
|
|
874
|
-
}
|
|
875
|
-
return Either.right({ kind: "todo-list", ...acc });
|
|
876
|
-
};
|
|
877
|
-
var positionalIdParser = (kind, label) => (state) => {
|
|
878
|
-
const id = state.tokens[state.index];
|
|
879
|
-
if (!id) {
|
|
880
|
-
return Either.left(usageError(`${label} requires <id>.`));
|
|
881
|
-
}
|
|
882
|
-
state.index += 1;
|
|
883
|
-
while (state.index < state.tokens.length) {
|
|
884
|
-
if (!maybeParseGlobalFlag(state)) {
|
|
885
|
-
return Either.left(
|
|
886
|
-
usageError(`Unknown flag for ${label}: ${state.tokens[state.index]}.`)
|
|
887
|
-
);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
return Either.right({ id, kind });
|
|
891
|
-
};
|
|
892
|
-
var parseTodoDone = positionalIdParser("todo-done", "todo done");
|
|
893
|
-
var parseTodoUndone = positionalIdParser(
|
|
894
|
-
"todo-undone",
|
|
895
|
-
"todo undone"
|
|
896
|
-
);
|
|
897
|
-
var TODO_MOVE_FLAGS = {
|
|
898
|
-
"--position": positiveIntFlag("position")
|
|
899
|
-
};
|
|
900
|
-
var parseTodoMove = (state) => {
|
|
901
|
-
const id = state.tokens[state.index];
|
|
902
|
-
if (!id) {
|
|
903
|
-
return Either.left(usageError("todo move requires <id>."));
|
|
904
|
-
}
|
|
905
|
-
state.index += 1;
|
|
906
|
-
const acc = { position: void 0 };
|
|
907
|
-
const error = runFlagLoop(state, acc, TODO_MOVE_FLAGS, "todo move");
|
|
908
|
-
if (Option.isSome(error)) {
|
|
909
|
-
return Either.left(error.value);
|
|
910
|
-
}
|
|
911
|
-
if (acc.position === void 0) {
|
|
912
|
-
return Either.left(usageError("todo move requires --position."));
|
|
913
|
-
}
|
|
914
|
-
return Either.right({
|
|
915
|
-
id,
|
|
916
|
-
kind: "todo-move",
|
|
917
|
-
position: acc.position
|
|
918
|
-
});
|
|
919
|
-
};
|
|
920
|
-
var TODO_REMOVE_FLAGS = {
|
|
921
|
-
"--yes": boolFlag("yes")
|
|
922
|
-
};
|
|
923
|
-
var parseTodoRemove = (state) => {
|
|
924
|
-
const id = state.tokens[state.index];
|
|
925
|
-
if (!id) {
|
|
926
|
-
return Either.left(usageError("todo remove requires <id>."));
|
|
927
|
-
}
|
|
928
|
-
state.index += 1;
|
|
929
|
-
const acc = { yes: false };
|
|
930
|
-
const error = runFlagLoop(state, acc, TODO_REMOVE_FLAGS, "todo remove");
|
|
931
|
-
if (Option.isSome(error)) {
|
|
932
|
-
return Either.left(error.value);
|
|
933
|
-
}
|
|
934
|
-
return Either.right({ id, kind: "todo-remove", ...acc });
|
|
935
|
-
};
|
|
936
|
-
var TODO_CLEAR_FLAGS = {
|
|
937
|
-
"--all": boolFlag("all"),
|
|
938
|
-
"--done-only": boolFlag("doneOnly"),
|
|
939
|
-
"--review": stringFlag("reviewId"),
|
|
940
|
-
"--yes": boolFlag("yes")
|
|
941
|
-
};
|
|
942
|
-
var parseTodoClear = (state) => {
|
|
943
|
-
const acc = {
|
|
944
|
-
all: false,
|
|
945
|
-
doneOnly: true,
|
|
946
|
-
reviewId: void 0,
|
|
947
|
-
yes: false
|
|
948
|
-
};
|
|
949
|
-
const error = runFlagLoop(state, acc, TODO_CLEAR_FLAGS, "todo clear");
|
|
950
|
-
if (Option.isSome(error)) {
|
|
951
|
-
return Either.left(error.value);
|
|
952
|
-
}
|
|
953
|
-
return Either.right({ kind: "todo-clear", ...acc });
|
|
954
|
-
};
|
|
955
|
-
var REVIEW_STATUS_FLAGS = {
|
|
956
|
-
"--review": stringFlag("reviewId"),
|
|
957
|
-
"--source": enumFlag("source", REVIEW_SOURCES, "review source")
|
|
958
|
-
};
|
|
959
|
-
var parseReviewStatus = (state) => {
|
|
960
|
-
const acc = { reviewId: void 0, source: void 0 };
|
|
961
|
-
const error = runFlagLoop(state, acc, REVIEW_STATUS_FLAGS, "review status");
|
|
962
|
-
if (Option.isSome(error)) {
|
|
963
|
-
return Either.left(error.value);
|
|
964
|
-
}
|
|
965
|
-
return Either.right({ kind: "review-status", ...acc });
|
|
966
|
-
};
|
|
967
|
-
var REVIEW_RESOLVE_FLAGS = {
|
|
968
|
-
"--all-comments": boolFlag("allComments"),
|
|
969
|
-
"--yes": boolFlag("yes")
|
|
970
|
-
};
|
|
971
|
-
var parseReviewResolve = (state) => {
|
|
972
|
-
const id = state.tokens[state.index];
|
|
973
|
-
if (!id) {
|
|
974
|
-
return Either.left(usageError("review resolve requires <id|last>."));
|
|
975
|
-
}
|
|
976
|
-
state.index += 1;
|
|
977
|
-
const acc = { allComments: true, yes: false };
|
|
978
|
-
const error = runFlagLoop(state, acc, REVIEW_RESOLVE_FLAGS, "review resolve");
|
|
979
|
-
if (Option.isSome(error)) {
|
|
980
|
-
return Either.left(error.value);
|
|
981
|
-
}
|
|
982
|
-
return Either.right({ id, kind: "review-resolve", ...acc });
|
|
983
|
-
};
|
|
984
|
-
var TODO_ADD_FLAGS = {
|
|
985
|
-
"--position": positiveIntFlag("position"),
|
|
986
|
-
"--review": stringFlag("reviewId"),
|
|
987
|
-
"--text": stringFlag("text")
|
|
988
|
-
};
|
|
989
|
-
var parseTodoAdd = (state) => {
|
|
990
|
-
const acc = {
|
|
991
|
-
position: void 0,
|
|
992
|
-
reviewId: void 0,
|
|
993
|
-
text: ""
|
|
994
|
-
};
|
|
995
|
-
const error = runFlagLoop(state, acc, TODO_ADD_FLAGS, "todo add");
|
|
996
|
-
if (Option.isSome(error)) {
|
|
997
|
-
return Either.left(error.value);
|
|
998
|
-
}
|
|
999
|
-
if (!acc.text.trim()) {
|
|
1000
|
-
return Either.left(usageError("todo add requires --text."));
|
|
1001
|
-
}
|
|
1002
|
-
return Either.right({ kind: "todo-add", ...acc });
|
|
1003
|
-
};
|
|
1004
|
-
var SERVE_FLAGS = {
|
|
1005
|
-
"--auth": boolFlag("auth"),
|
|
1006
|
-
"--cert": stringFlag("cert"),
|
|
1007
|
-
"--host": stringFlag("host"),
|
|
1008
|
-
"--https": boolFlag("https"),
|
|
1009
|
-
"--key": stringFlag("key"),
|
|
1010
|
-
"--no-open": boolFlag("noOpen"),
|
|
1011
|
-
"--password": stringFlag("password"),
|
|
1012
|
-
"--port": positiveIntFlag("port", { min: 0 }),
|
|
1013
|
-
"--username": stringFlag("username")
|
|
1014
|
-
};
|
|
1015
|
-
var parseServe = (state) => {
|
|
1016
|
-
const acc = {
|
|
1017
|
-
auth: false,
|
|
1018
|
-
cert: void 0,
|
|
1019
|
-
host: "127.0.0.1",
|
|
1020
|
-
https: false,
|
|
1021
|
-
key: void 0,
|
|
1022
|
-
noOpen: false,
|
|
1023
|
-
password: void 0,
|
|
1024
|
-
port: 3e3,
|
|
1025
|
-
username: void 0
|
|
1026
|
-
};
|
|
1027
|
-
const error = runFlagLoop(state, acc, SERVE_FLAGS, "serve");
|
|
1028
|
-
if (Option.isSome(error)) {
|
|
1029
|
-
return Either.left(error.value);
|
|
1030
|
-
}
|
|
1031
|
-
return Either.right({ kind: "serve", ...acc });
|
|
1032
|
-
};
|
|
1033
|
-
var MCP_LOG_LEVELS = /* @__PURE__ */ new Set(["debug", "error", "info", "silent"]);
|
|
1034
|
-
var MCP_FLAGS = {
|
|
1035
|
-
"--log-level": enumFlag("logLevel", MCP_LOG_LEVELS, "log level"),
|
|
1036
|
-
"--readonly": boolFlag("readonly")
|
|
1037
|
-
};
|
|
1038
|
-
var parseMcp = (state) => {
|
|
1039
|
-
const acc = { logLevel: "error", readonly: false };
|
|
1040
|
-
const error = runFlagLoop(state, acc, MCP_FLAGS, "mcp");
|
|
1041
|
-
if (Option.isSome(error)) {
|
|
1042
|
-
return Either.left(error.value);
|
|
1043
|
-
}
|
|
1044
|
-
return Either.right({ kind: "mcp", ...acc });
|
|
1045
|
-
};
|
|
1046
|
-
var EVENT_TYPES = /* @__PURE__ */ new Set(["comments", "files", "reviews", "todos"]);
|
|
1047
|
-
var EVENTS_FLAGS = {
|
|
1048
|
-
"--since": positiveIntFlag("since"),
|
|
1049
|
-
"--type": enumFlag("type", EVENT_TYPES, "event type")
|
|
1050
|
-
};
|
|
1051
|
-
var parseEvents = (state) => {
|
|
1052
|
-
const acc = { since: void 0, type: void 0 };
|
|
1053
|
-
const error = runFlagLoop(state, acc, EVENTS_FLAGS, "events");
|
|
1054
|
-
if (Option.isSome(error)) {
|
|
1055
|
-
return Either.left(error.value);
|
|
1056
|
-
}
|
|
1057
|
-
return Either.right({ kind: "events", ...acc });
|
|
1058
|
-
};
|
|
1059
|
-
var DATA_RESET_FLAGS = {
|
|
1060
|
-
"--keep-exports": boolFlag("keepExports"),
|
|
1061
|
-
"--yes": boolFlag("yes")
|
|
1062
|
-
};
|
|
1063
|
-
var parseDataReset = (state) => {
|
|
1064
|
-
const acc = { keepExports: false, yes: false };
|
|
1065
|
-
const error = runFlagLoop(state, acc, DATA_RESET_FLAGS, "data reset");
|
|
1066
|
-
if (Option.isSome(error)) {
|
|
1067
|
-
return Either.left(error.value);
|
|
1068
|
-
}
|
|
1069
|
-
return Either.right({ kind: "data-reset", ...acc });
|
|
1070
|
-
};
|
|
1071
|
-
var ensureNoExtraArgs = (state, label) => {
|
|
1072
|
-
if (state.index < state.tokens.length) {
|
|
1073
|
-
return Option.some(
|
|
1074
|
-
usageError(
|
|
1075
|
-
`Unexpected argument for ${label}: ${state.tokens[state.index]}.`
|
|
1076
|
-
)
|
|
1077
|
-
);
|
|
1078
|
-
}
|
|
1079
|
-
return Option.none();
|
|
1080
|
-
};
|
|
1081
|
-
var REVIEW_VERB_PARSERS = {
|
|
1082
|
-
create: parseReviewCreate,
|
|
1083
|
-
export: parseReviewExport,
|
|
1084
|
-
list: parseReviewList,
|
|
1085
|
-
resolve: parseReviewResolve,
|
|
1086
|
-
show: parseReviewShow,
|
|
1087
|
-
status: parseReviewStatus
|
|
1088
|
-
};
|
|
1089
|
-
var TODO_VERB_PARSERS = {
|
|
1090
|
-
add: parseTodoAdd,
|
|
1091
|
-
clear: parseTodoClear,
|
|
1092
|
-
done: parseTodoDone,
|
|
1093
|
-
list: parseTodoList,
|
|
1094
|
-
move: parseTodoMove,
|
|
1095
|
-
remove: parseTodoRemove,
|
|
1096
|
-
undone: parseTodoUndone
|
|
1097
|
-
};
|
|
1098
|
-
var DATA_VERB_PARSERS = {
|
|
1099
|
-
migrate: (state) => {
|
|
1100
|
-
const error = ensureNoExtraArgs(state, "data migrate");
|
|
1101
|
-
if (Option.isSome(error)) {
|
|
1102
|
-
return Either.left(error.value);
|
|
1103
|
-
}
|
|
1104
|
-
return Either.right({ kind: "data-migrate" });
|
|
1105
|
-
},
|
|
1106
|
-
reset: parseDataReset
|
|
1107
|
-
};
|
|
1108
|
-
var FAMILY_PARSERS = {
|
|
1109
|
-
data: (state) => {
|
|
1110
|
-
const verb = state.tokens[state.index];
|
|
1111
|
-
if (!verb) {
|
|
1112
|
-
return Either.right({
|
|
1113
|
-
kind: "help",
|
|
1114
|
-
topic: ["data"]
|
|
1115
|
-
});
|
|
1116
|
-
}
|
|
1117
|
-
state.index += 1;
|
|
1118
|
-
const parser = DATA_VERB_PARSERS[verb];
|
|
1119
|
-
if (!parser) {
|
|
1120
|
-
return Either.left(usageError(`Unknown data command: ${verb}.`));
|
|
1121
|
-
}
|
|
1122
|
-
return parser(state);
|
|
1123
|
-
},
|
|
1124
|
-
doctor: (state) => {
|
|
1125
|
-
const error = ensureNoExtraArgs(state, "doctor");
|
|
1126
|
-
if (Option.isSome(error)) {
|
|
1127
|
-
return Either.left(error.value);
|
|
1128
|
-
}
|
|
1129
|
-
return Either.right({ kind: "doctor" });
|
|
1130
|
-
},
|
|
1131
|
-
events: parseEvents,
|
|
1132
|
-
export: parseReviewExport,
|
|
1133
|
-
mcp: parseMcp,
|
|
1134
|
-
review: (state) => {
|
|
1135
|
-
const verb = state.tokens[state.index];
|
|
1136
|
-
if (!verb) {
|
|
1137
|
-
return Either.right({
|
|
1138
|
-
kind: "help",
|
|
1139
|
-
topic: ["review"]
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
1142
|
-
state.index += 1;
|
|
1143
|
-
const parser = REVIEW_VERB_PARSERS[verb];
|
|
1144
|
-
if (!parser) {
|
|
1145
|
-
return Either.left(usageError(`Unknown review command: ${verb}.`));
|
|
1146
|
-
}
|
|
1147
|
-
return parser(state);
|
|
1148
|
-
},
|
|
1149
|
-
serve: parseServe,
|
|
1150
|
-
source: (state) => {
|
|
1151
|
-
const verb = state.tokens[state.index];
|
|
1152
|
-
if (!verb) {
|
|
1153
|
-
return Either.right({
|
|
1154
|
-
kind: "help",
|
|
1155
|
-
topic: ["source"]
|
|
1156
|
-
});
|
|
1157
|
-
}
|
|
1158
|
-
state.index += 1;
|
|
1159
|
-
if (verb === "list") {
|
|
1160
|
-
const error = ensureNoExtraArgs(state, "source list");
|
|
1161
|
-
if (Option.isSome(error)) {
|
|
1162
|
-
return Either.left(error.value);
|
|
1163
|
-
}
|
|
1164
|
-
return Either.right({ kind: "source-list" });
|
|
1165
|
-
}
|
|
1166
|
-
if (verb === "diff") {
|
|
1167
|
-
return parseSourceDiff(state);
|
|
1168
|
-
}
|
|
1169
|
-
return Either.left(usageError(`Unknown source command: ${verb}.`));
|
|
1170
|
-
},
|
|
1171
|
-
todo: (state) => {
|
|
1172
|
-
const verb = state.tokens[state.index];
|
|
1173
|
-
if (!verb) {
|
|
1174
|
-
return Either.right({
|
|
1175
|
-
kind: "help",
|
|
1176
|
-
topic: ["todo"]
|
|
1177
|
-
});
|
|
1178
|
-
}
|
|
1179
|
-
state.index += 1;
|
|
1180
|
-
const parser = TODO_VERB_PARSERS[verb];
|
|
1181
|
-
if (!parser) {
|
|
1182
|
-
return Either.left(usageError(`Unknown todo command: ${verb}.`));
|
|
1183
|
-
}
|
|
1184
|
-
return parser(state);
|
|
1185
|
-
}
|
|
1186
|
-
};
|
|
1187
|
-
var parseWithState = (state) => {
|
|
1188
|
-
while (state.index < state.tokens.length && maybeParseGlobalFlag(state)) {
|
|
1189
|
-
}
|
|
1190
|
-
if (state.options.version) {
|
|
1191
|
-
return Either.right({ kind: "version" });
|
|
1192
|
-
}
|
|
1193
|
-
if (state.index >= state.tokens.length) {
|
|
1194
|
-
return Either.right({ kind: "help", topic: [] });
|
|
1195
|
-
}
|
|
1196
|
-
const first = state.tokens[state.index];
|
|
1197
|
-
if (!first) {
|
|
1198
|
-
return Either.right({ kind: "help", topic: [] });
|
|
1199
|
-
}
|
|
1200
|
-
if (first === "help") {
|
|
1201
|
-
const topic = state.tokens.slice(state.index + 1);
|
|
1202
|
-
state.index = state.tokens.length;
|
|
1203
|
-
return Either.right({ kind: "help", topic });
|
|
1204
|
-
}
|
|
1205
|
-
if (state.options.help) {
|
|
1206
|
-
return Either.right({
|
|
1207
|
-
kind: "help",
|
|
1208
|
-
topic: state.tokens.slice(state.index)
|
|
1209
|
-
});
|
|
1210
|
-
}
|
|
1211
|
-
state.index += 1;
|
|
1212
|
-
const familyParser = FAMILY_PARSERS[first];
|
|
1213
|
-
if (!familyParser) {
|
|
1214
|
-
return Either.left(usageError(`Unknown command: ${first}.`));
|
|
1215
|
-
}
|
|
1216
|
-
return familyParser(state);
|
|
1217
|
-
};
|
|
1218
|
-
var parseCliArgs = (argv) => {
|
|
1219
|
-
const options = createDefaultOptions();
|
|
1220
|
-
const state = { index: 0, options, tokens: argv };
|
|
1221
|
-
const result = parseWithState(state);
|
|
1222
|
-
if (Either.isLeft(result)) {
|
|
1223
|
-
return Either.left(result.left);
|
|
1224
|
-
}
|
|
1225
|
-
return Either.right({ command: result.right, options });
|
|
1226
|
-
};
|
|
1227
|
-
var resolveRepositoryRoot = (repoOverride) => {
|
|
1228
|
-
const cwd = repoOverride ? resolve(repoOverride) : process.cwd();
|
|
1229
|
-
try {
|
|
1230
|
-
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
1231
|
-
cwd,
|
|
1232
|
-
encoding: "utf8"
|
|
1233
|
-
}).trim();
|
|
1234
|
-
} catch {
|
|
1235
|
-
return new CliFailure({
|
|
1236
|
-
exitCode: ExitCode.StateUnavailable,
|
|
1237
|
-
message: repoOverride ? `Path ${cwd} is not a Git repository. Use --repo <path> with a valid repository root.` : `Could not resolve a Git repository from ${cwd}. Use --repo <path> with a valid repository root.`
|
|
1238
|
-
});
|
|
1239
|
-
}
|
|
1240
|
-
};
|
|
1241
|
-
var resolveDbPath = (repoRoot, dbPathOverride) => dbPathOverride ? resolve(dbPathOverride) : resolve(repoRoot, ".ringi/reviews.db");
|
|
1242
|
-
var commandNeedsRepository = (command) => command.kind !== "help" && command.kind !== "version" && command.kind !== "mcp" && command.kind !== "serve";
|
|
1243
|
-
var commandNeedsDatabase = (command) => command.kind === "review-list" || command.kind === "review-show" || command.kind === "review-export" || command.kind === "review-status" || command.kind === "todo-list" || command.kind === "doctor";
|
|
1244
|
-
var commandUsesCoreRuntime = (command) => command.kind === "review-list" || command.kind === "review-show" || command.kind === "review-export" || command.kind === "review-status" || command.kind === "todo-list" || command.kind === "review-create" || command.kind === "todo-add" || command.kind === "doctor";
|
|
1245
|
-
var resolveCliConfig = (args) => {
|
|
1246
|
-
const repoRootResult = resolveRepositoryRoot(args.repo);
|
|
1247
|
-
if (repoRootResult instanceof CliFailure) {
|
|
1248
|
-
return repoRootResult;
|
|
1249
|
-
}
|
|
1250
|
-
return {
|
|
1251
|
-
color: args.color,
|
|
1252
|
-
cwd: process.cwd(),
|
|
1253
|
-
dbPath: resolveDbPath(repoRootResult, args.dbPath),
|
|
1254
|
-
outputMode: "human",
|
|
1255
|
-
quiet: args.quiet,
|
|
1256
|
-
repoRoot: repoRootResult,
|
|
1257
|
-
verbose: args.verbose
|
|
1258
|
-
};
|
|
1259
|
-
};
|
|
1260
|
-
var ensureLocalStateAvailable = (config) => {
|
|
1261
|
-
if (!existsSync(config.dbPath)) {
|
|
1262
|
-
return new CliFailure({
|
|
1263
|
-
exitCode: ExitCode.StateUnavailable,
|
|
1264
|
-
message: `Local state is missing at ${config.dbPath}. Run 'ringi data migrate' or start 'ringi serve' once to initialize local state.`
|
|
1265
|
-
});
|
|
1266
|
-
}
|
|
1267
|
-
return void 0;
|
|
1268
|
-
};
|
|
1269
|
-
var makeConfigLayer = (config) => Layer2.setConfigProvider(
|
|
1270
|
-
ConfigProvider.fromMap(
|
|
1271
|
-
/* @__PURE__ */ new Map([
|
|
1272
|
-
["DB_PATH", config.dbPath],
|
|
1273
|
-
["REPOSITORY_PATH", config.repoRoot]
|
|
1274
|
-
])
|
|
1275
|
-
)
|
|
1276
|
-
);
|
|
1277
|
-
var createCoreCliRuntime = (config) => ManagedRuntime.make(
|
|
1278
|
-
Layer2.mergeAll(CoreLive, CliConfigLive(config)).pipe(
|
|
1279
|
-
Layer2.provideMerge(makeConfigLayer(config))
|
|
1280
|
-
)
|
|
1281
|
-
);
|
|
1282
|
-
var createGitCliRuntime = (config) => ManagedRuntime.make(
|
|
1283
|
-
Layer2.mergeAll(GitService.Default, CliConfigLive(config)).pipe(
|
|
1284
|
-
Layer2.provideMerge(makeConfigLayer(config))
|
|
1285
|
-
)
|
|
1286
|
-
);
|
|
1287
|
-
var createCliRuntimeResources = (command, args) => {
|
|
1288
|
-
if (!commandNeedsRepository(command)) {
|
|
1289
|
-
return null;
|
|
1290
|
-
}
|
|
1291
|
-
const configResult = resolveCliConfig(args);
|
|
1292
|
-
if (configResult instanceof CliFailure) {
|
|
1293
|
-
return configResult;
|
|
1294
|
-
}
|
|
1295
|
-
if (commandNeedsDatabase(command)) {
|
|
1296
|
-
const stateError = ensureLocalStateAvailable(configResult);
|
|
1297
|
-
if (stateError) {
|
|
1298
|
-
return stateError;
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
return {
|
|
1302
|
-
config: configResult,
|
|
1303
|
-
runtime: commandUsesCoreRuntime(command) ? createCoreCliRuntime(configResult) : createGitCliRuntime(configResult)
|
|
1304
|
-
};
|
|
1305
|
-
};
|
|
1306
|
-
|
|
1307
|
-
// src/cli/main.ts
|
|
1308
|
-
var CLI_VERSION = "0.2.0";
|
|
1309
|
-
var COMMAND_TREE = {
|
|
1310
|
-
commands: [
|
|
1311
|
-
{
|
|
1312
|
-
description: "List review sessions",
|
|
1313
|
-
name: "review list",
|
|
1314
|
-
usage: "ringi review list [--status <status>] [--source <type>] [--limit <n>] [--page <n>]"
|
|
1315
|
-
},
|
|
1316
|
-
{
|
|
1317
|
-
description: "Show review details",
|
|
1318
|
-
name: "review show",
|
|
1319
|
-
usage: "ringi review show <id|last> [--comments] [--todos]"
|
|
1320
|
-
},
|
|
1321
|
-
{
|
|
1322
|
-
description: "Create a review session",
|
|
1323
|
-
name: "review create",
|
|
1324
|
-
usage: "ringi review create [--source <staged|branch|commits>] [--branch <name>] [--commits <range>]"
|
|
1325
|
-
},
|
|
1326
|
-
{
|
|
1327
|
-
description: "Export review as markdown",
|
|
1328
|
-
name: "review export",
|
|
1329
|
-
usage: "ringi review export <id|last> [--output <path>] [--stdout]"
|
|
1330
|
-
},
|
|
1331
|
-
{
|
|
1332
|
-
description: "Resolve a review session",
|
|
1333
|
-
name: "review resolve",
|
|
1334
|
-
usage: "ringi review resolve <id|last> [--all-comments] [--yes]"
|
|
1335
|
-
},
|
|
1336
|
-
{
|
|
1337
|
-
description: "Show repository and review status",
|
|
1338
|
-
name: "review status",
|
|
1339
|
-
usage: "ringi review status [--review <id|last>] [--source <type>]"
|
|
1340
|
-
},
|
|
1341
|
-
{
|
|
1342
|
-
description: "List repository sources",
|
|
1343
|
-
name: "source list",
|
|
1344
|
-
usage: "ringi source list"
|
|
1345
|
-
},
|
|
1346
|
-
{
|
|
1347
|
-
description: "Show diff for a source",
|
|
1348
|
-
name: "source diff",
|
|
1349
|
-
usage: "ringi source diff <staged|branch|commits> [--branch <name>] [--commits <range>] [--stat]"
|
|
1350
|
-
},
|
|
1351
|
-
{
|
|
1352
|
-
description: "List todo items",
|
|
1353
|
-
name: "todo list",
|
|
1354
|
-
usage: "ringi todo list [--review <id>] [--status <pending|done|all>] [--limit <n>] [--offset <n>]"
|
|
1355
|
-
},
|
|
1356
|
-
{
|
|
1357
|
-
description: "Add a todo item",
|
|
1358
|
-
name: "todo add",
|
|
1359
|
-
usage: "ringi todo add --text <text> [--review <id>] [--position <n>]"
|
|
1360
|
-
},
|
|
1361
|
-
{
|
|
1362
|
-
description: "Mark a todo as done",
|
|
1363
|
-
name: "todo done",
|
|
1364
|
-
usage: "ringi todo done <id>"
|
|
1365
|
-
},
|
|
1366
|
-
{
|
|
1367
|
-
description: "Reopen a completed todo",
|
|
1368
|
-
name: "todo undone",
|
|
1369
|
-
usage: "ringi todo undone <id>"
|
|
1370
|
-
},
|
|
1371
|
-
{
|
|
1372
|
-
description: "Move a todo to a position",
|
|
1373
|
-
name: "todo move",
|
|
1374
|
-
usage: "ringi todo move <id> --position <n>"
|
|
1375
|
-
},
|
|
1376
|
-
{
|
|
1377
|
-
description: "Remove a todo",
|
|
1378
|
-
name: "todo remove",
|
|
1379
|
-
usage: "ringi todo remove <id> [--yes]"
|
|
1380
|
-
},
|
|
1381
|
-
{
|
|
1382
|
-
description: "Clear completed todos",
|
|
1383
|
-
name: "todo clear",
|
|
1384
|
-
usage: "ringi todo clear [--review <id>] [--done-only] [--all] [--yes]"
|
|
1385
|
-
},
|
|
1386
|
-
{
|
|
1387
|
-
description: "Start the local Ringi server",
|
|
1388
|
-
name: "serve",
|
|
1389
|
-
usage: "ringi serve [--host <host>] [--port <port>] [--https] [--auth] [--no-open]"
|
|
1390
|
-
},
|
|
1391
|
-
{
|
|
1392
|
-
description: "Start the MCP stdio server",
|
|
1393
|
-
name: "mcp",
|
|
1394
|
-
usage: "ringi mcp [--readonly] [--log-level <level>]"
|
|
1395
|
-
},
|
|
1396
|
-
{
|
|
1397
|
-
description: "Run local diagnostics",
|
|
1398
|
-
name: "doctor",
|
|
1399
|
-
usage: "ringi doctor"
|
|
1400
|
-
},
|
|
1401
|
-
{
|
|
1402
|
-
description: "Tail server events",
|
|
1403
|
-
name: "events",
|
|
1404
|
-
usage: "ringi events [--type <reviews|comments|todos|files>]"
|
|
1405
|
-
},
|
|
1406
|
-
{
|
|
1407
|
-
description: "Run database migrations",
|
|
1408
|
-
name: "data migrate",
|
|
1409
|
-
usage: "ringi data migrate"
|
|
1410
|
-
},
|
|
1411
|
-
{
|
|
1412
|
-
description: "Reset local data",
|
|
1413
|
-
name: "data reset",
|
|
1414
|
-
usage: "ringi data reset [--yes] [--keep-exports]"
|
|
1415
|
-
}
|
|
1416
|
-
],
|
|
1417
|
-
description: "ringi \u2014 local-first code review CLI",
|
|
1418
|
-
version: CLI_VERSION
|
|
1419
|
-
};
|
|
1420
|
-
var ROOT_NEXT_ACTIONS = [
|
|
1421
|
-
{
|
|
1422
|
-
command: "ringi review list [--status <status>] [--source <type>]",
|
|
1423
|
-
description: "List review sessions",
|
|
1424
|
-
params: {
|
|
1425
|
-
source: { enum: ["staged", "branch", "commits"] },
|
|
1426
|
-
status: { enum: ["in_progress", "approved", "changes_requested"] }
|
|
1427
|
-
}
|
|
1428
|
-
},
|
|
1429
|
-
{
|
|
1430
|
-
command: "ringi source list",
|
|
1431
|
-
description: "List repository sources"
|
|
1432
|
-
},
|
|
1433
|
-
{
|
|
1434
|
-
command: "ringi review create [--source <source>]",
|
|
1435
|
-
description: "Create a new review session",
|
|
1436
|
-
params: {
|
|
1437
|
-
source: { default: "staged", enum: ["staged", "branch", "commits"] }
|
|
1438
|
-
}
|
|
1439
|
-
},
|
|
1440
|
-
{
|
|
1441
|
-
command: "ringi todo list [--status <status>]",
|
|
1442
|
-
description: "List todos",
|
|
1443
|
-
params: {
|
|
1444
|
-
status: { default: "pending", enum: ["pending", "done", "all"] }
|
|
1445
|
-
}
|
|
1446
|
-
},
|
|
1447
|
-
{
|
|
1448
|
-
command: "ringi review status",
|
|
1449
|
-
description: "Show repository and review status"
|
|
1450
|
-
}
|
|
1451
|
-
];
|
|
1452
|
-
var ROOT_HELP = `ringi \u2014 local-first review CLI
|
|
1453
|
-
|
|
1454
|
-
Usage:
|
|
1455
|
-
ringi [global options] <command>
|
|
1456
|
-
|
|
1457
|
-
Global options:
|
|
1458
|
-
--json Emit structured JSON envelope to stdout
|
|
1459
|
-
--repo <path> Use a specific Git repository root
|
|
1460
|
-
--db-path <path> Override the SQLite database path
|
|
1461
|
-
--quiet Suppress human-readable success output
|
|
1462
|
-
--verbose Include stack traces on failures
|
|
1463
|
-
--no-color Disable ANSI color output
|
|
1464
|
-
--help Show help
|
|
1465
|
-
--version Show version
|
|
1466
|
-
|
|
1467
|
-
Commands:
|
|
1468
|
-
review list [--status <status>] [--source <type>] [--limit <n>] [--page <n>]
|
|
1469
|
-
review show <id|last> [--comments] [--todos]
|
|
1470
|
-
review create [--source <staged|branch|commits>] [--branch <name>] [--commits <range>]
|
|
1471
|
-
review export <id|last> [--output <path>] [--stdout]
|
|
1472
|
-
review resolve <id|last> [--all-comments] [--yes]
|
|
1473
|
-
review status [--review <id|last>] [--source <type>]
|
|
1474
|
-
source list
|
|
1475
|
-
source diff <staged|branch|commits> [--branch <name>] [--commits <range>] [--stat]
|
|
1476
|
-
todo list [--review <id>] [--status <pending|done|all>] [--limit <n>] [--offset <n>]
|
|
1477
|
-
todo add --text <text> [--review <id>]
|
|
1478
|
-
todo done <id>
|
|
1479
|
-
todo undone <id>
|
|
1480
|
-
todo move <id> --position <n>
|
|
1481
|
-
todo remove <id> [--yes]
|
|
1482
|
-
todo clear [--review <id>] [--done-only] [--all] [--yes]
|
|
1483
|
-
export <id|last> [--output <path>] [--stdout]
|
|
1484
|
-
`;
|
|
1485
|
-
var HELP_TOPICS = {
|
|
1486
|
-
data: `ringi data
|
|
1487
|
-
|
|
1488
|
-
Usage:
|
|
1489
|
-
ringi data migrate
|
|
1490
|
-
ringi data reset [--yes] [--keep-exports]
|
|
1491
|
-
`,
|
|
1492
|
-
review: `ringi review
|
|
1493
|
-
|
|
1494
|
-
Usage:
|
|
1495
|
-
ringi review list [--status <status>] [--source <type>] [--limit <n>] [--page <n>]
|
|
1496
|
-
ringi review show <id|last> [--comments] [--todos]
|
|
1497
|
-
ringi review create [--source <staged|branch|commits>] [--branch <name>] [--commits <range>]
|
|
1498
|
-
ringi review export <id|last> [--output <path>] [--stdout]
|
|
1499
|
-
ringi review resolve <id|last> [--all-comments] [--yes]
|
|
1500
|
-
ringi review status [--review <id|last>] [--source <type>]
|
|
1501
|
-
`,
|
|
1502
|
-
source: `ringi source
|
|
1503
|
-
|
|
1504
|
-
Usage:
|
|
1505
|
-
ringi source list
|
|
1506
|
-
ringi source diff <staged|branch|commits> [--branch <name>] [--commits <range>] [--stat]
|
|
1507
|
-
`,
|
|
1508
|
-
todo: `ringi todo
|
|
1509
|
-
|
|
1510
|
-
Usage:
|
|
1511
|
-
ringi todo list [--review <id>] [--status <pending|done|all>] [--limit <n>] [--offset <n>]
|
|
1512
|
-
ringi todo add --text <text> [--review <id>] [--position <n>]
|
|
1513
|
-
ringi todo done <id>
|
|
1514
|
-
ringi todo undone <id>
|
|
1515
|
-
ringi todo move <id> --position <n>
|
|
1516
|
-
ringi todo remove <id> [--yes]
|
|
1517
|
-
ringi todo clear [--review <id>] [--done-only] [--all] [--yes]
|
|
1518
|
-
`
|
|
1519
|
-
};
|
|
1520
|
-
var renderHelp = (command) => {
|
|
1521
|
-
if (command.kind !== "help") {
|
|
1522
|
-
return ROOT_HELP;
|
|
1523
|
-
}
|
|
1524
|
-
const [topic] = command.topic;
|
|
1525
|
-
return (topic && HELP_TOPICS[topic]) ?? ROOT_HELP;
|
|
1526
|
-
};
|
|
1527
|
-
var writeJson = (payload) => {
|
|
1528
|
-
process.stdout.write(`${JSON.stringify(payload, null, 2)}
|
|
1529
|
-
`);
|
|
1530
|
-
};
|
|
1531
|
-
var writeHuman = (text) => {
|
|
1532
|
-
if (text && text.length > 0) {
|
|
1533
|
-
process.stdout.write(`${text}
|
|
1534
|
-
`);
|
|
1535
|
-
}
|
|
1536
|
-
};
|
|
1537
|
-
var EXIT_CODE_META = {
|
|
1538
|
-
[ExitCode.AuthFailure]: {
|
|
1539
|
-
category: "auth",
|
|
1540
|
-
code: "AUTH_FAILURE",
|
|
1541
|
-
retryable: false
|
|
1542
|
-
},
|
|
1543
|
-
[ExitCode.ResourceNotFound]: {
|
|
1544
|
-
category: "not_found",
|
|
1545
|
-
code: "RESOURCE_NOT_FOUND",
|
|
1546
|
-
retryable: false
|
|
1547
|
-
},
|
|
1548
|
-
[ExitCode.RuntimeFailure]: {
|
|
1549
|
-
category: "server",
|
|
1550
|
-
code: "RUNTIME_FAILURE",
|
|
1551
|
-
retryable: true
|
|
1552
|
-
},
|
|
1553
|
-
[ExitCode.StateUnavailable]: {
|
|
1554
|
-
category: "config",
|
|
1555
|
-
code: "STATE_UNAVAILABLE",
|
|
1556
|
-
retryable: false
|
|
1557
|
-
},
|
|
1558
|
-
[ExitCode.UsageError]: {
|
|
1559
|
-
category: "validation",
|
|
1560
|
-
code: "USAGE_ERROR",
|
|
1561
|
-
retryable: false
|
|
1562
|
-
}
|
|
1563
|
-
};
|
|
1564
|
-
var mapFailure = (error) => {
|
|
1565
|
-
if (error instanceof CliFailure) {
|
|
1566
|
-
const meta = EXIT_CODE_META[error.exitCode] ?? {
|
|
1567
|
-
category: "server",
|
|
1568
|
-
code: "UNKNOWN",
|
|
1569
|
-
retryable: false
|
|
1570
|
-
};
|
|
1571
|
-
return {
|
|
1572
|
-
category: meta.category,
|
|
1573
|
-
code: meta.code,
|
|
1574
|
-
exitCode: error.exitCode,
|
|
1575
|
-
message: error.message,
|
|
1576
|
-
retryable: meta.retryable,
|
|
1577
|
-
verbose: error.details
|
|
1578
|
-
};
|
|
1579
|
-
}
|
|
1580
|
-
if (error instanceof ReviewNotFound || error instanceof TodoNotFound) {
|
|
1581
|
-
return {
|
|
1582
|
-
category: "not_found",
|
|
1583
|
-
code: "RESOURCE_NOT_FOUND",
|
|
1584
|
-
exitCode: ExitCode.ResourceNotFound,
|
|
1585
|
-
message: error.message,
|
|
1586
|
-
retryable: false,
|
|
1587
|
-
verbose: error.stack
|
|
1588
|
-
};
|
|
1589
|
-
}
|
|
1590
|
-
if (error instanceof Error) {
|
|
1591
|
-
return {
|
|
1592
|
-
category: "server",
|
|
1593
|
-
code: "RUNTIME_FAILURE",
|
|
1594
|
-
exitCode: ExitCode.RuntimeFailure,
|
|
1595
|
-
message: error.message,
|
|
1596
|
-
retryable: true,
|
|
1597
|
-
verbose: error.stack
|
|
1598
|
-
};
|
|
1599
|
-
}
|
|
1600
|
-
return {
|
|
1601
|
-
category: "server",
|
|
1602
|
-
code: "UNKNOWN_FAILURE",
|
|
1603
|
-
exitCode: ExitCode.RuntimeFailure,
|
|
1604
|
-
message: "Unknown CLI failure.",
|
|
1605
|
-
retryable: false
|
|
1606
|
-
};
|
|
1607
|
-
};
|
|
1608
|
-
var FIX_GUIDANCE = {
|
|
1609
|
-
auth: "Check authentication credentials or run 'ringi serve --auth' with valid credentials.",
|
|
1610
|
-
config: "Run 'ringi serve' once to initialize local state, or check --repo and --db-path flags.",
|
|
1611
|
-
conflict: "Resolve the conflict and retry the operation.",
|
|
1612
|
-
connection: "Ensure the Ringi server is running: ringi serve",
|
|
1613
|
-
not_found: "Verify the resource ID. Use 'ringi review list' or 'ringi todo list' to find valid IDs.",
|
|
1614
|
-
server: "Retry the command. If the error persists, check 'ringi serve' logs.",
|
|
1615
|
-
validation: "Check command usage with 'ringi --help'. Verify flag names and values."
|
|
1616
|
-
};
|
|
1617
|
-
var errorNextActions = (commandStr, normalized) => {
|
|
1618
|
-
const actions = [];
|
|
1619
|
-
if (normalized.retryable) {
|
|
1620
|
-
actions.push({
|
|
1621
|
-
command: commandStr,
|
|
1622
|
-
description: "Retry the failed command"
|
|
1623
|
-
});
|
|
1624
|
-
}
|
|
1625
|
-
if (normalized.category === "config" || normalized.category === "connection") {
|
|
1626
|
-
actions.push({
|
|
1627
|
-
command: "ringi serve",
|
|
1628
|
-
description: "Start the local Ringi server"
|
|
1629
|
-
});
|
|
1630
|
-
}
|
|
1631
|
-
if (normalized.category === "not_found") {
|
|
1632
|
-
actions.push(
|
|
1633
|
-
{
|
|
1634
|
-
command: "ringi review list",
|
|
1635
|
-
description: "List available reviews"
|
|
1636
|
-
},
|
|
1637
|
-
{
|
|
1638
|
-
command: "ringi todo list",
|
|
1639
|
-
description: "List available todos"
|
|
1640
|
-
}
|
|
1641
|
-
);
|
|
1642
|
-
}
|
|
1643
|
-
if (normalized.category === "validation") {
|
|
1644
|
-
actions.push({
|
|
1645
|
-
command: `${commandStr.split(" ").slice(0, 3).join(" ")} --help`,
|
|
1646
|
-
description: "Show command usage"
|
|
1647
|
-
});
|
|
1648
|
-
}
|
|
1649
|
-
return actions;
|
|
1650
|
-
};
|
|
1651
|
-
var buildErrorEnvelope = (commandStr, normalized) => {
|
|
1652
|
-
const errorDetail = {
|
|
1653
|
-
category: normalized.category,
|
|
1654
|
-
code: normalized.code,
|
|
1655
|
-
message: normalized.message,
|
|
1656
|
-
retryable: normalized.retryable,
|
|
1657
|
-
type: `ringi://errors/${normalized.code}`
|
|
1658
|
-
};
|
|
1659
|
-
return failure(
|
|
1660
|
-
commandStr,
|
|
1661
|
-
errorDetail,
|
|
1662
|
-
normalized.verbose ?? FIX_GUIDANCE[normalized.category],
|
|
1663
|
-
errorNextActions(commandStr, normalized)
|
|
1664
|
-
);
|
|
1665
|
-
};
|
|
1666
|
-
var installSignalHandlers = (dispose) => {
|
|
1667
|
-
const shutdown = async () => {
|
|
1668
|
-
await dispose();
|
|
1669
|
-
process.exit(ExitCode.RuntimeFailure);
|
|
1670
|
-
};
|
|
1671
|
-
process.once("SIGINT", shutdown);
|
|
1672
|
-
process.once("SIGTERM", shutdown);
|
|
1673
|
-
return () => {
|
|
1674
|
-
process.off("SIGINT", shutdown);
|
|
1675
|
-
process.off("SIGTERM", shutdown);
|
|
1676
|
-
};
|
|
1677
|
-
};
|
|
1678
|
-
var resolveServerEntry = () => {
|
|
1679
|
-
const candidates = [resolve(process.cwd(), ".output", "server", "index.mjs")];
|
|
1680
|
-
if (import.meta.dirname) {
|
|
1681
|
-
candidates.push(
|
|
1682
|
-
resolve(import.meta.dirname, "..", ".output", "server", "index.mjs")
|
|
1683
|
-
);
|
|
1684
|
-
}
|
|
1685
|
-
return candidates.find((candidate) => existsSync(candidate));
|
|
1686
|
-
};
|
|
1687
|
-
var runServe = (command) => {
|
|
1688
|
-
const serverEntry = resolveServerEntry();
|
|
1689
|
-
if (!serverEntry) {
|
|
1690
|
-
process.stderr.write(
|
|
1691
|
-
"No built server found. Run 'pnpm build' first, then retry 'ringi serve'.\n"
|
|
1692
|
-
);
|
|
1693
|
-
process.exit(ExitCode.RuntimeFailure);
|
|
1694
|
-
}
|
|
1695
|
-
const env = {
|
|
1696
|
-
...process.env,
|
|
1697
|
-
NITRO_HOST: command.host,
|
|
1698
|
-
NITRO_PORT: String(command.port)
|
|
1699
|
-
};
|
|
1700
|
-
if (command.https && command.cert && command.key) {
|
|
1701
|
-
env.NITRO_SSL_CERT = command.cert;
|
|
1702
|
-
env.NITRO_SSL_KEY = command.key;
|
|
1703
|
-
}
|
|
1704
|
-
const protocol = command.https ? "https" : "http";
|
|
1705
|
-
const url = `${protocol}://${command.host === "0.0.0.0" ? "localhost" : command.host}:${command.port}`;
|
|
1706
|
-
process.stderr.write(`ringi server starting on ${url}
|
|
1707
|
-
`);
|
|
1708
|
-
const child = fork(serverEntry, [], {
|
|
1709
|
-
env,
|
|
1710
|
-
// Clear execArgv so tsx/ts-node loaders from the parent don't interfere
|
|
1711
|
-
// with the built Nitro server (plain ESM).
|
|
1712
|
-
execArgv: [],
|
|
1713
|
-
stdio: "inherit"
|
|
1714
|
-
});
|
|
1715
|
-
if (!command.noOpen) {
|
|
1716
|
-
setTimeout(() => {
|
|
1717
|
-
let openCmd = "xdg-open";
|
|
1718
|
-
if (process.platform === "darwin") {
|
|
1719
|
-
openCmd = "open";
|
|
1720
|
-
} else if (process.platform === "win32") {
|
|
1721
|
-
openCmd = "start";
|
|
1722
|
-
}
|
|
1723
|
-
exec(`${openCmd} ${url}`, () => {
|
|
1724
|
-
});
|
|
1725
|
-
}, 1500);
|
|
1726
|
-
}
|
|
1727
|
-
const shutdown = () => {
|
|
1728
|
-
child.kill("SIGTERM");
|
|
1729
|
-
};
|
|
1730
|
-
process.once("SIGINT", shutdown);
|
|
1731
|
-
process.once("SIGTERM", shutdown);
|
|
1732
|
-
child.on("exit", (code) => {
|
|
1733
|
-
process.off("SIGINT", shutdown);
|
|
1734
|
-
process.off("SIGTERM", shutdown);
|
|
1735
|
-
process.exit(code ?? ExitCode.Success);
|
|
1736
|
-
});
|
|
1737
|
-
};
|
|
1738
|
-
var failAndExit = (opts) => {
|
|
1739
|
-
const normalized = mapFailure(opts.error);
|
|
1740
|
-
if (opts.json) {
|
|
1741
|
-
writeJson(buildErrorEnvelope(opts.cmdStr, normalized));
|
|
1742
|
-
}
|
|
1743
|
-
process.stderr.write(`${normalized.message}
|
|
1744
|
-
`);
|
|
1745
|
-
if (opts.verbose && normalized.verbose) {
|
|
1746
|
-
process.stderr.write(`${normalized.verbose}
|
|
1747
|
-
`);
|
|
1748
|
-
}
|
|
1749
|
-
return process.exit(normalized.exitCode);
|
|
1750
|
-
};
|
|
1751
|
-
var main = async () => {
|
|
1752
|
-
const argv = process.argv.slice(2);
|
|
1753
|
-
const parseResult = parseCliArgs(argv);
|
|
1754
|
-
if (Either.isLeft(parseResult)) {
|
|
1755
|
-
return failAndExit({
|
|
1756
|
-
cmdStr: "ringi",
|
|
1757
|
-
error: parseResult.left,
|
|
1758
|
-
json: argv.includes("--json"),
|
|
1759
|
-
verbose: false
|
|
1760
|
-
});
|
|
1761
|
-
}
|
|
1762
|
-
const { command, options } = parseResult.right;
|
|
1763
|
-
if (command.kind === "help") {
|
|
1764
|
-
if (options.json) {
|
|
1765
|
-
writeJson(success("ringi", COMMAND_TREE, ROOT_NEXT_ACTIONS));
|
|
1766
|
-
} else {
|
|
1767
|
-
writeHuman(renderHelp(command));
|
|
1768
|
-
}
|
|
1769
|
-
process.exit(ExitCode.Success);
|
|
1770
|
-
}
|
|
1771
|
-
if (command.kind === "version") {
|
|
1772
|
-
if (options.json) {
|
|
1773
|
-
writeJson(success("ringi --version", { version: CLI_VERSION }));
|
|
1774
|
-
} else {
|
|
1775
|
-
writeHuman(CLI_VERSION);
|
|
1776
|
-
}
|
|
1777
|
-
process.exit(ExitCode.Success);
|
|
1778
|
-
}
|
|
1779
|
-
if (command.kind === "serve") {
|
|
1780
|
-
runServe(command);
|
|
1781
|
-
return;
|
|
1782
|
-
}
|
|
1783
|
-
const runtimeResources = createCliRuntimeResources(command, {
|
|
1784
|
-
color: options.color,
|
|
1785
|
-
dbPath: options.dbPath,
|
|
1786
|
-
quiet: options.quiet,
|
|
1787
|
-
repo: options.repo,
|
|
1788
|
-
verbose: options.verbose
|
|
1789
|
-
});
|
|
1790
|
-
if (runtimeResources === null) {
|
|
1791
|
-
process.exit(ExitCode.Success);
|
|
1792
|
-
}
|
|
1793
|
-
const cmdStr = commandLabel(command);
|
|
1794
|
-
if (runtimeResources instanceof CliFailure) {
|
|
1795
|
-
return failAndExit({
|
|
1796
|
-
cmdStr,
|
|
1797
|
-
error: runtimeResources,
|
|
1798
|
-
json: options.json,
|
|
1799
|
-
verbose: options.verbose
|
|
1800
|
-
});
|
|
1801
|
-
}
|
|
1802
|
-
const removeSignalHandlers = installSignalHandlers(
|
|
1803
|
-
() => runtimeResources.runtime.dispose()
|
|
1804
|
-
);
|
|
1805
|
-
try {
|
|
1806
|
-
const output = await runtimeResources.runtime.runPromise(
|
|
1807
|
-
runCommand(command)
|
|
1808
|
-
);
|
|
1809
|
-
if (options.json) {
|
|
1810
|
-
writeJson(success(cmdStr, output.data, output.nextActions ?? []));
|
|
1811
|
-
} else if (!options.quiet) {
|
|
1812
|
-
writeHuman(output.human);
|
|
1813
|
-
}
|
|
1814
|
-
await runtimeResources.runtime.dispose();
|
|
1815
|
-
removeSignalHandlers();
|
|
1816
|
-
process.exit(ExitCode.Success);
|
|
1817
|
-
} catch (error) {
|
|
1818
|
-
await runtimeResources.runtime.dispose();
|
|
1819
|
-
removeSignalHandlers();
|
|
1820
|
-
failAndExit({
|
|
1821
|
-
cmdStr,
|
|
1822
|
-
error,
|
|
1823
|
-
json: options.json,
|
|
1824
|
-
verbose: options.verbose
|
|
1825
|
-
});
|
|
1826
|
-
}
|
|
1827
|
-
};
|
|
1828
|
-
try {
|
|
1829
|
-
await main();
|
|
1830
|
-
} catch (error) {
|
|
1831
|
-
failAndExit({
|
|
1832
|
-
cmdStr: "ringi",
|
|
1833
|
-
error,
|
|
1834
|
-
json: process.argv.slice(2).includes("--json"),
|
|
1835
|
-
verbose: false
|
|
1836
|
-
});
|
|
1837
|
-
}
|
|
1838
|
-
//# sourceMappingURL=cli.js.map
|
|
1839
|
-
//# sourceMappingURL=cli.js.map
|