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 +52 -0
- package/bin/nitpiq-mcp +52 -0
- package/package.json +9 -34
- package/postinstall.mjs +58 -0
- package/README.md +0 -173
- package/bin/nitpiq-mcp.ts +0 -2
- package/bin/nitpiq.ts +0 -2
- package/bunfig.toml +0 -1
- package/plugins/react-compiler.ts +0 -28
- package/src/cli/nitpiq-mcp.ts +0 -10
- package/src/cli/nitpiq.tsx +0 -36
- package/src/git/repo.ts +0 -237
- package/src/log/log.ts +0 -27
- package/src/mcp/server.test.ts +0 -37
- package/src/mcp/server.ts +0 -210
- package/src/review/anchor.ts +0 -118
- package/src/review/types.ts +0 -64
- package/src/store/store.ts +0 -315
- package/src/tui/app.tsx +0 -1089
- package/src/tui/demo.ts +0 -241
- package/src/tui/diff.ts +0 -99
- package/src/tui/highlight.ts +0 -208
- package/src/tui/theme.ts +0 -107
package/src/store/store.ts
DELETED
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
import { mkdirSync } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { Database } from "bun:sqlite";
|
|
4
|
-
import { AuthorHuman, ThreadOpen, type Author, type Comment, type NewComment, type NewThread, type ReviewSession, type Thread, type ThreadStatus } from "../review/types";
|
|
5
|
-
|
|
6
|
-
const schema = `
|
|
7
|
-
CREATE TABLE IF NOT EXISTS sessions (
|
|
8
|
-
id TEXT PRIMARY KEY,
|
|
9
|
-
repo_root TEXT NOT NULL,
|
|
10
|
-
active INTEGER NOT NULL DEFAULT 1,
|
|
11
|
-
created_at TEXT NOT NULL,
|
|
12
|
-
updated_at TEXT NOT NULL
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
CREATE TABLE IF NOT EXISTS threads (
|
|
16
|
-
id TEXT PRIMARY KEY,
|
|
17
|
-
session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
|
|
18
|
-
file_path TEXT NOT NULL,
|
|
19
|
-
side TEXT NOT NULL DEFAULT 'new',
|
|
20
|
-
original_line INTEGER NOT NULL DEFAULT 0,
|
|
21
|
-
line_end INTEGER NOT NULL DEFAULT 0,
|
|
22
|
-
current_line INTEGER NOT NULL DEFAULT 0,
|
|
23
|
-
anchor_content TEXT NOT NULL DEFAULT '',
|
|
24
|
-
context_before TEXT NOT NULL DEFAULT '',
|
|
25
|
-
context_after TEXT NOT NULL DEFAULT '',
|
|
26
|
-
is_outdated INTEGER NOT NULL DEFAULT 0,
|
|
27
|
-
status TEXT NOT NULL DEFAULT 'open',
|
|
28
|
-
created_at TEXT NOT NULL,
|
|
29
|
-
updated_at TEXT NOT NULL
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
CREATE TABLE IF NOT EXISTS comments (
|
|
33
|
-
id TEXT PRIMARY KEY,
|
|
34
|
-
thread_id TEXT NOT NULL REFERENCES threads(id) ON DELETE CASCADE,
|
|
35
|
-
author TEXT NOT NULL,
|
|
36
|
-
body TEXT NOT NULL,
|
|
37
|
-
created_at TEXT NOT NULL
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
CREATE INDEX IF NOT EXISTS idx_threads_session ON threads(session_id);
|
|
41
|
-
CREATE INDEX IF NOT EXISTS idx_threads_file ON threads(file_path);
|
|
42
|
-
CREATE INDEX IF NOT EXISTS idx_comments_thread ON comments(thread_id);
|
|
43
|
-
`;
|
|
44
|
-
|
|
45
|
-
export class Store {
|
|
46
|
-
readonly db: Database;
|
|
47
|
-
|
|
48
|
-
private constructor(db: Database) {
|
|
49
|
-
this.db = db;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
static open(repoRoot: string): Store {
|
|
53
|
-
const dir = path.join(repoRoot, ".git", "nitpiq");
|
|
54
|
-
mkdirSync(dir, { recursive: true });
|
|
55
|
-
const dbPath = path.join(dir, "review.db");
|
|
56
|
-
const db = new Database(dbPath, { create: true, strict: true });
|
|
57
|
-
db.exec("PRAGMA journal_mode = WAL;");
|
|
58
|
-
db.exec("PRAGMA foreign_keys = ON;");
|
|
59
|
-
db.exec(schema);
|
|
60
|
-
return new Store(db);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
close(): void {
|
|
64
|
-
this.db.close();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
createSession(repoRoot: string): ReviewSession {
|
|
68
|
-
const now = new Date();
|
|
69
|
-
const session: ReviewSession = {
|
|
70
|
-
id: crypto.randomUUID(),
|
|
71
|
-
repoRoot,
|
|
72
|
-
active: true,
|
|
73
|
-
createdAt: now,
|
|
74
|
-
updatedAt: now,
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
this.db.query("UPDATE sessions SET active = 0 WHERE active = 1").run();
|
|
78
|
-
this.db
|
|
79
|
-
.query(
|
|
80
|
-
"INSERT INTO sessions (id, repo_root, active, created_at, updated_at) VALUES (?, ?, 1, ?, ?)",
|
|
81
|
-
)
|
|
82
|
-
.run(session.id, session.repoRoot, now.toISOString(), now.toISOString());
|
|
83
|
-
|
|
84
|
-
return session;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
activeSession(): ReviewSession | null {
|
|
88
|
-
const row = this.db
|
|
89
|
-
.query("SELECT id, repo_root, created_at, updated_at FROM sessions WHERE active = 1 ORDER BY created_at DESC LIMIT 1")
|
|
90
|
-
.get() as
|
|
91
|
-
| {
|
|
92
|
-
id: string;
|
|
93
|
-
repo_root: string;
|
|
94
|
-
created_at: string;
|
|
95
|
-
updated_at: string;
|
|
96
|
-
}
|
|
97
|
-
| null;
|
|
98
|
-
|
|
99
|
-
if (!row) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
id: row.id,
|
|
105
|
-
repoRoot: row.repo_root,
|
|
106
|
-
active: true,
|
|
107
|
-
createdAt: new Date(row.created_at),
|
|
108
|
-
updatedAt: new Date(row.updated_at),
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
clearSession(id: string): void {
|
|
113
|
-
this.db.query("DELETE FROM sessions WHERE id = ?").run(id);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
createThread(input: NewThread): Thread {
|
|
117
|
-
const now = new Date();
|
|
118
|
-
const thread: Thread = {
|
|
119
|
-
id: crypto.randomUUID(),
|
|
120
|
-
sessionId: input.sessionId,
|
|
121
|
-
filePath: input.filePath,
|
|
122
|
-
side: input.side,
|
|
123
|
-
originalLine: input.originalLine,
|
|
124
|
-
lineEnd: input.lineEnd,
|
|
125
|
-
currentLine: input.currentLine,
|
|
126
|
-
anchorContent: input.anchorContent,
|
|
127
|
-
contextBefore: input.contextBefore,
|
|
128
|
-
contextAfter: input.contextAfter,
|
|
129
|
-
isOutdated: input.isOutdated ?? false,
|
|
130
|
-
status: input.status ?? ThreadOpen,
|
|
131
|
-
createdAt: now,
|
|
132
|
-
updatedAt: now,
|
|
133
|
-
commentCount: 0,
|
|
134
|
-
firstComment: "",
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
this.db
|
|
138
|
-
.query(
|
|
139
|
-
`INSERT INTO threads (
|
|
140
|
-
id, session_id, file_path, side, original_line, line_end,
|
|
141
|
-
current_line, anchor_content, context_before, context_after,
|
|
142
|
-
is_outdated, status, created_at, updated_at
|
|
143
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
144
|
-
)
|
|
145
|
-
.run(
|
|
146
|
-
thread.id,
|
|
147
|
-
thread.sessionId,
|
|
148
|
-
thread.filePath,
|
|
149
|
-
thread.side,
|
|
150
|
-
thread.originalLine,
|
|
151
|
-
thread.lineEnd,
|
|
152
|
-
thread.currentLine,
|
|
153
|
-
thread.anchorContent,
|
|
154
|
-
thread.contextBefore,
|
|
155
|
-
thread.contextAfter,
|
|
156
|
-
thread.isOutdated ? 1 : 0,
|
|
157
|
-
thread.status,
|
|
158
|
-
now.toISOString(),
|
|
159
|
-
now.toISOString(),
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
return thread;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
listThreads(sessionId: string, filePath = ""): Thread[] {
|
|
166
|
-
const query = `
|
|
167
|
-
SELECT t.id, t.session_id, t.file_path, t.side,
|
|
168
|
-
t.original_line, t.line_end, t.current_line,
|
|
169
|
-
t.anchor_content, t.context_before, t.context_after,
|
|
170
|
-
t.is_outdated, t.status, t.created_at, t.updated_at,
|
|
171
|
-
COALESCE((SELECT COUNT(*) FROM comments WHERE thread_id = t.id), 0) AS comment_count,
|
|
172
|
-
COALESCE((SELECT body FROM comments WHERE thread_id = t.id ORDER BY created_at LIMIT 1), '') AS first_comment
|
|
173
|
-
FROM threads t
|
|
174
|
-
WHERE t.session_id = ? ${filePath ? "AND t.file_path = ?" : ""}
|
|
175
|
-
ORDER BY t.file_path, t.original_line
|
|
176
|
-
`;
|
|
177
|
-
|
|
178
|
-
const rows = (filePath
|
|
179
|
-
? this.db.query(query).all(sessionId, filePath)
|
|
180
|
-
: this.db.query(query).all(sessionId)) as ThreadRow[];
|
|
181
|
-
|
|
182
|
-
return rows.map(mapThread);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
threadCountsByFile(sessionId: string): Record<string, number> {
|
|
186
|
-
const rows = this.db
|
|
187
|
-
.query("SELECT file_path, COUNT(*) AS count FROM threads WHERE session_id = ? AND status = 'open' GROUP BY file_path")
|
|
188
|
-
.all(sessionId) as Array<{ file_path: string; count: number }>;
|
|
189
|
-
|
|
190
|
-
return Object.fromEntries(rows.map((row) => [row.file_path, row.count]));
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
updateThreadLine(id: string, currentLine: number, outdated: boolean): void {
|
|
194
|
-
this.db
|
|
195
|
-
.query("UPDATE threads SET current_line = ?, is_outdated = ?, updated_at = ? WHERE id = ?")
|
|
196
|
-
.run(currentLine, outdated ? 1 : 0, new Date().toISOString(), id);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
updateThreadStatus(id: string, status: ThreadStatus): void {
|
|
200
|
-
this.db
|
|
201
|
-
.query("UPDATE threads SET status = ?, updated_at = ? WHERE id = ?")
|
|
202
|
-
.run(status, new Date().toISOString(), id);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
deleteThread(id: string): void {
|
|
206
|
-
this.db.query("DELETE FROM threads WHERE id = ?").run(id);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
addComment(input: NewComment): Comment {
|
|
210
|
-
const comment: Comment = {
|
|
211
|
-
id: crypto.randomUUID(),
|
|
212
|
-
threadId: input.threadId,
|
|
213
|
-
author: input.author ?? AuthorHuman,
|
|
214
|
-
body: input.body,
|
|
215
|
-
createdAt: new Date(),
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
this.db
|
|
219
|
-
.query("INSERT INTO comments (id, thread_id, author, body, created_at) VALUES (?, ?, ?, ?, ?)")
|
|
220
|
-
.run(comment.id, comment.threadId, comment.author, comment.body, comment.createdAt.toISOString());
|
|
221
|
-
|
|
222
|
-
return comment;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
listComments(threadId: string): Comment[] {
|
|
226
|
-
const rows = this.db
|
|
227
|
-
.query("SELECT id, thread_id, author, body, created_at FROM comments WHERE thread_id = ? ORDER BY created_at")
|
|
228
|
-
.all(threadId) as CommentRow[];
|
|
229
|
-
|
|
230
|
-
return rows.map(mapComment);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
listCommentsForThreads(threadIds: string[]): Record<string, Comment[]> {
|
|
234
|
-
if (threadIds.length === 0) {
|
|
235
|
-
return {};
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const placeholders = threadIds.map(() => "?").join(", ");
|
|
239
|
-
const rows = this.db
|
|
240
|
-
.query(
|
|
241
|
-
`SELECT id, thread_id, author, body, created_at FROM comments WHERE thread_id IN (${placeholders}) ORDER BY created_at`,
|
|
242
|
-
)
|
|
243
|
-
.all(...threadIds) as CommentRow[];
|
|
244
|
-
|
|
245
|
-
const result: Record<string, Comment[]> = {};
|
|
246
|
-
for (const row of rows) {
|
|
247
|
-
const comment = mapComment(row);
|
|
248
|
-
const existing = result[comment.threadId];
|
|
249
|
-
if (existing) {
|
|
250
|
-
existing.push(comment);
|
|
251
|
-
} else {
|
|
252
|
-
result[comment.threadId] = [comment];
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return result;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
interface ThreadRow {
|
|
260
|
-
id: string;
|
|
261
|
-
session_id: string;
|
|
262
|
-
file_path: string;
|
|
263
|
-
side: string;
|
|
264
|
-
original_line: number;
|
|
265
|
-
line_end: number;
|
|
266
|
-
current_line: number;
|
|
267
|
-
anchor_content: string;
|
|
268
|
-
context_before: string;
|
|
269
|
-
context_after: string;
|
|
270
|
-
is_outdated: number;
|
|
271
|
-
status: string;
|
|
272
|
-
created_at: string;
|
|
273
|
-
updated_at: string;
|
|
274
|
-
comment_count: number;
|
|
275
|
-
first_comment: string;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
interface CommentRow {
|
|
279
|
-
id: string;
|
|
280
|
-
thread_id: string;
|
|
281
|
-
author: string;
|
|
282
|
-
body: string;
|
|
283
|
-
created_at: string;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function mapThread(row: ThreadRow): Thread {
|
|
287
|
-
return {
|
|
288
|
-
id: row.id,
|
|
289
|
-
sessionId: row.session_id,
|
|
290
|
-
filePath: row.file_path,
|
|
291
|
-
side: row.side,
|
|
292
|
-
originalLine: row.original_line,
|
|
293
|
-
lineEnd: row.line_end,
|
|
294
|
-
currentLine: row.current_line,
|
|
295
|
-
anchorContent: row.anchor_content,
|
|
296
|
-
contextBefore: row.context_before,
|
|
297
|
-
contextAfter: row.context_after,
|
|
298
|
-
isOutdated: row.is_outdated !== 0,
|
|
299
|
-
status: row.status as ThreadStatus,
|
|
300
|
-
createdAt: new Date(row.created_at),
|
|
301
|
-
updatedAt: new Date(row.updated_at),
|
|
302
|
-
commentCount: row.comment_count,
|
|
303
|
-
firstComment: row.first_comment,
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function mapComment(row: CommentRow): Comment {
|
|
308
|
-
return {
|
|
309
|
-
id: row.id,
|
|
310
|
-
threadId: row.thread_id,
|
|
311
|
-
author: row.author as Author,
|
|
312
|
-
body: row.body,
|
|
313
|
-
createdAt: new Date(row.created_at),
|
|
314
|
-
};
|
|
315
|
-
}
|