nitpiq 0.1.0 → 0.3.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/bin/nitpiq ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ const childProcess = require("child_process");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const os = require("os");
7
+
8
+ function run(target) {
9
+ const result = childProcess.spawnSync(target, process.argv.slice(2), {
10
+ stdio: "inherit",
11
+ });
12
+ if (result.error) {
13
+ console.error(result.error.message);
14
+ process.exit(1);
15
+ }
16
+ process.exit(typeof result.status === "number" ? result.status : 0);
17
+ }
18
+
19
+ const scriptDir = path.dirname(fs.realpathSync(__filename));
20
+
21
+ const cached = path.join(scriptDir, ".nitpiq");
22
+ if (fs.existsSync(cached)) {
23
+ run(cached);
24
+ }
25
+
26
+ const platformMap = { darwin: "darwin", linux: "linux" };
27
+ const archMap = { x64: "x64", arm64: "arm64" };
28
+
29
+ const platform = platformMap[os.platform()] || os.platform();
30
+ const arch = archMap[os.arch()] || os.arch();
31
+ const pkg = `nitpiq-${platform}-${arch}`;
32
+
33
+ function findBinary(startDir) {
34
+ let current = startDir;
35
+ for (;;) {
36
+ const candidate = path.join(current, "node_modules", pkg, "bin", "nitpiq");
37
+ if (fs.existsSync(candidate)) return candidate;
38
+ const parent = path.dirname(current);
39
+ if (parent === current) return null;
40
+ current = parent;
41
+ }
42
+ }
43
+
44
+ const resolved = findBinary(scriptDir);
45
+ if (!resolved) {
46
+ console.error(
47
+ `Could not find the nitpiq binary. Try installing the platform package manually: npm install ${pkg}`,
48
+ );
49
+ process.exit(1);
50
+ }
51
+
52
+ run(resolved);
package/bin/nitpiq-mcp ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ const childProcess = require("child_process");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const os = require("os");
7
+
8
+ function run(target) {
9
+ const result = childProcess.spawnSync(target, process.argv.slice(2), {
10
+ stdio: "inherit",
11
+ });
12
+ if (result.error) {
13
+ console.error(result.error.message);
14
+ process.exit(1);
15
+ }
16
+ process.exit(typeof result.status === "number" ? result.status : 0);
17
+ }
18
+
19
+ const scriptDir = path.dirname(fs.realpathSync(__filename));
20
+
21
+ const cached = path.join(scriptDir, ".nitpiq-mcp");
22
+ if (fs.existsSync(cached)) {
23
+ run(cached);
24
+ }
25
+
26
+ const platformMap = { darwin: "darwin", linux: "linux" };
27
+ const archMap = { x64: "x64", arm64: "arm64" };
28
+
29
+ const platform = platformMap[os.platform()] || os.platform();
30
+ const arch = archMap[os.arch()] || os.arch();
31
+ const pkg = `nitpiq-${platform}-${arch}`;
32
+
33
+ function findBinary(startDir) {
34
+ let current = startDir;
35
+ for (;;) {
36
+ const candidate = path.join(current, "node_modules", pkg, "bin", "nitpiq-mcp");
37
+ if (fs.existsSync(candidate)) return candidate;
38
+ const parent = path.dirname(current);
39
+ if (parent === current) return null;
40
+ current = parent;
41
+ }
42
+ }
43
+
44
+ const resolved = findBinary(scriptDir);
45
+ if (!resolved) {
46
+ console.error(
47
+ `Could not find the nitpiq-mcp binary. Try installing the platform package manually: npm install ${pkg}`,
48
+ );
49
+ process.exit(1);
50
+ }
51
+
52
+ run(resolved);
package/package.json CHANGED
@@ -1,44 +1,19 @@
1
1
  {
2
2
  "name": "nitpiq",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Terminal-based code review tool for local git changes",
5
- "type": "module",
6
5
  "license": "MIT",
7
6
  "bin": {
8
- "nitpiq": "bin/nitpiq.ts",
9
- "nitpiq-mcp": "bin/nitpiq-mcp.ts"
7
+ "nitpiq": "bin/nitpiq",
8
+ "nitpiq-mcp": "bin/nitpiq-mcp"
10
9
  },
11
- "files": [
12
- "bin/",
13
- "src/",
14
- "plugins/",
15
- "bunfig.toml"
16
- ],
17
10
  "scripts": {
18
- "nitpiq": "bun run src/cli/nitpiq.tsx",
19
- "nitpiq-mcp": "bun run src/cli/nitpiq-mcp.ts",
20
- "dev": "bun run src/cli/nitpiq.tsx",
21
- "check": "tsc --noEmit"
11
+ "postinstall": "node postinstall.mjs"
22
12
  },
23
- "devDependencies": {
24
- "@babel/core": "^7.29.0",
25
- "@babel/plugin-syntax-jsx": "^7.28.6",
26
- "@babel/plugin-syntax-typescript": "^7.28.6",
27
- "@types/bun": "latest",
28
- "@types/react": "^19.2.14",
29
- "babel-plugin-react-compiler": "^1.0.0",
30
- "eslint-plugin-react-hooks": "^7.0.1"
31
- },
32
- "peerDependencies": {
33
- "typescript": "^5"
34
- },
35
- "dependencies": {
36
- "@modelcontextprotocol/sdk": "^1.27.1",
37
- "fuse.js": "^7.1.0",
38
- "ink": "^6.8.0",
39
- "parse-diff": "^0.11.1",
40
- "picocolors": "^1.1.1",
41
- "react": "^19.2.4",
42
- "zod": "^4.3.6"
13
+ "optionalDependencies": {
14
+ "nitpiq-linux-x64": "0.3.0",
15
+ "nitpiq-linux-arm64": "0.3.0",
16
+ "nitpiq-darwin-x64": "0.3.0",
17
+ "nitpiq-darwin-arm64": "0.3.0"
43
18
  }
44
19
  }
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import os from "os";
6
+ import { fileURLToPath } from "url";
7
+ import { createRequire } from "module";
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ const require = createRequire(import.meta.url);
11
+
12
+ const PLATFORM_MAP = { darwin: "darwin", linux: "linux" };
13
+ const ARCH_MAP = { x64: "x64", arm64: "arm64" };
14
+
15
+ function detect() {
16
+ const platform = PLATFORM_MAP[os.platform()];
17
+ const arch = ARCH_MAP[os.arch()];
18
+ if (!platform || !arch) {
19
+ throw new Error(`Unsupported platform: ${os.platform()}-${os.arch()}`);
20
+ }
21
+ return { platform, arch };
22
+ }
23
+
24
+ function findBinary(name) {
25
+ const { platform, arch } = detect();
26
+ const pkg = `nitpiq-${platform}-${arch}`;
27
+
28
+ const pkgJson = require.resolve(`${pkg}/package.json`);
29
+ const pkgDir = path.dirname(pkgJson);
30
+ const bin = path.join(pkgDir, "bin", name);
31
+
32
+ if (!fs.existsSync(bin)) {
33
+ throw new Error(`Binary not found at ${bin}`);
34
+ }
35
+ return bin;
36
+ }
37
+
38
+ function linkBinary(name) {
39
+ const source = findBinary(name);
40
+ const target = path.join(__dirname, "bin", `.${name}`);
41
+
42
+ if (fs.existsSync(target)) fs.unlinkSync(target);
43
+
44
+ try {
45
+ fs.linkSync(source, target);
46
+ } catch {
47
+ fs.copyFileSync(source, target);
48
+ }
49
+ fs.chmodSync(target, 0o755);
50
+ }
51
+
52
+ try {
53
+ linkBinary("nitpiq");
54
+ linkBinary("nitpiq-mcp");
55
+ } catch (error) {
56
+ console.error("nitpiq postinstall:", error.message);
57
+ process.exit(1);
58
+ }
package/README.md DELETED
@@ -1,173 +0,0 @@
1
- # nitpiq
2
-
3
- Terminal-based code review tool for local git changes. Built with Bun, TypeScript, React (Ink), and the React Compiler.
4
-
5
- Inspect uncommitted changes, leave anchored review comments, and expose an MCP server so AI tools can participate as a second reviewer.
6
-
7
- Review data is stored locally in `.git/nitpiq/review.db`.
8
-
9
- ## Prerequisites
10
-
11
- - [Bun](https://bun.sh) v1.1+
12
-
13
- ## Install
14
-
15
- ```bash
16
- bun install -g nitpiq
17
- ```
18
-
19
- Or from source:
20
-
21
- ```bash
22
- git clone <repo-url> && cd nitpiq
23
- bun install
24
- ```
25
-
26
- ## Usage
27
-
28
- ### TUI
29
-
30
- Run inside any git repository:
31
-
32
- ```bash
33
- nitpiq
34
- ```
35
-
36
- Or with npx:
37
-
38
- ```bash
39
- npx nitpiq
40
- ```
41
-
42
- Options:
43
-
44
- ```
45
- --theme=<name> Set color theme (dark, catppuccin, nord, gruvbox)
46
- --demo Launch with fixed demo data (no git required)
47
- --snapshot Render a single frame and exit (for screenshots)
48
- ```
49
-
50
- ### MCP Server
51
-
52
- Start the MCP server for AI tool integration:
53
-
54
- ```bash
55
- nitpiq-mcp /path/to/repo
56
- ```
57
-
58
- Or with npx (useful for MCP client configuration):
59
-
60
- ```bash
61
- npx nitpiq-mcp /path/to/repo
62
- ```
63
-
64
- The server exposes these tools over stdio:
65
-
66
- | Tool | Description |
67
- |------|-------------|
68
- | `review_list_changes` | List uncommitted file changes |
69
- | `review_list_threads` | List review threads (filterable by file/status) |
70
- | `review_reply_thread` | Add a reply to a thread |
71
- | `review_resolve_thread` | Mark a thread as resolved |
72
- | `review_reopen_thread` | Reopen a resolved thread |
73
- | `review_apply_edit` | Write content to a file |
74
- | `review_stage_file` | Stage a file |
75
- | `review_unstage_file` | Unstage a file |
76
-
77
- #### MCP Client Configuration
78
-
79
- For Claude Desktop, add to your config:
80
-
81
- ```json
82
- {
83
- "mcpServers": {
84
- "nitpiq": {
85
- "command": "npx",
86
- "args": ["nitpiq-mcp", "/path/to/your/repo"]
87
- }
88
- }
89
- }
90
- ```
91
-
92
- For Cursor, add to `.cursor/mcp.json`:
93
-
94
- ```json
95
- {
96
- "mcpServers": {
97
- "nitpiq": {
98
- "command": "npx",
99
- "args": ["nitpiq-mcp", "/path/to/your/repo"]
100
- }
101
- }
102
- }
103
- ```
104
-
105
- ## Keybindings
106
-
107
- ### File Sidebar
108
-
109
- | Key | Action |
110
- |-----|--------|
111
- | `j` / `k` | Navigate files |
112
- | `l` / `Enter` | Open file in diff pane |
113
- | `/` | Filter files by name |
114
- | `s` | Stage / unstage file |
115
- | `f` | Toggle between git changes and all files |
116
- | `r` | Refresh |
117
- | `Tab` | Switch to diff pane |
118
- | `q` | Quit |
119
-
120
- ### Diff Pane
121
-
122
- **Navigation (vim-style, supports count prefix e.g. `5j`):**
123
-
124
- | Key | Action |
125
- |-----|--------|
126
- | `j` / `k` | Line up / down |
127
- | `gg` | Jump to top (or `[count]gg` to line N) |
128
- | `G` | Jump to bottom (or `[count]G` to line N) |
129
- | `Ctrl+D` / `Ctrl+U` | Half-page down / up |
130
- | `Ctrl+F` / `Ctrl+B` | Full-page down / up |
131
- | `H` / `M` / `L` | Top / middle / bottom of visible screen |
132
- | `{` / `}` | Previous / next block (hunk headers, blank lines) |
133
- | `w` / `b` | Next / previous changed line |
134
- | `[` / `]` | Previous / next review thread (cross-file) |
135
- | `zz` / `zt` / `zb` | Center / top / bottom current line on screen |
136
- | `:` | Go to line number |
137
-
138
- **Actions:**
139
-
140
- | Key | Action |
141
- |-----|--------|
142
- | `c` | Comment on current line (or reply if thread exists) |
143
- | `v` | Enter visual mode for range selection |
144
- | `d` | Delete thread at cursor (with confirmation) |
145
- | `r` | Resolve / reopen thread at cursor |
146
- | `/` | Search diff |
147
- | `n` / `N` | Next / previous search match |
148
- | `f` | Toggle diff view / full file view |
149
- | `e` | Cycle diff context (3 / 10 / full) |
150
- | `h` / `Esc` / `q` | Back to file sidebar |
151
-
152
- ### Visual Mode
153
-
154
- | Key | Action |
155
- |-----|--------|
156
- | `j` / `k` | Extend selection |
157
- | `c` | Comment on selected range |
158
- | `Esc` | Cancel |
159
-
160
- ## Themes
161
-
162
- Set with `--theme=<name>`:
163
-
164
- - **dark** (default) -- blue accent on dark background
165
- - **catppuccin** -- pastel mocha palette
166
- - **nord** -- arctic blue tones
167
- - **gruvbox** -- warm retro colors
168
-
169
- ## Type Check
170
-
171
- ```bash
172
- bun run check
173
- ```
package/bin/nitpiq-mcp.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env bun
2
- import "../src/cli/nitpiq-mcp.ts";
package/bin/nitpiq.ts DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env bun
2
- import "../src/cli/nitpiq.tsx";
package/bunfig.toml DELETED
@@ -1 +0,0 @@
1
- preload = ["./plugins/react-compiler.ts"]
@@ -1,28 +0,0 @@
1
- import { plugin } from "bun";
2
- import { transformSync } from "@babel/core";
3
- import ReactCompilerPlugin from "babel-plugin-react-compiler";
4
-
5
- plugin({
6
- name: "react-compiler",
7
- setup(build) {
8
- build.onLoad({ filter: /\.tsx$/ }, async (args) => {
9
- const source = await Bun.file(args.path).text();
10
-
11
- const result = transformSync(source, {
12
- filename: args.path,
13
- plugins: [
14
- [ReactCompilerPlugin, {}],
15
- ["@babel/plugin-syntax-typescript", { isTSX: true }],
16
- "@babel/plugin-syntax-jsx",
17
- ],
18
- configFile: false,
19
- babelrc: false,
20
- });
21
-
22
- return {
23
- contents: result?.code ?? source,
24
- loader: "tsx",
25
- };
26
- });
27
- },
28
- });
@@ -1,10 +0,0 @@
1
- import { serveStdio } from "../mcp/server";
2
-
3
- const repoPath = process.argv[2];
4
-
5
- try {
6
- await serveStdio(repoPath);
7
- } catch (error) {
8
- console.error(`nitpiq-mcp: ${error instanceof Error ? error.message : String(error)}`);
9
- process.exit(1);
10
- }
@@ -1,36 +0,0 @@
1
- import React from "react";
2
- import { render } from "ink";
3
- import { openRepoAt } from "../git/repo";
4
- import { initLog } from "../log/log";
5
- import { Store } from "../store/store";
6
- import { NitpiqApp } from "../tui/app";
7
- import { createDemoState } from "../tui/demo";
8
-
9
- try {
10
- const args = process.argv.slice(2);
11
- const demo = args.includes("--demo");
12
- const snapshot = args.includes("--snapshot");
13
- const themeArg = args.find((a) => a.startsWith("--theme="));
14
- const themeName = themeArg?.split("=")[1];
15
- const demoState = demo ? createDemoState() : undefined;
16
- const repo = demoState?.repo ?? openRepoAt(process.cwd());
17
- const store = demo ? null : Store.open(repo.root);
18
- if (!demo) {
19
- initLog(repo.root);
20
- }
21
- const useAltScreen = Boolean(process.stdout.isTTY);
22
- if (useAltScreen) {
23
- process.stdout.write("\u001b[?1049h\u001b[H");
24
- }
25
-
26
- const instance = render(<NitpiqApp repo={repo} store={store} demoState={demoState} snapshot={snapshot} theme={themeName} />);
27
- instance.waitUntilExit().finally(() => {
28
- if (useAltScreen) {
29
- process.stdout.write("\u001b[?1049l");
30
- }
31
- store?.close();
32
- });
33
- } catch (error) {
34
- console.error(`nitpiq: ${error instanceof Error ? error.message : String(error)}`);
35
- process.exit(1);
36
- }