@happy-nut/monacori 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 happy-nut
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,177 @@
1
+ # monacori
2
+
3
+ Validation control plane for AI-generated code changes.
4
+
5
+ `monacori` is not an agent orchestrator. It does not manage panes, sessions, worktrees, or model adapters. Its job is narrower: after an AI edits a repository, produce verification evidence that a human can trust.
6
+
7
+ It gives you:
8
+
9
+ - detected verification commands in `.monacori/config.json`
10
+ - repeatable verification logs under `.monacori/logs/`
11
+ - a local desktop review app for live diff inspection
12
+ - diff2html review pages under `.monacori/diffs/`
13
+ - searchable source previews, including unchanged indexed files
14
+ - compact validation reports under `.monacori/reports/`
15
+
16
+ ## Why
17
+
18
+ AI coding output is hard to trust when review depends on chat memory or vague "done" claims. `monacori` keeps the review artifacts in the repository so the state can be inspected, rerun, and discussed.
19
+
20
+ The intended loop is simple:
21
+
22
+ ```text
23
+ AI edits code.
24
+ monacori runs verification.
25
+ monacori creates a reviewable diff artifact.
26
+ You inspect evidence before accepting the change.
27
+ ```
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ npm install -g @happy-nut/monacori
33
+ ```
34
+
35
+ After installation, the short command is:
36
+
37
+ ```bash
38
+ dg
39
+ ```
40
+
41
+ Homebrew tap distribution is prepared through `Formula/monacori.rb`. Once the scoped npm package is published and the formula is copied to the `happy-nut/homebrew-monacori` tap, the intended install path is:
42
+
43
+ ```bash
44
+ brew install happy-nut/monacori/monacori
45
+ ```
46
+
47
+ For local development:
48
+
49
+ ```bash
50
+ git clone https://github.com/happy-nut/monacori.git
51
+ cd monacori
52
+ npm install
53
+ npm run build
54
+ npm link
55
+ ```
56
+
57
+ ## Quick Start
58
+
59
+ Inside the repository you want to validate:
60
+
61
+ ```bash
62
+ dg
63
+ monacori check --include-untracked
64
+ ```
65
+
66
+ `dg` opens the local desktop review app for the current directory. On first run it creates `.monacori/`, updates Git ignore rules for `.monacori/`, and includes untracked files so new AI-created files show up immediately.
67
+
68
+ `check` runs configured verification commands, writes a log, creates a browser diff review, and records a compact report.
69
+
70
+ For a one-off command:
71
+
72
+ ```bash
73
+ monacori check -- npm test
74
+ ```
75
+
76
+ For live diff review while an AI is still editing:
77
+
78
+ ```bash
79
+ dg
80
+ ```
81
+
82
+ ## Diff Review
83
+
84
+ The desktop review app is powered by Electron and diff2html. It reads Git diff and source files directly from the local repository, writes a local `.monacori/app-review.html` file, and refreshes when the working tree changes. It does not start an HTTP server.
85
+
86
+ - `F7`: show the next changed hunk, starting from the current source file when possible
87
+ - `Shift+F7`: previous changed hunk
88
+ - `]` / `[`: fallback keys if the browser captures function keys
89
+ - `Shift Shift`: search indexed files, including unchanged files
90
+ - `Cmd/Ctrl+E`: open recent files
91
+ - `Cmd/Ctrl+Down`: jump from the source cursor to a declaration-like match for the symbol under it
92
+
93
+ The app opens as a read-only source viewer by default. The `Files` tab opens indexed files even when they are unchanged, and `F7` switches into the diff view when you want to inspect changes. Drag-copying source text adds `path:line-range` plus a fenced code block to the clipboard so the snippet can be pasted into an AI prompt with context. Viewed marks are stored with file signatures, so a file becomes unviewed again when it changes.
94
+
95
+ The browser artifact path is still available through `monacori diff`. Add `--watch` there only when you specifically want a browser-served live review.
96
+
97
+ ## Commands
98
+
99
+ ```bash
100
+ monacori open [--base HEAD] [--staged] [--tracked-only] [--context 12] [--no-watch]
101
+ ```
102
+
103
+ Opens the local desktop review app for the current directory. `dg` and bare `monacori` are aliases for this default flow. It auto-initializes local state when needed and includes untracked files by default; pass `--tracked-only` to inspect tracked changes only.
104
+
105
+ ```bash
106
+ monacori check [--include-untracked] [--staged] [--base HEAD] [--context 12] [--open] [--no-verify] [--no-diff] [-- <command>]
107
+ ```
108
+
109
+ Runs verification and creates a validation report. By default it uses commands from `.monacori/config.json`; pass `-- <command>` to override for one run.
110
+
111
+ ```bash
112
+ monacori init [--force]
113
+ ```
114
+
115
+ Creates `.monacori/` with config, state, decisions, reports, logs, and diff directories. Because these are local validation artifacts, monacori also adds `.monacori/` to `.gitignore` inside Git worktrees.
116
+
117
+ ```bash
118
+ monacori install [--force] [--apply-agent-docs]
119
+ ```
120
+
121
+ Writes `.monacori/agent-snippet.md`, a short instruction block telling AI agents to run validation before claiming completion. With `--apply-agent-docs`, it updates `AGENTS.md` and `CLAUDE.md` marker blocks where available.
122
+
123
+ ```bash
124
+ monacori verify [-- <command>]
125
+ ```
126
+
127
+ Runs configured verification commands and stores the log in `.monacori/logs/`. Exits non-zero on failure.
128
+
129
+ ```bash
130
+ monacori app [--base HEAD] [--staged] [--include-untracked] [--context 12] [--no-watch]
131
+ ```
132
+
133
+ Launches the local desktop review app. `dg`, `monacori open`, and `monacori review` are aliases. Prefer `dg` for normal use.
134
+
135
+ ```bash
136
+ monacori diff [--base HEAD] [--staged] [--include-untracked] [--context 12] [--output review.html] [--open] [--watch] [--port 0]
137
+ ```
138
+
139
+ Generates a browser-based side-by-side diff review. Add `--watch` to serve a live review that reloads when the working tree changes.
140
+
141
+ ```bash
142
+ monacori status
143
+ ```
144
+
145
+ Prints current branch, git status, diff stat, configured verification commands, recent reports, and recent logs.
146
+
147
+ ```bash
148
+ monacori report [--label manual] [--file report.md]
149
+ ```
150
+
151
+ Stores a manual report under `.monacori/reports/` and appends a compact summary to `.monacori/state.md`.
152
+
153
+ ## Repository State
154
+
155
+ `monacori init` creates:
156
+
157
+ ```text
158
+ .monacori/
159
+ config.json verification commands and diff defaults
160
+ state.md compact validation history
161
+ decisions.md durable validation decisions
162
+ diffs/ generated browser diff reviews
163
+ reports/ validation reports
164
+ logs/ verification logs
165
+ ```
166
+
167
+ `monacori install` also writes `.monacori/agent-snippet.md` for projects that want to paste or apply agent-facing validation instructions.
168
+
169
+ Keep `.monacori/` ignored unless your team explicitly wants to commit validation state.
170
+
171
+ ## Design Principles
172
+
173
+ - Verification evidence beats chat memory.
174
+ - Generated artifacts should be plain Markdown, JSON, logs, or static HTML.
175
+ - The tool should not require a specific AI agent, terminal multiplexer, editor, or worktree strategy.
176
+ - The default command should be useful after any AI-generated edit.
177
+ - A change is not accepted until verification evidence is clear or the gap is explicitly documented.
Binary file
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { main } from "../dist/cli.js";
4
+
5
+ main();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,121 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { app, BrowserWindow, Menu, nativeImage } from "electron";
5
+ import { buildDiffReview } from "./cli.js";
6
+ const FLOW_DIR = ".monacori";
7
+ const REVIEW_FILE = "app-review.html";
8
+ const WATCH_INTERVAL_MS = 1000;
9
+ app.setName("monacori");
10
+ const iconPath = join(dirname(fileURLToPath(import.meta.url)), "..", "assets", "icon.png");
11
+ const options = parseArgs(process.argv.slice(2));
12
+ let mainWindow;
13
+ let currentSignature = "";
14
+ let refreshTimer;
15
+ let refreshing = false;
16
+ if (!existsSync(options.root)) {
17
+ throw new Error(`Repository path does not exist: ${options.root}`);
18
+ }
19
+ app.whenReady().then(async () => {
20
+ process.chdir(options.root);
21
+ mkdirSync(FLOW_DIR, { recursive: true });
22
+ Menu.setApplicationMenu(null);
23
+ const appIcon = nativeImage.createFromPath(iconPath);
24
+ if (process.platform === "darwin" && app.dock && !appIcon.isEmpty()) {
25
+ app.dock.setIcon(appIcon);
26
+ }
27
+ const firstBuild = writeReviewFile(options);
28
+ currentSignature = firstBuild.signature;
29
+ mainWindow = new BrowserWindow({
30
+ width: 1440,
31
+ height: 960,
32
+ minWidth: 960,
33
+ minHeight: 640,
34
+ show: false,
35
+ title: "monacori",
36
+ icon: iconPath,
37
+ backgroundColor: "#2b2b2b",
38
+ autoHideMenuBar: true,
39
+ webPreferences: {
40
+ contextIsolation: true,
41
+ nodeIntegration: false,
42
+ sandbox: true,
43
+ spellcheck: false,
44
+ },
45
+ });
46
+ mainWindow.webContents.setWindowOpenHandler(() => ({ action: "deny" }));
47
+ mainWindow.once("ready-to-show", () => mainWindow?.show());
48
+ await mainWindow.loadFile(reviewPath());
49
+ if (options.watch) {
50
+ refreshTimer = setInterval(refreshIfChanged, WATCH_INTERVAL_MS);
51
+ }
52
+ }).catch((error) => {
53
+ console.error(error instanceof Error ? error.message : String(error));
54
+ app.quit();
55
+ });
56
+ app.on("window-all-closed", () => {
57
+ if (refreshTimer)
58
+ clearInterval(refreshTimer);
59
+ app.quit();
60
+ });
61
+ async function refreshIfChanged() {
62
+ if (refreshing || !mainWindow || mainWindow.isDestroyed())
63
+ return;
64
+ refreshing = true;
65
+ try {
66
+ const next = writeReviewFile(options);
67
+ if (next.signature !== currentSignature) {
68
+ currentSignature = next.signature;
69
+ mainWindow.webContents.reloadIgnoringCache();
70
+ }
71
+ }
72
+ catch (error) {
73
+ console.error(error instanceof Error ? error.message : String(error));
74
+ }
75
+ finally {
76
+ refreshing = false;
77
+ }
78
+ }
79
+ function writeReviewFile(input) {
80
+ const build = buildDiffReview({
81
+ base: input.base,
82
+ staged: input.staged,
83
+ includeUntracked: input.includeUntracked,
84
+ context: input.context,
85
+ title: "monacori",
86
+ });
87
+ writeFileSync(reviewPath(), build.html);
88
+ return { signature: build.signature };
89
+ }
90
+ function reviewPath() {
91
+ return join(options.root, FLOW_DIR, REVIEW_FILE);
92
+ }
93
+ function parseArgs(args) {
94
+ const root = readOption(args, "--cwd") ?? process.cwd();
95
+ const contextValue = readOption(args, "--context");
96
+ return {
97
+ root: resolve(root),
98
+ base: readOption(args, "--base"),
99
+ staged: args.includes("--staged"),
100
+ includeUntracked: args.includes("--include-untracked"),
101
+ context: contextValue ? parsePositiveInteger(contextValue, "--context") : 12,
102
+ watch: !args.includes("--no-watch"),
103
+ };
104
+ }
105
+ function readOption(args, name) {
106
+ const index = args.indexOf(name);
107
+ if (index < 0)
108
+ return undefined;
109
+ const value = args[index + 1];
110
+ if (!value || value.startsWith("--")) {
111
+ throw new Error(`Missing value for ${name}`);
112
+ }
113
+ return value;
114
+ }
115
+ function parsePositiveInteger(value, optionName) {
116
+ const parsed = Number(value);
117
+ if (!Number.isInteger(parsed) || parsed < 0) {
118
+ throw new Error(`${optionName} must be a non-negative integer`);
119
+ }
120
+ return parsed;
121
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ export type HttpSendRequest = {
3
+ method: string;
4
+ url: string;
5
+ headers: Record<string, string>;
6
+ body?: string;
7
+ };
8
+ export type HttpSendResult = {
9
+ ok: boolean;
10
+ status?: number;
11
+ statusText?: string;
12
+ headers?: Record<string, string>;
13
+ body?: string;
14
+ error?: string;
15
+ durationMs: number;
16
+ };
17
+ type DiffReviewBuild = {
18
+ html: string;
19
+ files: number;
20
+ hunks: number;
21
+ signature: string;
22
+ generatedAt: string;
23
+ };
24
+ export declare function main(): void;
25
+ export declare function buildDiffReview(input: {
26
+ base?: string;
27
+ staged: boolean;
28
+ includeUntracked: boolean;
29
+ context: number;
30
+ title: string;
31
+ watch?: boolean;
32
+ }): DiffReviewBuild;
33
+ export declare function performHttpRequest(request: HttpSendRequest): Promise<HttpSendResult>;
34
+ export {};