@happy-nut/monacori 0.1.2 → 0.1.5
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 +25 -147
- package/dist/app-main.js +205 -14
- package/dist/assets.d.ts +2 -0
- package/dist/assets.js +21 -0
- package/dist/build.d.ts +1 -0
- package/dist/build.js +30 -5
- package/dist/commands.js +12 -326
- package/dist/diff.js +41 -0
- package/dist/i18n.d.ts +1 -0
- package/dist/i18n.js +266 -0
- package/dist/preload.cjs +70 -0
- package/dist/render.d.ts +12 -0
- package/dist/render.js +102 -22
- package/dist/server.js +6 -0
- package/dist/types.d.ts +14 -0
- package/dist/viewer.client.js +950 -125
- package/dist/viewer.css +248 -44
- package/package.json +6 -2
- package/scripts/patch-electron-name.mjs +8 -0
package/dist/commands.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { basename, dirname, join
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
3
|
import { spawn, spawnSync } from "node:child_process";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import { AGENT_SNIPPET_FILE, CONFIG_FILE, DECISIONS_FILE, FLOW_DIR, GITIGNORE_FILE, STATE_FILE } from "./constants.js";
|
|
7
|
-
import {
|
|
8
|
-
import { git
|
|
9
|
-
import { createDiffReview, serveDiffWatch } from "./server.js";
|
|
7
|
+
import { parsePositiveInteger, readOption } from "./util.js";
|
|
8
|
+
import { git } from "./git.js";
|
|
10
9
|
const nodeRequire = createRequire(import.meta.url);
|
|
11
10
|
export function main() {
|
|
12
11
|
const rawArgs = process.argv.slice(2);
|
|
@@ -27,16 +26,6 @@ export function main() {
|
|
|
27
26
|
case "install":
|
|
28
27
|
installFlow(args);
|
|
29
28
|
break;
|
|
30
|
-
case "check":
|
|
31
|
-
case "go":
|
|
32
|
-
runCheck(args);
|
|
33
|
-
break;
|
|
34
|
-
case "verify":
|
|
35
|
-
runVerification(args);
|
|
36
|
-
break;
|
|
37
|
-
case "diff":
|
|
38
|
-
renderDiffReview(args);
|
|
39
|
-
break;
|
|
40
29
|
case "app":
|
|
41
30
|
case "review":
|
|
42
31
|
launchReviewApp(args);
|
|
@@ -44,12 +33,6 @@ export function main() {
|
|
|
44
33
|
case "open":
|
|
45
34
|
openCurrentRepository(args);
|
|
46
35
|
break;
|
|
47
|
-
case "status":
|
|
48
|
-
printStatus();
|
|
49
|
-
break;
|
|
50
|
-
case "report":
|
|
51
|
-
recordReport(args);
|
|
52
|
-
break;
|
|
53
36
|
case "--help":
|
|
54
37
|
case "-h":
|
|
55
38
|
case "help":
|
|
@@ -94,7 +77,7 @@ function initFlow(args) {
|
|
|
94
77
|
if (ignored) {
|
|
95
78
|
console.log(`Updated ${GITIGNORE_FILE} to ignore ${FLOW_DIR}/ validation artifacts.`);
|
|
96
79
|
}
|
|
97
|
-
console.log("Next: run `
|
|
80
|
+
console.log("Next: run `mo` to open the diff review app.");
|
|
98
81
|
}
|
|
99
82
|
}
|
|
100
83
|
function installFlow(args) {
|
|
@@ -115,117 +98,6 @@ function installFlow(args) {
|
|
|
115
98
|
console.log(`Next: add ${FLOW_DIR}/${AGENT_SNIPPET_FILE} to your agent instructions if desired.`);
|
|
116
99
|
}
|
|
117
100
|
}
|
|
118
|
-
function runCheck(args) {
|
|
119
|
-
if (args.includes("--help") || args.includes("-h")) {
|
|
120
|
-
printCheckHelp();
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
ensureWritableFlowState();
|
|
124
|
-
const config = loadConfig();
|
|
125
|
-
const separator = args.indexOf("--");
|
|
126
|
-
const commandArgs = separator >= 0 ? args.slice(separator + 1) : [];
|
|
127
|
-
const optionArgs = separator >= 0 ? args.slice(0, separator) : args;
|
|
128
|
-
const noVerify = optionArgs.includes("--no-verify");
|
|
129
|
-
const noDiff = optionArgs.includes("--no-diff");
|
|
130
|
-
const openInBrowser = optionArgs.includes("--open");
|
|
131
|
-
const includeUntracked = optionArgs.includes("--include-untracked") || config.diff.includeUntracked;
|
|
132
|
-
const staged = optionArgs.includes("--staged");
|
|
133
|
-
const base = readOption(optionArgs, "--base");
|
|
134
|
-
const contextValue = readOption(optionArgs, "--context");
|
|
135
|
-
const context = contextValue ? parsePositiveInteger(contextValue, "--context") : config.diff.context;
|
|
136
|
-
const verification = noVerify
|
|
137
|
-
? { commands: [], failed: false, skipped: true }
|
|
138
|
-
: executeVerification(commandArgs.join(" "));
|
|
139
|
-
let review;
|
|
140
|
-
if (!noDiff) {
|
|
141
|
-
review = createDiffReview({
|
|
142
|
-
base,
|
|
143
|
-
staged,
|
|
144
|
-
includeUntracked,
|
|
145
|
-
context,
|
|
146
|
-
output: join(process.cwd(), FLOW_DIR, "diffs", `${timestampForFile()}-check.html`),
|
|
147
|
-
title: "monacori validation diff",
|
|
148
|
-
});
|
|
149
|
-
if (openInBrowser) {
|
|
150
|
-
spawnSync("open", [review.path], { stdio: "ignore" });
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
const reportPath = writeCheckReport({ verification, review });
|
|
154
|
-
console.log("# monacori check");
|
|
155
|
-
console.log(`Verification: ${verification.skipped ? "skipped" : verification.failed ? "failed" : "passed"}`);
|
|
156
|
-
if (verification.logPath) {
|
|
157
|
-
console.log(`Log: ${relative(process.cwd(), verification.logPath)}`);
|
|
158
|
-
}
|
|
159
|
-
if (review) {
|
|
160
|
-
console.log(`Diff review: ${relative(process.cwd(), review.path)}`);
|
|
161
|
-
console.log(`Files: ${review.files}`);
|
|
162
|
-
console.log(`Hunks: ${review.hunks}`);
|
|
163
|
-
}
|
|
164
|
-
console.log(`Report: ${relative(process.cwd(), reportPath)}`);
|
|
165
|
-
if (verification.failed) {
|
|
166
|
-
process.exit(1);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
function runVerification(args) {
|
|
170
|
-
const separator = args.indexOf("--");
|
|
171
|
-
const explicitCommand = separator >= 0 ? args.slice(separator + 1).join(" ") : "";
|
|
172
|
-
const result = executeVerification(explicitCommand, { requireCommands: true });
|
|
173
|
-
if (result.logPath) {
|
|
174
|
-
console.log(`Verification log: ${relative(process.cwd(), result.logPath)}`);
|
|
175
|
-
}
|
|
176
|
-
if (result.failed) {
|
|
177
|
-
console.error("Verification failed.");
|
|
178
|
-
process.exit(1);
|
|
179
|
-
}
|
|
180
|
-
console.log("Verification passed.");
|
|
181
|
-
}
|
|
182
|
-
function renderDiffReview(args) {
|
|
183
|
-
if (args.includes("--help") || args.includes("-h")) {
|
|
184
|
-
printDiffHelp();
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
ensureWritableFlowState();
|
|
188
|
-
const config = loadConfig();
|
|
189
|
-
const contextValue = readOption(args, "--context");
|
|
190
|
-
const context = contextValue ? parsePositiveInteger(contextValue, "--context") : config.diff.context;
|
|
191
|
-
const base = readOption(args, "--base");
|
|
192
|
-
const staged = args.includes("--staged");
|
|
193
|
-
const includeUntracked = args.includes("--include-untracked") || config.diff.includeUntracked;
|
|
194
|
-
const openInBrowser = args.includes("--open");
|
|
195
|
-
const watch = args.includes("--watch");
|
|
196
|
-
const ignoreWhitespace = args.includes("--ignore-whitespace");
|
|
197
|
-
if (watch) {
|
|
198
|
-
serveDiffWatch({
|
|
199
|
-
base,
|
|
200
|
-
staged,
|
|
201
|
-
includeUntracked,
|
|
202
|
-
context,
|
|
203
|
-
openInBrowser,
|
|
204
|
-
port: readOption(args, "--port"),
|
|
205
|
-
ignoreWhitespace,
|
|
206
|
-
});
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
const output = readOption(args, "--output") ??
|
|
210
|
-
join(process.cwd(), FLOW_DIR, "diffs", `${timestampForFile()}-review.html`);
|
|
211
|
-
const result = createDiffReview({
|
|
212
|
-
base,
|
|
213
|
-
staged,
|
|
214
|
-
includeUntracked,
|
|
215
|
-
context,
|
|
216
|
-
output,
|
|
217
|
-
title: "monacori diff review",
|
|
218
|
-
ignoreWhitespace,
|
|
219
|
-
});
|
|
220
|
-
if (openInBrowser) {
|
|
221
|
-
spawnSync("open", [result.path], { stdio: "ignore" });
|
|
222
|
-
}
|
|
223
|
-
console.log(`Diff review: ${relative(process.cwd(), result.path)}`);
|
|
224
|
-
console.log(`URL: ${result.url}`);
|
|
225
|
-
console.log(`Files: ${result.files}`);
|
|
226
|
-
console.log(`Hunks: ${result.hunks}`);
|
|
227
|
-
console.log("Keys: F7 next hunk, Shift+F7 previous hunk, Shift Shift search files, Cmd/Ctrl+E recent files, Cmd/Ctrl+Down jump to symbol.");
|
|
228
|
-
}
|
|
229
101
|
function launchReviewApp(args) {
|
|
230
102
|
if (args.includes("--help") || args.includes("-h")) {
|
|
231
103
|
printAppHelp();
|
|
@@ -290,138 +162,6 @@ function resolveElectronBinary() {
|
|
|
290
162
|
function appMainPath() {
|
|
291
163
|
return join(dirname(fileURLToPath(import.meta.url)), "app-main.js");
|
|
292
164
|
}
|
|
293
|
-
function printStatus() {
|
|
294
|
-
ensureInitialized();
|
|
295
|
-
const config = loadConfig();
|
|
296
|
-
const git = readGitSnapshot(process.cwd());
|
|
297
|
-
const reports = listRecentFiles(join(process.cwd(), FLOW_DIR, "reports"), 5);
|
|
298
|
-
const logs = listRecentFiles(join(process.cwd(), FLOW_DIR, "logs"), 5);
|
|
299
|
-
console.log(`# ${config.projectName} validation status`);
|
|
300
|
-
console.log("");
|
|
301
|
-
console.log(`Branch: ${git.branch || "(unknown)"}`);
|
|
302
|
-
console.log("");
|
|
303
|
-
console.log("## Git status");
|
|
304
|
-
console.log(git.status || "clean");
|
|
305
|
-
console.log("");
|
|
306
|
-
console.log("## Diff stat");
|
|
307
|
-
console.log(git.diffStat || "no diff");
|
|
308
|
-
console.log("");
|
|
309
|
-
console.log("## Verification commands");
|
|
310
|
-
const commands = getVerificationCommands(config);
|
|
311
|
-
if (commands.length === 0) {
|
|
312
|
-
console.log("none configured");
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
for (const command of commands) {
|
|
316
|
-
console.log(`- ${command}`);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
console.log("");
|
|
320
|
-
console.log("## Recent reports");
|
|
321
|
-
console.log(reports.length === 0 ? "none" : reports.map((path) => `- ${relative(process.cwd(), path)}`).join("\n"));
|
|
322
|
-
console.log("");
|
|
323
|
-
console.log("## Recent logs");
|
|
324
|
-
console.log(logs.length === 0 ? "none" : logs.map((path) => `- ${relative(process.cwd(), path)}`).join("\n"));
|
|
325
|
-
}
|
|
326
|
-
function recordReport(args) {
|
|
327
|
-
ensureWritableFlowState();
|
|
328
|
-
const file = readOption(args, "--file");
|
|
329
|
-
const label = readOption(args, "--label") ?? "manual";
|
|
330
|
-
const body = file ? readFileSync(file, "utf8") : readStdin();
|
|
331
|
-
if (body.trim().length === 0) {
|
|
332
|
-
throw new Error("No report content provided. Pass --file or pipe report text on stdin.");
|
|
333
|
-
}
|
|
334
|
-
const timestamp = timestampForFile();
|
|
335
|
-
const reportDir = join(process.cwd(), FLOW_DIR, "reports");
|
|
336
|
-
mkdirSync(reportDir, { recursive: true });
|
|
337
|
-
const reportPath = join(reportDir, `${timestamp}-${sanitizeFilePart(label)}.md`);
|
|
338
|
-
writeFileSync(reportPath, [
|
|
339
|
-
`# Monacori Report: ${label}`,
|
|
340
|
-
"",
|
|
341
|
-
`Recorded: ${new Date().toISOString()}`,
|
|
342
|
-
"",
|
|
343
|
-
body.trim(),
|
|
344
|
-
"",
|
|
345
|
-
].join("\n"));
|
|
346
|
-
appendToState(`\n## Report ${timestamp} (${label})\n\n${summarizeForState(body)}\n`);
|
|
347
|
-
console.log(`Recorded ${relative(process.cwd(), reportPath)}`);
|
|
348
|
-
}
|
|
349
|
-
function executeVerification(explicitCommand = "", options = {}) {
|
|
350
|
-
ensureWritableFlowState();
|
|
351
|
-
const config = loadConfig();
|
|
352
|
-
const commands = explicitCommand.trim() ? [explicitCommand.trim()] : getVerificationCommands(config);
|
|
353
|
-
if (commands.length === 0) {
|
|
354
|
-
if (options.requireCommands) {
|
|
355
|
-
throw new Error(`No verification commands found. Add them to ${FLOW_DIR}/${CONFIG_FILE} or pass \`-- <command>\`.`);
|
|
356
|
-
}
|
|
357
|
-
return { commands: [], failed: false, skipped: true };
|
|
358
|
-
}
|
|
359
|
-
const logPath = join(process.cwd(), FLOW_DIR, "logs", `verify-${timestampForFile()}.log`);
|
|
360
|
-
const chunks = [];
|
|
361
|
-
let failed = false;
|
|
362
|
-
for (const command of commands) {
|
|
363
|
-
chunks.push(`$ ${command}\n`);
|
|
364
|
-
const result = spawnSync(command, {
|
|
365
|
-
cwd: process.cwd(),
|
|
366
|
-
shell: true,
|
|
367
|
-
encoding: "utf8",
|
|
368
|
-
env: process.env,
|
|
369
|
-
maxBuffer: 1024 * 1024 * 100,
|
|
370
|
-
});
|
|
371
|
-
chunks.push(result.stdout ?? "");
|
|
372
|
-
chunks.push(result.stderr ?? "");
|
|
373
|
-
chunks.push(`\nexit: ${result.status ?? 1}\n\n`);
|
|
374
|
-
if ((result.status ?? 1) !== 0) {
|
|
375
|
-
failed = true;
|
|
376
|
-
break;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
writeFileSync(logPath, chunks.join(""));
|
|
380
|
-
return { commands, failed, skipped: false, logPath };
|
|
381
|
-
}
|
|
382
|
-
function writeCheckReport(input) {
|
|
383
|
-
const timestamp = timestampForFile();
|
|
384
|
-
const git = readGitSnapshot(process.cwd());
|
|
385
|
-
const reportDir = join(process.cwd(), FLOW_DIR, "reports");
|
|
386
|
-
mkdirSync(reportDir, { recursive: true });
|
|
387
|
-
const reportPath = join(reportDir, `${timestamp}-check.md`);
|
|
388
|
-
const verificationStatus = input.verification.skipped
|
|
389
|
-
? "skipped"
|
|
390
|
-
: input.verification.failed
|
|
391
|
-
? "failed"
|
|
392
|
-
: "passed";
|
|
393
|
-
const report = [
|
|
394
|
-
"# Monacori Validation Check",
|
|
395
|
-
"",
|
|
396
|
-
`Recorded: ${new Date().toISOString()}`,
|
|
397
|
-
`Branch: ${git.branch || "(unknown)"}`,
|
|
398
|
-
`Verification: ${verificationStatus}`,
|
|
399
|
-
input.verification.logPath ? `Log: ${relative(process.cwd(), input.verification.logPath)}` : "",
|
|
400
|
-
input.review ? `Diff review: ${relative(process.cwd(), input.review.path)}` : "",
|
|
401
|
-
input.review ? `Changed files: ${input.review.files}` : "",
|
|
402
|
-
input.review ? `Changed hunks: ${input.review.hunks}` : "",
|
|
403
|
-
"",
|
|
404
|
-
"## Commands",
|
|
405
|
-
input.verification.commands.length === 0
|
|
406
|
-
? "- none"
|
|
407
|
-
: input.verification.commands.map((command) => `- \`${command}\``).join("\n"),
|
|
408
|
-
"",
|
|
409
|
-
"## Git Status",
|
|
410
|
-
codeBlock(git.status || "clean"),
|
|
411
|
-
"",
|
|
412
|
-
"## Diff Stat",
|
|
413
|
-
codeBlock(git.diffStat || "no diff"),
|
|
414
|
-
"",
|
|
415
|
-
].filter((line) => line !== "").join("\n");
|
|
416
|
-
writeFileSync(reportPath, report);
|
|
417
|
-
appendToState(`\n## Check ${timestamp}\n\n- Verification: ${verificationStatus}\n${input.review ? `- Diff review: ${relative(process.cwd(), input.review.path)}\n` : ""}`);
|
|
418
|
-
return reportPath;
|
|
419
|
-
}
|
|
420
|
-
function appendToState(content) {
|
|
421
|
-
const path = join(process.cwd(), FLOW_DIR, STATE_FILE);
|
|
422
|
-
const current = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
423
|
-
writeFileSync(path, `${current.trimEnd()}\n${content}`);
|
|
424
|
-
}
|
|
425
165
|
function initialState(config) {
|
|
426
166
|
return [
|
|
427
167
|
"# Monacori Validation State",
|
|
@@ -442,27 +182,24 @@ function initialDecisions() {
|
|
|
442
182
|
return [
|
|
443
183
|
"# Monacori Decisions",
|
|
444
184
|
"",
|
|
445
|
-
"Record durable
|
|
185
|
+
"Record durable review decisions here so they do not depend on chat memory.",
|
|
446
186
|
"",
|
|
447
187
|
].join("\n");
|
|
448
188
|
}
|
|
449
189
|
function agentSnippet() {
|
|
450
190
|
return [
|
|
451
191
|
"<!-- MONACORI:START -->",
|
|
452
|
-
"## monacori
|
|
192
|
+
"## monacori Diff Review",
|
|
453
193
|
"",
|
|
454
|
-
"This repository uses monacori to
|
|
194
|
+
"This repository uses monacori to help humans review AI-generated code changes side-by-side.",
|
|
455
195
|
"",
|
|
456
|
-
"
|
|
196
|
+
"After making code changes:",
|
|
457
197
|
"",
|
|
458
|
-
"-
|
|
459
|
-
"- Use `monacori app --include-untracked` while changes are still moving.",
|
|
198
|
+
"- The user can run `mo` to open the diff review app and inspect your changes.",
|
|
460
199
|
"- Inspect changed hunks with F7 / Shift+F7.",
|
|
461
200
|
"- Use Shift Shift in the diff review to search indexed files, including unchanged files.",
|
|
462
201
|
"- In source previews, use Cmd/Ctrl+Down to jump to the declaration-like match under the cursor.",
|
|
463
|
-
"-
|
|
464
|
-
"",
|
|
465
|
-
"Do not claim a change is done without verification evidence or a precise explanation of why verification could not run.",
|
|
202
|
+
"- Inline comments left in the review are bundled into a prompt and sent back to the session.",
|
|
466
203
|
"<!-- MONACORI:END -->",
|
|
467
204
|
"",
|
|
468
205
|
].join("\n");
|
|
@@ -508,9 +245,6 @@ function loadConfig() {
|
|
|
508
245
|
},
|
|
509
246
|
};
|
|
510
247
|
}
|
|
511
|
-
function getVerificationCommands(config) {
|
|
512
|
-
return config.verification.commands.filter((command) => command.trim().length > 0);
|
|
513
|
-
}
|
|
514
248
|
function writeIfMissing(path, content, force) {
|
|
515
249
|
if (!force && existsSync(path)) {
|
|
516
250
|
return;
|
|
@@ -582,26 +316,14 @@ function packageScriptCommand(manager, script) {
|
|
|
582
316
|
function printHelp() {
|
|
583
317
|
console.log(`monacori
|
|
584
318
|
|
|
585
|
-
|
|
319
|
+
Desktop review app for AI-generated code changes.
|
|
586
320
|
|
|
587
321
|
Usage:
|
|
588
322
|
mo
|
|
589
323
|
monacori open [--base HEAD] [--staged] [--tracked-only]
|
|
590
|
-
monacori
|
|
324
|
+
monacori app [--base HEAD] [--staged] [--include-untracked]
|
|
591
325
|
monacori init [--force]
|
|
592
326
|
monacori install [--force] [--apply-agent-docs]
|
|
593
|
-
monacori verify [-- <command>]
|
|
594
|
-
monacori diff [--base HEAD] [--staged] [--include-untracked] [--open] [--watch]
|
|
595
|
-
monacori app [--base HEAD] [--staged] [--include-untracked]
|
|
596
|
-
monacori review [--base HEAD] [--staged] [--include-untracked]
|
|
597
|
-
monacori status
|
|
598
|
-
monacori report [--label manual] [--file report.md]
|
|
599
|
-
|
|
600
|
-
Default loop:
|
|
601
|
-
1. Let an AI agent edit code.
|
|
602
|
-
2. Run: mo
|
|
603
|
-
3. Run: monacori check --include-untracked
|
|
604
|
-
4. Only accept the change when verification evidence is clear.
|
|
605
327
|
|
|
606
328
|
Diff review keys:
|
|
607
329
|
F7 next changed hunk
|
|
@@ -626,42 +348,6 @@ Options:
|
|
|
626
348
|
--tracked-only inspect tracked changes only
|
|
627
349
|
`);
|
|
628
350
|
}
|
|
629
|
-
function printCheckHelp() {
|
|
630
|
-
console.log(`monacori check
|
|
631
|
-
|
|
632
|
-
Run configured verification and create a reviewable diff artifact.
|
|
633
|
-
|
|
634
|
-
Usage:
|
|
635
|
-
monacori check [--include-untracked] [--staged] [--base HEAD] [--context 12] [--open] [--no-verify] [--no-diff] [-- <command>]
|
|
636
|
-
|
|
637
|
-
Examples:
|
|
638
|
-
monacori check --include-untracked --open
|
|
639
|
-
monacori check -- npm test
|
|
640
|
-
monacori check --no-verify --include-untracked
|
|
641
|
-
`);
|
|
642
|
-
}
|
|
643
|
-
function printDiffHelp() {
|
|
644
|
-
console.log(`monacori diff
|
|
645
|
-
|
|
646
|
-
Generate a browser-based side-by-side Git diff review.
|
|
647
|
-
|
|
648
|
-
Usage:
|
|
649
|
-
monacori diff [--base HEAD] [--staged] [--include-untracked] [--context 12] [--output review.html] [--open] [--watch] [--port 0]
|
|
650
|
-
|
|
651
|
-
Keys in the review page:
|
|
652
|
-
F7 next changed hunk
|
|
653
|
-
Shift+F7 previous changed hunk
|
|
654
|
-
] / [ fallback hunk navigation
|
|
655
|
-
Shift Shift search indexed files, including unchanged files
|
|
656
|
-
Cmd/Ctrl+E recent files
|
|
657
|
-
Cmd/Ctrl+Down jump to symbol under cursor
|
|
658
|
-
|
|
659
|
-
The sidebar groups changed files as a folder tree. Use Search to filter paths and indexed file contents.
|
|
660
|
-
The Files tab opens read-only source previews, including unchanged files when they fit the local review budget.
|
|
661
|
-
Viewed marks are tied to file signatures, so a changed file becomes unviewed again after reload.
|
|
662
|
-
Use --watch to serve a live review that reloads when the working tree changes.
|
|
663
|
-
`);
|
|
664
|
-
}
|
|
665
351
|
function printAppHelp() {
|
|
666
352
|
console.log(`monacori app
|
|
667
353
|
|
package/dist/diff.js
CHANGED
|
@@ -174,6 +174,40 @@ function imageMimeForPath(path) {
|
|
|
174
174
|
default: return null;
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
|
+
// Working-tree git status per path (git status --porcelain) for IntelliJ-style sidebar coloring:
|
|
178
|
+
// untracked => "new" (red), index/staged change => "staged" (green, git add'd), unstaged worktree
|
|
179
|
+
// change => "edited" (blue). "git add까지 되었으면" the index column wins, so staged > new/edited.
|
|
180
|
+
function gitStatusMap(cwd) {
|
|
181
|
+
const map = new Map();
|
|
182
|
+
let out = "";
|
|
183
|
+
try {
|
|
184
|
+
out = git(cwd, ["status", "--porcelain"]);
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return map;
|
|
188
|
+
}
|
|
189
|
+
for (const line of out.split(/\r?\n/)) {
|
|
190
|
+
if (line.length < 3)
|
|
191
|
+
continue;
|
|
192
|
+
const x = line[0];
|
|
193
|
+
const y = line[1];
|
|
194
|
+
let path = line.slice(3);
|
|
195
|
+
const arrow = path.indexOf(" -> ");
|
|
196
|
+
if (arrow >= 0)
|
|
197
|
+
path = path.slice(arrow + 4); // rename: color the new path
|
|
198
|
+
if (path.startsWith('"') && path.endsWith('"'))
|
|
199
|
+
path = path.slice(1, -1);
|
|
200
|
+
let kind;
|
|
201
|
+
if (x === "?" && y === "?")
|
|
202
|
+
kind = "new";
|
|
203
|
+
else if (x !== " " && x !== "?")
|
|
204
|
+
kind = "staged";
|
|
205
|
+
else
|
|
206
|
+
kind = "edited";
|
|
207
|
+
map.set(path, kind);
|
|
208
|
+
}
|
|
209
|
+
return map;
|
|
210
|
+
}
|
|
177
211
|
export function collectSourceFiles(diffFiles) {
|
|
178
212
|
const changed = new Set(diffFiles
|
|
179
213
|
.map((file) => file.displayPath)
|
|
@@ -191,6 +225,12 @@ export function collectSourceFiles(diffFiles) {
|
|
|
191
225
|
}
|
|
192
226
|
changedLinesByPath.set(file.displayPath, nums);
|
|
193
227
|
}
|
|
228
|
+
const vcsByPath = gitStatusMap(process.cwd());
|
|
229
|
+
for (const file of diffFiles) {
|
|
230
|
+
const kind = vcsByPath.get(file.displayPath);
|
|
231
|
+
if (kind)
|
|
232
|
+
file.vcs = kind; // color the Changes list from the same status map
|
|
233
|
+
}
|
|
194
234
|
const paths = new Set();
|
|
195
235
|
const gitFiles = git(process.cwd(), ["ls-files", "--cached", "--others", "--exclude-standard"]);
|
|
196
236
|
for (const file of gitFiles.split(/\r?\n/)) {
|
|
@@ -219,6 +259,7 @@ export function collectSourceFiles(diffFiles) {
|
|
|
219
259
|
embedded: false,
|
|
220
260
|
changedLines: changedLinesByPath.get(path) || [],
|
|
221
261
|
signature: "",
|
|
262
|
+
vcs: vcsByPath.get(path),
|
|
222
263
|
};
|
|
223
264
|
if (!existsSync(absolute)) {
|
|
224
265
|
const skippedReason = "file is not present in the working tree";
|
package/dist/i18n.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const MESSAGES: Record<string, Record<string, string>>;
|