@stackmemoryai/stackmemory 0.5.0 → 0.5.1

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,144 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { readFileSync } from "fs";
4
+ import { join, dirname } from "path";
5
+ import { fileURLToPath } from "url";
6
+ import Database from "better-sqlite3";
7
+ import { FrameManager } from "../core/context/frame-manager.js";
8
+ import { SessionManager } from "../core/session/session-manager.js";
9
+ import { pluginManager, loadPlugins } from "../plugins/loader.js";
10
+ import { logger } from "../core/monitoring/logger.js";
11
+ const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const packageJson = JSON.parse(
13
+ readFileSync(join(__dirname, "../../package.json"), "utf-8")
14
+ );
15
+ const program = new Command();
16
+ program.name("stackmemory").description("Git for AI context - persistent memory across sessions").version(packageJson.version);
17
+ program.command("init").description("Initialize StackMemory in current directory").action(async () => {
18
+ const sessionManager = SessionManager.getInstance();
19
+ await sessionManager.initialize();
20
+ const session = await sessionManager.getOrCreateSession();
21
+ console.log("\u2705 StackMemory initialized");
22
+ console.log(` Project: ${session.projectId}`);
23
+ console.log(` Session: ${session.sessionId.slice(0, 8)}`);
24
+ });
25
+ program.command("status").description("Show current context status").action(async () => {
26
+ const sessionManager = SessionManager.getInstance();
27
+ await sessionManager.initialize();
28
+ const session = await sessionManager.getOrCreateSession();
29
+ const dbPath = join(process.cwd(), ".stackmemory", "context.db");
30
+ const db = new Database(dbPath);
31
+ const frameManager = new FrameManager(db, session.projectId);
32
+ const activeFrames = frameManager.getActiveFramePath();
33
+ const depth = frameManager.getStackDepth();
34
+ console.log("\u{1F4CA} StackMemory Status:");
35
+ console.log(` Session: ${session.sessionId.slice(0, 8)}`);
36
+ console.log(` Stack depth: ${depth}`);
37
+ console.log(` Active frames: ${activeFrames.length}`);
38
+ if (activeFrames.length > 0) {
39
+ console.log("\n Current stack:");
40
+ activeFrames.slice(0, 5).forEach((frame, i) => {
41
+ console.log(` ${i + 1}. ${frame.name} [${frame.type}]`);
42
+ });
43
+ }
44
+ });
45
+ program.command("save [message]").description("Save current context with optional message").action(async (message) => {
46
+ const sessionManager = SessionManager.getInstance();
47
+ await sessionManager.initialize();
48
+ const session = await sessionManager.getOrCreateSession();
49
+ const dbPath = join(process.cwd(), ".stackmemory", "context.db");
50
+ const db = new Database(dbPath);
51
+ const frameManager = new FrameManager(db, session.projectId);
52
+ const frame = await frameManager.createFrame({
53
+ type: "context",
54
+ name: message || "Manual save",
55
+ content: message || "",
56
+ metadata: {
57
+ savedAt: (/* @__PURE__ */ new Date()).toISOString(),
58
+ manual: true
59
+ }
60
+ });
61
+ console.log("\u2705 Context saved");
62
+ console.log(` Frame ID: ${frame.frame_id.slice(0, 8)}`);
63
+ });
64
+ program.command("load [query]").description("Load context by search query").argument("[query]", "Search query for context").action(async (query) => {
65
+ const sessionManager = SessionManager.getInstance();
66
+ await sessionManager.initialize();
67
+ const dbPath = join(process.cwd(), ".stackmemory", "context.db");
68
+ const db = new Database(dbPath);
69
+ let whereClause = "";
70
+ const params = [];
71
+ if (query) {
72
+ whereClause = "WHERE name LIKE ? OR content LIKE ?";
73
+ params.push(`%${query}%`, `%${query}%`);
74
+ }
75
+ const frames = db.prepare(
76
+ `
77
+ SELECT frame_id, name, type, datetime(created_at, 'unixepoch') as created
78
+ FROM frames
79
+ ${whereClause}
80
+ ORDER BY created_at DESC
81
+ LIMIT 10
82
+ `
83
+ ).all(...params);
84
+ if (frames.length === 0) {
85
+ console.log("No matching context found");
86
+ return;
87
+ }
88
+ console.log("\u{1F4DA} Found contexts:");
89
+ frames.forEach((f, i) => {
90
+ console.log(` ${i + 1}. ${f.name} [${f.type}] - ${f.created}`);
91
+ });
92
+ });
93
+ program.command("sync").description("Sync context with remote storage").option("--push", "Push local changes to remote").option("--pull", "Pull remote changes to local").action(async (options) => {
94
+ console.log("\u{1F504} Sync functionality:");
95
+ if (options.push) {
96
+ console.log(" Pushing to remote storage...");
97
+ } else if (options.pull) {
98
+ console.log(" Pulling from remote storage...");
99
+ } else {
100
+ console.log(" Use --push or --pull to specify direction");
101
+ }
102
+ });
103
+ program.command("plugin <action> [name]").description("Manage plugins (list/enable/disable)").action(async (action, name) => {
104
+ if (action === "list") {
105
+ await loadPlugins();
106
+ const plugins = pluginManager.getAllPlugins();
107
+ console.log("\u{1F4E6} Available plugins:");
108
+ if (plugins.length === 0) {
109
+ console.log(" No plugins loaded");
110
+ console.log(
111
+ " Set ENABLE_LINEAR_PLUGIN=true to enable Linear integration"
112
+ );
113
+ } else {
114
+ plugins.forEach((p) => {
115
+ console.log(` - ${p.name} v${p.version}: ${p.description}`);
116
+ });
117
+ }
118
+ } else if (action === "enable" && name) {
119
+ console.log(`Enabling plugin: ${name}`);
120
+ } else if (action === "disable" && name) {
121
+ console.log(`Disabling plugin: ${name}`);
122
+ } else {
123
+ console.log("Usage: stackmemory plugin <list|enable|disable> [name]");
124
+ }
125
+ });
126
+ loadPlugins().then(() => {
127
+ const pluginCommands = pluginManager.getAllCommands();
128
+ for (const cmd of pluginCommands) {
129
+ const command = program.command(cmd.name).description(cmd.description).action(cmd.action);
130
+ if (cmd.options) {
131
+ for (const opt of cmd.options) {
132
+ command.option(opt.flag, opt.description, opt.defaultValue);
133
+ }
134
+ }
135
+ }
136
+ program.parse(process.argv);
137
+ }).catch((error) => {
138
+ logger.error("Failed to load plugins", error);
139
+ program.parse(process.argv);
140
+ });
141
+ if (!process.argv.slice(2).length) {
142
+ program.outputHelp();
143
+ }
144
+ //# sourceMappingURL=streamlined-cli.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/cli/streamlined-cli.ts"],
4
+ "sourcesContent": ["#!/usr/bin/env node\n/**\n * Streamlined StackMemory CLI\n * Essential commands only - focused on core functionality\n */\n\nimport { Command } from 'commander';\nimport { readFileSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport Database from 'better-sqlite3';\nimport { FrameManager } from '../core/context/frame-manager.js';\nimport { SessionManager } from '../core/session/session-manager.js';\nimport { pluginManager, loadPlugins } from '../plugins/loader.js';\nimport { logger } from '../core/monitoring/logger.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst packageJson = JSON.parse(\n readFileSync(join(__dirname, '../../package.json'), 'utf-8')\n);\n\nconst program = new Command();\n\nprogram\n .name('stackmemory')\n .description('Git for AI context - persistent memory across sessions')\n .version(packageJson.version);\n\n// 1. INIT - Setup StackMemory in a project\nprogram\n .command('init')\n .description('Initialize StackMemory in current directory')\n .action(async () => {\n const sessionManager = SessionManager.getInstance();\n await sessionManager.initialize();\n const session = await sessionManager.getOrCreateSession();\n console.log('\u2705 StackMemory initialized');\n console.log(` Project: ${session.projectId}`);\n console.log(` Session: ${session.sessionId.slice(0, 8)}`);\n });\n\n// 2. STATUS - Check current status\nprogram\n .command('status')\n .description('Show current context status')\n .action(async () => {\n const sessionManager = SessionManager.getInstance();\n await sessionManager.initialize();\n const session = await sessionManager.getOrCreateSession();\n\n const dbPath = join(process.cwd(), '.stackmemory', 'context.db');\n const db = new Database(dbPath);\n const frameManager = new FrameManager(db, session.projectId);\n\n const activeFrames = frameManager.getActiveFramePath();\n const depth = frameManager.getStackDepth();\n\n console.log('\uD83D\uDCCA StackMemory Status:');\n console.log(` Session: ${session.sessionId.slice(0, 8)}`);\n console.log(` Stack depth: ${depth}`);\n console.log(` Active frames: ${activeFrames.length}`);\n\n if (activeFrames.length > 0) {\n console.log('\\n Current stack:');\n activeFrames.slice(0, 5).forEach((frame, i) => {\n console.log(` ${i + 1}. ${frame.name} [${frame.type}]`);\n });\n }\n });\n\n// 3. SAVE - Save current context\nprogram\n .command('save [message]')\n .description('Save current context with optional message')\n .action(async (message) => {\n const sessionManager = SessionManager.getInstance();\n await sessionManager.initialize();\n const session = await sessionManager.getOrCreateSession();\n\n const dbPath = join(process.cwd(), '.stackmemory', 'context.db');\n const db = new Database(dbPath);\n const frameManager = new FrameManager(db, session.projectId);\n\n const frame = await frameManager.createFrame({\n type: 'context',\n name: message || 'Manual save',\n content: message || '',\n metadata: {\n savedAt: new Date().toISOString(),\n manual: true,\n },\n });\n\n console.log('\u2705 Context saved');\n console.log(` Frame ID: ${frame.frame_id.slice(0, 8)}`);\n });\n\n// 4. LOAD - Load context by query\nprogram\n .command('load [query]')\n .description('Load context by search query')\n .argument('[query]', 'Search query for context')\n .action(async (query) => {\n const sessionManager = SessionManager.getInstance();\n await sessionManager.initialize();\n\n const dbPath = join(process.cwd(), '.stackmemory', 'context.db');\n const db = new Database(dbPath);\n\n let whereClause = '';\n const params: any[] = [];\n\n if (query) {\n whereClause = 'WHERE name LIKE ? OR content LIKE ?';\n params.push(`%${query}%`, `%${query}%`);\n }\n\n const frames = db\n .prepare(\n `\n SELECT frame_id, name, type, datetime(created_at, 'unixepoch') as created\n FROM frames\n ${whereClause}\n ORDER BY created_at DESC\n LIMIT 10\n `\n )\n .all(...params);\n\n if (frames.length === 0) {\n console.log('No matching context found');\n return;\n }\n\n console.log('\uD83D\uDCDA Found contexts:');\n frames.forEach((f: any, i: number) => {\n console.log(` ${i + 1}. ${f.name} [${f.type}] - ${f.created}`);\n });\n });\n\n// 5. SYNC - Sync with remote storage (if configured)\nprogram\n .command('sync')\n .description('Sync context with remote storage')\n .option('--push', 'Push local changes to remote')\n .option('--pull', 'Pull remote changes to local')\n .action(async (options) => {\n // This would integrate with the simplified storage\n console.log('\uD83D\uDD04 Sync functionality:');\n if (options.push) {\n console.log(' Pushing to remote storage...');\n // Implement push to S3/compatible storage\n } else if (options.pull) {\n console.log(' Pulling from remote storage...');\n // Implement pull from S3/compatible storage\n } else {\n console.log(' Use --push or --pull to specify direction');\n }\n });\n\n// 6. PLUGIN - Manage plugins\nprogram\n .command('plugin <action> [name]')\n .description('Manage plugins (list/enable/disable)')\n .action(async (action, name) => {\n if (action === 'list') {\n await loadPlugins();\n const plugins = pluginManager.getAllPlugins();\n\n console.log('\uD83D\uDCE6 Available plugins:');\n if (plugins.length === 0) {\n console.log(' No plugins loaded');\n console.log(\n ' Set ENABLE_LINEAR_PLUGIN=true to enable Linear integration'\n );\n } else {\n plugins.forEach((p) => {\n console.log(` - ${p.name} v${p.version}: ${p.description}`);\n });\n }\n } else if (action === 'enable' && name) {\n console.log(`Enabling plugin: ${name}`);\n // Would update plugins.json\n } else if (action === 'disable' && name) {\n console.log(`Disabling plugin: ${name}`);\n // Would update plugins.json\n } else {\n console.log('Usage: stackmemory plugin <list|enable|disable> [name]');\n }\n });\n\n// Load plugin commands dynamically\nloadPlugins()\n .then(() => {\n const pluginCommands = pluginManager.getAllCommands();\n\n // Register each plugin command\n for (const cmd of pluginCommands) {\n const command = program\n .command(cmd.name)\n .description(cmd.description)\n .action(cmd.action);\n\n // Add options if specified\n if (cmd.options) {\n for (const opt of cmd.options) {\n command.option(opt.flag, opt.description, opt.defaultValue);\n }\n }\n }\n\n // Parse arguments after plugins are loaded\n program.parse(process.argv);\n })\n .catch((error) => {\n logger.error('Failed to load plugins', error);\n program.parse(process.argv);\n });\n\n// Show help if no command provided\nif (!process.argv.slice(2).length) {\n program.outputHelp();\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,110 @@
1
+ class SimpleEventBus {
2
+ handlers = /* @__PURE__ */ new Map();
3
+ eventHistory = [];
4
+ maxHistorySize = 1e3;
5
+ subscriptionCounter = 0;
6
+ emit(event) {
7
+ const fullEvent = {
8
+ ...event,
9
+ id: this.generateEventId(),
10
+ timestamp: Date.now()
11
+ };
12
+ this.eventHistory.push(fullEvent);
13
+ if (this.eventHistory.length > this.maxHistorySize) {
14
+ this.eventHistory.shift();
15
+ }
16
+ const handlers = this.handlers.get(event.type);
17
+ if (handlers) {
18
+ const handlersCopy = Array.from(handlers);
19
+ for (const handler of handlersCopy) {
20
+ try {
21
+ const result = handler(fullEvent);
22
+ if (result instanceof Promise) {
23
+ result.catch((error) => {
24
+ console.error(`Event handler error for ${event.type}:`, error);
25
+ });
26
+ }
27
+ } catch (error) {
28
+ console.error(`Event handler error for ${event.type}:`, error);
29
+ }
30
+ }
31
+ }
32
+ }
33
+ on(eventType, handler) {
34
+ if (!this.handlers.has(eventType)) {
35
+ this.handlers.set(eventType, /* @__PURE__ */ new Set());
36
+ }
37
+ this.handlers.get(eventType).add(handler);
38
+ const subscriptionId = `sub-${++this.subscriptionCounter}`;
39
+ return {
40
+ id: subscriptionId,
41
+ unsubscribe: () => this.off(eventType, handler)
42
+ };
43
+ }
44
+ once(eventType, handler) {
45
+ const wrapper = (event) => {
46
+ handler(event);
47
+ this.off(eventType, wrapper);
48
+ };
49
+ return this.on(eventType, wrapper);
50
+ }
51
+ off(eventType, handler) {
52
+ this.handlers.get(eventType)?.delete(handler);
53
+ }
54
+ offAll(eventType) {
55
+ if (eventType) {
56
+ this.handlers.delete(eventType);
57
+ } else {
58
+ this.handlers.clear();
59
+ }
60
+ }
61
+ async waitFor(eventType, timeout = 3e4) {
62
+ return new Promise((resolve, reject) => {
63
+ const timer = setTimeout(() => {
64
+ reject(new Error(`Timeout waiting for event: ${eventType}`));
65
+ }, timeout);
66
+ const subscription = this.once(eventType, (event) => {
67
+ clearTimeout(timer);
68
+ resolve(event);
69
+ });
70
+ });
71
+ }
72
+ generateEventId() {
73
+ return `evt-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
74
+ }
75
+ getHistory(eventType, limit = 100) {
76
+ let events = this.eventHistory;
77
+ if (eventType) {
78
+ events = events.filter((e) => e.type === eventType);
79
+ }
80
+ return events.slice(-limit);
81
+ }
82
+ }
83
+ const globalEventBus = new SimpleEventBus();
84
+ const EventTypes = {
85
+ // Ralph Loop events
86
+ RALPH_ITERATION_START: "ralph.iteration.start",
87
+ RALPH_ITERATION_COMPLETE: "ralph.iteration.complete",
88
+ RALPH_ITERATION_FAILED: "ralph.iteration.failed",
89
+ RALPH_LOOP_COMPLETE: "ralph.loop.complete",
90
+ // Swarm events
91
+ SWARM_AGENT_STARTED: "swarm.agent.started",
92
+ SWARM_AGENT_STOPPED: "swarm.agent.stopped",
93
+ SWARM_TASK_ASSIGNED: "swarm.task.assigned",
94
+ SWARM_TASK_COMPLETE: "swarm.task.complete",
95
+ SWARM_CONFLICT_DETECTED: "swarm.conflict.detected",
96
+ // Context events
97
+ CONTEXT_SAVED: "context.saved",
98
+ CONTEXT_LOADED: "context.loaded",
99
+ CONTEXT_SYNCED: "context.synced",
100
+ // Git events
101
+ GIT_COMMIT_CREATED: "git.commit.created",
102
+ GIT_BRANCH_CREATED: "git.branch.created",
103
+ GIT_MERGE_COMPLETE: "git.merge.complete"
104
+ };
105
+ export {
106
+ EventTypes,
107
+ SimpleEventBus,
108
+ globalEventBus
109
+ };
110
+ //# sourceMappingURL=event-bus.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/events/event-bus.ts"],
4
+ "sourcesContent": ["/**\n * Event Bus - Core event-driven communication system\n * Decouples components through publish/subscribe pattern\n */\n\nexport interface Event {\n id: string;\n type: string;\n timestamp: number;\n source: string;\n data: Record<string, any>;\n metadata?: Record<string, any>;\n}\n\nexport type EventHandler = (event: Event) => void | Promise<void>;\n\nexport interface EventSubscription {\n id: string;\n unsubscribe(): void;\n}\n\nexport interface EventBus {\n emit(event: Omit<Event, 'id' | 'timestamp'>): void;\n on(eventType: string, handler: EventHandler): EventSubscription;\n once(eventType: string, handler: EventHandler): EventSubscription;\n off(eventType: string, handler: EventHandler): void;\n offAll(eventType?: string): void;\n waitFor(eventType: string, timeout?: number): Promise<Event>;\n}\n\nexport class SimpleEventBus implements EventBus {\n private handlers = new Map<string, Set<EventHandler>>();\n private eventHistory: Event[] = [];\n private maxHistorySize = 1000;\n private subscriptionCounter = 0;\n\n emit(event: Omit<Event, 'id' | 'timestamp'>): void {\n const fullEvent: Event = {\n ...event,\n id: this.generateEventId(),\n timestamp: Date.now()\n };\n\n // Store in history\n this.eventHistory.push(fullEvent);\n if (this.eventHistory.length > this.maxHistorySize) {\n this.eventHistory.shift();\n }\n\n // Notify handlers\n const handlers = this.handlers.get(event.type);\n if (handlers) {\n // Clone to avoid modification during iteration\n const handlersCopy = Array.from(handlers);\n for (const handler of handlersCopy) {\n try {\n const result = handler(fullEvent);\n if (result instanceof Promise) {\n result.catch(error => {\n console.error(`Event handler error for ${event.type}:`, error);\n });\n }\n } catch (error) {\n console.error(`Event handler error for ${event.type}:`, error);\n }\n }\n }\n }\n\n on(eventType: string, handler: EventHandler): EventSubscription {\n if (!this.handlers.has(eventType)) {\n this.handlers.set(eventType, new Set());\n }\n this.handlers.get(eventType)!.add(handler);\n\n const subscriptionId = `sub-${++this.subscriptionCounter}`;\n return {\n id: subscriptionId,\n unsubscribe: () => this.off(eventType, handler)\n };\n }\n\n once(eventType: string, handler: EventHandler): EventSubscription {\n const wrapper: EventHandler = (event) => {\n handler(event);\n this.off(eventType, wrapper);\n };\n return this.on(eventType, wrapper);\n }\n\n off(eventType: string, handler: EventHandler): void {\n this.handlers.get(eventType)?.delete(handler);\n }\n\n offAll(eventType?: string): void {\n if (eventType) {\n this.handlers.delete(eventType);\n } else {\n this.handlers.clear();\n }\n }\n\n async waitFor(eventType: string, timeout = 30000): Promise<Event> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(`Timeout waiting for event: ${eventType}`));\n }, timeout);\n\n const subscription = this.once(eventType, (event) => {\n clearTimeout(timer);\n resolve(event);\n });\n });\n }\n\n private generateEventId(): string {\n return `evt-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }\n\n getHistory(eventType?: string, limit = 100): Event[] {\n let events = this.eventHistory;\n if (eventType) {\n events = events.filter(e => e.type === eventType);\n }\n return events.slice(-limit);\n }\n}\n\n// Global event bus instance\nexport const globalEventBus = new SimpleEventBus();\n\n// Event types\nexport const EventTypes = {\n // Ralph Loop events\n RALPH_ITERATION_START: 'ralph.iteration.start',\n RALPH_ITERATION_COMPLETE: 'ralph.iteration.complete',\n RALPH_ITERATION_FAILED: 'ralph.iteration.failed',\n RALPH_LOOP_COMPLETE: 'ralph.loop.complete',\n\n // Swarm events\n SWARM_AGENT_STARTED: 'swarm.agent.started',\n SWARM_AGENT_STOPPED: 'swarm.agent.stopped',\n SWARM_TASK_ASSIGNED: 'swarm.task.assigned',\n SWARM_TASK_COMPLETE: 'swarm.task.complete',\n SWARM_CONFLICT_DETECTED: 'swarm.conflict.detected',\n\n // Context events\n CONTEXT_SAVED: 'context.saved',\n CONTEXT_LOADED: 'context.loaded',\n CONTEXT_SYNCED: 'context.synced',\n\n // Git events\n GIT_COMMIT_CREATED: 'git.commit.created',\n GIT_BRANCH_CREATED: 'git.branch.created',\n GIT_MERGE_COMPLETE: 'git.merge.complete'\n} as const;"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,87 @@
1
+ class BasePlugin {
2
+ dependencies;
3
+ context;
4
+ status = { state: "uninitialized" };
5
+ metrics = {
6
+ eventsEmitted: 0,
7
+ eventsReceived: 0,
8
+ errors: 0
9
+ };
10
+ startTime;
11
+ async initialize(context) {
12
+ this.context = context;
13
+ this.status = { state: "initialized" };
14
+ await this.onInitialize();
15
+ }
16
+ async start() {
17
+ this.status = { state: "starting" };
18
+ this.startTime = Date.now();
19
+ await this.onStart();
20
+ this.status = { state: "running", lastActivity: Date.now() };
21
+ }
22
+ async stop() {
23
+ this.status = { state: "stopping" };
24
+ await this.onStop();
25
+ this.status = { state: "stopped" };
26
+ }
27
+ async shutdown() {
28
+ if (this.status.state === "running") {
29
+ await this.stop();
30
+ }
31
+ await this.onShutdown();
32
+ this.status = { state: "uninitialized" };
33
+ }
34
+ getStatus() {
35
+ return { ...this.status };
36
+ }
37
+ getMetrics() {
38
+ return {
39
+ ...this.metrics,
40
+ startTime: this.startTime,
41
+ uptime: this.startTime ? Date.now() - this.startTime : 0
42
+ };
43
+ }
44
+ validateConfig(config) {
45
+ const errors = [];
46
+ const warnings = [];
47
+ if (config.name !== this.name) {
48
+ errors.push(`Config name '${config.name}' doesn't match plugin name '${this.name}'`);
49
+ }
50
+ const customValidation = this.onValidateConfig(config);
51
+ if (customValidation.errors) errors.push(...customValidation.errors);
52
+ if (customValidation.warnings) warnings.push(...customValidation.warnings);
53
+ return {
54
+ valid: errors.length === 0,
55
+ errors: errors.length > 0 ? errors : void 0,
56
+ warnings: warnings.length > 0 ? warnings : void 0
57
+ };
58
+ }
59
+ emit(eventType, data) {
60
+ if (!this.context) throw new Error("Plugin not initialized");
61
+ this.context.eventBus.emit({
62
+ type: eventType,
63
+ source: this.name,
64
+ data
65
+ });
66
+ this.metrics.eventsEmitted++;
67
+ this.status.lastActivity = Date.now();
68
+ }
69
+ on(eventType, handler) {
70
+ if (!this.context) throw new Error("Plugin not initialized");
71
+ this.context.eventBus.on(eventType, async (event) => {
72
+ this.metrics.eventsReceived++;
73
+ this.status.lastActivity = Date.now();
74
+ try {
75
+ await handler(event);
76
+ } catch (error) {
77
+ this.metrics.errors++;
78
+ this.status.error = error;
79
+ console.error(`Plugin ${this.name} error handling ${eventType}:`, error);
80
+ }
81
+ });
82
+ }
83
+ }
84
+ export {
85
+ BasePlugin
86
+ };
87
+ //# sourceMappingURL=plugin-interface.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/plugins/plugin-interface.ts"],
4
+ "sourcesContent": ["/**\n * Plugin Interface - Defines contract for all StackMemory plugins\n * Enables modular architecture with clear boundaries\n */\n\nimport { EventBus } from '../events/event-bus.js';\n\nexport interface PluginConfig {\n name: string;\n version: string;\n enabled: boolean;\n options?: Record<string, any>;\n}\n\nexport interface PluginContext {\n eventBus: EventBus;\n config: PluginConfig;\n dataDir: string;\n getRepository<T>(name: string): T;\n registerRepository(name: string, repository: any): void;\n}\n\nexport interface Plugin {\n readonly name: string;\n readonly version: string;\n readonly description: string;\n readonly dependencies?: string[];\n\n initialize(context: PluginContext): Promise<void>;\n start(): Promise<void>;\n stop(): Promise<void>;\n shutdown(): Promise<void>;\n \n getStatus(): PluginStatus;\n getMetrics(): PluginMetrics;\n validateConfig(config: PluginConfig): ValidationResult;\n}\n\nexport interface PluginStatus {\n state: 'uninitialized' | 'initialized' | 'starting' | 'running' | 'stopping' | 'stopped' | 'error';\n message?: string;\n lastActivity?: number;\n error?: Error;\n}\n\nexport interface PluginMetrics {\n startTime?: number;\n uptime?: number;\n eventsEmitted: number;\n eventsReceived: number;\n errors: number;\n custom?: Record<string, number>;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors?: string[];\n warnings?: string[];\n}\n\nexport abstract class BasePlugin implements Plugin {\n abstract readonly name: string;\n abstract readonly version: string;\n abstract readonly description: string;\n readonly dependencies?: string[];\n\n protected context?: PluginContext;\n protected status: PluginStatus = { state: 'uninitialized' };\n protected metrics: PluginMetrics = {\n eventsEmitted: 0,\n eventsReceived: 0,\n errors: 0\n };\n protected startTime?: number;\n\n async initialize(context: PluginContext): Promise<void> {\n this.context = context;\n this.status = { state: 'initialized' };\n await this.onInitialize();\n }\n\n async start(): Promise<void> {\n this.status = { state: 'starting' };\n this.startTime = Date.now();\n await this.onStart();\n this.status = { state: 'running', lastActivity: Date.now() };\n }\n\n async stop(): Promise<void> {\n this.status = { state: 'stopping' };\n await this.onStop();\n this.status = { state: 'stopped' };\n }\n\n async shutdown(): Promise<void> {\n if (this.status.state === 'running') {\n await this.stop();\n }\n await this.onShutdown();\n this.status = { state: 'uninitialized' };\n }\n\n getStatus(): PluginStatus {\n return { ...this.status };\n }\n\n getMetrics(): PluginMetrics {\n return {\n ...this.metrics,\n startTime: this.startTime,\n uptime: this.startTime ? Date.now() - this.startTime : 0\n };\n }\n\n validateConfig(config: PluginConfig): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n if (config.name !== this.name) {\n errors.push(`Config name '${config.name}' doesn't match plugin name '${this.name}'`);\n }\n\n const customValidation = this.onValidateConfig(config);\n if (customValidation.errors) errors.push(...customValidation.errors);\n if (customValidation.warnings) warnings.push(...customValidation.warnings);\n\n return {\n valid: errors.length === 0,\n errors: errors.length > 0 ? errors : undefined,\n warnings: warnings.length > 0 ? warnings : undefined\n };\n }\n\n protected emit(eventType: string, data: Record<string, any>): void {\n if (!this.context) throw new Error('Plugin not initialized');\n \n this.context.eventBus.emit({\n type: eventType,\n source: this.name,\n data\n });\n this.metrics.eventsEmitted++;\n this.status.lastActivity = Date.now();\n }\n\n protected on(eventType: string, handler: (event: any) => void | Promise<void>): void {\n if (!this.context) throw new Error('Plugin not initialized');\n \n this.context.eventBus.on(eventType, async (event) => {\n this.metrics.eventsReceived++;\n this.status.lastActivity = Date.now();\n try {\n await handler(event);\n } catch (error) {\n this.metrics.errors++;\n this.status.error = error as Error;\n console.error(`Plugin ${this.name} error handling ${eventType}:`, error);\n }\n });\n }\n\n // Hooks for subclasses\n protected abstract onInitialize(): Promise<void>;\n protected abstract onStart(): Promise<void>;\n protected abstract onStop(): Promise<void>;\n protected abstract onShutdown(): Promise<void>;\n protected abstract onValidateConfig(config: PluginConfig): ValidationResult;\n}"],
5
+ "mappings": "AA4DO,MAAe,WAA6B;AAAA,EAIxC;AAAA,EAEC;AAAA,EACA,SAAuB,EAAE,OAAO,gBAAgB;AAAA,EAChD,UAAyB;AAAA,IACjC,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,QAAQ;AAAA,EACV;AAAA,EACU;AAAA,EAEV,MAAM,WAAW,SAAuC;AACtD,SAAK,UAAU;AACf,SAAK,SAAS,EAAE,OAAO,cAAc;AACrC,UAAM,KAAK,aAAa;AAAA,EAC1B;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,SAAS,EAAE,OAAO,WAAW;AAClC,SAAK,YAAY,KAAK,IAAI;AAC1B,UAAM,KAAK,QAAQ;AACnB,SAAK,SAAS,EAAE,OAAO,WAAW,cAAc,KAAK,IAAI,EAAE;AAAA,EAC7D;AAAA,EAEA,MAAM,OAAsB;AAC1B,SAAK,SAAS,EAAE,OAAO,WAAW;AAClC,UAAM,KAAK,OAAO;AAClB,SAAK,SAAS,EAAE,OAAO,UAAU;AAAA,EACnC;AAAA,EAEA,MAAM,WAA0B;AAC9B,QAAI,KAAK,OAAO,UAAU,WAAW;AACnC,YAAM,KAAK,KAAK;AAAA,IAClB;AACA,UAAM,KAAK,WAAW;AACtB,SAAK,SAAS,EAAE,OAAO,gBAAgB;AAAA,EACzC;AAAA,EAEA,YAA0B;AACxB,WAAO,EAAE,GAAG,KAAK,OAAO;AAAA,EAC1B;AAAA,EAEA,aAA4B;AAC1B,WAAO;AAAA,MACL,GAAG,KAAK;AAAA,MACR,WAAW,KAAK;AAAA,MAChB,QAAQ,KAAK,YAAY,KAAK,IAAI,IAAI,KAAK,YAAY;AAAA,IACzD;AAAA,EACF;AAAA,EAEA,eAAe,QAAwC;AACrD,UAAM,SAAmB,CAAC;AAC1B,UAAM,WAAqB,CAAC;AAE5B,QAAI,OAAO,SAAS,KAAK,MAAM;AAC7B,aAAO,KAAK,gBAAgB,OAAO,IAAI,gCAAgC,KAAK,IAAI,GAAG;AAAA,IACrF;AAEA,UAAM,mBAAmB,KAAK,iBAAiB,MAAM;AACrD,QAAI,iBAAiB,OAAQ,QAAO,KAAK,GAAG,iBAAiB,MAAM;AACnE,QAAI,iBAAiB,SAAU,UAAS,KAAK,GAAG,iBAAiB,QAAQ;AAEzE,WAAO;AAAA,MACL,OAAO,OAAO,WAAW;AAAA,MACzB,QAAQ,OAAO,SAAS,IAAI,SAAS;AAAA,MACrC,UAAU,SAAS,SAAS,IAAI,WAAW;AAAA,IAC7C;AAAA,EACF;AAAA,EAEU,KAAK,WAAmB,MAAiC;AACjE,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,wBAAwB;AAE3D,SAAK,QAAQ,SAAS,KAAK;AAAA,MACzB,MAAM;AAAA,MACN,QAAQ,KAAK;AAAA,MACb;AAAA,IACF,CAAC;AACD,SAAK,QAAQ;AACb,SAAK,OAAO,eAAe,KAAK,IAAI;AAAA,EACtC;AAAA,EAEU,GAAG,WAAmB,SAAqD;AACnF,QAAI,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,wBAAwB;AAE3D,SAAK,QAAQ,SAAS,GAAG,WAAW,OAAO,UAAU;AACnD,WAAK,QAAQ;AACb,WAAK,OAAO,eAAe,KAAK,IAAI;AACpC,UAAI;AACF,cAAM,QAAQ,KAAK;AAAA,MACrB,SAAS,OAAO;AACd,aAAK,QAAQ;AACb,aAAK,OAAO,QAAQ;AACpB,gBAAQ,MAAM,UAAU,KAAK,IAAI,mBAAmB,SAAS,KAAK,KAAK;AAAA,MACzE;AAAA,IACF,CAAC;AAAA,EACH;AAQF;",
6
+ "names": []
7
+ }