athar-mcp 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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +242 -0
  3. package/dist/cli/commands/list.d.ts +10 -0
  4. package/dist/cli/commands/list.d.ts.map +1 -0
  5. package/dist/cli/commands/list.js +73 -0
  6. package/dist/cli/commands/list.js.map +1 -0
  7. package/dist/cli/commands/review.d.ts +2 -0
  8. package/dist/cli/commands/review.d.ts.map +1 -0
  9. package/dist/cli/commands/review.js +112 -0
  10. package/dist/cli/commands/review.js.map +1 -0
  11. package/dist/cli/commands/setup.d.ts +2 -0
  12. package/dist/cli/commands/setup.d.ts.map +1 -0
  13. package/dist/cli/commands/setup.js +55 -0
  14. package/dist/cli/commands/setup.js.map +1 -0
  15. package/dist/cli/commands/status.d.ts +2 -0
  16. package/dist/cli/commands/status.d.ts.map +1 -0
  17. package/dist/cli/commands/status.js +39 -0
  18. package/dist/cli/commands/status.js.map +1 -0
  19. package/dist/cli/index.d.ts +12 -0
  20. package/dist/cli/index.d.ts.map +1 -0
  21. package/dist/cli/index.js +43 -0
  22. package/dist/cli/index.js.map +1 -0
  23. package/dist/db/connection.d.ts +11 -0
  24. package/dist/db/connection.d.ts.map +1 -0
  25. package/dist/db/connection.js +49 -0
  26. package/dist/db/connection.js.map +1 -0
  27. package/dist/db/schema.d.ts +12 -0
  28. package/dist/db/schema.d.ts.map +1 -0
  29. package/dist/db/schema.js +95 -0
  30. package/dist/db/schema.js.map +1 -0
  31. package/dist/index.d.ts +19 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +48 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/server.d.ts +7 -0
  36. package/dist/server.d.ts.map +1 -0
  37. package/dist/server.js +139 -0
  38. package/dist/server.js.map +1 -0
  39. package/dist/smoke-test.d.ts +7 -0
  40. package/dist/smoke-test.d.ts.map +1 -0
  41. package/dist/smoke-test.js +99 -0
  42. package/dist/smoke-test.js.map +1 -0
  43. package/dist/spaced-repetition/scheduler.d.ts +46 -0
  44. package/dist/spaced-repetition/scheduler.d.ts.map +1 -0
  45. package/dist/spaced-repetition/scheduler.js +98 -0
  46. package/dist/spaced-repetition/scheduler.js.map +1 -0
  47. package/dist/spaced-repetition/sm2.d.ts +36 -0
  48. package/dist/spaced-repetition/sm2.d.ts.map +1 -0
  49. package/dist/spaced-repetition/sm2.js +90 -0
  50. package/dist/spaced-repetition/sm2.js.map +1 -0
  51. package/dist/tools/memory.d.ts +25 -0
  52. package/dist/tools/memory.d.ts.map +1 -0
  53. package/dist/tools/memory.js +112 -0
  54. package/dist/tools/memory.js.map +1 -0
  55. package/dist/tools/save-lesson.d.ts +11 -0
  56. package/dist/tools/save-lesson.d.ts.map +1 -0
  57. package/dist/tools/save-lesson.js +137 -0
  58. package/dist/tools/save-lesson.js.map +1 -0
  59. package/dist/tools/validators.d.ts +82 -0
  60. package/dist/tools/validators.d.ts.map +1 -0
  61. package/dist/tools/validators.js +86 -0
  62. package/dist/tools/validators.js.map +1 -0
  63. package/dist/utils/logger.d.ts +23 -0
  64. package/dist/utils/logger.d.ts.map +1 -0
  65. package/dist/utils/logger.js +55 -0
  66. package/dist/utils/logger.js.map +1 -0
  67. package/dist/utils/paths.d.ts +17 -0
  68. package/dist/utils/paths.d.ts.map +1 -0
  69. package/dist/utils/paths.js +43 -0
  70. package/dist/utils/paths.js.map +1 -0
  71. package/package.json +60 -0
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Athar CLI (أثر)
4
+ *
5
+ * Commands:
6
+ * athar setup — Configure Antigravity IDE integration
7
+ * athar review — Interactive spaced repetition review
8
+ * athar status — Show pending reviews and stats
9
+ * athar list — Browse all lessons
10
+ */
11
+ import { Command } from 'commander';
12
+ import { reviewCommand } from './commands/review.js';
13
+ import { statusCommand } from './commands/status.js';
14
+ import { listCommand } from './commands/list.js';
15
+ import { setupCommand } from './commands/setup.js';
16
+ const program = new Command();
17
+ program
18
+ .name('athar')
19
+ .description('أثر — AI-powered programming lesson memory with spaced repetition')
20
+ .version('0.1.0');
21
+ program
22
+ .command('setup')
23
+ .description('Configure Antigravity IDE to use Athar MCP server')
24
+ .action(setupCommand);
25
+ program
26
+ .command('review')
27
+ .description('Start an interactive spaced repetition review session')
28
+ .action(reviewCommand);
29
+ program
30
+ .command('status')
31
+ .description('Show pending reviews and lesson statistics')
32
+ .action(statusCommand);
33
+ program
34
+ .command('list')
35
+ .description('Browse all saved lessons')
36
+ .option('-t, --tag <tag>', 'Filter by tag')
37
+ .option('-l, --language <lang>', 'Filter by programming language')
38
+ .option('-s, --status <status>', 'Filter by status (new, learning, learned, mastered)')
39
+ .option('-q, --search <query>', 'Search lessons by keyword')
40
+ .option('-n, --limit <number>', 'Maximum results', '20')
41
+ .action(listCommand);
42
+ program.parse();
43
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,mEAAmE,CAAC;KAChF,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,mDAAmD,CAAC;KAChE,MAAM,CAAC,YAAY,CAAC,CAAC;AAExB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,uDAAuD,CAAC;KACpE,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,4CAA4C,CAAC;KACzD,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,0BAA0B,CAAC;KACvC,MAAM,CAAC,iBAAiB,EAAE,eAAe,CAAC;KAC1C,MAAM,CAAC,uBAAuB,EAAE,gCAAgC,CAAC;KACjE,MAAM,CAAC,uBAAuB,EAAE,qDAAqD,CAAC;KACtF,MAAM,CAAC,sBAAsB,EAAE,2BAA2B,CAAC;KAC3D,MAAM,CAAC,sBAAsB,EAAE,iBAAiB,EAAE,IAAI,CAAC;KACvD,MAAM,CAAC,WAAW,CAAC,CAAC;AAEvB,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ /**
3
+ * Get the singleton database connection.
4
+ * Creates the database and schema on first call.
5
+ */
6
+ export declare function getDatabase(): DatabaseSync;
7
+ /**
8
+ * Close the database connection gracefully.
9
+ */
10
+ export declare function closeDatabase(): void;
11
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAU3C;;;GAGG;AACH,wBAAgB,WAAW,IAAI,YAAY,CA6B1C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAMpC"}
@@ -0,0 +1,49 @@
1
+ import { DatabaseSync } from 'node:sqlite';
2
+ import { existsSync } from 'node:fs';
3
+ import { getDatabasePath } from '../utils/paths.js';
4
+ import { CREATE_TABLES } from './schema.js';
5
+ import { createLogger } from '../utils/logger.js';
6
+ const log = createLogger('db');
7
+ let db = null;
8
+ /**
9
+ * Get the singleton database connection.
10
+ * Creates the database and schema on first call.
11
+ */
12
+ export function getDatabase() {
13
+ if (db)
14
+ return db;
15
+ const dbPath = getDatabasePath();
16
+ const isNew = !existsSync(dbPath);
17
+ log.info(`Opening database at: ${dbPath}`);
18
+ db = new DatabaseSync(dbPath);
19
+ // Enable WAL mode for better concurrent read performance
20
+ db.exec('PRAGMA journal_mode = WAL');
21
+ db.exec('PRAGMA foreign_keys = ON');
22
+ if (isNew) {
23
+ log.info('Creating database schema (first run)...');
24
+ db.exec(CREATE_TABLES);
25
+ log.info('Schema created successfully.');
26
+ }
27
+ else {
28
+ // Ensure FTS table and triggers exist (in case of upgrades)
29
+ try {
30
+ db.exec(CREATE_TABLES);
31
+ }
32
+ catch {
33
+ // Tables already exist — that's fine
34
+ log.debug('Schema already up to date.');
35
+ }
36
+ }
37
+ return db;
38
+ }
39
+ /**
40
+ * Close the database connection gracefully.
41
+ */
42
+ export function closeDatabase() {
43
+ if (db) {
44
+ db.close();
45
+ db = null;
46
+ log.info('Database connection closed.');
47
+ }
48
+ }
49
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;AAE/B,IAAI,EAAE,GAAwB,IAAI,CAAC;AAEnC;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC;IAElB,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAElC,GAAG,CAAC,IAAI,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC;IAE3C,EAAE,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IAE9B,yDAAyD;IACzD,EAAE,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IACrC,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAEpC,IAAI,KAAK,EAAE,CAAC;QACV,GAAG,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;QACpD,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACvB,GAAG,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IAC3C,CAAC;SAAM,CAAC;QACN,4DAA4D;QAC5D,IAAI,CAAC;YACH,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,qCAAqC;YACrC,GAAG,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,IAAI,EAAE,EAAE,CAAC;QACP,EAAE,CAAC,KAAK,EAAE,CAAC;QACX,EAAE,GAAG,IAAI,CAAC;QACV,GAAG,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC1C,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Database schema for Athar lessons.
3
+ *
4
+ * Designed for:
5
+ * - Structured lesson storage with rich metadata
6
+ * - SM-2 spaced repetition scheduling
7
+ * - Full-text search across lesson content
8
+ * - Bilingual support (Arabic + English)
9
+ */
10
+ export declare const SCHEMA_VERSION = 1;
11
+ export declare const CREATE_TABLES = "\n -- Main lessons table\n CREATE TABLE IF NOT EXISTS lessons (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n \n -- Core lesson content (bilingual: AR/EN)\n title TEXT NOT NULL,\n problem TEXT NOT NULL,\n error_message TEXT,\n root_cause TEXT NOT NULL,\n bad_code TEXT,\n good_code TEXT,\n lesson TEXT NOT NULL,\n \n -- Categorization\n tags TEXT, -- JSON array: [\"async\", \"react\", \"typescript\"]\n language TEXT, -- Programming language: \"typescript\", \"python\", etc.\n file_path TEXT, -- Original file where error occurred\n git_diff TEXT, -- Git diff context (auto-captured or provided)\n \n -- Spaced repetition (SM-2 algorithm)\n repetitions INTEGER DEFAULT 0,\n easiness_factor REAL DEFAULT 2.5,\n interval_days INTEGER DEFAULT 0,\n next_review_at TEXT, -- ISO 8601 datetime\n last_reviewed_at TEXT,\n \n -- Review questions: JSON array of {q, a} objects\n review_questions TEXT NOT NULL,\n \n -- Status tracking\n status TEXT DEFAULT 'new' CHECK(status IN ('new', 'learning', 'learned', 'mastered')),\n quality_score INTEGER, -- Last SM-2 quality score (0-5)\n review_count INTEGER DEFAULT 0,\n \n -- Metadata\n created_at TEXT DEFAULT (datetime('now')),\n updated_at TEXT DEFAULT (datetime('now'))\n );\n\n -- Performance indexes\n CREATE INDEX IF NOT EXISTS idx_lessons_next_review ON lessons(next_review_at);\n CREATE INDEX IF NOT EXISTS idx_lessons_status ON lessons(status);\n CREATE INDEX IF NOT EXISTS idx_lessons_language ON lessons(language);\n CREATE INDEX IF NOT EXISTS idx_lessons_created ON lessons(created_at);\n\n -- Full-text search virtual table\n CREATE VIRTUAL TABLE IF NOT EXISTS lessons_fts USING fts5(\n title,\n problem,\n root_cause,\n lesson,\n tags,\n content=lessons,\n content_rowid=id\n );\n\n -- Triggers to keep FTS in sync\n CREATE TRIGGER IF NOT EXISTS lessons_ai AFTER INSERT ON lessons BEGIN\n INSERT INTO lessons_fts(rowid, title, problem, root_cause, lesson, tags)\n VALUES (new.id, new.title, new.problem, new.root_cause, new.lesson, new.tags);\n END;\n\n CREATE TRIGGER IF NOT EXISTS lessons_ad AFTER DELETE ON lessons BEGIN\n INSERT INTO lessons_fts(lessons_fts, rowid, title, problem, root_cause, lesson, tags)\n VALUES ('delete', old.id, old.title, old.problem, old.root_cause, old.lesson, old.tags);\n END;\n\n CREATE TRIGGER IF NOT EXISTS lessons_au AFTER UPDATE ON lessons BEGIN\n INSERT INTO lessons_fts(lessons_fts, rowid, title, problem, root_cause, lesson, tags)\n VALUES ('delete', old.id, old.title, old.problem, old.root_cause, old.lesson, old.tags);\n INSERT INTO lessons_fts(rowid, title, problem, root_cause, lesson, tags)\n VALUES (new.id, new.title, new.problem, new.root_cause, new.lesson, new.tags);\n END;\n\n -- Schema version tracking\n CREATE TABLE IF NOT EXISTS schema_meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n\n INSERT OR REPLACE INTO schema_meta (key, value)\n VALUES ('version', '1');\n";
12
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,eAAO,MAAM,aAAa,+sGAmFzB,CAAC"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Database schema for Athar lessons.
3
+ *
4
+ * Designed for:
5
+ * - Structured lesson storage with rich metadata
6
+ * - SM-2 spaced repetition scheduling
7
+ * - Full-text search across lesson content
8
+ * - Bilingual support (Arabic + English)
9
+ */
10
+ export const SCHEMA_VERSION = 1;
11
+ export const CREATE_TABLES = `
12
+ -- Main lessons table
13
+ CREATE TABLE IF NOT EXISTS lessons (
14
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
15
+
16
+ -- Core lesson content (bilingual: AR/EN)
17
+ title TEXT NOT NULL,
18
+ problem TEXT NOT NULL,
19
+ error_message TEXT,
20
+ root_cause TEXT NOT NULL,
21
+ bad_code TEXT,
22
+ good_code TEXT,
23
+ lesson TEXT NOT NULL,
24
+
25
+ -- Categorization
26
+ tags TEXT, -- JSON array: ["async", "react", "typescript"]
27
+ language TEXT, -- Programming language: "typescript", "python", etc.
28
+ file_path TEXT, -- Original file where error occurred
29
+ git_diff TEXT, -- Git diff context (auto-captured or provided)
30
+
31
+ -- Spaced repetition (SM-2 algorithm)
32
+ repetitions INTEGER DEFAULT 0,
33
+ easiness_factor REAL DEFAULT 2.5,
34
+ interval_days INTEGER DEFAULT 0,
35
+ next_review_at TEXT, -- ISO 8601 datetime
36
+ last_reviewed_at TEXT,
37
+
38
+ -- Review questions: JSON array of {q, a} objects
39
+ review_questions TEXT NOT NULL,
40
+
41
+ -- Status tracking
42
+ status TEXT DEFAULT 'new' CHECK(status IN ('new', 'learning', 'learned', 'mastered')),
43
+ quality_score INTEGER, -- Last SM-2 quality score (0-5)
44
+ review_count INTEGER DEFAULT 0,
45
+
46
+ -- Metadata
47
+ created_at TEXT DEFAULT (datetime('now')),
48
+ updated_at TEXT DEFAULT (datetime('now'))
49
+ );
50
+
51
+ -- Performance indexes
52
+ CREATE INDEX IF NOT EXISTS idx_lessons_next_review ON lessons(next_review_at);
53
+ CREATE INDEX IF NOT EXISTS idx_lessons_status ON lessons(status);
54
+ CREATE INDEX IF NOT EXISTS idx_lessons_language ON lessons(language);
55
+ CREATE INDEX IF NOT EXISTS idx_lessons_created ON lessons(created_at);
56
+
57
+ -- Full-text search virtual table
58
+ CREATE VIRTUAL TABLE IF NOT EXISTS lessons_fts USING fts5(
59
+ title,
60
+ problem,
61
+ root_cause,
62
+ lesson,
63
+ tags,
64
+ content=lessons,
65
+ content_rowid=id
66
+ );
67
+
68
+ -- Triggers to keep FTS in sync
69
+ CREATE TRIGGER IF NOT EXISTS lessons_ai AFTER INSERT ON lessons BEGIN
70
+ INSERT INTO lessons_fts(rowid, title, problem, root_cause, lesson, tags)
71
+ VALUES (new.id, new.title, new.problem, new.root_cause, new.lesson, new.tags);
72
+ END;
73
+
74
+ CREATE TRIGGER IF NOT EXISTS lessons_ad AFTER DELETE ON lessons BEGIN
75
+ INSERT INTO lessons_fts(lessons_fts, rowid, title, problem, root_cause, lesson, tags)
76
+ VALUES ('delete', old.id, old.title, old.problem, old.root_cause, old.lesson, old.tags);
77
+ END;
78
+
79
+ CREATE TRIGGER IF NOT EXISTS lessons_au AFTER UPDATE ON lessons BEGIN
80
+ INSERT INTO lessons_fts(lessons_fts, rowid, title, problem, root_cause, lesson, tags)
81
+ VALUES ('delete', old.id, old.title, old.problem, old.root_cause, old.lesson, old.tags);
82
+ INSERT INTO lessons_fts(rowid, title, problem, root_cause, lesson, tags)
83
+ VALUES (new.id, new.title, new.problem, new.root_cause, new.lesson, new.tags);
84
+ END;
85
+
86
+ -- Schema version tracking
87
+ CREATE TABLE IF NOT EXISTS schema_meta (
88
+ key TEXT PRIMARY KEY,
89
+ value TEXT NOT NULL
90
+ );
91
+
92
+ INSERT OR REPLACE INTO schema_meta (key, value)
93
+ VALUES ('version', '${SCHEMA_VERSION}');
94
+ `;
95
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAkFL,cAAc;CACrC,CAAC"}
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Athar (أثر) — MCP Server Entry Point
4
+ *
5
+ * This is the main entry point for the MCP server.
6
+ * It initializes the server and connects via stdio transport.
7
+ *
8
+ * Usage in Antigravity IDE mcp_config.json:
9
+ * {
10
+ * "mcpServers": {
11
+ * "athar": {
12
+ * "command": "node",
13
+ * "args": ["path/to/dist/index.js"]
14
+ * }
15
+ * }
16
+ * }
17
+ */
18
+ export {};
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;GAeG"}
package/dist/index.js ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Athar (أثر) — MCP Server Entry Point
4
+ *
5
+ * This is the main entry point for the MCP server.
6
+ * It initializes the server and connects via stdio transport.
7
+ *
8
+ * Usage in Antigravity IDE mcp_config.json:
9
+ * {
10
+ * "mcpServers": {
11
+ * "athar": {
12
+ * "command": "node",
13
+ * "args": ["path/to/dist/index.js"]
14
+ * }
15
+ * }
16
+ * }
17
+ */
18
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
19
+ import { createAtharServer } from './server.js';
20
+ import { closeDatabase } from './db/connection.js';
21
+ import { createLogger } from './utils/logger.js';
22
+ const log = createLogger('main');
23
+ async function main() {
24
+ log.info('Starting Athar MCP server v0.1.0...');
25
+ const server = createAtharServer();
26
+ const transport = new StdioServerTransport();
27
+ // Graceful shutdown
28
+ const shutdown = () => {
29
+ log.info('Shutting down...');
30
+ closeDatabase();
31
+ process.exit(0);
32
+ };
33
+ process.on('SIGINT', shutdown);
34
+ process.on('SIGTERM', shutdown);
35
+ process.on('uncaughtException', (err) => {
36
+ log.error('Uncaught exception:', String(err));
37
+ closeDatabase();
38
+ process.exit(1);
39
+ });
40
+ await server.connect(transport);
41
+ log.info('Athar MCP server connected and ready.');
42
+ }
43
+ main().catch((err) => {
44
+ log.error('Fatal error:', String(err));
45
+ closeDatabase();
46
+ process.exit(1);
47
+ });
48
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;AAEjC,KAAK,UAAU,IAAI;IACjB,GAAG,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;IAEhD,MAAM,MAAM,GAAG,iBAAiB,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAE7C,oBAAoB;IACpB,MAAM,QAAQ,GAAG,GAAS,EAAE;QAC1B,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAC7B,aAAa,EAAE,CAAC;QAChB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,GAAG,EAAE,EAAE;QACtC,GAAG,CAAC,KAAK,CAAC,qBAAqB,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9C,aAAa,EAAE,CAAC;QAChB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,GAAG,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;AACpD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,aAAa,EAAE,CAAC;IAChB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ /**
3
+ * Create and configure the Athar MCP server.
4
+ * Registers all tools and their handlers.
5
+ */
6
+ export declare function createAtharServer(): McpServer;
7
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAQpE;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,SAAS,CA6I7C"}
package/dist/server.js ADDED
@@ -0,0 +1,139 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { SaveLessonSchema, MemorySchema } from './tools/validators.js';
3
+ import { saveLesson } from './tools/save-lesson.js';
4
+ import { searchMemory } from './tools/memory.js';
5
+ import { createLogger } from './utils/logger.js';
6
+ const log = createLogger('server');
7
+ /**
8
+ * Create and configure the Athar MCP server.
9
+ * Registers all tools and their handlers.
10
+ */
11
+ export function createAtharServer() {
12
+ const server = new McpServer({
13
+ name: 'athar',
14
+ version: '0.1.0',
15
+ }, {
16
+ instructions: `You have access to **Athar (أثر)**, a programming lesson memory system that captures and recalls lessons from real coding mistakes.
17
+
18
+ ## WHEN TO SAVE A LESSON (save_lesson):
19
+ - When you help fix a **non-trivial bug** and identify a clear root cause
20
+ - When a **misconception** about an API, language feature, or framework is corrected
21
+ - When a **debugging session** reveals unexpected behavior worth remembering
22
+ - When an **architectural mistake** is identified and a better pattern is suggested
23
+ - When a **security vulnerability** or performance anti-pattern is discovered
24
+
25
+ ## WHEN NOT TO SAVE:
26
+ - Simple typos, formatting changes, or import order fixes
27
+ - Routine code generation without any mistake involved
28
+ - Style preferences or subjective code choices
29
+ - Changes so trivial they wouldn't teach anyone anything
30
+
31
+ ## WHEN TO SEARCH MEMORY (memory):
32
+ - When you detect an error pattern that looks like a past mistake
33
+ - When the developer explicitly asks "have I done this before?" or similar
34
+ - When starting work in an area where past lessons might prevent repeated errors
35
+ - Proactively at the start of a debugging session in a known problem area
36
+
37
+ ## IMPORTANT GUIDELINES:
38
+ - Always include code examples (bad_code + good_code) when applicable
39
+ - Write lessons in the same language the developer uses (Arabic or English)
40
+ - Tags should be specific and lowercase (e.g., "async-await", "react-hooks", "sql-injection")
41
+ - Review questions should test understanding, not just recall
42
+ - The git_diff field is optional — include it if the fix involves specific file changes`,
43
+ });
44
+ // ═══════════════════════════════════════════════════════
45
+ // Tool: save_lesson
46
+ // ═══════════════════════════════════════════════════════
47
+ server.tool('save_lesson', `Save a programming lesson learned from a real mistake during this coding session.
48
+
49
+ ONLY call this when you identify a genuine, non-trivial learning moment — NOT for formatting changes, simple typos, or routine code adjustments.
50
+
51
+ The lesson must contain:
52
+ - A clear problem description (what went wrong)
53
+ - A root cause analysis (WHY it happened)
54
+ - An actionable takeaway (what to remember)
55
+ - At least one review question for spaced repetition`, {
56
+ title: SaveLessonSchema.shape.title,
57
+ problem: SaveLessonSchema.shape.problem,
58
+ error_message: SaveLessonSchema.shape.error_message,
59
+ root_cause: SaveLessonSchema.shape.root_cause,
60
+ bad_code: SaveLessonSchema.shape.bad_code,
61
+ good_code: SaveLessonSchema.shape.good_code,
62
+ lesson: SaveLessonSchema.shape.lesson,
63
+ tags: SaveLessonSchema.shape.tags,
64
+ language: SaveLessonSchema.shape.language,
65
+ file_path: SaveLessonSchema.shape.file_path,
66
+ git_diff: SaveLessonSchema.shape.git_diff,
67
+ review_questions: SaveLessonSchema.shape.review_questions,
68
+ }, async (args) => {
69
+ log.info('save_lesson called', args.title);
70
+ try {
71
+ const result = saveLesson(args);
72
+ return {
73
+ content: [
74
+ {
75
+ type: 'text',
76
+ text: result.message,
77
+ },
78
+ ],
79
+ isError: !result.success,
80
+ };
81
+ }
82
+ catch (err) {
83
+ log.error('save_lesson failed', String(err));
84
+ return {
85
+ content: [
86
+ {
87
+ type: 'text',
88
+ text: `❌ Failed to save lesson: ${err instanceof Error ? err.message : String(err)}`,
89
+ },
90
+ ],
91
+ isError: true,
92
+ };
93
+ }
94
+ });
95
+ // ═══════════════════════════════════════════════════════
96
+ // Tool: memory
97
+ // ═══════════════════════════════════════════════════════
98
+ server.tool('memory', `Search your programming lesson history for past mistakes and solutions.
99
+
100
+ Use this when:
101
+ - You detect an error pattern that might match a previously learned lesson
102
+ - The developer asks about past mistakes in a specific area
103
+ - You want to proactively check if a similar mistake has been made before
104
+
105
+ Returns relevant lessons with their problem, root cause, solution, and code examples.`, {
106
+ query: MemorySchema.shape.query,
107
+ limit: MemorySchema.shape.limit,
108
+ language: MemorySchema.shape.language,
109
+ tags: MemorySchema.shape.tags,
110
+ }, async (args) => {
111
+ log.info('memory called', args.query);
112
+ try {
113
+ const result = searchMemory(args);
114
+ return {
115
+ content: [
116
+ {
117
+ type: 'text',
118
+ text: result.message,
119
+ },
120
+ ],
121
+ };
122
+ }
123
+ catch (err) {
124
+ log.error('memory search failed', String(err));
125
+ return {
126
+ content: [
127
+ {
128
+ type: 'text',
129
+ text: `❌ Memory search failed: ${err instanceof Error ? err.message : String(err)}`,
130
+ },
131
+ ],
132
+ isError: true,
133
+ };
134
+ }
135
+ });
136
+ log.info('Athar MCP server created with 2 tools: save_lesson, memory');
137
+ return server;
138
+ }
139
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Smoke test for Athar MCP server.
4
+ * Tests: DB creation, save_lesson, memory search.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=smoke-test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smoke-test.d.ts","sourceRoot":"","sources":["../src/smoke-test.ts"],"names":[],"mappings":";AAEA;;;GAGG"}
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Smoke test for Athar MCP server.
4
+ * Tests: DB creation, save_lesson, memory search.
5
+ */
6
+ import { getDatabase, closeDatabase } from './db/connection.js';
7
+ import { saveLesson } from './tools/save-lesson.js';
8
+ import { searchMemory } from './tools/memory.js';
9
+ import { getDatabasePath } from './utils/paths.js';
10
+ // Use a test database
11
+ const testDbPath = getDatabasePath();
12
+ console.log(`\n🧪 Athar Smoke Test`);
13
+ console.log(`📂 Database path: ${testDbPath}\n`);
14
+ // Test 1: Database initialization
15
+ console.log('━━━ Test 1: Database Initialization ━━━');
16
+ try {
17
+ const db = getDatabase();
18
+ console.log('✅ Database initialized successfully\n');
19
+ }
20
+ catch (err) {
21
+ console.error('❌ Database initialization failed:', err);
22
+ process.exit(1);
23
+ }
24
+ // Test 2: Save a valid lesson
25
+ console.log('━━━ Test 2: Save Valid Lesson ━━━');
26
+ const validLesson = {
27
+ title: 'React useEffect cleanup prevents memory leaks in async operations',
28
+ problem: 'Component fetches data in useEffect but the response arrives after unmounting, causing "Cannot update state on unmounted component" warning',
29
+ error_message: 'Warning: Can\'t perform a React state update on an unmounted component',
30
+ root_cause: 'The useEffect callback starts an async fetch but has no cleanup function. When the component unmounts before the fetch completes, the .then() callback tries to call setState on a component that no longer exists in the DOM.',
31
+ bad_code: `useEffect(() => {
32
+ fetch('/api/data')
33
+ .then(res => res.json())
34
+ .then(data => setData(data));
35
+ }, []);`,
36
+ good_code: `useEffect(() => {
37
+ const controller = new AbortController();
38
+ fetch('/api/data', { signal: controller.signal })
39
+ .then(res => res.json())
40
+ .then(data => setData(data))
41
+ .catch(err => {
42
+ if (err.name !== 'AbortError') throw err;
43
+ });
44
+ return () => controller.abort();
45
+ }, []);`,
46
+ lesson: 'Always return a cleanup function from useEffect when performing async operations. Use AbortController to cancel pending fetch requests when the component unmounts.',
47
+ tags: ['react', 'useEffect', 'async', 'memory-leak', 'cleanup'],
48
+ language: 'typescript',
49
+ file_path: 'src/components/UserProfile.tsx',
50
+ review_questions: [
51
+ {
52
+ q: 'What happens when a useEffect fetches data but the component unmounts before the response arrives?',
53
+ a: 'The state update runs on an unmounted component, causing a memory leak warning. The fix is to use AbortController and return a cleanup function.',
54
+ },
55
+ {
56
+ q: 'How do you properly cancel a fetch request in a useEffect cleanup?',
57
+ a: 'Create an AbortController, pass its signal to fetch(), and call controller.abort() in the cleanup return function.',
58
+ },
59
+ ],
60
+ };
61
+ const saveResult = saveLesson(validLesson);
62
+ console.log(`Result: ${saveResult.success ? '✅' : '❌'} ${saveResult.message.split('\n')[0]}`);
63
+ if (saveResult.lessonId) {
64
+ console.log(`Lesson ID: ${saveResult.lessonId}\n`);
65
+ }
66
+ // Test 3: Reject trivial lesson
67
+ console.log('━━━ Test 3: Reject Trivial Lesson ━━━');
68
+ const trivialLesson = {
69
+ title: 'Fix indentation in utils file',
70
+ problem: 'The code had wrong indentation',
71
+ root_cause: 'The code had wrong indentation in the utility file',
72
+ lesson: 'Always use consistent indentation in utility files',
73
+ tags: ['formatting'],
74
+ review_questions: [{ q: 'Why is indentation important?', a: 'For readability' }],
75
+ };
76
+ const trivialResult = saveLesson(trivialLesson);
77
+ console.log(`Result: ${trivialResult.success ? '❌ Should have been rejected!' : '✅ Correctly rejected'}`);
78
+ console.log(`Message: ${trivialResult.message.substring(0, 100)}...\n`);
79
+ // Test 4: Reject duplicate
80
+ console.log('━━━ Test 4: Duplicate Detection ━━━');
81
+ const duplicateResult = saveLesson(validLesson);
82
+ console.log(`Result: ${duplicateResult.success ? '❌ Should have been rejected!' : '✅ Correctly detected duplicate'}`);
83
+ console.log(`Message: ${duplicateResult.message.substring(0, 100)}...\n`);
84
+ // Test 5: Memory search
85
+ console.log('━━━ Test 5: Memory Search ━━━');
86
+ const searchResult = searchMemory({ query: 'useEffect memory leak', limit: 3 });
87
+ console.log(`Found: ${searchResult.results.length} result(s)`);
88
+ console.log(`Result: ${searchResult.results.length > 0 ? '✅' : '❌'} ${searchResult.results.length > 0 ? 'Search found the saved lesson!' : 'Search returned no results'}\n`);
89
+ // Test 6: Memory search with no matches
90
+ console.log('━━━ Test 6: Search With No Matches ━━━');
91
+ const emptySearch = searchMemory({ query: 'xyznonexistent', limit: 3 });
92
+ console.log(`Found: ${emptySearch.results.length} result(s)`);
93
+ console.log(`Result: ${emptySearch.results.length === 0 ? '✅' : '❌'} Correctly returned no results\n`);
94
+ // Cleanup
95
+ closeDatabase();
96
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
97
+ console.log('🎉 All smoke tests passed!');
98
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
99
+ //# sourceMappingURL=smoke-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smoke-test.js","sourceRoot":"","sources":["../src/smoke-test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ export interface LessonForReview {
2
+ id: number;
3
+ title: string;
4
+ problem: string;
5
+ root_cause: string;
6
+ lesson: string;
7
+ bad_code: string | null;
8
+ good_code: string | null;
9
+ tags: string;
10
+ language: string | null;
11
+ review_questions: string;
12
+ repetitions: number;
13
+ easiness_factor: number;
14
+ interval_days: number;
15
+ status: string;
16
+ review_count: number;
17
+ created_at: string;
18
+ }
19
+ /**
20
+ * Get all lessons that are due for review (next_review_at <= now).
21
+ */
22
+ export declare function getDueReviews(): LessonForReview[];
23
+ /**
24
+ * Update a lesson after a review session.
25
+ * Applies the SM-2 algorithm and updates the database.
26
+ */
27
+ export declare function recordReview(lessonId: number, quality: number): {
28
+ success: boolean;
29
+ nextReviewAt: Date;
30
+ newStatus: string;
31
+ intervalDays: number;
32
+ };
33
+ /**
34
+ * Get statistics about all lessons.
35
+ */
36
+ export declare function getStats(): {
37
+ total: number;
38
+ dueToday: number;
39
+ dueThisWeek: number;
40
+ byStatus: Record<string, number>;
41
+ topTags: Array<{
42
+ tag: string;
43
+ count: number;
44
+ }>;
45
+ };
46
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/spaced-repetition/scheduler.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,eAAe,EAAE,CAejD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG;IAC/D,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,IAAI,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB,CAsDA;AAED;;GAEG;AACH,wBAAgB,QAAQ,IAAI;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD,CA2CA"}