@happy-nut/monacori 0.1.5 → 0.1.7

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 CHANGED
@@ -1,10 +1,30 @@
1
1
  # monacori
2
2
 
3
- **A local desktop diff-review app for AI-generated code changes.** After an AI edits your repo, run `mo` to open a side-by-side diff — read it, comment on it, and send your comments straight to an AI CLI running in the built-in terminal.
3
+ **A local desktop review workspace for AI-generated code changes.**
4
4
 
5
- ## Why
5
+ Run `mo` after an AI edits your repository. monacori opens a side-by-side diff, lets you attach line-level questions or change requests, and bundles that feedback back into the AI CLI (command-line interface) session running in the built-in terminal.
6
6
 
7
- A chat log or a "done" claim is a poor way to review what an AI changed. monacori puts the change in front of you as a real diff you can read and annotate — then turns your comments into a prompt you hand right back to `claude` or `codex`, without leaving the app or copy-pasting between windows.
7
+ ![monacori reviewing a diff, adding a change request, and targeting the built-in terminal](assets/monacori-demo.gif)
8
+
9
+ ## Why monacori
10
+
11
+ AI coding tools are fast, but their "done" message is not a review. monacori gives the human reviewer a dedicated control surface for the gap between generated code and trusted code:
12
+
13
+ - See every changed, added, and untracked file in an IntelliJ-style review sidebar.
14
+ - Review side-by-side diffs with syntax highlighting, changed-line emphasis, and keyboard navigation.
15
+ - Leave questions or change requests directly on the relevant line.
16
+ - Send all reviewer comments, with file paths and code context, into `claude`, `codex`, or another terminal session without copy-paste.
17
+ - Keep all generated review state local, plain, and inspectable under `.monacori/`.
18
+
19
+ ## Workflow
20
+
21
+ 1. Let an AI coding tool make changes in your repository.
22
+ 2. Run `mo` from that repository.
23
+ 3. Inspect the diff, mark files as viewed, and attach line comments where needed.
24
+ 4. Open the built-in terminal and keep your AI CLI session beside the review.
25
+ 5. Send the merged questions or change requests back to the session as a focused follow-up prompt.
26
+
27
+ The result is a tighter review loop: the AI produces changes, the human reviews the actual diff, and the next prompt is grounded in exact file and line context.
8
28
 
9
29
  ## Install
10
30
 
@@ -12,43 +32,55 @@ A chat log or a "done" claim is a poor way to review what an AI changed. monacor
12
32
  npm install -g @happy-nut/monacori
13
33
  ```
14
34
 
15
- After install, the short command is `mo`. A Homebrew tap (`happy-nut/monacori/monacori`) is also available.
16
-
17
- ## What you get
35
+ The short command is `mo`.
18
36
 
19
- - **Desktop diff review** — side-by-side diff with changed-line highlighting and an IntelliJ-style sidebar that colors files by git status. Reads the repo directly, refreshes on change, no HTTP server.
20
- - **Integrated terminal** — run AI CLIs like `claude` or `codex` right inside the app, split into panes.
21
- - **Comments → session** — annotate any line, then send your comments (bundled with their code context) into a terminal pane as one merged prompt: pick the target pane visually and press Enter.
37
+ ## Quick Start
22
38
 
23
- ## Quick start
24
-
25
- Inside the repository you want to review:
39
+ Inside any Git repository:
26
40
 
27
41
  ```bash
28
42
  mo
29
43
  ```
30
44
 
31
- On first run, `mo` creates `.monacori/`, adds it to `.gitignore`, and includes untracked files so new AI-created files show up immediately.
45
+ On first run, `mo` creates `.monacori/`, adds it to `.gitignore`, and includes untracked files so new AI-created files appear immediately.
46
+
47
+ ## Highlights
48
+
49
+ - **Desktop diff review**: reads the repository directly, refreshes from local Git state, and does not require a web server.
50
+ - **AI handoff comments**: questions and change requests are stored with their file, line, and code context.
51
+ - **Integrated terminal**: keep `claude`, `codex`, or a shell open inside the same window, with split panes when needed.
52
+ - **Source navigation**: jump between changed files, search indexed files, preview source, and move through hunks from the keyboard.
53
+ - **Plain local artifacts**: generated review files and state are Markdown, JSON, and static HTML under `.monacori/`.
32
54
 
33
55
  ## Commands
34
56
 
35
57
  | Command | What it does |
36
58
  | --- | --- |
37
- | `mo` | Open the desktop diff-review app (alias for `monacori open`). |
38
- | `monacori app` | Launch the desktop review app (same as `mo`). |
59
+ | `mo` | Open the desktop diff-review app for the current repository. Alias for `monacori open`. |
60
+ | `monacori open` | Launch the review app, auto-initialize `.monacori/`, and include untracked files by default. |
61
+ | `monacori app` | Launch the same desktop app explicitly. |
39
62
  | `monacori init` | Initialize `.monacori/` in the current directory. |
40
- | `monacori install` | Initialize and write agent instruction snippets. `--apply-agent-docs` patches `AGENTS.md` / `CLAUDE.md`. |
63
+ | `monacori install` | Initialize and write agent instruction snippets. Use `--apply-agent-docs` to patch `AGENTS.md` or `CLAUDE.md`. |
64
+
65
+ Useful review options:
66
+
67
+ ```bash
68
+ mo --staged # review only staged changes
69
+ mo --tracked-only # exclude untracked files
70
+ mo --base main # compare against a specific base
71
+ mo --context 20 # show more context around each hunk
72
+ ```
41
73
 
42
- ## Repository state
74
+ ## Local State
43
75
 
44
- `monacori init` (run automatically by `mo`) creates a git-ignored `.monacori/` directory holding generated diff reviews and local config. Keep it ignored unless your team explicitly wants to commit review state.
76
+ `monacori init` creates a git-ignored `.monacori/` directory for generated diff reviews, local config, comments, logs, and validation notes. Keep it ignored unless your team intentionally wants to version review artifacts.
45
77
 
46
- ## Design principles
78
+ ## Design Principles
47
79
 
48
- - A real diff beats a chat log or a "done" claim.
49
- - Review, comment, and hand-off live in one window no copy-paste loop.
50
- - Generated artifacts are plain static HTML and JSON.
51
- - No required AI agent, terminal multiplexer, editor, or worktree strategy.
80
+ - Real diffs beat chat summaries.
81
+ - Human review should stay close to the code and the running AI session.
82
+ - The core should be local, inspectable, and agent-agnostic.
83
+ - No required terminal multiplexer, editor plugin, hosted service, or worktree strategy.
52
84
 
53
85
  ## License
54
86
 
Binary file
package/dist/app-main.js CHANGED
@@ -71,37 +71,10 @@ ipcMain.handle("monacori:pty-spawn", (_event, size) => {
71
71
  if (mainWindow && !mainWindow.isDestroyed())
72
72
  mainWindow.webContents.send(channel, payload);
73
73
  };
74
- // pty output can arrive as many tiny chunks (thousands/sec under fast output). One IPC per chunk floods
75
- // the renderer, so coalesce: buffer chunks and flush on a short timer, or immediately once the buffer is
76
- // large bounding both IPC traffic and added latency.
77
- let outBuf = "";
78
- let flushTimer;
79
- const flushOut = () => {
80
- flushTimer = undefined;
81
- if (!outBuf)
82
- return;
83
- const data = outBuf;
84
- outBuf = "";
85
- deliver("monacori:pty-data", { id, data });
86
- };
87
- t.onData((data) => {
88
- outBuf += data;
89
- if (outBuf.length >= 64 * 1024) {
90
- if (flushTimer)
91
- clearTimeout(flushTimer);
92
- flushOut();
93
- }
94
- else if (!flushTimer) {
95
- flushTimer = setTimeout(flushOut, 12);
96
- }
97
- });
98
- t.onExit(() => {
99
- if (flushTimer)
100
- clearTimeout(flushTimer);
101
- flushOut(); // deliver any buffered tail before signaling exit
102
- terms.delete(id);
103
- deliver("monacori:pty-exit", { id });
104
- });
74
+ // Relay pty output to the renderer immediately, one IPC per chunk. (A coalescing buffer was tried as an
75
+ // optimization but it broke terminal I/O the shell prompt and echo stopped appearing so it's removed.)
76
+ t.onData((data) => deliver("monacori:pty-data", { id, data }));
77
+ t.onExit(() => { terms.delete(id); deliver("monacori:pty-exit", { id }); });
105
78
  return { ok: true, id };
106
79
  });
107
80
  ipcMain.on("monacori:pty-write", (_event, msg) => { terms.get(msg?.id)?.write(msg.data); });
@@ -455,6 +455,20 @@ function scheduleDiffScroll(row) {
455
455
  });
456
456
  }
457
457
 
458
+ // Coalesced scrollIntoView for caret/focus moves. Holding an arrow key fires key-repeat faster than a
459
+ // reflow-forcing scrollIntoView can run, so collapse them to one (latest element) per animation frame and
460
+ // use block:nearest (no per-row center-jump). Shared by the tree, source caret, and diff caret.
461
+ var pendingScrollEl = null, scrollElRaf = 0;
462
+ function scheduleScrollIntoView(el) {
463
+ pendingScrollEl = el || null;
464
+ if (scrollElRaf) return;
465
+ scrollElRaf = requestAnimationFrame(function () {
466
+ scrollElRaf = 0;
467
+ var e = pendingScrollEl; pendingScrollEl = null;
468
+ if (e && e.scrollIntoView) { try { e.scrollIntoView({ block: 'nearest', inline: 'nearest' }); } catch (x) {} }
469
+ });
470
+ }
471
+
458
472
  var setActiveRaf = 0, setActiveScrollPending = true;
459
473
  function setActive(index, shouldScroll = true) {
460
474
  if (hunkTotal() === 0) return;
@@ -814,10 +828,11 @@ function treeRows() {
814
828
  function focusTree(index) {
815
829
  const rows = treeRows();
816
830
  if (rows.length === 0) return;
831
+ // Incremental: drop the old focus class and add the new one — no full forEach over every row per keystroke.
832
+ if (treeFocusIndex >= 0 && treeFocusIndex < rows.length) rows[treeFocusIndex]?.classList.remove('tree-focus');
817
833
  treeFocusIndex = Math.max(0, Math.min(rows.length - 1, index));
818
- rows.forEach((row, i) => row.classList.toggle('tree-focus', i === treeFocusIndex));
819
834
  const el = rows[treeFocusIndex];
820
- if (el) el.scrollIntoView({ block: 'nearest' });
835
+ if (el) { el.classList.add('tree-focus'); scheduleScrollIntoView(el); }
821
836
  }
822
837
 
823
838
  function clearTreeFocus() {
@@ -1454,10 +1469,7 @@ function setDiffCursor(path, side, rowIndex, column, reveal) {
1454
1469
  diffSelectionAnchor = null; // any direct caret placement (click/F7/Cmd-arrow) drops the selection; Shift+Arrow re-sets it
1455
1470
  renderDiffCaret();
1456
1471
  applyDiffSelection();
1457
- if (reveal) {
1458
- var r = diffRowAt(wrapper, side, ri);
1459
- if (r && r.scrollIntoView) requestAnimationFrame(function () { try { r.scrollIntoView({ block: 'nearest' }); } catch (e) {} });
1460
- }
1472
+ if (reveal) scheduleScrollIntoView(diffRowAt(wrapper, side, ri));
1461
1473
  recordNav(navEntryOf('diff'));
1462
1474
  }
1463
1475
  function navEntryOf(kind) {
@@ -2138,7 +2150,12 @@ refreshComments();
2138
2150
  // 2+ panes: dim every pane but the active one (no border, just a clean focus cue). A lone pane stays full.
2139
2151
  q.el.classList.toggle('is-inactive', panes.length > 1 && q !== p);
2140
2152
  });
2141
- if (p) requestAnimationFrame(function () { try { p.term.focus(); } catch (e) {} });
2153
+ if (p) requestAnimationFrame(function () {
2154
+ try {
2155
+ if (p.labelEl && p.labelEl.getAttribute('contenteditable') === 'true') return;
2156
+ p.term.focus();
2157
+ } catch (e) {}
2158
+ });
2142
2159
  }
2143
2160
 
2144
2161
  function makePane() {
@@ -2918,11 +2935,7 @@ function setSourceCursor(path, lineIndex, column, shouldReveal = false, targetLi
2918
2935
  const shouldSwitch = !viewer || viewer.dataset.openPath !== path || viewer.classList.contains('hidden');
2919
2936
  openSourceFile(path, shouldSwitch);
2920
2937
  }
2921
- if (shouldReveal) {
2922
- requestAnimationFrame(() => {
2923
- document.querySelector('.source-row.cursor-line')?.scrollIntoView({ block: 'center' });
2924
- });
2925
- }
2938
+ if (shouldReveal) scheduleScrollIntoView(document.querySelector('.source-row.cursor-line'));
2926
2939
  recordNav(navEntryOf('source'));
2927
2940
  }
2928
2941
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happy-nut/monacori",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Validation control plane for AI-generated code changes.",
5
5
  "type": "module",
6
6
  "repository": {