@imayuur/contexthub-core 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.
@@ -0,0 +1,21 @@
1
+ export interface ContexthubConfig {
2
+ roots?: string[];
3
+ query?: {
4
+ rrfK?: number;
5
+ };
6
+ embeddings?: {
7
+ mode?: 'local' | 'off' | 'transformers';
8
+ };
9
+ graph?: {
10
+ maxNodes?: number;
11
+ reportPath?: string;
12
+ };
13
+ watch?: {
14
+ debounceMs?: number;
15
+ maxFilesPerBatch?: number;
16
+ };
17
+ memory?: {
18
+ maxAgeDays?: number;
19
+ };
20
+ }
21
+ export declare function loadConfig(repoPath: string): ContexthubConfig;
package/dist/config.js ADDED
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loadConfig = loadConfig;
37
+ const module_1 = require("module");
38
+ const path = __importStar(require("path"));
39
+ const fs = __importStar(require("fs"));
40
+ const security_1 = require("./security");
41
+ function loadConfig(repoPath) {
42
+ const configPath = path.join(repoPath, 'contexthub.config.js');
43
+ let config = { roots: ['.'] };
44
+ if (fs.existsSync(configPath)) {
45
+ try {
46
+ // Use createRequire to safely load .js file relative to the repoPath
47
+ const requireRel = (0, module_1.createRequire)(path.join(repoPath, 'index.js'));
48
+ const loaded = requireRel('./contexthub.config.js');
49
+ if (loaded && typeof loaded === 'object') {
50
+ config = { ...config, ...loaded };
51
+ }
52
+ }
53
+ catch (e) {
54
+ console.error(`Warning: Failed to load config from ${configPath}:`, e);
55
+ }
56
+ }
57
+ // Validate numbers with validateLimit
58
+ const security = new security_1.SecurityManager(repoPath);
59
+ if (config.query && typeof config.query.rrfK === 'number') {
60
+ config.query.rrfK = security.validateLimit(config.query.rrfK, 1, 1000);
61
+ }
62
+ if (config.graph && typeof config.graph.maxNodes === 'number') {
63
+ config.graph.maxNodes = security.validateLimit(config.graph.maxNodes, 1, 100000);
64
+ }
65
+ if (config.watch && typeof config.watch.debounceMs === 'number') {
66
+ config.watch.debounceMs = security.validateLimit(config.watch.debounceMs, 100, 60000);
67
+ }
68
+ if (config.watch && typeof config.watch.maxFilesPerBatch === 'number') {
69
+ config.watch.maxFilesPerBatch = security.validateLimit(config.watch.maxFilesPerBatch, 1, 10000);
70
+ }
71
+ if (config.memory && typeof config.memory.maxAgeDays === 'number') {
72
+ config.memory.maxAgeDays = security.validateLimit(config.memory.maxAgeDays, 1, 3650);
73
+ }
74
+ return config;
75
+ }
@@ -0,0 +1,7 @@
1
+ export declare class ContexthubIgnore {
2
+ private repoPath;
3
+ private rules;
4
+ constructor(repoPath: string);
5
+ private loadIgnoreFiles;
6
+ ignores(targetPath: string): boolean;
7
+ }
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ContexthubIgnore = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ class ContexthubIgnore {
40
+ constructor(repoPath) {
41
+ this.repoPath = repoPath;
42
+ this.rules = [];
43
+ this.loadIgnoreFiles();
44
+ // Default ignores
45
+ this.rules.push(/^\.git([\\/]|$)/);
46
+ this.rules.push(/^\.contexthub([\\/]|$)/);
47
+ this.rules.push(/^node_modules([\\/]|$)/);
48
+ }
49
+ loadIgnoreFiles() {
50
+ const filesToTry = ['.contexthubignore', '.gitignore'];
51
+ for (const filename of filesToTry) {
52
+ const filePath = path.join(this.repoPath, filename);
53
+ if (fs.existsSync(filePath)) {
54
+ const content = fs.readFileSync(filePath, 'utf8');
55
+ const lines = content.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#'));
56
+ for (const line of lines) {
57
+ try {
58
+ // Simple conversion of glob to regex
59
+ let regexStr = line
60
+ .replace(/\./g, '\\.')
61
+ .replace(/\*\*/g, '.*')
62
+ .replace(/\*/g, '[^/\\\\]*')
63
+ .replace(/\?/g, '.');
64
+ if (line.startsWith('/')) {
65
+ regexStr = '^' + regexStr.substring(1);
66
+ }
67
+ else if (!line.startsWith('**') && !line.startsWith('.*')) {
68
+ // If it's a file or directory name, it could match anywhere unless specified
69
+ regexStr = '(^|[\\\\/])' + regexStr;
70
+ }
71
+ if (!line.endsWith('/') && !line.endsWith('*')) {
72
+ regexStr = regexStr + '([\\\\/]|$)';
73
+ }
74
+ this.rules.push(new RegExp(regexStr));
75
+ }
76
+ catch (e) {
77
+ // Ignore invalid regex
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+ ignores(targetPath) {
84
+ // Normalize path to relative posix path for testing
85
+ let relPath = path.relative(this.repoPath, targetPath);
86
+ // Replace windows separators with posix for consistent regex matching if needed
87
+ // but our regex handles both
88
+ for (const rule of this.rules) {
89
+ if (rule.test(relPath) || rule.test('/' + relPath)) {
90
+ return true;
91
+ }
92
+ }
93
+ return false;
94
+ }
95
+ }
96
+ exports.ContexthubIgnore = ContexthubIgnore;
@@ -0,0 +1,30 @@
1
+ import type { Session, MemoryEntry, ProjectMetadata } from '@imayuur/contexthub-shared-types';
2
+ export { SecurityManager } from './security';
3
+ export { runUnifiedQuery, UnifiedQueryResult } from './query-pipeline';
4
+ export * from './limits';
5
+ export * from './contexthub-ignore';
6
+ export { ContexthubConfig, loadConfig } from './config';
7
+ export declare class ContextHubCore {
8
+ private storage;
9
+ private repoPath;
10
+ constructor(repoPath: string);
11
+ initStorage(): Promise<void>;
12
+ createSession(agent: string, metadata?: Record<string, any>): Promise<string>;
13
+ endSession(sessionId: string): Promise<void>;
14
+ getSession(sessionId: string): Promise<Session | null>;
15
+ getSessions(limit?: number): Promise<Session[]>;
16
+ saveMemory(memory: Omit<MemoryEntry, 'id'>): Promise<string>;
17
+ getMemory(id: string): Promise<MemoryEntry | null>;
18
+ searchMemories(options: {
19
+ sessionId?: string;
20
+ type?: string;
21
+ tags?: string[];
22
+ limit?: number;
23
+ offset?: number;
24
+ }): Promise<MemoryEntry[]>;
25
+ archiveOldMemories(maxAgeDays: number): Promise<number>;
26
+ compactMemories(): Promise<number>;
27
+ saveProjectMetadata(metadata: ProjectMetadata): Promise<void>;
28
+ getProjectMetadata(): Promise<ProjectMetadata | null>;
29
+ close(): Promise<void>;
30
+ }
package/dist/index.js ADDED
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.ContextHubCore = exports.loadConfig = exports.runUnifiedQuery = exports.SecurityManager = void 0;
18
+ const memory_storage_1 = require("./memory-storage");
19
+ var security_1 = require("./security");
20
+ Object.defineProperty(exports, "SecurityManager", { enumerable: true, get: function () { return security_1.SecurityManager; } });
21
+ var query_pipeline_1 = require("./query-pipeline");
22
+ Object.defineProperty(exports, "runUnifiedQuery", { enumerable: true, get: function () { return query_pipeline_1.runUnifiedQuery; } });
23
+ __exportStar(require("./limits"), exports);
24
+ __exportStar(require("./contexthub-ignore"), exports);
25
+ var config_1 = require("./config");
26
+ Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_1.loadConfig; } });
27
+ class ContextHubCore {
28
+ constructor(repoPath) {
29
+ this.repoPath = repoPath;
30
+ this.storage = new memory_storage_1.MemoryStorage(repoPath);
31
+ }
32
+ async initStorage() {
33
+ // The storage is initialized in the constructor
34
+ // This method is for any additional initialization if needed
35
+ }
36
+ // Session management
37
+ async createSession(agent, metadata = {}) {
38
+ return this.storage.createSession(agent, metadata);
39
+ }
40
+ async endSession(sessionId) {
41
+ return this.storage.endSession(sessionId);
42
+ }
43
+ async getSession(sessionId) {
44
+ return this.storage.getSession(sessionId);
45
+ }
46
+ async getSessions(limit) {
47
+ return this.storage.getSessions(limit);
48
+ }
49
+ // Memory management
50
+ async saveMemory(memory) {
51
+ return this.storage.saveMemory(memory);
52
+ }
53
+ async getMemory(id) {
54
+ return this.storage.getMemory(id);
55
+ }
56
+ async searchMemories(options) {
57
+ return this.storage.searchMemories(options);
58
+ }
59
+ // Maintenance & Compaction
60
+ async archiveOldMemories(maxAgeDays) {
61
+ return this.storage.archiveOldMemories(maxAgeDays);
62
+ }
63
+ async compactMemories() {
64
+ return this.storage.compactMemories();
65
+ }
66
+ // Project metadata
67
+ async saveProjectMetadata(metadata) {
68
+ return this.storage.saveProjectMetadata(metadata);
69
+ }
70
+ async getProjectMetadata() {
71
+ return this.storage.getProjectMetadata();
72
+ }
73
+ // Cleanup
74
+ async close() {
75
+ await this.storage.close();
76
+ }
77
+ }
78
+ exports.ContextHubCore = ContextHubCore;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Centralized system limits and performance caps for ContextHub.
3
+ *
4
+ * These constants enforce strict boundaries on memory consumption,
5
+ * response payload sizes, and parsing limits to ensure the MCP server
6
+ * remains lightweight and stable.
7
+ */
8
+ export declare const MAX_MEMORY_CONTENT_LENGTH = 51200;
9
+ export declare const MAX_MEMORY_TAGS = 20;
10
+ export declare const MAX_RELATED_PATHS = 20;
11
+ export declare const MAX_RELATED_SYMBOLS = 20;
12
+ export declare const MAX_MEMORIES_TOTAL = 10000;
13
+ export declare const MAX_QUERY_LIMIT = 100;
14
+ export declare const DEFAULT_QUERY_LIMIT = 10;
15
+ export declare const MAX_SEARCH_CANDIDATES = 1000;
16
+ export declare const MAX_QUERY_LENGTH = 1000;
17
+ export declare const MAX_GRAPH_DISPLAY_NODES = 5000;
18
+ export declare const MAX_FILES_PER_SCAN = 1000;
19
+ export declare const MAX_INGEST_FILE_SIZE: number;
20
+ export declare const MAX_PDF_PAGES = 50;
21
+ export declare const MAX_PDF_SIZE_BYTES: number;
22
+ export declare const MAX_WATCH_FILES_PER_BATCH = 100;
23
+ export declare const DASHBOARD_MAX_RECORDS = 500;
24
+ export declare const MAX_COMMIT_HASH_LENGTH = 40;
25
+ export declare const MAX_BRANCH_LENGTH = 100;
package/dist/limits.js ADDED
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ /**
3
+ * Centralized system limits and performance caps for ContextHub.
4
+ *
5
+ * These constants enforce strict boundaries on memory consumption,
6
+ * response payload sizes, and parsing limits to ensure the MCP server
7
+ * remains lightweight and stable.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.MAX_BRANCH_LENGTH = exports.MAX_COMMIT_HASH_LENGTH = exports.DASHBOARD_MAX_RECORDS = exports.MAX_WATCH_FILES_PER_BATCH = exports.MAX_PDF_SIZE_BYTES = exports.MAX_PDF_PAGES = exports.MAX_INGEST_FILE_SIZE = exports.MAX_FILES_PER_SCAN = exports.MAX_GRAPH_DISPLAY_NODES = exports.MAX_QUERY_LENGTH = exports.MAX_SEARCH_CANDIDATES = exports.DEFAULT_QUERY_LIMIT = exports.MAX_QUERY_LIMIT = exports.MAX_MEMORIES_TOTAL = exports.MAX_RELATED_SYMBOLS = exports.MAX_RELATED_PATHS = exports.MAX_MEMORY_TAGS = exports.MAX_MEMORY_CONTENT_LENGTH = void 0;
11
+ // Memory limits
12
+ exports.MAX_MEMORY_CONTENT_LENGTH = 51200; // 50KB
13
+ exports.MAX_MEMORY_TAGS = 20;
14
+ exports.MAX_RELATED_PATHS = 20;
15
+ exports.MAX_RELATED_SYMBOLS = 20;
16
+ exports.MAX_MEMORIES_TOTAL = 10000;
17
+ // Query limits
18
+ exports.MAX_QUERY_LIMIT = 100;
19
+ exports.DEFAULT_QUERY_LIMIT = 10;
20
+ exports.MAX_SEARCH_CANDIDATES = 1000;
21
+ exports.MAX_QUERY_LENGTH = 1000;
22
+ // Graph & Parsing limits
23
+ exports.MAX_GRAPH_DISPLAY_NODES = 5000;
24
+ exports.MAX_FILES_PER_SCAN = 1000;
25
+ exports.MAX_INGEST_FILE_SIZE = 1024 * 1024; // 1MB
26
+ exports.MAX_PDF_PAGES = 50;
27
+ exports.MAX_PDF_SIZE_BYTES = 20 * 1024 * 1024; // 20MB
28
+ // Watcher limits
29
+ exports.MAX_WATCH_FILES_PER_BATCH = 100;
30
+ // Dashboard limits
31
+ exports.DASHBOARD_MAX_RECORDS = 500;
32
+ // Security limits
33
+ exports.MAX_COMMIT_HASH_LENGTH = 40;
34
+ exports.MAX_BRANCH_LENGTH = 100;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * MemoryStorage — Secure, encrypted file-based storage for ContextHub
3
+ *
4
+ * Security features:
5
+ * - AES-256-GCM encryption at rest for all JSON data files
6
+ * - Atomic writes (write to .tmp, then rename) to prevent corruption
7
+ * - In-process mutex to prevent race conditions on concurrent access
8
+ * - File size limits to prevent OOM/DoS
9
+ * - Entry count caps with automatic archival
10
+ * - Secure file permissions (0600 for files, 0700 for directories)
11
+ * - Automatic migration from plaintext to encrypted format
12
+ */
13
+ import { Session, MemoryEntry, ProjectMetadata } from '@imayuur/contexthub-shared-types';
14
+ export declare class MemoryStorage {
15
+ private repoPath;
16
+ private contexthubPath;
17
+ private sessionsPath;
18
+ private memoriesPath;
19
+ private projectMetadataPath;
20
+ private security;
21
+ private mutex;
22
+ constructor(repoPath: string);
23
+ private initFile;
24
+ /**
25
+ * Read and decrypt a JSON file from disk.
26
+ * Handles both encrypted and plaintext formats (for migration).
27
+ */
28
+ private readJSONFile;
29
+ /**
30
+ * Encrypt and write JSON data to disk using atomic write.
31
+ * Writes to a .tmp file first, then renames — prevents corruption on crash.
32
+ */
33
+ private writeJSONFileSync;
34
+ createSession(agent: string, metadata?: Record<string, any>): Promise<string>;
35
+ endSession(sessionId: string): Promise<void>;
36
+ getSession(sessionId: string): Promise<Session | null>;
37
+ getSessions(limit?: number): Promise<Session[]>;
38
+ private calculateJaccard;
39
+ saveMemory(memory: Omit<MemoryEntry, 'id'>): Promise<string>;
40
+ getMemory(id: string): Promise<MemoryEntry | null>;
41
+ searchMemories(options: {
42
+ sessionId?: string;
43
+ type?: string;
44
+ tags?: string[];
45
+ limit?: number;
46
+ offset?: number;
47
+ }): Promise<MemoryEntry[]>;
48
+ saveProjectMetadata(metadata: ProjectMetadata): Promise<void>;
49
+ getProjectMetadata(): Promise<ProjectMetadata | null>;
50
+ archiveOldMemories(maxAgeDays: number): Promise<number>;
51
+ compactMemories(): Promise<number>;
52
+ close(): Promise<void>;
53
+ }