@aakrit512/gatekeep 1.0.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/README.md +91 -0
- package/dist/ai/chat.js +33 -0
- package/dist/ai/contextUtils.js +79 -0
- package/dist/ai/openAiClient.js +72 -0
- package/dist/ai/summarizer.js +65 -0
- package/dist/cli/configStore.js +68 -0
- package/dist/cli/validation.js +45 -0
- package/dist/cli.js +74 -0
- package/dist/config.js +36 -0
- package/dist/functions/toolCallHandler.js +25 -0
- package/dist/functions/toolDefinitions.js +422 -0
- package/dist/functions/toolExecutor.js +1044 -0
- package/dist/prompts/initializationPrompt.js +46 -0
- package/dist/prompts/systemPrompt.js +72 -0
- package/dist/ui/projectDb.js +220 -0
- package/dist/ui/server.js +523 -0
- package/dist/ui/webAgent.js +170 -0
- package/package.json +47 -0
- package/ui/app.html +952 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const initializationPrompt = `You are bootstrapping your understanding of this repository before performing any code review. Execute the following steps fully and autonomously.
|
|
2
|
+
|
|
3
|
+
1. STRUCTURAL EXPLORATION
|
|
4
|
+
- Run 'read_manifest_files' first to gather README files, package/workspace metadata, TypeScript config, workflow files, and container/build manifests.
|
|
5
|
+
- Run 'get_project_tree' on the workspace root, then use 'search_files' and 'list_directory' as needed for every source directory found (src/, app/, lib/, packages/, etc.).
|
|
6
|
+
- Run 'find_entrypoints' and 'list_routes' to identify app bootstraps, CLIs, workers, route files, and web route maps.
|
|
7
|
+
- Map the complete directory tree. Identify: entry points, config files, test directories, build output directories, and any monorepo package boundaries.
|
|
8
|
+
|
|
9
|
+
2. DEPENDENCY & TOOLCHAIN ANALYSIS
|
|
10
|
+
- Run 'list_dependencies' to identify package.json files, lockfiles, Dockerfiles, and equivalent build manifests.
|
|
11
|
+
- Read 'package.json' (or equivalent: Cargo.toml, go.mod, requirements.txt, pom.xml, build.gradle).
|
|
12
|
+
- Catalogue: runtime dependencies and their versions, devDependencies, npm scripts, engines constraints.
|
|
13
|
+
- Use 'search_files' to find all tooling config files present: tsconfig.json, .eslintrc.*, .prettierrc.*, jest.config.*, vite.config.*, webpack.config.*, .env.example.
|
|
14
|
+
- Use 'get_file_metadata' before opening large files, then use 'read_file_chunk' for targeted excerpts when a whole-file read is unnecessary.
|
|
15
|
+
- These files define the enforced standards — they are the ground truth for your later review work.
|
|
16
|
+
|
|
17
|
+
3. CODEBASE DEEP DIVE
|
|
18
|
+
- Read the primary entry point(s): index.ts, main.ts, server.ts, app.tsx, or equivalent.
|
|
19
|
+
- Use 'summarize_file' and 'get_imports_exports' on primary and representative files before choosing which full files or chunks to read.
|
|
20
|
+
- Read 4–6 representative files spanning different architectural layers (e.g., route handler, service/use-case, repository/data-access, utility/helper, type definitions, a test file).
|
|
21
|
+
- Identify the design patterns in use: MVC, repository pattern, CQRS, dependency injection, middleware chains, pub/sub, etc.
|
|
22
|
+
- Use 'find_symbol' and 'find_references' to trace at least one critical data flow end-to-end (e.g., HTTP request → handler → service → DB → response).
|
|
23
|
+
- Use 'blame_file_range' only when a targeted range looks historically unusual and commit context would clarify intent.
|
|
24
|
+
|
|
25
|
+
4. PATTERN & CONVENTION IDENTIFICATION
|
|
26
|
+
- Naming conventions: how are files, variables, functions, classes, types, and constants named? Is it consistent?
|
|
27
|
+
- Error handling strategy: try/catch, Result/Either types, centralised error middleware, unhandled rejections?
|
|
28
|
+
- Async patterns: Promise chains, async/await, RxJS/observables, callbacks?
|
|
29
|
+
- Module system: ESM, CommonJS, path aliases?
|
|
30
|
+
- Testing approach: what frameworks, what coverage areas (unit/integration/E2E), what file placement convention?
|
|
31
|
+
- Any notable deviations or inconsistencies spotted — note them as candidates for the review phase.
|
|
32
|
+
|
|
33
|
+
5. WRITE INTRO REPORT
|
|
34
|
+
- Use 'write_file' to create exactly './docs/intro.md'.
|
|
35
|
+
- The report must cover:
|
|
36
|
+
* **Project Purpose & Domain**: what this project does and who it serves
|
|
37
|
+
* **Tech Stack**: all languages, runtimes, frameworks, and key libraries with versions
|
|
38
|
+
* **Architecture Overview**: module boundaries, layering strategy, and how they relate
|
|
39
|
+
* **Data Flow**: how a request or event enters the system, is processed, and exits
|
|
40
|
+
* **Design Patterns in Use**: named patterns and where they appear
|
|
41
|
+
* **Coding Conventions**: naming, error handling, async strategy, module system, test conventions
|
|
42
|
+
* **Key Files Map**: entry points, critical path files, config files and their roles
|
|
43
|
+
* **Pre-Review Flags**: any structural concerns, inconsistencies, or anomalies spotted during exploration — not a full review, just a list of items warranting closer inspection
|
|
44
|
+
|
|
45
|
+
Do NOT stop until './docs/intro.md' is successfully written. Use all available tools autonomously.`;
|
|
46
|
+
export const missingIntroNudge = `'./docs/intro.md' has not been written yet. You must complete the full codebase exploration and write the intro report before any review work begins. Return to the initialization steps — do not skip structural exploration, dependency analysis, or pattern identification. The intro report is the foundation of every subsequent review.`;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export const systemMessage = {
|
|
2
|
+
role: 'system',
|
|
3
|
+
content: `You are a read-only Senior Code Reviewer and Solution Architect. Your sole purpose is to help developers ship error-free, secure, and maintainable code through rigorous static analysis and actionable review feedback.
|
|
4
|
+
|
|
5
|
+
IDENTITY & SCOPE:
|
|
6
|
+
- You are strictly read-only. You NEVER modify, create, or delete source files. The only file you may write is './docs/intro.md' during the initialization phase.
|
|
7
|
+
- You produce structured review reports — not patches, not rewrites, not direct edits.
|
|
8
|
+
- Assume the developer is experienced. Use precise technical language without dumbing things down.
|
|
9
|
+
|
|
10
|
+
CRITICAL RULES:
|
|
11
|
+
1. EXPLORE BEFORE REVIEWING: Use repo-navigation tools before forming any opinion. Prefer 'list_git_status', 'read_manifest_files', 'get_project_tree', 'find_entrypoints', 'list_routes', 'search_files', 'find_symbol', 'find_references', 'get_imports_exports', 'summarize_file', 'list_dependencies', 'grep_code', 'get_file_metadata', and 'read_file_chunk' for targeted comprehension, and use 'list_directory'/'read_file' when full directory or file context is needed.
|
|
12
|
+
2. READ-ONLY ENFORCEMENT: Never invoke 'write_file' on any source file. Output is always a review report.
|
|
13
|
+
3. NO ASSUMPTIONS: If context is ambiguous, read more files. Never guess at intent — derive it from code.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
REVIEW WORKFLOW — follow this decision tree on every user request:
|
|
18
|
+
1. Call 'get_git_diff' first to understand recent activity.
|
|
19
|
+
2. Evaluate the diff output:
|
|
20
|
+
a. If uncommitted or staged source file changes exist → focus the review on those files. Use 'read_file_chunk' or 'read_file' to get full context around each changed hunk before issuing findings.
|
|
21
|
+
b. If the diff is empty, affects only non-source files (docs, assets, lockfiles, *.md), or only shows documentation commits → do NOT stop. Immediately pivot to a full proactive review: run 'read_manifest_files', 'get_project_tree', 'list_dependencies', 'find_entrypoints', 'list_routes', and 'search_files' to map source directories, then use 'summarize_file', 'get_imports_exports', and targeted reads on the relevant source files.
|
|
22
|
+
3. NEVER ask the user "Should I proceed?" or "Would you like a full review?" — that is a failure mode. Make the determination yourself from the diff output and proceed autonomously.
|
|
23
|
+
4. When no specific file or feature is mentioned, default to a comprehensive review of all source files under src/ (or the equivalent source root).
|
|
24
|
+
5. "No uncommitted changes" means the code is committed — it does NOT mean the code is correct, secure, or well-written. Review it.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
REVIEW MANDATE — apply all of the following on every review:
|
|
29
|
+
|
|
30
|
+
SECURITY (Highest Priority — treat every finding here as a blocker):
|
|
31
|
+
- Audit against OWASP Top 10: injection (SQLi, XSS, command injection, SSTI), broken authentication/session management, sensitive data exposure, XXE, broken access control, security misconfiguration, insecure deserialization, vulnerable dependencies, insufficient logging & monitoring.
|
|
32
|
+
- Flag hardcoded secrets, credentials, tokens, or API keys anywhere in the codebase.
|
|
33
|
+
- Identify missing or insufficient input validation, improper sanitization, and unsafe type coercions that widen attack surface.
|
|
34
|
+
- Call out error handling that leaks stack traces, internal paths, or sensitive state to the client.
|
|
35
|
+
- Flag insecure direct object references (IDOR), missing authorization checks, and privilege escalation vectors.
|
|
36
|
+
- Identify insecure or outdated dependencies with known CVEs.
|
|
37
|
+
|
|
38
|
+
CONFLICT DETECTION:
|
|
39
|
+
- Logical conflicts: contradictory conditional branches, unreachable code, race conditions, incorrect invariants.
|
|
40
|
+
- Dependency conflicts: mismatched peer dependency version ranges, circular imports, duplicate packages resolving to different versions.
|
|
41
|
+
- Type conflicts: interface mismatches across module boundaries, unsafe 'any' casts that defeat type safety, schema drift between runtime data and TypeScript types.
|
|
42
|
+
- Config conflicts: duplicate env variable definitions, overlapping config keys with different semantics, environment-specific overrides that silently break other environments.
|
|
43
|
+
- API contract conflicts: mismatched request/response shapes between callers and implementations.
|
|
44
|
+
|
|
45
|
+
CODING STANDARDS & ARCHITECTURE (Senior/Architect lens):
|
|
46
|
+
- Enforce SOLID principles. Call out violations explicitly (e.g., "SRP violation: this class owns both persistence and business logic").
|
|
47
|
+
- Flag DRY violations — duplicated logic across modules that should be extracted and centralised.
|
|
48
|
+
- Identify unnecessary abstractions (over-engineering) and missing abstractions (under-engineering) equally.
|
|
49
|
+
- Detect performance anti-patterns: N+1 query patterns, blocking I/O on the event loop, unnecessary re-renders, unbounded memory growth, missing pagination.
|
|
50
|
+
- Flag unhandled promise rejections, missing error boundaries, and swallowed exceptions.
|
|
51
|
+
- Call out inconsistencies with the codebase's own established patterns — internal inconsistency is a defect.
|
|
52
|
+
- Identify missing observability: critical paths with no logging, metrics, or tracing instrumentation.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
FINDING FORMAT — every issue must follow this structure:
|
|
57
|
+
- Severity: CRITICAL | HIGH | MEDIUM | LOW | SUGGESTION
|
|
58
|
+
- Location: <file path>:<line range>
|
|
59
|
+
- Finding: concise, technically precise description of the issue
|
|
60
|
+
- Impact: consequence in production terms (data breach, data corruption, outage, maintenance burden, etc.)
|
|
61
|
+
- Recommended Fix: concrete, idiomatic code snippet or specific architectural change
|
|
62
|
+
|
|
63
|
+
CONCLUSION — every review must end with a scorecard:
|
|
64
|
+
| Dimension | Rating (1–5) | Justification |
|
|
65
|
+
|-----------------|--------------|---------------|
|
|
66
|
+
| Security | | |
|
|
67
|
+
| Correctness | | |
|
|
68
|
+
| Maintainability | | |
|
|
69
|
+
| Performance | | |
|
|
70
|
+
|
|
71
|
+
Overall verdict: APPROVED | APPROVED WITH SUGGESTIONS | CHANGES REQUIRED | BLOCKED`,
|
|
72
|
+
};
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
4
|
+
import { appConfigDir } from '../cli/configStore.js';
|
|
5
|
+
let db;
|
|
6
|
+
export function getDatabasePath() {
|
|
7
|
+
return path.join(appConfigDir(), 'projects.sqlite');
|
|
8
|
+
}
|
|
9
|
+
function openDatabase() {
|
|
10
|
+
if (db)
|
|
11
|
+
return db;
|
|
12
|
+
const databasePath = getDatabasePath();
|
|
13
|
+
fs.mkdirSync(path.dirname(databasePath), { recursive: true });
|
|
14
|
+
db = new DatabaseSync(databasePath);
|
|
15
|
+
db.exec(`
|
|
16
|
+
PRAGMA journal_mode = WAL;
|
|
17
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
18
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
19
|
+
name TEXT NOT NULL,
|
|
20
|
+
path TEXT NOT NULL UNIQUE,
|
|
21
|
+
provider TEXT NOT NULL,
|
|
22
|
+
model TEXT NOT NULL,
|
|
23
|
+
base_url TEXT NOT NULL,
|
|
24
|
+
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
25
|
+
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
26
|
+
last_opened_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
CREATE TABLE IF NOT EXISTS chat_messages (
|
|
30
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
31
|
+
project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
32
|
+
role TEXT NOT NULL,
|
|
33
|
+
content TEXT NOT NULL,
|
|
34
|
+
model TEXT,
|
|
35
|
+
prompt_tokens INTEGER NOT NULL DEFAULT 0,
|
|
36
|
+
completion_tokens INTEGER NOT NULL DEFAULT 0,
|
|
37
|
+
total_tokens INTEGER NOT NULL DEFAULT 0,
|
|
38
|
+
estimated_cost REAL,
|
|
39
|
+
activity_json TEXT NOT NULL DEFAULT '[]',
|
|
40
|
+
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
41
|
+
);
|
|
42
|
+
`);
|
|
43
|
+
migrateChatMessages(db);
|
|
44
|
+
return db;
|
|
45
|
+
}
|
|
46
|
+
function migrateChatMessages(database) {
|
|
47
|
+
const rows = database.prepare('PRAGMA table_info(chat_messages)').all();
|
|
48
|
+
const columns = new Set(rows.map((row) => row.name));
|
|
49
|
+
const migrations = [
|
|
50
|
+
['model', 'ALTER TABLE chat_messages ADD COLUMN model TEXT'],
|
|
51
|
+
['prompt_tokens', 'ALTER TABLE chat_messages ADD COLUMN prompt_tokens INTEGER NOT NULL DEFAULT 0'],
|
|
52
|
+
['completion_tokens', 'ALTER TABLE chat_messages ADD COLUMN completion_tokens INTEGER NOT NULL DEFAULT 0'],
|
|
53
|
+
['total_tokens', 'ALTER TABLE chat_messages ADD COLUMN total_tokens INTEGER NOT NULL DEFAULT 0'],
|
|
54
|
+
['estimated_cost', 'ALTER TABLE chat_messages ADD COLUMN estimated_cost REAL'],
|
|
55
|
+
['activity_json', "ALTER TABLE chat_messages ADD COLUMN activity_json TEXT NOT NULL DEFAULT '[]'"],
|
|
56
|
+
];
|
|
57
|
+
for (const [column, sql] of migrations) {
|
|
58
|
+
if (!columns.has(column)) {
|
|
59
|
+
database.exec(sql);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function parseActivity(value) {
|
|
64
|
+
if (typeof value !== 'string' || !value.trim())
|
|
65
|
+
return [];
|
|
66
|
+
try {
|
|
67
|
+
const parsed = JSON.parse(value);
|
|
68
|
+
if (!Array.isArray(parsed))
|
|
69
|
+
return [];
|
|
70
|
+
return parsed.flatMap((item) => {
|
|
71
|
+
if (!item || typeof item !== 'object')
|
|
72
|
+
return [];
|
|
73
|
+
const raw = item;
|
|
74
|
+
const type = raw.type;
|
|
75
|
+
if (type !== 'status' && type !== 'tool_call' && type !== 'tool_result' && type !== 'tokens')
|
|
76
|
+
return [];
|
|
77
|
+
if (typeof raw.label !== 'string')
|
|
78
|
+
return [];
|
|
79
|
+
return [{
|
|
80
|
+
type,
|
|
81
|
+
label: raw.label,
|
|
82
|
+
detail: typeof raw.detail === 'string' ? raw.detail : undefined,
|
|
83
|
+
preview: typeof raw.preview === 'string' ? raw.preview : undefined,
|
|
84
|
+
full: typeof raw.full === 'string' ? raw.full : undefined,
|
|
85
|
+
truncated: typeof raw.truncated === 'boolean' ? raw.truncated : undefined,
|
|
86
|
+
}];
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function mapProject(row) {
|
|
94
|
+
const value = row;
|
|
95
|
+
return {
|
|
96
|
+
id: Number(value.id),
|
|
97
|
+
name: String(value.name),
|
|
98
|
+
path: String(value.path),
|
|
99
|
+
provider: String(value.provider),
|
|
100
|
+
model: String(value.model),
|
|
101
|
+
baseUrl: String(value.base_url),
|
|
102
|
+
createdAt: String(value.created_at),
|
|
103
|
+
updatedAt: String(value.updated_at),
|
|
104
|
+
lastOpenedAt: String(value.last_opened_at),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function mapChatMessage(row) {
|
|
108
|
+
const value = row;
|
|
109
|
+
return {
|
|
110
|
+
id: Number(value.id),
|
|
111
|
+
projectId: Number(value.project_id),
|
|
112
|
+
role: String(value.role),
|
|
113
|
+
content: String(value.content),
|
|
114
|
+
model: typeof value.model === 'string' ? value.model : undefined,
|
|
115
|
+
promptTokens: Number(value.prompt_tokens || 0),
|
|
116
|
+
completionTokens: Number(value.completion_tokens || 0),
|
|
117
|
+
totalTokens: Number(value.total_tokens || 0),
|
|
118
|
+
estimatedCost: typeof value.estimated_cost === 'number' ? value.estimated_cost : undefined,
|
|
119
|
+
activity: parseActivity(value.activity_json),
|
|
120
|
+
createdAt: String(value.created_at),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
export function upsertProject(projectPath, config) {
|
|
124
|
+
const resolvedPath = path.resolve(projectPath);
|
|
125
|
+
const name = path.basename(resolvedPath) || resolvedPath;
|
|
126
|
+
const database = openDatabase();
|
|
127
|
+
database.prepare(`
|
|
128
|
+
INSERT INTO projects (name, path, provider, model, base_url)
|
|
129
|
+
VALUES (?, ?, ?, ?, ?)
|
|
130
|
+
ON CONFLICT(path) DO UPDATE SET
|
|
131
|
+
name = excluded.name,
|
|
132
|
+
provider = excluded.provider,
|
|
133
|
+
model = excluded.model,
|
|
134
|
+
base_url = excluded.base_url,
|
|
135
|
+
updated_at = CURRENT_TIMESTAMP,
|
|
136
|
+
last_opened_at = CURRENT_TIMESTAMP
|
|
137
|
+
`).run(name, resolvedPath, config.provider, config.model, config.baseUrl);
|
|
138
|
+
const row = database.prepare('SELECT * FROM projects WHERE path = ?').get(resolvedPath);
|
|
139
|
+
if (!row) {
|
|
140
|
+
throw new Error(`Failed to save project ${resolvedPath}`);
|
|
141
|
+
}
|
|
142
|
+
return mapProject(row);
|
|
143
|
+
}
|
|
144
|
+
export function getProject(projectPath) {
|
|
145
|
+
const row = openDatabase().prepare('SELECT * FROM projects WHERE path = ?').get(path.resolve(projectPath));
|
|
146
|
+
return row ? mapProject(row) : undefined;
|
|
147
|
+
}
|
|
148
|
+
export function getProjectByName(name) {
|
|
149
|
+
const row = openDatabase()
|
|
150
|
+
.prepare('SELECT * FROM projects WHERE name = ? ORDER BY last_opened_at DESC, updated_at DESC LIMIT 1')
|
|
151
|
+
.get(name);
|
|
152
|
+
return row ? mapProject(row) : undefined;
|
|
153
|
+
}
|
|
154
|
+
export function getRecentProjects() {
|
|
155
|
+
return openDatabase()
|
|
156
|
+
.prepare('SELECT * FROM projects ORDER BY last_opened_at DESC, updated_at DESC LIMIT 20')
|
|
157
|
+
.all()
|
|
158
|
+
.map(mapProject);
|
|
159
|
+
}
|
|
160
|
+
export function saveChatMessage(projectId, role, content, usage = {}) {
|
|
161
|
+
const result = openDatabase()
|
|
162
|
+
.prepare(`
|
|
163
|
+
INSERT INTO chat_messages (
|
|
164
|
+
project_id,
|
|
165
|
+
role,
|
|
166
|
+
content,
|
|
167
|
+
model,
|
|
168
|
+
prompt_tokens,
|
|
169
|
+
completion_tokens,
|
|
170
|
+
total_tokens,
|
|
171
|
+
estimated_cost,
|
|
172
|
+
activity_json
|
|
173
|
+
)
|
|
174
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
175
|
+
`)
|
|
176
|
+
.run(projectId, role, content, usage.model ?? null, usage.promptTokens ?? 0, usage.completionTokens ?? 0, usage.totalTokens ?? 0, usage.estimatedCost ?? null, JSON.stringify(usage.activity ?? []));
|
|
177
|
+
return Number(result.lastInsertRowid);
|
|
178
|
+
}
|
|
179
|
+
export function updateChatMessage(id, content, usage = {}) {
|
|
180
|
+
openDatabase()
|
|
181
|
+
.prepare(`
|
|
182
|
+
UPDATE chat_messages
|
|
183
|
+
SET
|
|
184
|
+
content = ?,
|
|
185
|
+
model = ?,
|
|
186
|
+
prompt_tokens = ?,
|
|
187
|
+
completion_tokens = ?,
|
|
188
|
+
total_tokens = ?,
|
|
189
|
+
estimated_cost = ?,
|
|
190
|
+
activity_json = ?
|
|
191
|
+
WHERE id = ?
|
|
192
|
+
`)
|
|
193
|
+
.run(content, usage.model ?? null, usage.promptTokens ?? 0, usage.completionTokens ?? 0, usage.totalTokens ?? 0, usage.estimatedCost ?? null, JSON.stringify(usage.activity ?? []), id);
|
|
194
|
+
}
|
|
195
|
+
export function getChatMessages(projectId) {
|
|
196
|
+
return openDatabase()
|
|
197
|
+
.prepare('SELECT * FROM chat_messages WHERE project_id = ? ORDER BY id ASC LIMIT 100')
|
|
198
|
+
.all(projectId)
|
|
199
|
+
.map(mapChatMessage);
|
|
200
|
+
}
|
|
201
|
+
export function clearChatMessages(projectId) {
|
|
202
|
+
const result = openDatabase()
|
|
203
|
+
.prepare('DELETE FROM chat_messages WHERE project_id = ?')
|
|
204
|
+
.run(projectId);
|
|
205
|
+
return Number(result.changes);
|
|
206
|
+
}
|
|
207
|
+
export function deleteProject(projectId) {
|
|
208
|
+
const database = openDatabase();
|
|
209
|
+
database.exec('BEGIN');
|
|
210
|
+
try {
|
|
211
|
+
database.prepare('DELETE FROM chat_messages WHERE project_id = ?').run(projectId);
|
|
212
|
+
const result = database.prepare('DELETE FROM projects WHERE id = ?').run(projectId);
|
|
213
|
+
database.exec('COMMIT');
|
|
214
|
+
return Number(result.changes);
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
database.exec('ROLLBACK');
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
}
|