@prajwolkc/stk 0.5.1 → 0.7.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.
@@ -1,11 +1,44 @@
1
1
  import { Command } from "commander";
2
2
  import chalk from "chalk";
3
3
  import ora from "ora";
4
- import { syncBrain, pushToCloud, pullFromCloud, loadBrainStore, getAllEntries } from "../services/brain.js";
4
+ import { syncBrain, pushToCloud, pullFromCloud, loadBrainStore, getAllEntries, seedBrain } from "../services/brain.js";
5
+ import { readFileSync } from "fs";
6
+ import { fileURLToPath } from "url";
7
+ import { dirname, join } from "path";
5
8
  export const brainCommand = new Command("brain")
6
9
  .description("Manage the stk knowledge brain — sync, push, pull across machines")
7
- .argument("[action]", "push | pull | sync | stats (default: sync)")
8
- .action(async (action = "sync") => {
10
+ .argument("[action]", "push | pull | sync | stats | seed (default: sync)")
11
+ .option("--force", "force re-seed (replace existing seed entries)")
12
+ .action(async (action = "sync", _extra, cmd) => {
13
+ const opts = cmd?.opts?.() ?? {};
14
+ if (action === "seed") {
15
+ const spinner = ora(" Loading seed patterns...").start();
16
+ try {
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = dirname(__filename);
19
+ // Data lives in src/data (shipped with npm), not dist/data
20
+ const seedPath = join(__dirname, "..", "..", "src", "data", "seed-patterns.json");
21
+ const patterns = JSON.parse(readFileSync(seedPath, "utf-8"));
22
+ spinner.text = ` Seeding ${patterns.length} patterns...`;
23
+ const { added, skipped } = seedBrain(patterns, opts.force);
24
+ spinner.succeed(` Seeded ${chalk.white(added)} patterns (${skipped} already existed)`);
25
+ console.log();
26
+ const categories = {};
27
+ for (const p of patterns)
28
+ categories[p.category] = (categories[p.category] || 0) + 1;
29
+ console.log(chalk.bold(" Patterns by category:"));
30
+ for (const [cat, count] of Object.entries(categories).sort(([, a], [, b]) => b - a)) {
31
+ console.log(` ${chalk.dim("●")} ${cat}: ${count}`);
32
+ }
33
+ console.log();
34
+ }
35
+ catch (err) {
36
+ spinner.fail(" Seed failed");
37
+ console.log(` ${chalk.red(err instanceof Error ? err.message : String(err))}`);
38
+ console.log();
39
+ }
40
+ return;
41
+ }
9
42
  if (action === "stats") {
10
43
  const store = loadBrainStore();
11
44
  const projects = Object.entries(store.projects);
@@ -11,7 +11,9 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
11
11
  import { z } from "zod";
12
12
  import { loadConfig, enabledServices } from "../lib/config.js";
13
13
  import { getChecker, allCheckerNames, loadPluginCheckers } from "../services/registry.js";
14
- import { getLocalBrainClient, ingestProject, loadBrainStore, saveBrainStore, syncBrain, pushToCloud, pullFromCloud } from "../services/brain.js";
14
+ import { getLocalBrainClient, ingestProject, loadBrainStore, saveBrainStore, syncBrain, pushToCloud, pullFromCloud, brainCheck, brainDiagnose, reviewDiff } from "../services/brain.js";
15
+ import { recordMetric, getMetrics, compareToBaseline, getDeployFrequency, getErrorRate, getUptime } from "../services/metrics.js";
16
+ import { runAllChecks } from "../services/security.js";
15
17
  import { execSync } from "child_process";
16
18
  const server = new McpServer({
17
19
  name: "stk",
@@ -1163,6 +1165,83 @@ server.tool("stk_brain_stats", "Check what the brain knows — total knowledge e
1163
1165
  };
1164
1166
  });
1165
1167
  // ──────────────────────────────────────────
1168
+ // Tool: stk_brain_check
1169
+ // ──────────────────────────────────────────
1170
+ server.tool("stk_brain_check", "PROACTIVE GOTCHA DETECTION — Run this BEFORE implementing any feature or fixing any bug. Searches the brain for known gotchas, pitfalls, and patterns related to your task. Returns warnings ranked by relevance so you can avoid repeating past mistakes. Claude should call this automatically before writing code.", {
1171
+ task: z.string().describe("What you're about to implement or fix (e.g., 'add email verification', 'update user model', 'fix auth redirect')"),
1172
+ }, async ({ task }) => {
1173
+ const results = brainCheck(task);
1174
+ if (results.length === 0) {
1175
+ return {
1176
+ content: [{
1177
+ type: "text",
1178
+ text: JSON.stringify({
1179
+ task,
1180
+ warnings: [],
1181
+ message: "No known gotchas found. Proceed with caution — this might be new territory.",
1182
+ }, null, 2),
1183
+ }],
1184
+ };
1185
+ }
1186
+ const warnings = results.slice(0, 5).map(r => ({
1187
+ title: r.entry.title,
1188
+ content: r.entry.content,
1189
+ relevance: r.score,
1190
+ matchedTerms: r.matchedTerms,
1191
+ source: r.entry.source,
1192
+ category: r.entry.category,
1193
+ }));
1194
+ return {
1195
+ content: [{
1196
+ type: "text",
1197
+ text: JSON.stringify({
1198
+ task,
1199
+ warnings,
1200
+ totalMatches: results.length,
1201
+ message: `Found ${results.length} relevant pattern(s). Review warnings before coding.`,
1202
+ }, null, 2),
1203
+ }],
1204
+ };
1205
+ });
1206
+ // ──────────────────────────────────────────
1207
+ // Tool: stk_brain_diagnose
1208
+ // ──────────────────────────────────────────
1209
+ server.tool("stk_brain_diagnose", "ERROR PATTERN MATCHING — Run this when you encounter an error or bug. Searches the brain for matching patterns from past issues and returns known solutions. Claude should call this automatically when debugging.", {
1210
+ error: z.string().describe("The error message, symptom, or bug description (e.g., 'redirect not working after verification', 'emails not sending', 'prisma field undefined')"),
1211
+ }, async ({ error }) => {
1212
+ const results = brainDiagnose(error);
1213
+ if (results.length === 0) {
1214
+ return {
1215
+ content: [{
1216
+ type: "text",
1217
+ text: JSON.stringify({
1218
+ error,
1219
+ solutions: [],
1220
+ message: "No matching patterns found. This is a new issue — after fixing it, use stk_brain_learn to save the pattern.",
1221
+ }, null, 2),
1222
+ }],
1223
+ };
1224
+ }
1225
+ const solutions = results.slice(0, 5).map(r => ({
1226
+ title: r.entry.title,
1227
+ solution: r.entry.content,
1228
+ relevance: r.score,
1229
+ matchedTerms: r.matchedTerms,
1230
+ source: r.entry.source,
1231
+ }));
1232
+ return {
1233
+ content: [{
1234
+ type: "text",
1235
+ text: JSON.stringify({
1236
+ error,
1237
+ solutions,
1238
+ totalMatches: results.length,
1239
+ message: `Found ${results.length} matching pattern(s). Apply relevant solutions.`,
1240
+ }, null, 2),
1241
+ }],
1242
+ };
1243
+ });
1244
+ // ──────────────────────────────────────────
1166
1245
  // Tool: stk_brain_ingest
1167
1246
  // ──────────────────────────────────────────
1168
1247
  server.tool("stk_brain_ingest", "Scan the current project and ingest architecture knowledge into the local brain (~/.stk/brain.json). Automatically reads CLAUDE.md, package.json, Prisma schema, Dockerfile, CI config, and route files. Run this when setting up stk in a new project or after major changes.", {
@@ -1244,6 +1323,83 @@ server.tool("stk_brain_sync", "Sync brain knowledge between local (~/.stk/brain.
1244
1323
  };
1245
1324
  });
1246
1325
  // ──────────────────────────────────────────
1326
+ // Tool: stk_brain_check (PROACTIVE — call before writing code)
1327
+ // ──────────────────────────────────────────
1328
+ server.tool("stk_brain_check", "PROACTIVE: Call this BEFORE implementing a feature or making changes. Searches the brain for known gotchas, past bugs, and patterns relevant to what you're about to build. Returns warnings that can prevent mistakes. Use this whenever you start a non-trivial coding task.", {
1329
+ task: z.string().describe("What you're about to implement (e.g., 'add email verification', 'update user model', 'add webhook endpoint')"),
1330
+ }, async ({ task }) => {
1331
+ const results = brainCheck(task);
1332
+ if (results.length === 0) {
1333
+ return {
1334
+ content: [{
1335
+ type: "text",
1336
+ text: JSON.stringify({
1337
+ task,
1338
+ warnings: [],
1339
+ message: "No known gotchas found. Proceed carefully.",
1340
+ }, null, 2),
1341
+ }],
1342
+ };
1343
+ }
1344
+ const warnings = results.slice(0, 8).map(r => ({
1345
+ title: r.entry.title,
1346
+ warning: r.entry.content,
1347
+ relevance: r.score,
1348
+ matchedTerms: r.matchedTerms,
1349
+ source: r.entry.source,
1350
+ category: r.entry.category,
1351
+ }));
1352
+ return {
1353
+ content: [{
1354
+ type: "text",
1355
+ text: JSON.stringify({
1356
+ task,
1357
+ warnings,
1358
+ totalMatches: results.length,
1359
+ message: `Found ${results.length} relevant entries. Review warnings before coding.`,
1360
+ }, null, 2),
1361
+ }],
1362
+ };
1363
+ });
1364
+ // ──────────────────────────────────────────
1365
+ // Tool: stk_brain_diagnose (REACTIVE — call when you hit an error)
1366
+ // ──────────────────────────────────────────
1367
+ server.tool("stk_brain_diagnose", "REACTIVE: Call this when you encounter an error or bug. Searches the brain for matching patterns from past issues and returns known solutions. Use this before debugging from scratch — the answer may already be in the brain.", {
1368
+ error: z.string().describe("The error message, bug description, or unexpected behavior you're seeing"),
1369
+ }, async ({ error }) => {
1370
+ const results = brainDiagnose(error);
1371
+ if (results.length === 0) {
1372
+ return {
1373
+ content: [{
1374
+ type: "text",
1375
+ text: JSON.stringify({
1376
+ error,
1377
+ solutions: [],
1378
+ message: "No matching patterns found in the brain. This is a new issue — debug it, fix it, then use stk_brain_learn to save the solution.",
1379
+ }, null, 2),
1380
+ }],
1381
+ };
1382
+ }
1383
+ const solutions = results.slice(0, 5).map(r => ({
1384
+ title: r.entry.title,
1385
+ solution: r.entry.content,
1386
+ relevance: r.score,
1387
+ matchedTerms: r.matchedTerms,
1388
+ source: r.entry.source,
1389
+ }));
1390
+ return {
1391
+ content: [{
1392
+ type: "text",
1393
+ text: JSON.stringify({
1394
+ error,
1395
+ solutions,
1396
+ totalMatches: results.length,
1397
+ message: `Found ${results.length} matching patterns. Apply the most relevant solution.`,
1398
+ }, null, 2),
1399
+ }],
1400
+ };
1401
+ });
1402
+ // ──────────────────────────────────────────
1247
1403
  // Tool: stk_brain_claudemd
1248
1404
  // ──────────────────────────────────────────
1249
1405
  server.tool("stk_brain_claudemd", "Auto-generate a CLAUDE.md file for the current project. Analyzes the tech stack, project structure, services, and brain knowledge to create comprehensive project instructions for Claude Code.", {
@@ -1409,6 +1565,246 @@ function detectGitHubRepo() {
1409
1565
  return null;
1410
1566
  }
1411
1567
  }
1568
+ // ──────────────────────────────────────────
1569
+ // Tool: stk_autofix
1570
+ // ──────────────────────────────────────────
1571
+ server.tool("stk_autofix", "Auto-diagnose errors from production logs. Fetches recent logs, extracts errors, and searches the brain for matching solutions. Returns errors with matched fix patterns and confidence scores.", {
1572
+ provider: z.enum(["railway", "vercel"]).optional(),
1573
+ lines: z.number().optional().default(50),
1574
+ }, async ({ provider, lines }) => {
1575
+ // Determine provider
1576
+ const config = loadConfig();
1577
+ const detected = provider ?? (enabledServices(config).includes("railway") ? "railway" : "vercel");
1578
+ // Fetch logs using existing stk_logs logic (inline)
1579
+ let logs = [];
1580
+ try {
1581
+ if (detected === "railway") {
1582
+ const token = process.env.RAILWAY_API_TOKEN;
1583
+ const projectId = process.env.RAILWAY_PROJECT_ID;
1584
+ const serviceId = process.env.RAILWAY_SERVICE_ID;
1585
+ if (token && projectId && serviceId) {
1586
+ const envId = process.env.RAILWAY_ENVIRONMENT_ID;
1587
+ const gql = `query { deployments(first:1, input:{projectId:"${projectId}",serviceId:"${serviceId}"${envId ? `,environmentId:"${envId}"` : ""}}) { edges { node { id } } } }`;
1588
+ const depRes = await fetch("https://backboard.railway.app/graphql/v2", {
1589
+ method: "POST",
1590
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
1591
+ body: JSON.stringify({ query: gql }),
1592
+ });
1593
+ const depData = await depRes.json();
1594
+ const depId = (depData?.data?.deployments?.edges?.[0]?.node?.id) ?? null;
1595
+ if (depId) {
1596
+ const logGql = `query { deploymentLogs(deploymentId:"${depId}",limit:${lines}) { timestamp message severity } }`;
1597
+ const logRes = await fetch("https://backboard.railway.app/graphql/v2", {
1598
+ method: "POST",
1599
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
1600
+ body: JSON.stringify({ query: logGql }),
1601
+ });
1602
+ const logData = await logRes.json();
1603
+ logs = (logData?.data?.deploymentLogs ?? []);
1604
+ }
1605
+ }
1606
+ }
1607
+ }
1608
+ catch { /* log fetch failed */ }
1609
+ // Filter for errors
1610
+ const errorPattern = /error|exception|fail|crash|ECONNREFUSED|timeout|TypeError|ReferenceError|rejected|FATAL/i;
1611
+ const errorLogs = logs.filter(l => l.severity === "error" || (l.message && errorPattern.test(l.message)));
1612
+ // Deduplicate by first 100 chars
1613
+ const grouped = new Map();
1614
+ for (const log of errorLogs) {
1615
+ const key = log.message.slice(0, 100);
1616
+ const existing = grouped.get(key);
1617
+ if (existing) {
1618
+ existing.count++;
1619
+ existing.lastSeen = log.timestamp;
1620
+ }
1621
+ else {
1622
+ grouped.set(key, { message: log.message, count: 1, firstSeen: log.timestamp, lastSeen: log.timestamp });
1623
+ }
1624
+ }
1625
+ // Diagnose each unique error
1626
+ const errors = [];
1627
+ let matchedErrors = 0;
1628
+ for (const error of grouped.values()) {
1629
+ const matches = brainDiagnose(error.message);
1630
+ const solutions = matches.slice(0, 3).map(m => ({
1631
+ title: m.entry.title,
1632
+ content: m.entry.content.slice(0, 400),
1633
+ confidence: Math.min(1, m.score / 10),
1634
+ source: m.entry.source,
1635
+ }));
1636
+ if (solutions.length > 0)
1637
+ matchedErrors++;
1638
+ errors.push({ ...error, solutions });
1639
+ }
1640
+ return {
1641
+ content: [{
1642
+ type: "text",
1643
+ text: JSON.stringify({
1644
+ provider: detected,
1645
+ errors,
1646
+ summary: {
1647
+ totalErrors: errorLogs.length,
1648
+ uniqueErrors: grouped.size,
1649
+ matchedErrors,
1650
+ unmatchedErrors: grouped.size - matchedErrors,
1651
+ },
1652
+ }, null, 2),
1653
+ }],
1654
+ };
1655
+ });
1656
+ // ──────────────────────────────────────────
1657
+ // Tool: stk_brain_review
1658
+ // ──────────────────────────────────────────
1659
+ server.tool("stk_brain_review", "Review code changes against the brain's knowledge base. Checks a git diff or PR for known gotchas per file. Use before merging PRs or after making changes.", {
1660
+ diff: z.string().optional().describe("Raw git diff output"),
1661
+ pr: z.number().optional().describe("GitHub PR number to review"),
1662
+ }, async ({ diff, pr }) => {
1663
+ let diffContent = diff ?? "";
1664
+ // If PR number, fetch diff from GitHub
1665
+ if (pr && !diff) {
1666
+ const token = process.env.GITHUB_TOKEN;
1667
+ let repo = process.env.GITHUB_REPO ?? "";
1668
+ if (!repo) {
1669
+ try {
1670
+ repo = execSync("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim().replace(/.*github\.com[:/]/, "").replace(/\.git$/, "");
1671
+ }
1672
+ catch { /* */ }
1673
+ }
1674
+ if (repo && token) {
1675
+ try {
1676
+ const res = await fetch(`https://api.github.com/repos/${repo}/pulls/${pr}`, {
1677
+ headers: { Authorization: `Bearer ${token}`, Accept: "application/vnd.github.diff" },
1678
+ });
1679
+ if (res.ok)
1680
+ diffContent = await res.text();
1681
+ }
1682
+ catch { /* */ }
1683
+ }
1684
+ }
1685
+ // If no diff provided, get latest commit diff
1686
+ if (!diffContent) {
1687
+ try {
1688
+ diffContent = execSync("git diff HEAD~1", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], maxBuffer: 1024 * 1024 * 5 });
1689
+ }
1690
+ catch { /* */ }
1691
+ }
1692
+ if (!diffContent) {
1693
+ return { content: [{ type: "text", text: JSON.stringify({ error: "No diff available. Provide diff text, PR number, or ensure you're in a git repo." }) }] };
1694
+ }
1695
+ const results = reviewDiff(diffContent);
1696
+ const filesWithWarnings = results.filter(r => r.warnings.length > 0);
1697
+ return {
1698
+ content: [{
1699
+ type: "text",
1700
+ text: JSON.stringify({
1701
+ files: results,
1702
+ summary: {
1703
+ filesReviewed: results.length,
1704
+ filesWithWarnings: filesWithWarnings.length,
1705
+ totalWarnings: filesWithWarnings.reduce((s, r) => s + r.warnings.length, 0),
1706
+ },
1707
+ }, null, 2),
1708
+ }],
1709
+ };
1710
+ });
1711
+ // ──────────────────────────────────────────
1712
+ // Tool: stk_metrics
1713
+ // ──────────────────────────────────────────
1714
+ server.tool("stk_metrics", "Track and analyze infrastructure metrics over time — deploy frequency, error rates, response times, uptime. Compare current performance against historical baselines to detect regressions.", {
1715
+ action: z.enum(["view", "record", "compare"]).optional().default("view"),
1716
+ type: z.enum(["deploy", "health_check", "error", "response_time"]).optional(),
1717
+ value: z.number().optional(),
1718
+ metadata: z.record(z.string(), z.string()).optional(),
1719
+ days: z.number().optional().default(7),
1720
+ }, async ({ action, type, value, metadata, days }) => {
1721
+ if (action === "record" && type && value !== undefined) {
1722
+ recordMetric(type, value, metadata ?? {});
1723
+ return { content: [{ type: "text", text: JSON.stringify({ recorded: true, type, value }) }] };
1724
+ }
1725
+ if (action === "compare") {
1726
+ const types = ["deploy", "health_check", "error", "response_time"];
1727
+ const comparisons = types.map(t => ({ type: t, ...compareToBaseline(t) }));
1728
+ const degraded = comparisons.filter(c => c.status === "degraded");
1729
+ return {
1730
+ content: [{
1731
+ type: "text",
1732
+ text: JSON.stringify({
1733
+ comparisons,
1734
+ degraded: degraded.length > 0 ? degraded : "none",
1735
+ summary: `${degraded.length} metric(s) degraded`,
1736
+ }, null, 2),
1737
+ }],
1738
+ };
1739
+ }
1740
+ // Default: view
1741
+ return {
1742
+ content: [{
1743
+ type: "text",
1744
+ text: JSON.stringify({
1745
+ period: `${days} days`,
1746
+ deploys: getDeployFrequency(days),
1747
+ errors: getErrorRate(days),
1748
+ uptime: getUptime(days),
1749
+ recentMetrics: getMetrics(type ?? undefined, days, 20),
1750
+ }, null, 2),
1751
+ }],
1752
+ };
1753
+ });
1754
+ // ──────────────────────────────────────────
1755
+ // Tool: stk_secure
1756
+ // ──────────────────────────────────────────
1757
+ server.tool("stk_secure", "Security scan — check for exposed secrets, vulnerable dependencies, missing rate limiting, open CORS, and unprotected routes. Returns findings with severity levels and fix suggestions.", {
1758
+ checks: z.array(z.enum(["secrets", "deps", "rate_limit", "cors", "auth"])).optional().describe("Specific checks to run (default: all)"),
1759
+ }, async ({ checks }) => {
1760
+ const findings = runAllChecks(checks);
1761
+ const critical = findings.filter(f => f.level === "critical").length;
1762
+ const warning = findings.filter(f => f.level === "warning").length;
1763
+ const info = findings.filter(f => f.level === "info").length;
1764
+ return {
1765
+ content: [{
1766
+ type: "text",
1767
+ text: JSON.stringify({
1768
+ findings,
1769
+ summary: { critical, warning, info, total: findings.length },
1770
+ status: critical > 0 ? "CRITICAL" : warning > 0 ? "WARNING" : "CLEAN",
1771
+ }, null, 2),
1772
+ }],
1773
+ };
1774
+ });
1775
+ // ──────────────────────────────────────────
1776
+ // Tool: stk_brain_team
1777
+ // ──────────────────────────────────────────
1778
+ server.tool("stk_brain_team", "Show team brain contributions — who learned what, from which projects, and when. Tracks knowledge sharing across team members.", {}, async () => {
1779
+ const store = loadBrainStore();
1780
+ const allEntries = [...store.global];
1781
+ for (const proj of Object.values(store.projects))
1782
+ allEntries.push(...proj.entries);
1783
+ const contributors = {};
1784
+ for (const entry of allEntries) {
1785
+ const contributor = entry.contributor ?? "auto";
1786
+ if (!contributors[contributor]) {
1787
+ contributors[contributor] = { count: 0, lastActive: entry.created_at, categories: {} };
1788
+ }
1789
+ contributors[contributor].count++;
1790
+ if (entry.created_at > contributors[contributor].lastActive) {
1791
+ contributors[contributor].lastActive = entry.created_at;
1792
+ }
1793
+ contributors[contributor].categories[entry.category] = (contributors[contributor].categories[entry.category] || 0) + 1;
1794
+ }
1795
+ return {
1796
+ content: [{
1797
+ type: "text",
1798
+ text: JSON.stringify({
1799
+ totalEntries: allEntries.length,
1800
+ contributors,
1801
+ projects: Object.entries(store.projects).map(([name, p]) => ({
1802
+ name, entries: p.entries.length, ingestedAt: p.ingestedAt,
1803
+ })),
1804
+ }, null, 2),
1805
+ }],
1806
+ };
1807
+ });
1412
1808
  // Start
1413
1809
  async function main() {
1414
1810
  const transport = new StdioServerTransport();
@@ -63,4 +63,36 @@ export declare function pushToCloud(): Promise<SyncResult>;
63
63
  export declare function pullFromCloud(): Promise<SyncResult>;
64
64
  /** Full sync: push local → cloud, then pull cloud → local */
65
65
  export declare function syncBrain(): Promise<SyncResult>;
66
+ interface ScoredEntry {
67
+ entry: KnowledgeEntry;
68
+ score: number;
69
+ matchedTerms: string[];
70
+ }
71
+ /** Search brain with relevance scoring — returns entries ranked by how many terms match */
72
+ export declare function smartSearch(terms: string[], category?: string): ScoredEntry[];
73
+ /** Extract relevant terms from a task description */
74
+ export declare function extractTerms(description: string): string[];
75
+ /** Proactive check — find gotchas relevant to a task before coding */
76
+ export declare function brainCheck(taskDescription: string): ScoredEntry[];
77
+ /** Diagnose an error — find matching patterns from past issues */
78
+ export declare function brainDiagnose(error: string): ScoredEntry[];
79
+ /** Seed brain with curated patterns (idempotent) */
80
+ export declare function seedBrain(patterns: KnowledgeEntry[], force?: boolean): {
81
+ added: number;
82
+ skipped: number;
83
+ };
84
+ /** Review a diff against brain knowledge — find gotchas per file */
85
+ export interface ReviewWarning {
86
+ file: string;
87
+ linesChanged: number;
88
+ warnings: Array<{
89
+ title: string;
90
+ content: string;
91
+ relevance: number;
92
+ source: string;
93
+ }>;
94
+ }
95
+ export declare function reviewDiff(diff: string): ReviewWarning[];
96
+ /** Get contributor name from git config */
97
+ export declare function getContributor(): string;
66
98
  export {};