kachow 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.
Files changed (48) hide show
  1. package/README.md +77 -0
  2. package/_server/dist/app.js +130 -0
  3. package/_server/dist/db/index.js +50 -0
  4. package/_server/dist/db/schema.js +247 -0
  5. package/_server/dist/queues/ingestQueue.js +49 -0
  6. package/_server/dist/queues/redis.js +58 -0
  7. package/_server/dist/routes/agents.js +162 -0
  8. package/_server/dist/routes/architecture.js +88 -0
  9. package/_server/dist/routes/config.js +24 -0
  10. package/_server/dist/routes/github.js +158 -0
  11. package/_server/dist/routes/graph.js +112 -0
  12. package/_server/dist/routes/healing.js +137 -0
  13. package/_server/dist/routes/impact.js +100 -0
  14. package/_server/dist/routes/ingest.js +182 -0
  15. package/_server/dist/routes/manager.js +179 -0
  16. package/_server/dist/routes/notifications.js +85 -0
  17. package/_server/dist/routes/qa.js +68 -0
  18. package/_server/dist/routes/scanner.js +221 -0
  19. package/_server/dist/routes/stream.js +179 -0
  20. package/_server/dist/routes/webhooks.js +168 -0
  21. package/_server/dist/server.js +46 -0
  22. package/_server/dist/services/agentService.js +715 -0
  23. package/_server/dist/services/architectureService.js +172 -0
  24. package/_server/dist/services/demoSeed.js +181 -0
  25. package/_server/dist/services/graphLayout.js +102 -0
  26. package/_server/dist/services/graphService.js +532 -0
  27. package/_server/dist/services/healingService.js +253 -0
  28. package/_server/dist/services/impactService.js +304 -0
  29. package/_server/dist/services/ingestService.js +129 -0
  30. package/_server/dist/services/managerService.js +260 -0
  31. package/_server/dist/services/notificationService.js +283 -0
  32. package/_server/dist/services/qaService.js +413 -0
  33. package/_server/dist/services/scannerService.js +748 -0
  34. package/_server/dist/services/seedService.js +215 -0
  35. package/_server/dist/sse/sseManager.js +101 -0
  36. package/_server/dist/types/index.js +38 -0
  37. package/_server/dist/workers/ingestWorker.js +274 -0
  38. package/_server/public/assets/index-BTkbB_YF.js +4546 -0
  39. package/_server/public/assets/index-Bmh3jWBm.css +1 -0
  40. package/_server/public/favicon.ico +0 -0
  41. package/_server/public/images/glass-waves-bg.png +0 -0
  42. package/_server/public/index.html +29 -0
  43. package/_server/public/placeholder.svg +1 -0
  44. package/_server/public/robots.txt +14 -0
  45. package/dist/config.js +133 -0
  46. package/dist/index.js +510 -0
  47. package/dist/setup.js +223 -0
  48. package/package.json +62 -0
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # ⚡ KA-CHOW
2
+
3
+ **Living knowledge graph for your engineering system.**
4
+
5
+ Scan any codebase and get an interactive architecture dashboard — dependency graph, health scores, impact analysis, and AI-powered insights — all in your browser.
6
+
7
+ ![KA-CHOW Dashboard](https://raw.githubusercontent.com/HarshXAI/kachow/main/docs/dashboard.png)
8
+
9
+ ## Quick Start
10
+
11
+ ```bash
12
+ npx kachow init
13
+ ```
14
+
15
+ That's it. KA-CHOW will:
16
+ 1. 🔑 Ask for your API keys (Anthropic required, others optional)
17
+ 2. 🔍 Auto-detect and scan your repositories
18
+ 3. 🚀 Start a local server with an interactive dashboard
19
+ 4. 🌐 Open your browser at `http://localhost:3000`
20
+
21
+ ## What You Get
22
+
23
+ - **Interactive Knowledge Graph** — Visualize every service, dependency, and data flow
24
+ - **Health Scores** — Per-service health with latency, error rate, and coverage metrics
25
+ - **Impact Analysis** — "What breaks if I change this?" with blast radius visualization
26
+ - **AI Q&A Terminal** — Ask questions about your architecture in natural language
27
+ - **Self-Healing Suggestions** — AI-detected issues with one-click fix proposals
28
+ - **Architecture Blueprints** — Per-service compliance reports against your standards
29
+ - **Manager Dashboard** — Team onboarding, critical issues, and deployment overview
30
+
31
+ ## Commands
32
+
33
+ ```bash
34
+ kachow init # Full setup: credentials → scan → dashboard
35
+ kachow serve # Start server without re-scanning
36
+ kachow scan -p ./path # Scan a specific repo
37
+ kachow health # Print system health summary
38
+ kachow reset --force # Wipe local data and start fresh
39
+ ```
40
+
41
+ ## Requirements
42
+
43
+ - **Node.js 18+**
44
+ - **Anthropic API key** (for AI features — get one at [console.anthropic.com](https://console.anthropic.com))
45
+
46
+ Optional: GitHub token (for private repos), OpenAI key (alternate models), Slack/Jira tokens (integrations).
47
+
48
+ ## How It Works
49
+
50
+ KA-CHOW scans your codebase to build a knowledge graph:
51
+ - Detects services, packages, and modules
52
+ - Maps imports, API calls, and data flows into dependency edges
53
+ - Scores health based on test coverage, documentation, error handling, and more
54
+ - Stores everything in a local SQLite database (`.kachow/kachow.db`)
55
+
56
+ No data ever leaves your machine unless you explicitly configure cloud integrations.
57
+
58
+ ## Configuration
59
+
60
+ Create a `kachow.config.yaml` in your project root (auto-generated on first `init`):
61
+
62
+ ```yaml
63
+ version: 1
64
+ project:
65
+ name: "My Platform"
66
+ repos:
67
+ - name: api-service
68
+ path: ./services/api
69
+ language: typescript
70
+ standards:
71
+ require_readme: true
72
+ min_test_coverage: 70
73
+ ```
74
+
75
+ ## License
76
+
77
+ MIT
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ /**
3
+ * KA-CHOW Express application.
4
+ * Sets up middleware, mounts route handlers, and defines error handling.
5
+ * This file exports the `app` — it does NOT call app.listen().
6
+ * Server startup lives in server.ts.
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.app = void 0;
13
+ const express_1 = __importDefault(require("express"));
14
+ const cors_1 = __importDefault(require("cors"));
15
+ const dotenv_1 = __importDefault(require("dotenv"));
16
+ const path_1 = require("path");
17
+ const fs_1 = require("fs");
18
+ const ingest_js_1 = require("./routes/ingest.js");
19
+ const graph_js_1 = require("./routes/graph.js");
20
+ const github_js_1 = require("./routes/github.js");
21
+ const impact_js_1 = require("./routes/impact.js");
22
+ const qa_js_1 = require("./routes/qa.js");
23
+ const stream_js_1 = require("./routes/stream.js");
24
+ const manager_js_1 = require("./routes/manager.js");
25
+ const architecture_js_1 = require("./routes/architecture.js");
26
+ const healing_js_1 = require("./routes/healing.js");
27
+ const webhooks_js_1 = require("./routes/webhooks.js");
28
+ const scanner_js_1 = require("./routes/scanner.js");
29
+ const agents_js_1 = require("./routes/agents.js");
30
+ const notifications_js_1 = require("./routes/notifications.js");
31
+ const config_js_1 = require("./routes/config.js");
32
+ // Load .env — try monorepo root first (packages/server → ../../), then local fallback
33
+ dotenv_1.default.config({ path: (0, path_1.resolve)(process.cwd(), '../../.env') });
34
+ dotenv_1.default.config(); // no-op if already loaded; handles local .env for non-monorepo setups
35
+ const app = (0, express_1.default)();
36
+ exports.app = app;
37
+ // ── Middleware ────────────────────────────────────────────────────────────────
38
+ const PORT = process.env.PORT || '3000';
39
+ const ALLOWED_ORIGINS = (process.env.CORS_ORIGIN || '')
40
+ .split(',')
41
+ .map(o => o.trim())
42
+ .filter(Boolean)
43
+ .concat([
44
+ `http://localhost:${PORT}`, // same-origin when served by Express
45
+ `http://127.0.0.1:${PORT}`,
46
+ 'http://localhost:5173', // Vite dev server
47
+ 'http://localhost:8080',
48
+ 'http://127.0.0.1:8080',
49
+ ]);
50
+ app.use((0, cors_1.default)({
51
+ origin: (origin, cb) => {
52
+ // Allow requests with no origin (curl, Postman, server-to-server)
53
+ if (!origin)
54
+ return cb(null, true);
55
+ if (ALLOWED_ORIGINS.includes(origin))
56
+ return cb(null, true);
57
+ cb(new Error(`CORS: origin ${origin} not allowed`));
58
+ },
59
+ credentials: true,
60
+ }));
61
+ app.use(express_1.default.json());
62
+ app.use(express_1.default.urlencoded({ extended: true }));
63
+ // ── Static files — serve built React app ──────────────────────────────────────
64
+ // __dirname works in CJS (tsc compiles to CJS). In dev (tsx) we fallback.
65
+ const publicDir = (0, path_1.resolve)(__dirname, '../public');
66
+ if ((0, fs_1.existsSync)(publicDir)) {
67
+ app.use(express_1.default.static(publicDir));
68
+ }
69
+ // ── Routes ────────────────────────────────────────────────────────────────────
70
+ /**
71
+ * GET /health
72
+ * Server health check used by load balancers and the frontend status strip.
73
+ *
74
+ * @returns Standard ApiResponse with server status and version
75
+ */
76
+ app.get('/health', (_req, res) => {
77
+ res.json({
78
+ success: true,
79
+ data: { status: 'ok', version: '1.0.0' },
80
+ error: null,
81
+ meta: { timestamp: new Date().toISOString(), version: '1.0.0' },
82
+ });
83
+ });
84
+ // ── API Routes ───────────────────────────────────────────────────────────────
85
+ app.use('/api/ingest', ingest_js_1.ingestRouter);
86
+ app.use('/api/graph', graph_js_1.graphRouter);
87
+ app.use('/api/github', github_js_1.githubRouter);
88
+ app.use('/api/impact', impact_js_1.impactRouter);
89
+ app.use('/api/qa', qa_js_1.qaRouter);
90
+ app.use('/api/stream', stream_js_1.streamRouter);
91
+ app.use('/api/manager', manager_js_1.managerRouter);
92
+ app.use('/api/onboarding', manager_js_1.managerRouter); // brief: GET /api/onboarding/path/:role
93
+ app.use('/api/architecture', architecture_js_1.architectureRouter);
94
+ app.use('/api/heal', healing_js_1.healingRouter);
95
+ app.use('/api/webhooks', webhooks_js_1.webhooksRouter);
96
+ app.use('/api/scanner', scanner_js_1.scannerRouter);
97
+ app.use('/api/agents', agents_js_1.agentRouter);
98
+ app.use('/api/notifications', notifications_js_1.notificationRouter);
99
+ app.use('/api/ci', webhooks_js_1.webhooksRouter);
100
+ app.use('/api/config', config_js_1.configRouter);
101
+ // ── SPA catch-all — serve index.html for non-API routes ──────────────────────
102
+ const indexPath = (0, path_1.join)(publicDir, 'index.html');
103
+ if ((0, fs_1.existsSync)(indexPath)) {
104
+ app.use((req, res, next) => {
105
+ if (req.path.startsWith('/api') || req.path === '/health')
106
+ return next();
107
+ res.sendFile(indexPath);
108
+ });
109
+ }
110
+ // ── 404 handler (only hits for unmatched /api/* routes) ───────────────────────
111
+ app.use((req, res) => {
112
+ res.status(404).json({
113
+ success: false,
114
+ data: null,
115
+ error: `Route ${req.method} ${req.path} not found`,
116
+ meta: { timestamp: new Date().toISOString(), version: '1.0.0' },
117
+ });
118
+ });
119
+ // ── Global error handler ──────────────────────────────────────────────────────
120
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
121
+ app.use((err, _req, res, _next) => {
122
+ console.error('[KA-CHOW Error]', err.stack);
123
+ res.status(500).json({
124
+ success: false,
125
+ data: null,
126
+ error: err.message || 'Internal Server Error',
127
+ meta: { timestamp: new Date().toISOString(), version: '1.0.0' },
128
+ });
129
+ });
130
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ /**
3
+ * KA-CHOW database singleton.
4
+ * Returns the same better-sqlite3 instance for the lifetime of the process.
5
+ * In test mode (NODE_ENV=test) uses TEST_DB_PATH (defaults to :memory:).
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.getDb = getDb;
12
+ exports.closeDb = closeDb;
13
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
14
+ const path_1 = __importDefault(require("path"));
15
+ const schema_js_1 = require("./schema.js");
16
+ let _db = null;
17
+ /**
18
+ * Returns the initialised SQLite database singleton.
19
+ * Creates the database file and runs all migrations on first call.
20
+ * Subsequent calls return the same cached instance.
21
+ *
22
+ * @returns The better-sqlite3 Database instance
23
+ * @throws If the database file cannot be created or migrations fail
24
+ */
25
+ function getDb() {
26
+ if (_db)
27
+ return _db;
28
+ const isTest = process.env.NODE_ENV === 'test';
29
+ const dbPath = isTest
30
+ ? (process.env.TEST_DB_PATH ?? ':memory:')
31
+ : (process.env.DB_PATH ?? path_1.default.resolve(process.cwd(), 'kachow.db'));
32
+ _db = new better_sqlite3_1.default(dbPath);
33
+ _db.pragma('journal_mode = WAL');
34
+ _db.pragma('foreign_keys = ON');
35
+ (0, schema_js_1.runMigrations)(_db);
36
+ return _db;
37
+ }
38
+ /**
39
+ * Closes the database connection and clears the singleton.
40
+ * Must be called on graceful shutdown and in test afterAll hooks.
41
+ *
42
+ * @returns void
43
+ */
44
+ function closeDb() {
45
+ if (_db) {
46
+ _db.close();
47
+ _db = null;
48
+ }
49
+ }
50
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ /**
3
+ * KA-CHOW SQLite schema.
4
+ * All CREATE TABLE statements live here.
5
+ * Called once by db/index.ts on first startup and by test-helpers in tests.
6
+ *
7
+ * Rule: Never DROP or ALTER tables directly — write a migration in
8
+ * packages/server/src/db/migrations/ instead.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.TABLES = void 0;
12
+ exports.runMigrations = runMigrations;
13
+ /**
14
+ * All table names in the KA-CHOW schema.
15
+ * Used by tests to assert every table was created.
16
+ */
17
+ exports.TABLES = [
18
+ 'services',
19
+ 'dependencies',
20
+ 'incidents',
21
+ 'endpoints',
22
+ 'impact_analyses',
23
+ 'adrs',
24
+ 'healing_prs',
25
+ 'qa_sessions',
26
+ 'activity_feed',
27
+ 'snapshots',
28
+ 'teams',
29
+ 'team_members',
30
+ 'agent_analyses',
31
+ 'knowledge_graph',
32
+ ];
33
+ /**
34
+ * Runs all DDL statements against the provided database instance.
35
+ * Uses CREATE TABLE IF NOT EXISTS — idempotent and safe to call multiple times.
36
+ *
37
+ * @param db - An initialised better-sqlite3 Database instance
38
+ * @returns void
39
+ * @throws If SQLite reports a syntax or constraint error in the DDL
40
+ */
41
+ function runMigrations(db) {
42
+ db.exec(`
43
+ -- ── TEAMS (manager-created, join-code gated) ─────────────────────
44
+ -- Created FIRST because services references teams(id)
45
+ CREATE TABLE IF NOT EXISTS teams (
46
+ id TEXT PRIMARY KEY,
47
+ name TEXT NOT NULL,
48
+ repo TEXT DEFAULT '—',
49
+ join_code TEXT NOT NULL UNIQUE, -- 6-digit numeric code
50
+ description TEXT DEFAULT '',
51
+ color TEXT DEFAULT '#6366F1',
52
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
53
+ updated_at TEXT DEFAULT CURRENT_TIMESTAMP
54
+ );
55
+
56
+ -- ── TEAM MEMBERS ─────────────────────────────────────────────────
57
+ CREATE TABLE IF NOT EXISTS team_members (
58
+ id TEXT PRIMARY KEY,
59
+ team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
60
+ name TEXT NOT NULL,
61
+ role TEXT DEFAULT 'Engineer',
62
+ online INTEGER DEFAULT 0, -- 0 = offline, 1 = online
63
+ progress INTEGER DEFAULT 0, -- onboarding progress (0-7)
64
+ joined_at TEXT DEFAULT CURRENT_TIMESTAMP
65
+ );
66
+
67
+ -- ── SERVICES (graph nodes) ──────────────────────────────────────
68
+ CREATE TABLE IF NOT EXISTS services (
69
+ id TEXT PRIMARY KEY, -- slug: 'auth-service'
70
+ name TEXT NOT NULL, -- 'Auth Service'
71
+ repo_url TEXT, -- github.com/org/auth-service
72
+ repo_subpath TEXT, -- subdir within monorepo e.g. 'services/blog'
73
+ team TEXT, -- legacy sub-team label e.g. 'platform'
74
+ team_id TEXT REFERENCES teams(id) ON DELETE CASCADE, -- FK to teams table for multi-tenancy
75
+ language TEXT, -- 'typescript'
76
+ health_score INTEGER DEFAULT 100, -- 0-100
77
+ health_tier TEXT DEFAULT 'healthy', -- healthy/warning/critical
78
+ doc_coverage INTEGER DEFAULT 0, -- % documented
79
+ test_coverage INTEGER DEFAULT 0, -- % tested
80
+ is_external INTEGER DEFAULT 0, -- 1 = unresolved external dep, not a real service
81
+ last_commit TEXT, -- ISO timestamp
82
+ last_ingested TEXT, -- ISO timestamp
83
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
84
+ updated_at TEXT DEFAULT CURRENT_TIMESTAMP
85
+ );
86
+
87
+ -- ── DEPENDENCIES (graph edges) ──────────────────────────────────
88
+ CREATE TABLE IF NOT EXISTS dependencies (
89
+ id TEXT PRIMARY KEY,
90
+ source_id TEXT REFERENCES services(id) ON DELETE CASCADE,
91
+ target_id TEXT REFERENCES services(id) ON DELETE CASCADE,
92
+ type TEXT NOT NULL, -- REST/Event/gRPC/DB
93
+ endpoints TEXT, -- JSON array of endpoint strings
94
+ strength INTEGER DEFAULT 1, -- call frequency weight
95
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
96
+ UNIQUE(source_id, target_id, type) -- one edge per direction+type pair
97
+ );
98
+
99
+ -- ── INCIDENTS ───────────────────────────────────────────────────
100
+ CREATE TABLE IF NOT EXISTS incidents (
101
+ id TEXT PRIMARY KEY,
102
+ service_id TEXT REFERENCES services(id) ON DELETE CASCADE,
103
+ severity TEXT NOT NULL, -- P0/P1/P2/P3
104
+ title TEXT NOT NULL,
105
+ root_cause TEXT,
106
+ status TEXT DEFAULT 'open', -- open/resolved/postmortem
107
+ occurred_at TEXT NOT NULL,
108
+ resolved_at TEXT,
109
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
110
+ );
111
+
112
+ -- ── ENDPOINTS ───────────────────────────────────────────────────
113
+ CREATE TABLE IF NOT EXISTS endpoints (
114
+ id TEXT PRIMARY KEY,
115
+ service_id TEXT REFERENCES services(id) ON DELETE CASCADE,
116
+ method TEXT NOT NULL, -- GET/POST/PUT/DELETE
117
+ path TEXT NOT NULL, -- /api/v1/users/:id
118
+ deprecated INTEGER DEFAULT 0, -- 0 or 1
119
+ has_spec INTEGER DEFAULT 0, -- OpenAPI spec exists
120
+ consumers TEXT, -- JSON array of service ids
121
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
122
+ updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
123
+ UNIQUE(service_id, path, method) -- one row per service+route+verb
124
+ );
125
+
126
+ -- ── IMPACT ANALYSES ─────────────────────────────────────────────
127
+ CREATE TABLE IF NOT EXISTS impact_analyses (
128
+ id TEXT PRIMARY KEY,
129
+ trigger_type TEXT NOT NULL, -- deprecation/schema-change/delete
130
+ trigger_ref TEXT NOT NULL, -- endpoint or schema name
131
+ service_id TEXT REFERENCES services(id) ON DELETE CASCADE,
132
+ team_id TEXT REFERENCES teams(id) ON DELETE CASCADE,
133
+ affected TEXT NOT NULL, -- JSON: [{id, name, impact_type}]
134
+ risk_score REAL NOT NULL, -- 0.0-10.0
135
+ confidence REAL NOT NULL, -- 0.0-1.0
136
+ precedent TEXT, -- historical match description
137
+ status TEXT DEFAULT 'pending', -- pending/reviewed/actioned
138
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
139
+ );
140
+
141
+ -- ── ADR DRAFTS ──────────────────────────────────────────────────
142
+ CREATE TABLE IF NOT EXISTS adrs (
143
+ id TEXT PRIMARY KEY,
144
+ analysis_id TEXT REFERENCES impact_analyses(id) ON DELETE CASCADE,
145
+ title TEXT NOT NULL,
146
+ context TEXT NOT NULL,
147
+ decision TEXT NOT NULL,
148
+ consequences TEXT NOT NULL,
149
+ status TEXT DEFAULT 'draft', -- draft/approved/rejected
150
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
151
+ );
152
+
153
+ -- ── SELF-HEALING PRs ────────────────────────────────────────────
154
+ CREATE TABLE IF NOT EXISTS healing_prs (
155
+ id TEXT PRIMARY KEY,
156
+ service_id TEXT REFERENCES services(id) ON DELETE CASCADE,
157
+ team_id TEXT REFERENCES teams(id) ON DELETE CASCADE,
158
+ issue_type TEXT NOT NULL, -- missing-retry/no-error-handling/etc
159
+ patch TEXT NOT NULL, -- the actual code diff
160
+ explanation TEXT NOT NULL,
161
+ github_pr_url TEXT,
162
+ github_pr_num INTEGER,
163
+ status TEXT DEFAULT 'open', -- open/merged/closed
164
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
165
+ );
166
+
167
+ -- ── Q&A HISTORY ─────────────────────────────────────────────────
168
+ CREATE TABLE IF NOT EXISTS qa_sessions (
169
+ id TEXT PRIMARY KEY,
170
+ team_id TEXT REFERENCES teams(id) ON DELETE CASCADE,
171
+ question TEXT NOT NULL,
172
+ answer TEXT NOT NULL,
173
+ citations TEXT NOT NULL, -- JSON array of {file, line, snippet}
174
+ related_nodes TEXT NOT NULL, -- JSON array of service ids
175
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
176
+ );
177
+
178
+ -- ── ACTIVITY FEED ───────────────────────────────────────────────
179
+ CREATE TABLE IF NOT EXISTS activity_feed (
180
+ id TEXT PRIMARY KEY,
181
+ type TEXT NOT NULL, -- commit/health-drop/pr-opened/etc
182
+ service_id TEXT,
183
+ team_id TEXT REFERENCES teams(id) ON DELETE CASCADE,
184
+ title TEXT NOT NULL,
185
+ detail TEXT,
186
+ severity TEXT DEFAULT 'info', -- info/warning/critical
187
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
188
+ );
189
+
190
+ -- ── GRAPH SNAPSHOTS ─────────────────────────────────────────────
191
+ CREATE TABLE IF NOT EXISTS snapshots (
192
+ id TEXT PRIMARY KEY,
193
+ version TEXT NOT NULL,
194
+ team_id TEXT REFERENCES teams(id) ON DELETE CASCADE,
195
+ nodes TEXT NOT NULL, -- JSON: GraphNode[]
196
+ edges TEXT NOT NULL, -- JSON: GraphEdge[]
197
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
198
+ UNIQUE(version, team_id) -- one snapshot per version per team
199
+ );
200
+
201
+ -- ── AGENT ANALYSES ──────────────────────────────────────────────
202
+ CREATE TABLE IF NOT EXISTS agent_analyses (
203
+ id TEXT PRIMARY KEY,
204
+ analysis_id TEXT NOT NULL,
205
+ agent_name TEXT NOT NULL,
206
+ service_id TEXT,
207
+ service_name TEXT,
208
+ team_id TEXT REFERENCES teams(id) ON DELETE CASCADE,
209
+ findings TEXT NOT NULL,
210
+ summary TEXT NOT NULL,
211
+ duration_ms INTEGER,
212
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
213
+ );
214
+
215
+ -- ── KNOWLEDGE GRAPH (agent-produced) ────────────────────────────
216
+ CREATE TABLE IF NOT EXISTS knowledge_graph (
217
+ id TEXT PRIMARY KEY,
218
+ team_id TEXT REFERENCES teams(id) ON DELETE CASCADE,
219
+ nodes TEXT NOT NULL, -- JSON: KnowledgeNode[]
220
+ edges TEXT NOT NULL, -- JSON: KnowledgeEdge[]
221
+ architecture TEXT NOT NULL, -- JSON: { layers, dataFlows }
222
+ self_healing TEXT NOT NULL, -- JSON: SelfHealingSuggestion[]
223
+ summary TEXT NOT NULL,
224
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
225
+ );
226
+ `);
227
+ // ── Run additive migrations for existing databases ────────────────────────
228
+ // These are safe to run repeatedly — they only add columns if missing.
229
+ const safeAddColumn = (table, column, definition) => {
230
+ try {
231
+ db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
232
+ }
233
+ catch {
234
+ // Column already exists — this is expected and safe to ignore
235
+ }
236
+ };
237
+ // Multi-tenancy columns for existing DBs
238
+ safeAddColumn('services', 'team_id', 'TEXT REFERENCES teams(id) ON DELETE CASCADE');
239
+ safeAddColumn('impact_analyses', 'team_id', 'TEXT REFERENCES teams(id) ON DELETE CASCADE');
240
+ safeAddColumn('healing_prs', 'team_id', 'TEXT REFERENCES teams(id) ON DELETE CASCADE');
241
+ safeAddColumn('qa_sessions', 'team_id', 'TEXT REFERENCES teams(id) ON DELETE CASCADE');
242
+ safeAddColumn('activity_feed', 'team_id', 'TEXT REFERENCES teams(id) ON DELETE CASCADE');
243
+ safeAddColumn('snapshots', 'team_id', 'TEXT REFERENCES teams(id) ON DELETE CASCADE');
244
+ safeAddColumn('agent_analyses', 'team_id', 'TEXT REFERENCES teams(id) ON DELETE CASCADE');
245
+ safeAddColumn('knowledge_graph', 'team_id', 'TEXT REFERENCES teams(id) ON DELETE CASCADE');
246
+ }
247
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ /**
3
+ * BullMQ ingest queue definition.
4
+ * All repository ingestion jobs are enqueued here and processed by ingestWorker.ts.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.getIngestQueue = getIngestQueue;
8
+ exports.closeIngestQueue = closeIngestQueue;
9
+ const bullmq_1 = require("bullmq");
10
+ const redis_js_1 = require("./redis.js");
11
+ let _ingestQueue = null;
12
+ /**
13
+ * Returns (and lazily creates) the ingest BullMQ queue.
14
+ * Returns null when Redis is disabled — callers should run jobs in-process instead.
15
+ *
16
+ * @returns BullMQ Queue for ingestion jobs, or null if Redis is disabled
17
+ */
18
+ function getIngestQueue() {
19
+ if ((0, redis_js_1.isRedisDisabled)())
20
+ return null;
21
+ const redis = (0, redis_js_1.getRedis)();
22
+ if (!redis)
23
+ return null;
24
+ if (!_ingestQueue) {
25
+ _ingestQueue = new bullmq_1.Queue('ingest', {
26
+ connection: redis,
27
+ defaultJobOptions: {
28
+ attempts: 2,
29
+ backoff: { type: 'exponential', delay: 3000 },
30
+ removeOnComplete: { count: 100 },
31
+ removeOnFail: { count: 50 },
32
+ },
33
+ });
34
+ }
35
+ return _ingestQueue;
36
+ }
37
+ /**
38
+ * Closes the ingest queue connection.
39
+ * Call during graceful server shutdown.
40
+ *
41
+ * @returns Promise that resolves when queue is closed
42
+ */
43
+ async function closeIngestQueue() {
44
+ if (_ingestQueue) {
45
+ await _ingestQueue.close();
46
+ _ingestQueue = null;
47
+ }
48
+ }
49
+ //# sourceMappingURL=ingestQueue.js.map
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ /**
3
+ * Redis connection for BullMQ.
4
+ * A single shared IORedis instance reused by all queues and workers.
5
+ * Connection parameters are read from environment variables.
6
+ *
7
+ * Set DISABLE_REDIS=true to skip Redis entirely (jobs run in-process).
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.isRedisDisabled = isRedisDisabled;
11
+ exports.getRedis = getRedis;
12
+ exports.closeRedis = closeRedis;
13
+ const ioredis_1 = require("ioredis");
14
+ let _redis = null;
15
+ /** Returns true when Redis has been explicitly disabled via env var. */
16
+ function isRedisDisabled() {
17
+ return process.env.DISABLE_REDIS === 'true';
18
+ }
19
+ /**
20
+ * Returns (and lazily creates) the shared Redis client.
21
+ * Returns null when DISABLE_REDIS=true — callers must handle the null case.
22
+ *
23
+ * @returns Shared IORedis instance or null if Redis is disabled
24
+ */
25
+ function getRedis() {
26
+ if (isRedisDisabled())
27
+ return null;
28
+ if (!_redis) {
29
+ _redis = new ioredis_1.Redis({
30
+ host: process.env.REDIS_HOST || '127.0.0.1',
31
+ port: Number(process.env.REDIS_PORT) || 6379,
32
+ password: process.env.REDIS_PASSWORD || undefined,
33
+ maxRetriesPerRequest: null, // required by BullMQ
34
+ enableReadyCheck: false,
35
+ // Stop hammering the console — log once, then back off silently
36
+ retryStrategy: (times) => {
37
+ if (times === 1)
38
+ console.warn('[Redis] Not available — ingest jobs will run in-process. Set DISABLE_REDIS=true to silence this.');
39
+ return Math.min(times * 500, 10_000);
40
+ },
41
+ });
42
+ _redis.on('error', () => { });
43
+ }
44
+ return _redis;
45
+ }
46
+ /**
47
+ * Closes the shared Redis connection.
48
+ * Call during graceful server shutdown.
49
+ *
50
+ * @returns Promise that resolves when connection is fully closed
51
+ */
52
+ async function closeRedis() {
53
+ if (_redis) {
54
+ await _redis.quit();
55
+ _redis = null;
56
+ }
57
+ }
58
+ //# sourceMappingURL=redis.js.map