@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/dist/commands.js CHANGED
@@ -1,12 +1,11 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { basename, dirname, join, relative } from "node:path";
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 { codeBlock, listRecentFiles, parsePositiveInteger, readOption, readStdin, sanitizeFilePart, summarizeForState, timestampForFile } from "./util.js";
8
- import { git, readGitSnapshot } from "./git.js";
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 `monacori app --include-untracked` to inspect changes, then `monacori check --include-untracked` to record verification.");
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 validation decisions here so future checks do not depend on chat memory.",
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 Validation",
192
+ "## monacori Diff Review",
453
193
  "",
454
- "This repository uses monacori to verify AI-generated code changes.",
194
+ "This repository uses monacori to help humans review AI-generated code changes side-by-side.",
455
195
  "",
456
- "Before claiming completion on a code change:",
196
+ "After making code changes:",
457
197
  "",
458
- "- Run `monacori check --include-untracked` or a more specific `monacori verify -- <command>`.",
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
- "- Report the verification commands, results, and remaining risks.",
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
- Validation control plane for AI-generated code changes.
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 check [--include-untracked] [--open] [--no-verify] [--no-diff] [-- <command>]
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>>;